A&D社製の重量計の値を取り込んでみる。
お仕事でA&Dの重量計の値ををPCに取り込みたくなったのでサクッと作ってみました。
備忘メモとして残します。
【やりたいこと】
重量計ではかる。そしてエクセルに重量を手入力する。
それを何回もやる。打ち間違えるし、面倒だからやりたくな~い!
ボタンを押したらそのまま値をキーボード入力打ちする。
パソコンも変わるしドライバ入れたりとかやるのもめんどくさいからやりたくない。
だからUSB HIDで入力できるようにしたい。
基本的に前作ったDSUB9ピンから出力されるRS232cを電圧をTTLレベルに変換して、
USB HIDで出力するものと同じです。
何が違うかというと、A&D標準フォーマットの値部分のみを、出力してくれるだけ。
入力速度は遅いですが、手入力を置き換える程度のPC入力なので問題ないです。
【構成】
重量計のRS232c端子から出力した信号をTTLレベルに変換して、値部分のみをUSBのHIDで出力する。
A&D FX-i シリーズの計測器
汎用電子天びん FX-iシリーズ | 計量 | 商品・サービス | 株式会社エー・アンド・デイ
|
RS232cストレートケーブル(9pinオスーオス)
|
RS232c→USB変換器(★今回作ったやつ)
|
USBケーブル(TypeA to C)
|
取り込みたいPC
【RS232c→USB変換器の部材】
・Seeeduino XINO
https://wiki.seeedstudio.com/jp/Seeeduino-XIAO/
・極小RS232-TTLコンバータモジュール- Dサブ9ピン オスコネクタ(NS-RS232-02)
https://www.nulsom.com/datasheet/NS-RS232_en.pdf
・ちょうど良いサイズの容器(やっぱりフリ〇クが最高)
【機器の入出力のフォーマット】
A&D標準フォーマットのデータ例:ST,+00012.78 gCrLf ←CrLfは改行コード
USB HIDで出力されるデータ例12.78CrLf
ちなみに「ST,-00012.78 gCrLf」の場合は「-12.78CrLf」が出力される。
【変換器とRS323C TTLコンバータとの接続】
Seeedino XINOのUART1(RX:GPIO7、TX:GPIO6)とRS232-TTLコンバータを以下のように結線する。
接続
Seeedino XINO--RS232-TTLコンバータ
RX:GPIO7 -- ②TX
TX:GPIO6 -- ③RX
GND --①GND
3.3V --④VCC
【A&D重量計の設定】
ボーレート:9600
ビット長:8ビット、パリティ:なし、ストップビット:なし(SERIAL_8N1)
設定方法は計測器のメーカサイトの取扱説明書を参照してください。
【開発環境】
・Arduino IDE2.0.0
【コード】Arduino IDEで書いてみました。
ライブラリ(adafruit/Adafruit_TinyUSB_Arduino)を利用しました。
https://github.com/adafruit/Adafruit_TinyUSB_Arduino
#include <Keyboard.h> #define BPS0 9600 //シリアル送信側UART0(USB) #define BPS1 9600 //シリアル受信側UART1 #define DATA_BUFFER 17 // 文字列の格納領域のバイト数 char resDATA[DATA_BUFFER]; //送信用データ uint8_t pData = 0; void AandDFormatSend() { // A&D標準フォーマットから値部分のデータを切り出してデータ送信 // データ例:ST,+00012.78 gCrLf //CrLfはresDATA格納時に除いている。 //HIDでデータをPCに送信マイナスの場合は'-'(0x2d)を送信 if (resDATA[3] == '-') { // Serial.print('-'); Keyboard.print('-'); } else if (resDATA[3] != '+') { // '+'(0x2b)フォーマットエラー // Serial.println("Data Format Error"); return; } uint8_t pPass = 4; do { if (resDATA[pPass] != '0') { // 重量値の上位'0'(0x30)埋めの値は送信しない break; //数値がゼロ以外になったら抜ける } pPass++; } while (pPass < 8); for (uint8_t pSend = pPass; pSend <= 12; pSend++) { // Serial.print(resDATA[pSend]); Keyboard.print(resDATA[pSend]); } // Serial.println(""); Keyboard.println(""); return; } void bufferReset() { //char配列とシリアルバッファを消去する。 for (pData = 0; pData < DATA_BUFFER; pData++) { resDATA[pData] = '\0'; } pData = 0; while (Serial1.available() > 0) { Serial1.read(); } } /*********************************************************************** 初期化 **********************************************************************/ void setup() { Keyboard.begin(); // Serial.begin(BPS0); // 通信速度bps、UART0 Serial1.begin(BPS1); // 通信速度bps、UART1 delay(50); bufferReset(); } /*********************************************************************** UART1から受信シリアル受信 1バイトづつ読み込みstrDATAに結合しUSB HID送信 **********************************************************************/ void loop() { while (Serial1.available() > 0) { // 受信したデータバッファが1バイト以上存在する場合 char inChar = (char)Serial1.read(); // シリアル1からデータ読み込み if (inChar == '\n') { // 改行(LF:0x0a)がある場合の処理 for (uint i = 0; i < pData; i++) { // Serial.print(resDATA[i]); // Keyboard.print(resDATA[i]); } // Serial.println(""); // Keyboard.println(""); AandDFormatSend(); bufferReset(); } else if (inChar != '\r') { // 復帰(CR:0x0d)の場合は結合しない if (pData < DATA_BUFFER) { resDATA[pData] = inChar; pData++; } else { //データバッファ溢れが発生する不正データは一旦リセット // Serial.println("Buffer over Error"); bufferReset(); } } } delay(1); }
【アソビ】フォトリレー使って24v制御してみた
社内でIoT事情としてはセキュリティガーによってなかなか幅が絞られてます。
とりあえずWiFiはグレー、Bluetoothはマウスとかにも入ってるしOK?
Raspberry PiはSD入れる口やLANポートあるからなんか...
マイコンはマウスにも入ってるし、一緒だよね!うん。
そんなわけでちょっと進まないので番外編として親戚のおじさんと
ドローン✕エアガン✕マイコンをやってみました。
事の始まり
親戚のおじさんがJDIのドローン持ってて、実家の敷地で飛ばしてたのは知ってたのです...
久々に連絡来たのですが、
「メカリレーモジュール(13g)をもっと軽くする方法ある?」
どういう事??
聞いてみるとドローンに合わせて作られた自作エアガン?を搭載しているではないですか(笑)
この前布教したarduino使って圧縮空気をエアバルブで制御してるし!
ライトを光らせると射出!
うーんガチやんけ!
要するともっとカッコ良くしたいから、軽く小さくしたいとのこと。
小さいは正義!
軽いは最高!
要件整理
H/W
①メカリレーを軽く、フォトリレーに変更。
②マイコンもarduino NANO→Seeeduino XINOに変更。
③バッテリーは7.2v一つで5v降圧と24v昇圧の小さいモジュールにする。
④基板に実装するのはマイコン、変圧モジュール、フォトリレー、ボタン。
⑤バッテリー、エアバルブ、光センサーモジュールは基板の端子と電線で繋ぐ。
S/W
⑥マイコンの制御はドローンのライトを光らせた時、光センサーモジュール使って信号を受けたら、フォトリレーを1回だけ10秒間ONにして、エアバルブのソレノイドを解放。
⑦マイコンに安全装置としてボタンを接続し、そのボタンを押すと光センサーによる④の制御がされるようにする。
そこでモジュール買ってフォトリレーの回路でわからないところは会社の回路設計してる友人に聞いたりと、なんやかんやで基板作って総重量13gに収まった!あと動いた!
まずはブロック図書いて
部品選定とフォトリレー周りの回路図は知り合いの回路設計とかやってた人に教えてもらって
部品リスト作って
配線図作りたいんですが、まだスキルが無くてとりあえずExcelで引いてみるという愚策でやってみる。
Amazonから届いた部品を並べて
重量測ったらいけそうですね!
Seeeduino XINOに光センサ反応したらエアバルブを制御するフォトリレーへの信号ONにするプログラム書いてみる。
はんだ付けして
直流安定化電源ないので、電池を使って7.2vくらいの電圧作って出力電圧の調整してみる。
フォトリレー:24v
マイコン :5v
光センサ反応して電圧出てるかチェックしたら動いたー
やったぜ!
はんだ付け面はセリアの100均でレジンとブラックライトを買って固めようとしたけど、あまり固まらなかった...
結局翌日の朝からお日様に当てて固めました。
あとはおっちゃんに任せた!
マビックエアー2に乗せるところの設計からは任せた!
おぉ〜飛んだ〜
風船狙って割れました‼︎
うおー
BB弾の威力は法律に引っかからないのは確認済み。
楽しかったなぁ〜
24vの電圧のスイッチがマイコンでできるようになったので
シーケンサとかで制御してた機器とかにもつかえるかもしれませんね。
どう使うかはもうすこしかんがえてみます。
おわり
M5StakCでBluetoothストップウォッチ作ってみた2
前回の記事から500件のメモリ機能とまとめてキーボード入力追加してみました。
現場に持っていくことを想像したらPC持っていくのが手間だと思ったからです。
だんだん凝り始めて高性能なストップウォッチになってきました。
3000円で結構な時間遊べてコスパいいっす。
趣味なら時間もお金も、労力もある限り費やして探求できるし、おもしろいですね。
そもそも趣味と仕事って金になるかならないかという指標があると思いますが、色々考えるそうと言えるものもあれば、そうでも無いような事があるし、境目って曖昧だと私は思うのですよ。
あとワーク・ライフ・バランスとか聞いたことありますが、ワークもライフだろって突っ込みたいのです。
なんでも分けたがる人には理解されないですよね。なんかブラック企業の経営者っぽいし。
この記事を立てたのも趣味です。でも仕事にも生かして、誰かが喜んでくれたら満足なのです。
そして結果的にお金になったらさらに幸せです。
さて本質の追求はここまでにして本題です。
機能は以下としました。
Aボタン:スタート(+Bluetoothキーボード入力)、ストップ
Bボタン:ストップ(2度押しで保存したメモリをすべてBluetoothキーボードで入力)
Cボタン:リセット(メモリのカウントもリセット)
【プログラム】
/* キーボードライブラリをインストール https://github.com/asterics/esp32_mouse_keyboard ・最大約49日まで ・件数は500まで */ #include <BleKeyboard.h> //Bluetooth HIDプロファイルの設定ライブラリ、キーボード送信用 BleKeyboard bleKeyboard("M5StickC StopWatch 001"); //Bluetoothペアリング時の表示名 #include <M5StickC.h> uint32_t tm; uint32_t startTime; uint32_t LastTime; bool stopF = false; uint32_t stopTime; uint32_t stopTimeSum; uint16_t count; uint16_t datasize = 500; uint32_t lapArray[500]; uint32_t timeArray[500]; uint16_t BtnF; uint32_t i; bool flag = true; void m5lcd() { M5.Lcd.fillScreen(BLACK); //背景色 M5.Lcd.setTextColor(WHITE); //テキストカラー M5.Lcd.setTextSize(1); //文字サイズ M5.Lcd.setCursor(5, 0); M5.Lcd.println("StopWatch:001"); M5.Lcd.setCursor(95, 0); if (BtnF == 1) { if(flag == true){ M5.Lcd.setTextColor(GREEN); }else{ M5.Lcd.setTextColor(WHITE); } flag = !flag; M5.Lcd.print("Start"); M5.Lcd.setTextColor(GREEN); } else if (BtnF == 2) { M5.Lcd.print("Stop"); M5.Lcd.setTextColor(RED); } else if (BtnF == 3) { M5.Lcd.print("Reset"); M5.Lcd.setTextColor(YELLOW); M5.Lcd.setCursor(0, 10); M5.Lcd.printf("Battery\n"); M5.Lcd.printf(" Warn :%6d\n" , M5.Axp.GetWarningLevel()); // バッテリー残量警告 0:残あり, 1:残なし M5.Lcd.printf(" Temp :%6.1f\n", M5.Axp.GetTempInAXP192()); // AXP192の内部温度 M5.Lcd.printf(" V(V) :%6.3f\n", M5.Axp.GetBatVoltage()); // バッテリー電圧(3.0V-4.2V程度) M5.Lcd.printf(" I(mA):%6.1f\n", M5.Axp.GetBatCurrent()); // バッテリー電流(プラスが充電、マイナスが放電) M5.Lcd.printf("VBus(USB)\n"); M5.Lcd.printf(" V(V) :%6.3f\n", M5.Axp.GetVBusVoltage()); // USB電源からの電圧 M5.Lcd.printf(" I(mA):%6.1f\n", M5.Axp.GetVBusCurrent()); // USB電源からの電流 } else if (BtnF == 4) { M5.Lcd.setTextColor(RED); M5.Lcd.setTextSize(2); M5.Lcd.setCursor(5, 10); M5.Lcd.print("Start writing data"); } else if (BtnF == 5) { M5.Lcd.setTextColor(BLUE); M5.Lcd.setTextSize(2); M5.Lcd.setCursor(5, 10); M5.Lcd.print("Data writing"); M5.Lcd.setCursor(5, 25); M5.Lcd.print("completed"); } else if (BtnF == 6) { M5.Lcd.setTextColor(BLUE); M5.Lcd.setTextSize(2); M5.Lcd.setCursor(5, 10); M5.Lcd.print("Count limit"); M5.Lcd.setCursor(5, 25); M5.Lcd.print("Please reset"); } else { //起動時 M5.Lcd.setTextColor(YELLOW); } if (BtnF < 4) { if (count > 0) { M5.Lcd.setTextSize(3); M5.Lcd.setCursor(5, 15); M5.Lcd.printf("Cnt:%d",count); M5.Lcd.setTextColor(WHITE); M5.Lcd.setTextSize(2); M5.Lcd.setCursor(5, 42); M5.Lcd.printf("Lap :%.2f", (float)lapArray[count] / 1000.0); M5.Lcd.setCursor(5, 62); M5.Lcd.printf("Time:%.2f", (float)timeArray[count] / 1000.0); } } } void setup() { // Serial.begin(115200); //通信速度bps delay(30); bleKeyboard.begin(); //BLE接続開始 M5.begin(); M5.Axp.ScreenBreath(10); //バックライトの輝度(デフォルト12) setCpuFrequencyMhz(80); //CPU周波数を下げる(デフォルト240) M5.Lcd.setRotation(1); m5lcd(); // Serial.println("M5Stack StopWatch"); } void loop() { M5.update(); int axpButton = M5.Axp.GetBtnPress(); //Aボタンでタイマー開始、ラップタイム記録 if (M5.BtnA.wasPressed()) { if (startTime == 0) { startTime = millis(); LastTime = startTime; // Serial.println("RAPCOUNT,RAP,TIME"); BtnF = 1; m5lcd(); //Bluetooth HIDプロファイルで送信 bleKeyboard.print("RAPCOUNT"); bleKeyboard.write(KEY_TAB); //TAB(0xB3)をBluetooth送信 bleKeyboard.print("LAP"); bleKeyboard.write(KEY_TAB); //TAB(0xB3)をBluetooth送信 bleKeyboard.print("TIME"); bleKeyboard.write(KEY_TAB); //TAB(0xB3)をBluetooth送信 bleKeyboard.write(KEY_RETURN); //改行(0xB0)をBluetooth送信 //ストップ後の再開 } else if (stopF == true) { tm = millis(); stopTime = tm - stopTime; stopTimeSum += stopTime; stopF = false; //Serial.println("Timer Restart"); //LCD表示 BtnF = 1; m5lcd(); //ラップタイム } else { tm = millis(); count += 1; //配列に入力 lapArray[count] = tm - LastTime - stopTime; timeArray[count] = tm - startTime - stopTimeSum; LastTime = tm; stopTime = 0; //serial出力 // Serial.printf("%d,%.2f,%.2f\r\n", count, (float)lapArray[count] / 1000.0, (float)timeArray[count] / 1000.0); //LCD表示 if (count <= datasize) { BtnF = 1; m5lcd(); //Bluetooth HIDプロファイルで送信 bleKeyboard.print(count); bleKeyboard.write(KEY_TAB); //TAB(0xB3)キー bleKeyboard.print((float)lapArray[count] / 1000.0, 2); bleKeyboard.write(KEY_TAB); //TAB(0xB3)キー bleKeyboard.print((float)timeArray[count] / 1000.0, 2); bleKeyboard.write(KEY_TAB); //TAB(0xB3)キー bleKeyboard.write(KEY_RETURN); //改行(0xB0)キー } else { BtnF = 6; m5lcd(); } } } else if (M5.BtnB.wasPressed()) { //Bボタンでタイマーリセット count = 0; startTime = 0; LastTime = 0; stopF = false; stopTime = 0; stopTimeSum = 0; // Serial.println("Timer Reset"); BtnF = 3; m5lcd(); } else if (axpButton == 2 && stopF == true) { //Bボタン2回押しデータの一括送信 // Serial.println("Data write"); BtnF = 4; m5lcd(); bleKeyboard.print("RAPCOUNT"); bleKeyboard.write(KEY_TAB); //TAB(0xB3)をBluetooth送信 bleKeyboard.print("LAP"); bleKeyboard.write(KEY_TAB); //TAB(0xB3)をBluetooth送信 bleKeyboard.print("TIME"); bleKeyboard.write(KEY_TAB); //TAB(0xB3)をBluetooth送信 bleKeyboard.write(KEY_RETURN); //改行(0xB0)をBluetooth送信 for (int j = 1; j <= count; j++) { delay(350); bleKeyboard.print(j); bleKeyboard.write(KEY_TAB); //TAB(0xB3)キー bleKeyboard.print((float)lapArray[j] / 1000.0, 2); bleKeyboard.write(KEY_TAB); //TAB(0xB3)キー bleKeyboard.print((float)timeArray[j] / 1000.0, 2); bleKeyboard.write(KEY_TAB); //TAB(0xB3)キー bleKeyboard.write(KEY_RETURN); //改行(0xB0)キー } delay(500); BtnF = 5; m5lcd(); } else if (axpButton == 2 && stopF == false && startTime > 0) {//電源ボタンでタイマーストップ stopTime = millis(); stopF = true; // Serial.println("Timer Stop"); BtnF = 2; m5lcd(); } if (i % 50 == 0 && BtnF == 1) { m5lcd(); } //PCロックが掛からないように一定時間にShiftキーを押す if (i % 6000 == 0) { // Serial.println("Shift"); setCpuFrequencyMhz(240); M5.Axp.ScreenBreath(12); //バックライトの輝度(デフォルト12) bleKeyboard.write(0x81); //KEY_LEFT_SHIFT M5.Axp.ScreenBreath(10); //バックライトの輝度(デフォルト12) setCpuFrequencyMhz(80); i = 0; } i += 1; delay(10); }
【困ったこと】
2次元配列でラップタイムと累計時間入れてみたんですが、累計時間の値がおかしくてどうにもこうにもならんかったので、一次元配列でラップタイムと分けました。
配列の使い方調べてたらポインタとか出てきてややこしくて泣きそうです。
わかりそうな気になったんですが、わかりませんでした。
M5StakCでBluetoothストップウォッチ作ってみた
タイトルの通りBluetooth HID入力できるストップウォッチ作りました。
まずは作ろうと思ったきっかけです。
私が所属している部署の機能の1つに図面に対して標準時間を設定する業務がありまして、その根拠となる時間研究をしています。
紙に経過時間と要素作業名を書いてExcelに手入力して、関数で計算してect...
とにかく時間と手間がかかるわ、入力ミスの懸念もあるわと大変なんです。
そこの担当者がデシマルストップウォッチ故障して買い替えたいと1.2万のものを数個個買いたいと課長に予算の承認依頼したときに
「ストップウォッチの情報って無線で直接送れるやつとか売ってないの?」
「IoTとか言ってる時代なんだから世の中あるんじゃないの?」
と言われてたので私も調べてみたらフェリカとか使って通信しているものを見つけました。
しかし、まぁ高いし、ドライバーインストールして、かざしてCSVで保存して、コピペして、時分秒を秒に換算して...ぁあ~もう
....なんか面白くない!値段高いしあんまり変わらんやんけ。
ピッて押して、Excelのフォーマットに直接バット入ってうぇ~い!!
ってなって欲しいのですよ。
私の知る限りまだ世の中にないので作ります。
よかったらこのアイディアで商品開発したいくらいです。
時間研究とは
工場や事務所,倉庫などにおける作業を基本動作に分解して,各動作の時間を測定し,これを分析して各作業の標準時間を決定する技法。科学的管理法の始祖 F.W.テーラーが提唱したもので,現実の方法としては,ストップウォッチを用いて測定する方法,作業動作を要素に分解して記号化していくワーク・ファクター法,作業の基本動作に標準時間値テーブルを用いるMTMなどがある。作業方法の標準化,作業計画,作業量の決定などに利用される。
出典 ブリタニカ国際大百科事典 小項目事典ブリタニカ国際大百科事典 小項目事典について
【作業の流れ】
①現場で作業とストップウォッチをみながら紙に経過時間と要素作業名を書く。
②事務所に戻って時間と要素作業をExcelにて入力。
③時間を秒に換算ためにExcel関数を入力して計算。
④要素ごとの時間をExcel関数を入力して計算。
⑤項目毎に集計したり、なんやかんやして標準時間を設定。
今回やりたいことは①~④の時間の入力と計算の手数を極限まで減らしたいのです。
やりましょう。
【開発環境】
・Windows10 2H1
・Arduino IDE 1.8.15
・Arduino IDEに手動で入れたBluetooth HIDキーボードライブラリ
github.com
・Arduino IDEのボードマネージャーでインストール:M5Stack by M5Stack official 1.0.7
【使用機器】
・M5StickC M5StickC - スイッチサイエンス
【機能】
Aボタン :スタート/ラップタイム/再開
Bボタン :ストップ
電源ボタン:リセット
【コード】
/* キーボードライブラリをインストール https://github.com/asterics/esp32_mouse_keyboard ・最大約49日まで ・件数は65535まで */ //Bluetooth HIDプロファイルの設定ライブラリ、キーボード送信用 #include <BleKeyboard.h> //Bluetoothペアリング時の表示名 BleKeyboard bleKeyboard("M5StickC StopWatch 002"); #include <M5StickC.h> uint16_t count; float tm; float startTime; float LastTime; float Lap1; float Lap2; float Lap3; float Time1; float Time2; float Time3; bool stopF = false; float stopTime; float stopTimeSum; uint16_t i; uint16_t BtnF; void m5lcd() { M5.Lcd.fillScreen(BLACK); //背景色 M5.Lcd.setTextColor(WHITE); //テキストカラー M5.Lcd.setTextSize(1); //文字サイズ M5.Lcd.setCursor(0, 0); M5.Lcd.println("StopWatch 002"); M5.Lcd.setCursor(90, 0); if (BtnF == 1) { M5.Lcd.print("Start"); M5.Lcd.setTextColor(GREEN); } else if (BtnF == 2) { M5.Lcd.print("Stop"); M5.Lcd.setTextColor(RED); } else if (BtnF == 3) { M5.Lcd.print("Reset"); M5.Lcd.setTextColor(YELLOW); } else { M5.Lcd.setTextColor(YELLOW); } M5.Lcd.setTextSize(2); if (count > 0) { M5.Lcd.setCursor(0, 10); M5.Lcd.printf("Cnt :%d", count); M5.Lcd.setCursor(0, 25); M5.Lcd.printf("lap :%.2f", Lap1); M5.Lcd.setCursor(0, 40); M5.Lcd.printf("Time:%.2f", Time1); M5.Lcd.setTextSize(1); //文字サイズ M5.Lcd.setTextColor(WHITE); if (count > 1) { M5.Lcd.setCursor(0, 60); M5.Lcd.printf("lap2:%.2f Time:%.2f", Lap2, Time2); if (count > 2) { M5.Lcd.setCursor(0, 70); M5.Lcd.printf("lap3:%.2f Time:%.2f", Lap3, Time3); } } } } void setup() { Serial.begin(115200); //通信速度bps delay(30); bleKeyboard.begin(); //BLE接続開始 M5.begin(); M5.Axp.ScreenBreath(9); //バックライトの輝度(デフォルト12) setCpuFrequencyMhz(160); //CPU周波数を下げる(デフォルト240) M5.Lcd.setRotation(1); m5lcd(); Serial.println("M5Stack StopWatch"); } void loop() { M5.update(); int axpButton = M5.Axp.GetBtnPress(); //Aボタンでタイマー開始、ラップタイム記録 if (M5.BtnA.wasPressed()) { if (startTime == 0.0) { startTime = (float)millis() / 1000.0; LastTime = startTime; Serial.println("RAPCOUNT,RAP,TIME"); BtnF = 1; m5lcd(); //Bluetooth HIDプロファイルで送信 bleKeyboard.print("RAPCOUNT"); bleKeyboard.write(KEY_TAB); //TAB(0xB3) bleKeyboard.print("LAP"); bleKeyboard.write(KEY_TAB); //TAB(0xB3) bleKeyboard.print("TIME"); bleKeyboard.write(KEY_TAB); //TAB(0xB3) bleKeyboard.write(KEY_RETURN); //改行(0xB0) //ストップ後の再開 } else if (stopF == true) { tm = (float)millis() / 1000.0; stopTime = tm - stopTime; stopTimeSum += stopTime; stopF = false; //LCD表示 BtnF = 1; m5lcd(); //ラップタイム } else { tm = (float)millis() / 1000.0; count += 1; Time3 = Time2; Time2 = Time1; Time1 = tm - startTime - stopTimeSum; Lap3 = Lap2; Lap2 = Lap1; Lap1 = tm - LastTime - stopTime; LastTime = tm; stopTime = 0.0; //serial出力 Serial.printf("%d,%.2f,%.2f\r\n", count, Lap1, Time1); //LCD表示 BtnF = 1; m5lcd(); //Bluetooth HIDプロファイルで送信 bleKeyboard.print(count); bleKeyboard.write(KEY_TAB); //TAB(0xB3) bleKeyboard.print(Lap1, 2); bleKeyboard.write(KEY_TAB); //TAB(0xB3) bleKeyboard.print(Time1, 2); bleKeyboard.write(KEY_TAB); //TAB(0xB3) bleKeyboard.write(KEY_RETURN); //改行(0xB0) } //Bボタンでタイマーリセット } else if (M5.BtnB.wasPressed()) { count = 0; startTime = 0.0; LastTime = 0.0; Time1 = 0.0; Time2 = 0.0; Time3 = 0.0; Lap1 = 0.0; Lap2 = 0.0; Lap3 = 0.0; stopF = false; stopTime = 0.0; stopTimeSum = 0.0; Serial.println("Timer Reset"); BtnF = 3; m5lcd(); //電源ボタンでタイマーストップ } else if (axpButton == 2 && stopF == false && startTime > 0.0) { stopTime = (float)millis() / 1000.0; stopF = true; Serial.println("Timer Stop"); BtnF = 2; m5lcd(); } //一定時間毎にPCロックがかからないようにShiftキーを押す。 if (i >= 24000) { Serial.println("Shift"); bleKeyboard.write(0x81); //KEY_LEFT_SHIFT i = 0; } i += 1; delay(10); }
【その他】
モバイルバッテリー繋いで使ってみたんですがすぐ切れちゃうんですが、消費電力低いせいですかね。
輝度下げたり、CPUのクロック下げたりとか知らずにやって省電力化を試みたけど気づかなかった~
逆に負荷抵抗とか入れて消費電力増やしたいんですが、なんかいい方法ないかなぁ。
写真だと見えないんですが、ボールペンと一体にしたくて、アルミでブラケットを作ってもらったんですが、フィット感抜群でした(笑)協力してもらった方には感謝です。
とりあえず課長にも担当者にも見せたら結構ウケたので業務に採用してもらえそうです。
1個あたり既製品:12,000円→今回のおもちゃ:3000円で節約できました。開発工数考慮するとあれですが、自宅で趣味としてやってたからまぁ...あれですね。
おしまい
ESP32でブレーカーの電流値を測定
電流計のIoTに挑戦です。
少しは使い方とかプログラミングとかできるようになったんで、そろそろ会社でのアウトプットとして何か作りたいです。
ESP32→WiFiルーター→インターネット→Ambientのサーバー
グラフを見るときはAmbientにアクセスして見れます。
AmbientはIoTデータの可視化サービスです。とりあえずやってみたい人にはおすすめです。面白いです。
ESP32はADCの精度が悪いってネット調べてたら色々出てきました。
直接電圧値を返してくれる関数ないかなと思って調べてたら最近のバージョンで出てきました。
どうやらanalogRead()ではなくESP32のanalogReadMilliVolts()を使うようです。。
これはGPIOを引数で渡すと電圧をmVで返してくれる関数みたいです。
あんまり情報がなかったのですが、ESP32の電圧3.3V/分解能4056をanalogRead()にかける手間減りますね。
以下のプログラムはタイマー割り込みで200μs毎に100回、10周期分で1000回のデータを取得しています。
以下の部分はご自分の情報を入れてください。
#define WIFISSID "************" // ルーターのSSID
#define WIFIPASS "************" // ルーターのpassword
const uint32_t channelId = ******; // AmbientのチャネルID Ambientに登録してゲットしてください!
const char* writeKey = "***************"; // ライトキー
素人のプログラミングなので変数名とか書き方のお作法などあれですが、まぁ許してください。
プログラム
/************************************** 電流のセンサ情報を取得してAmbientにデータを送る **************************************/ #include <WiFi.h> #include "Ambient.h" volatile float coefficient; //係数 const float OFFSET_CH0 = 0.00; //CH0の補正値 const float OFFSET_CH1 = 0.00; //CH1の補正値 #define CH0_APIN 34 //センサ0のアナログピン番号:A6:34 #define CH1_APIN 35 //センサ1のアナログピン番号:A7:35 #define CH2_APIN 32 //アナログピン番号中間電位 :A4:32 volatile uint16_t value_CN2; volatile uint32_t SquareAdd_CN0; volatile uint32_t SquareAdd_CN1; const uint16_t SAMPLE = 1000; //ポーリング回数:200μsごとに100サンプルで1周期(50Hz)ぶん取得する volatile uint16_t isrCounter; //電流値のポーリング回数のカウント変数 #define WIFISSID "*****" // ルーターのSSID #define WIFIPASS "********" // ルーターのpassword //Ambient WiFiClient client; Ambient ambient; const uint32_t channelId = *****; // AmbientのチャネルID const char* writeKey = "****"; // ライトキー // タイマー処理用タスク hw_timer_t* tm0 = NULL; //timer 初期化 volatile SemaphoreHandle_t timerSemaphore; portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; volatile boolean t0flag = true; // 割り込み待ち変数 const uint32_t DELAYTIME = 15000000; //送信頻度μsで指定 volatile uint32_t startTime; void IRAM_ATTR WiFi_ON() { //WiFiの接続 WiFi.begin(WIFISSID, WIFIPASS); Serial.println(WIFISSID); int waiting = 0; while (WiFi.status() != WL_CONNECTED) { delay(100); waiting = waiting + 1; if (waiting >= 200) { //タイムアウト Serial.println(F("WiFi Err ESP Reset")); esp_restart(); } } Serial.println(F("----WiFi ON")); // 本機のIPアドレスをシリアル出力 Serial.print("IP address: "); Serial.println(WiFi.localIP()); } /********************************************* タイマ割り込みによるポーリングでアナログ電圧値取得 *********************************************/ void IRAM_ATTR Task0() { //電流データをサンプル回数分ポーリングする uint16_t loopTime = micros(); portENTER_CRITICAL_ISR(&timerMux); // 排他制御で下記を実行 isrCounter += 1; //電流値値取得 uint32_t AnlogValue0 = analogReadMilliVolts(CH0_APIN) - value_CN2; //中間電位とCH1の差分を取る uint32_t AnlogValue1 = analogReadMilliVolts(CH1_APIN) - value_CN2; //中間電位とCH2の差分を取る //2乗した値を加算していく if (AnlogValue0 > 0) { SquareAdd_CN0 += +AnlogValue0 * AnlogValue0; } if (AnlogValue1 > 0) { SquareAdd_CN1 += AnlogValue1 * AnlogValue1; } portEXIT_CRITICAL_ISR(&timerMux); // 排他制御終了 if (isrCounter >= SAMPLE) { loopTime = micros() - loopTime; Serial.printf("Task0 Time: %d μs\n",loopTime); //実測だと104μs t0flag = true; timerEnd(tm0); tm0 = NULL; xSemaphoreGiveFromISR(timerSemaphore, NULL); // セマフォを開放 } } /********************************************* セットアップ関数 *********************************************/ void setup() { //CTL-10-CLS 超小型クランプ式交流センサ(Φ10/80Arms) const uint16_t RL = 100; //センサ抵抗値 const float K = 0.98; //結合係数 const uint16_t N = 3000; //巻数比 //1mVあたりの電流Io(A) coefficient = N / RL / K / 1000; //0.030612448 Serial.begin(115200); ambient.begin(channelId, writeKey, &client); // チャネルIDとライトキーを指定してAmbientの初期化 delay(1000); Serial.print("coefficient: "); Serial.println(coefficient, 5); Serial.printf("CH0:analogRead()=%d\n", (int)analogRead(CH0_APIN)); Serial.printf("CH1:analogRead()=%d\n", (int)analogRead(CH1_APIN)); Serial.printf("CH2:analogRead()=%d\n", (int)analogRead(CH2_APIN)); Serial.printf("CH0:analogReadMilliVolts()=%d mV\n", (int)analogReadMilliVolts(CH0_APIN)); Serial.printf("CH1:analogReadMilliVolts()=%d mV\n", (int)analogReadMilliVolts(CH1_APIN)); Serial.printf("CH2:analogReadMilliVolts()=%d mV\n", (int)analogReadMilliVolts(CH2_APIN)); Serial.println(F("Loop start")); } void loop() { while (t0flag == true) { startTime = micros(); // 開始時間保存 t0flag = false; isrCounter = 0; SquareAdd_CN0 = 0.00; SquareAdd_CN1 = 0.00; value_CN2 = 0; for (int i = 0; i < 10; i++) { value_CN2 += analogReadMilliVolts(CH2_APIN); //中間電位を取得 } value_CN2 = value_CN2 / 10; //平均値を利用する。 Serial.printf("CH2: %d mV\n", value_CN2); // WDT(ウォッチドックタイマ)設定 timerSemaphore = xSemaphoreCreateBinary(); //バイナリセマフォを作成 tm0 = timerBegin(0, getApbFrequency() / 1000000, true); //タイマ番号0-3まで利用可,ペリフェラル周波数:timer=1us, timerAttachInterrupt(tm0, &Task0, true); //タイマ割り込みが入ったときにタスク実行 timerAlarmWrite(tm0, 200, true); //アラーム、引数2はμ秒で指定:200μs timerAlarmEnable(tm0); //タイマー有効化 } delay(110); //電圧の取得が終わるまで待つ if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE) { //2乗平均平方根で電流実効値を計算する。 float rms_CN0 = 0.00; if (1.0 < SquareAdd_CN0) { rms_CN0 = sqrt(SquareAdd_CN0 / SAMPLE); rms_CN0 = rms_CN0 * coefficient + OFFSET_CH0; } float rms_CN1 = 0.00; if (1.0 < SquareAdd_CN1) { rms_CN1 = sqrt(SquareAdd_CN1 / SAMPLE); rms_CN1 = rms_CN1 * coefficient + OFFSET_CH1; } float sumRms = rms_CN0 + rms_CN1; Serial.print(F("Ch0: ")); Serial.print(rms_CN0); Serial.println(F(" A")); Serial.print(F("Ch1: ")); Serial.print(rms_CN1); Serial.println(F(" A")); Serial.print(F("ChSum: ")); Serial.print(sumRms); Serial.println(F(" A")); WiFi_ON(); // 温度、湿度の値をAmbientに送信する ambient.set(1, String(sumRms).c_str()); if (ambient.send()) { Serial.println(F("ambient Send OK!")); } else { Serial.println(F("ambient Send NG")); } delay(200); WiFi.mode(WIFI_OFF); //WiFiをOFFにして省電力 Serial.println(F("----WiFi OFF")); //ループの設定時間から処理時間分を引いてdylayする。 uint32_t delaytaTime = micros() - startTime; delaytaTime = (DELAYTIME - delaytaTime) / 1000; Serial.printf("LOOPTIME: %d ms\n", (int)(DELAYTIME / 1000)); Serial.printf("DelayTime: %d ms\n", delaytaTime); delay(delaytaTime); } }
【何も繋がない時の出力結果】
22:42:00.014 -> coefficient: 0.03061
22:42:00.014 -> CH0:analogRead()=1841
22:42:00.014 -> CH1:analogRead()=1840
22:42:00.014 -> CH2:analogRead()=1840
22:42:00.014 -> CH0:analogReadMilliVolts()=1672 mV
22:42:00.014 -> CH1:analogReadMilliVolts()=1672 mV
22:42:00.014 -> CH2:analogReadMilliVolts()=1672 mV
22:42:00.014 -> Loop start
22:42:00.014 -> CH2: 1669 mV
22:42:00.201 -> Task0 Time: 104 μs
22:42:00.248 -> Ch0: 0.05 A
22:42:00.248 -> Ch1: 0.04 A
22:42:00.248 -> ChSum: 0.10 A
22:42:00.342 -> Buffalo-G-3CAA
【Ambientに送信されるデータ】
CH0: 0.1
割り込みでは104μsかかっているので、サンプルの取得間隔は200μsで良かったです。
【大変だったこと】
延長コードを割いてクランプセンサーを1CH挟んで測定したときにドライヤーつけて電圧図ろうとしたらコンセントの端子に触れて強い光と共にタップが黒くなった(笑)
テスターや工具による感電には気をつけましょう。
おわり
Micro:bitでブレーカーの電流速定2その後
前回マイクロビットとクランプ電流センサーで電流速定して、もう一個のマイクロビットに無線で電流値送って現在の電流値をとれるようにしました。
カロリー○イトの箱に収めたのはウケたんで、とりあえず良かったということにしましょう!うん。
実際に使ってもらった感想聞いたら以下のような会話ががありました。
・面白いし、いい感じだった。
・以前arduinoで挫折したけど、Micro:bitはブロックのプログラミングで面白そうだから、自分でも買って子供とプログラミングの勉強やってみた。
・最近あたたかくなってブレーカー落ちないから、そんなに必要じゃなくなってきたなぁ〜←んっ?
・次はどのくらい使ってるか推移が見たくなるなぁ〜←おっ?
ケン:見れたらなにをしたいですか?
ブレーカーが落ちる前に電流値の閾値超えたらブレーカー落とすとか?
課長:それ余計に落ちやすくなるから(笑)
課長:特に決まってないけど、傾向見れたらなんかできないかなぁ
ケン:じゃやってみますか!
(あっ、これ会社のIoT化とか話が出た時だったら時間とお金かけて失敗するパターンだ。)
次回はブレーカーの電流値を飛ばすところから、外でもスマホで見れるようにしてみましょう。
話は大きく変わりますが、
IoTとか会社でやるのにあんまり上手く行ってる例が少ないと思ってるので、自分で開発して運用できるものであるかを検証してみます。
ちなみに以下のパターンは私が見た、まぁ良くある失敗のパターンです。
①今の時代はDX、IoT入れよう。この機器入れるんやぁ!
②ビッグデータ必要だよね。色々なデータを蓄積しよう。
③やっぱりIoTだからリアルタイムに見える化だよね。
(更新間隔は短くして沢山取らないとね!)
〜〜導入に4ヶ月経過〜〜
⑤ネットワークの設定とかわからんなぁ。
IP?サブネットマスク?良くわからん!
LANケーブルこの穴に挿したら動くの?助けて。
〜〜設定後〜〜
⑥見える化して何しようか、うーん
⑦思いつかないからとりあえず考えよう。
〜〜1年後〜〜
⑧そういえばアレどうなってるんだっけ?
見える化出来てる?なんか使えるんじゃない?
ect...
いつまでカイゼンせずに数字見とるんかい!
"見とる化"か〜ぃ!!
よくわからずに横文字並んだツール入れて、
入れたら良くなると思ってる人って多いんですよね。
そもそも導入してなにをどうしたいか、目的とか目標とかわからないと何が良いのか悪いのかわからないですね。
時間は有限だし、新しいことをやるためには今やってる何かをやめる必要がありそうです。
やめるってことは業務フローを変えないとですね。
前提知識としてトヨタ生産方式とかの知識もないと見える化で終わっちゃうので気をつけよう...
おわり
Micro:bitで電流測定
事の発端は課長からの一言
最近ドライヤー使った時にブレーカー落ちるからなんとかしたいんだけどどうにかできない??
私:炊飯器やポット使ってる時にドライヤー使わなければ大丈夫ですねーもしくは電力契約見直して増やすとかどうでしょう!
課長:契約変えるのはなんか負けた気がするし、ドライヤー使う前にブレーカー落ちないか見える化したいんだ。
私:電力量を見たいならスマートメータに変えてBルート契約したらNature Rumo Eとか、自作でやるならESP32とかでデータ引っこ抜けるのでは?
課長:スマートメータじゃないんだよね~Bルート契約とか面倒だなぁクランプメータとかからデータ引っこ抜けないの?
私:ほほう!面白そうですね。やってみますか!(自分の家でやると無駄遣いって怒られるからこれは楽しみだなぁ)
そこで今回はマイコンとクランプセンサー繋いで交流の電流値測定してまずは見える化してみましょうってことがきっかけです。電力だと電圧とか力率とか面倒だから電流のみをを測ります。
せっかく遊びでやるからオモチャっぽい作りにしてみましょう!(笑)
ケースは今回も菓子の箱、カ○リーメイト中身は美味しくいただきました!
そして使うはマイクロビットでブロック使ってプログラミング、子供のおもちゃ感出して行きましょう!
電流測定側の完成品の写真ははこちらです。
ちなみにもう一個のマイクロビットで電流値を受信してみました。
【材料】大体8700円くらいでした
・Micro:bit v2 2個
・クランプ式交流電流センサ(CTL-10-CLS) 2個
・Micro:bitプロトタイプ基板 1枚
https://www.amazon.co.jp/gp/aw/d/B07TQJJ4YT/ref=yo_ii_img?ie=UTF8&psc=1
・JST コネクタ 2P JST-SM オスメス
https://www.amazon.co.jp/gp/aw/d/B01M63SOL2?psc=1&ref=ppx_pop_mob_b_asin_image
・抵抗(100Ω) 2個
・抵抗(4.7kΩ) 2個
・セラミックコンデンサ(105) 2個※見様見真似で定数も適切かわからないです。リップルとかノイズとかオシロで見たいんだがほしいなぁ
・お菓子の箱(カロリーメイトは幅がぴったりでした。)
【予備知識】
クランプセンサーの電流から交流電流の実行値を算出する。
超小型クランプ式交流電流センサ(φ10/80Arms)CTL-10-CLS
www.u-rd.com
①データシートより
Eo=K×Io×RL÷n(VDC)
②①より変換
電流Io=電圧Eo×変流比N÷負荷抵抗RL÷結合係数K
Io=Eo × 3000 ÷ 100 ÷ 0.99
= 30.612244898 × Eo(A)
③マイコンにかかる電圧Eo = マイコンの電圧 × マイコンの分解能 × analog Readの読み取り値
Eo=3.3 ÷ 1024 × analog Readの読み取り値
=0.00322265625 × analog Readの読み取り値(V)
②に③代入により
④マイコンAD変換の値1当たりの電流値
Io=3.3 ÷ 1024 × 3000 ÷ 100 ÷ 0.99 × analog Readの読み取り値
= 0.09765625 × analog Readの読み取り値(A)
⑤交流の場合のanalog Readの読み取り値は測定電圧の実効値を求めます。
電流波形はマイナスもあるのでそのまま足すと値がゼロになるので、読み取った値を2乗してn回合計し、平均して平方根で求めます。
AnalogReedの値を2乗して最低1周期何度もポーリングして合計する。
1/fより
50Hz→1/50=20(ms)
60Hz→1/60=16.666...(ms)
じゃ1周期分を何回取得するんだいってところが結構悩みました。
ブロックだとタイマ割り込みできないじゃない(私の知識不足?)、Ardoino IDEで書くのは子供のおもちゃっぽく?という謎の意地でやらないとして、とりあえず測定周期分ひたすら読み取って合計してみます。
結論としてはブロックだとfor文で50Hzで1周期(20ms)で2chで100回程度とれてました。
タイマ割り込みでやってないからかやはりビミョーに100回の測定時間が1周期分とずれるのが気持ち悪いです。
500ms分取得したら50Hzだと25期分になるので測定時間が少しずれてもまあ誤差で済むと思います。
60Hzでもちょうど30周期取れるのでどちらにも対応できるってことにしよう。うん
てか1回の測定が100μsって結構遅いんだなぁ~
micro:bit V2はSoCがNordic nRF52833だったので、Nordicのサイトを知り合いに聞きながら調べたら内部抵抗10Ωだと読み込みに5μsだっけ?(うろ覚え)
v1.5:16MHz→v2:64MHzと高速化されてますので、旧バージョン(v1.5)だとサンプリングはもっと少ないと思われます。
【要件や仕様】
・測定範囲は1A~40Aくらい
・測定間隔は5sに1回更新する。
・ブレーカーにクランプして測定する(L1とL2の2ch)
・測定側と表示側は無線で送信する。(マイクロビットの無線を使おう)
・表示はドライヤーの近くで2ch文の合計を表示する。
・表示側は数字でも見たいし、2AごとにLEDで埋めてくれる機能も入れよう。
・値によって制御とかは今回はしない。表示だけ。
【回路図】
3.3VとGNDを分圧して1.65Vの基準電位を作ってその差を読み取って行きます。
これで交流波形のマイナスも測定できるようになります。
調べてたらレールスプリッタっていう回路だそうです。(間違ってたらすみません)
とりあえず汚い手書き画像載せときます。
【電流測定側のプログラム】
makecode.microbit.org【表示側のプログラム】
makecode.microbit.org余談ですが
将来的に設備の稼働率や稼働状況のモニターとか作って組み合わせとかスケジューリングのための分析とかできそうだなぁ〜
機会があったらESP32とルーターとラズパイで複数の設備の電流値を取ってみたいですね。
今まではなんか高いIoT機器を買って稼働率見えるようにしてるみたいで、満足して改善までいきつかないか、分析できるスキルがないかで終わってたような気がします。
安い早いでこれならたくさん検証とフィードバックを繰り返して次への学びに繋げられそうですね。
おわり