2023年12月31日日曜日

MT6701のI2C基板をSSIで使ってみた。

 先日MT6701のI2C版基板をArduinoで動作確認が取れたのでSPIライクなSSI接続でも使えるように改造してみた。I2C接続だと双方向通信だけども、SSIだと受信のみしかできないのでパラメータの書き換えとかはできないけど高速に通信できるらしい。

MT6701

まずはMT6701を剥がしてみた。どうやら8番ピンがVCCに直結になっているのでこれをCSとして出さないといけない。PWM/Analog出力は使う予定がないので、ICの下のパターンをカットしてPWM/AnalogのところにCSを出してみた。
画像でいうと赤い線を2箇所カットしてMT6701を実装したところで8番Pinを真ん中の端子にジャンパした感じ。

MT6701

これでI2CとSSIが使える基板となった。CS(改造後の真ん中のPin)をVCCに接続して、SCLとSDAをプルアップすればI2Cとして使えるし(Internal Pullupでもいいし)、SSIとして使う場合はCSとMISO(SDAのピン)とCLK(SCLのピン)の3本をマイコンに接続すれば良い。

#include <SPI.h>

#define CS_PIN PA15

SPIClass SPI_1(PB5, PB4, PB3);  //MOSI=PB5, MISO=PB4, SCK=PB3

void setup() {
  Serial.begin(115200);
  SPI_1.begin();

  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH);
  Serial.println("MT6701 SSI Angle Reader Start");
}

void loop() {
  digitalWrite(CS_PIN, LOW);
  SPI_1.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE1));
  delayMicroseconds(1);
  uint16_t raw = SPI_1.transfer16(0xFFFF);//ダミーデータ送信
  SPI_1.endTransaction();
  digitalWrite(CS_PIN, HIGH);

  uint16_t angle = raw >> 2;
  float degrees = (angle * 360.0) / 16384.0;
  uint8_t mg = raw & 0b11;

  Serial.print("Raw: ");
  Serial.print(angle);
  Serial.print(", Degrees: ");
  Serial.print(degrees, 2);
  Serial.print(", Status: ");
  switch (mg) {
    case 0b00: Serial.println("OK"); break;
    case 0b01: Serial.println("Too Strong"); break;
    case 0b10: Serial.println("Too Weak"); break;
    case 0b11: Serial.println("Error"); break;
  }

  delay(200);
}

こんな感じでArduno上のBluepillのSPI1のAlternativeピンを使用して読み込んでみた。とりあえずI2C同様問題なく読み込めた。SimpleFOCとかでも使えるようなのでこれならモータ制御で問題なく使えるのかなぁ。

DOに10kのプルアップがあるとより安定するとかなんとか。

SSIだとMT6701には書き込みができないので同じピン配置でなにかデータを書き込んだり設定変更する場合はCSをHighにした状態でInternal Pullupを使ってソフトウェアI2Cを使えば書き込めるかも?

2023年12月30日土曜日

MT6701のI2C版をArduinoで使ってみた。

 モータの回転制御をやってみるのに磁気式エンコーダを購入してみた。Arduinoで使うならAS5600がお手軽で良さそうだったんだけど、MT6701というやつが14bitでいろんなインターフェースに対応しているようだったので購入してみた。

MT6701

小さい磁石も付属していた。

MT6701

今回はI2C版が安かったのでSPI使いたかったら後で改造すればいいかということでI2C版にしてみた。
MT6701のインターフェースとしてはI2C、SSI、アナログ、PWM、ABZに対応してる。アナログとPWMの切り替えはI2Cでレジスタの書き込みを行わないといけないみたい。今回のモジュールはPWM/アナログとI2Cに対応するような配線になっていた。

とりあえずBluepillを使ってI2Cで動作確認してみた。I2CのスレーブIDは0x06。

#include <Wire.h>

#define MT6701_ADDR 0x06  // MT6701のI2Cアドレス

void setup() {
  Serial.begin(115200);
  Wire.begin();  //SDA=PB7, SCL=PB6
  Serial.println("MT6701 Angle Reader Start");
}

void loop() {
  Wire.beginTransmission(MT6701_ADDR);
  Wire.write(0x03);  //MT6701の角度データ上位バイト
  if (Wire.endTransmission(false) != 0) {
    Serial.println("I2C Write Error");
  } else {
    Wire.requestFrom(MT6701_ADDR, 2);  //2バイト読み込み
    if (Wire.available() >= 2) {
      uint8_t high = Wire.read();
      uint8_t low = Wire.read();
      uint16_t angle = ((high & 0x3F) << 8) | low;  //14bit角度データ
      float degrees = (angle / 16384.0) * 360.0;    //degに変換
      uint8_t mg = (high >> 4) & 0x0F; //ステータス
      Serial.print("Raw: ");
      Serial.print(angle);
      Serial.print(", Degrees: ");
      Serial.print(degrees, 2);
      Serial.print(", Status: ");
      Serial.println(mg,BIN);
    } else {
      Serial.println("I2C Read Error");
    }
  }
  delay(200);
}

これでI2CでPB7(SDA)とPB6(SCL)にセンサーを接続すると値が出る用になった。
Statusにはマグネットが近すぎるかとか遠すぎるの他に、プッシュボタン機能や検知できないエラー情報とかも含まれている。
先頭の2bitは磁力の状態。(0→ノーマル、1→近すぎる、10→遠すぎる)

例えば0010だったらプッシュボタンが押されたとか、1だったら磁石が強すぎるとか。詳しくはデータシートのmgの所を参照するといいかも。

SSIモードはSPIライクな受信オンリーモードみたいなのでマイコンのSPIポートを使用して高速に使えるみたい。高回転なモータとかだとSSIモードのほうが良いかもしれないので次回は基板を改造してマイコンのSPIポートで試してみようかと思う。

この基板は8番PinがVCCに直結されてしまっているのでこれをCSとして取り出せば行けそうな感じ。SPIはActive Lowなのでうまく作られている気はする。ちなみに8番PinはプルアップされているのでCSに何も繋がなければI2Cとして動かせそう。6Pinだしてそういう基板にしておいてほしかったな。

データシートによると6VのTVSを電源ラインに追加しておくと良いみたいだけど、こういったモジュールにこそ追加しておかないとな…

2023年12月26日火曜日

ダイソーのUSB Type Cケーブルを買ってみた。

 久しぶりにダイソーに行ったらUSB Type Cケーブルが売っていたので試しに買ってみた。

何種類かあったんだけど100円の5V3Aタイプにしてみた。
セリアで前に60Wタイプを購入したときはデータ転送ができなかったんだけど、これは5V3Aのかわりにデータ転送ができる感じかな。300円商品になるとどっちもできるのが売っていたけど、おそらくケーブルの太さ=銅の量で電流と値段が決まっていそう。
青とシルバーの在庫があった。ケーブルはツルツルしていて少し硬い感じ。やすいケーブルってこんな感じだよね。

そんでもって試しに60WなPD充電器でノートパソコンに繋いでみたら(良い子は真似しないほうがいいかも)ちゃんと充電できた。ってことはこのケーブルの中身は6本でUSB2.0が4本+CC1とCC2で2本結線されてそう。前に買った給電専用のセリアのケーブルは電源2本とCC1とCC2が結線されている感じだったので、おそらく通信用のケーブルが結線されている分電流控えめって感じなのかな。

コネクタ自体はカチッと挿さるのでとりあえずはUSB2.0デバイス用としてはまずまずかも。


2023年12月9日土曜日

PJ-1103Aをtinytuyaで使ってみた。

 前回登録したクランプ式電力計、PJ-1103AのデータをPythonから取ってみることに。tuyaのSmart Homeだけだとなんか使いにくいらしいし…

今回はThonnyを使ってPythonでtinytuyaライブラリを使用してワットチェッカーのデータを吸い出してみた。

まずはThonnyのシステムシェルからtinytuyaをインストール。Windowsのファイヤーウォールを一旦切った状態で

python -m tinytuya scan

をしてみる。するとデバイスが表示された。しかしLocal keyがないためデータが取得できない。iot.tuya.comで開発用アカウントを作ってやる必要があるっぽい。

登録したらCloudのCloud ServicesからIoT Coreを有効にして、CloudのDevelopmentで適当なプロジェクトを作成する。このときSmart Homeアプリのアカウントが日本になっている場合は"Western America Data Center"を選択する。

プロジェクトを開いたら右上のGuide ModeのCloseをクリックしてDevicesのLink App AccountでSmart HomeアカウントとQRコードでリンクさせる。これでデバイスのリストにWIFI dual meterが出てくるはず。

あとはpythonのシステムシェルに戻って

python -m tinytuya wizard

を実行して、先程のプロジェクトのAuthorization KeyとPJ-1103AのDevice IDをコピペ。サーバーはusと入力すればOK。そうするとJSON方式で色々表示される中、keyがゲットできるはず。ちなみにproduct_idはbbcg1hrkrj5rifsdで、modelはPJ1103Bになっていた。

あとはこれでPythonからデータが取れるようになった。

DPSとデータの対比表はこんな感じらしい。

これをベースに簡単なPythonツールを作成してみた。


# -*- coding:utf-8 -*-
import tinytuya
import tkinter
import time
import threading

# 更新する間隔
INTERVAL = 3

d = tinytuya.Device('Device ID', '192.168.x.xxx', 'Local Key', version=3.4)
def poll(): while 1: data = d.status() #print('Device status: %r' % data) print(data["dps"]['112']/10) label.config(text="電圧:" + str(data["dps"]['112']/10) + "V") label1.config(text="周波数:" + str(data["dps"]['111']/100) + "Hz") label2.config(text="電流:" + str(data["dps"]['113']/1000 + data["dps"]['114']/1000) + "A") label3.config(text="(" + str(data["dps"]['113']/1000) + "A+" + str(data["dps"]['114']/1000) + "A)") label4.config(text="電力:" + str(data["dps"]['115']/10) + "W") time.sleep(INTERVAL) def start(): global app th = threading.Thread(target=poll)# スレッドインスタンス生成 th.start()# スレッドスタート # メインウィンドウ作成 app = tkinter.Tk() app.title("電力") app.geometry("500x500") label = tkinter.Label( app, text="0.00", width=12, font=("", 50, "bold"), ) label.pack(padx=10) label1 = tkinter.Label( app, text="0.00", width=12, font=("", 50, "bold"), ) label1.pack(padx=10) label2 = tkinter.Label( app, text="0.00", width=12, font=("", 50, "bold"), ) label2.pack(padx=10) label3 = tkinter.Label( app, text="0.00", width=12, font=("", 50, "bold"), ) label3.pack(padx=10) label4 = tkinter.Label( app, text="0.00", width=12, font=("", 50, "bold"), ) label4.pack(padx=10) start() app.mainloop()

今回は単純にリアルタイムデータを確認するだけだけど、ラズパイとかを使ってAmbientとかに蓄積しても面白いかも。
たしかにこのクランプ電流計、Local Keyを取得するのはちょっと面倒かもしれないけど、ラズパイとかでCTセンサを直接繋いで電力測定するよりも安全で楽かもしれない。内部ではHLW8112という交流の電圧や電流をSPI通信で取得できるICを使ってるみたいだけど、絶縁してラズパイにそのICを直結することを考えてもすごくコスパ高いと思う。