IM920 ワイヤレス I/O (その3) 2019/08/13
(その2)では、USB 接続で Windows PC/ Android 端末から操作していましたが、
仕事用のスマホが OTG (USBホスト)機能に対応していないので、
RN42 Bluetooth モジュールを使って Android スマホで使えるようにしてみました。
920MHz 無線モジュール IM920 シリーズ のうち、外部アンテナタイプの IM920XS を使っています。
全体の構成は下図↓のようになります。接続図(PDF)はこちらです。
■概要
500 ミリ秒周期で、DI 4 点の状態、AI 4 点の値を読み込んでいます。
DI の状態が前回と異なる場合は、"SW 1 ON" のように音声合成が発生されます。
AI (アナログ入力)は 0V~1.1V に対応し、そのままの値が表示されます。
DO は 2 点。ON/OFF と 500 ミリ秒の 1 パルス動作が行えます。
操作が受信側に届いたときは、アンサーバックとしてピピッ音が、失敗の時は、ブー音が鳴ります。
AO(アナログ出力)は、PWM出力です。0~5.0 の入力値がほぼ出力電圧値になります。
■ Android スマホ画面
■送信側(RN42 + IM920)
Android から通信を受けて、それを受信側の IM920 に飛ばします。
ユニバーサル基板は、IM315-UNB。RN42は、秋月電子の Bluetooth 無線モジュール評価キット を使っています。
■受信側(IM920 + Arduino UNO)
送信側の信号を受けて、Arduino に渡します。
IM920 シールド IM315-SHLD-RX-V2 は使わず、ユニバーサル基板 IM315-UNB を使っています。
出力リレーは、SainSMART の 2 チャンネルリレーモジュールを HIGH で ON になるようにプチ改造しています。(接続図 PDF を参照)
※共に Amazon にて購入可能です。
■サンプルコード
Android スマホ側 : Delphi + IM920 子機側 : Arduino
※Arduino スケッチは、かなり下の方になります。
// Delphi 10.3 Community Edition unit Unit4; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, System.Bluetooth, System.Bluetooth.Components, FMX.ScrollBox, FMX.Memo, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, FMX.Edit, System.Rtti, FMX.Grid.Style, FMX.Grid,{ Math,} FMX.Objects, System.UIConsts, FMX.ListBox, System.IOUtils, System.IniFiles, // for TTS Androidapi.JNI.TTS,AndroidAPI.JNIBridge, AndroidApi.JNI.Media; type TBtThread = class(TThread) private { Private 宣言 } procedure BtOpen; protected procedure Execute; override; public constructor Create; virtual; end; type TForm4 = class(TForm) Timer1: TTimer; ScaledLayout1: TScaledLayout; Pwm2: TButton; Rectangle4: TRectangle; Label7: TLabel; ComboBox3: TComboBox; Button2: TButton; Rectangle6: TRectangle; Label8: TLabel; updn2: TRectangle; Label9: TLabel; Rectangle8: TRectangle; Label6: TLabel; updn3: TRectangle; Label10: TLabel; updn4: TRectangle; Label12: TLabel; Pwm1: TButton; Rectangle12: TRectangle; Label19: TLabel; Label18: TLabel; updn5: TRectangle; Label13: TLabel; updn6: TRectangle; Label20: TLabel; Label21: TLabel; Rectangle16: TRectangle; Rectangle17: TRectangle; Rectangle18: TRectangle; Rectangle19: TRectangle; Rectangle20: TRectangle; Rectangle21: TRectangle; Rectangle22: TRectangle; Label22: TLabel; Label23: TLabel; Label24: TLabel; Label25: TLabel; Label26: TLabel; Label27: TLabel; Label28: TLabel; Label29: TLabel; Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; Button7: TButton; Button8: TButton; Label3: TLabel; updn7: TRectangle; Label4: TLabel; updn8: TRectangle; Label5: TLabel; updn1: TRectangle; Rectangle1: TRectangle; Rectangle2: TRectangle; Rectangle3: TRectangle; Rectangle5: TRectangle; Rectangle7: TRectangle; Rectangle10: TRectangle; Rectangle11: TRectangle; Rectangle13: TRectangle; Label1: TLabel; procedure Timer1Timer(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Pwm1Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure updn1Click(Sender: TObject); // TTS type TttsOnInitListener = class(TJavaLocal, JTextToSpeech_OnInitListener) private [weak] FParent : TForm4; public constructor Create(AParent : TForm4); procedure onInit(status: Integer); cdecl; end; private { private 宣言 } ttsListener : TttsOnInitListener; tts : JTextToSpeech; procedure SpeakOut(const s :string); procedure InitTTS; public { public 宣言 } constructor Create(AOwner : TComponent); override; destructor Destroy; override; end; type TSwAry = array[0..3] of boolean; var Form4: TForm4; // スイッチ入力の比較 swAryNew : TSwAry; swAryOld : TSwAry; ADevice : TBluetoothDevice; ASocket : TBluetoothSocket; GThdMode : integer; GCmdMode : integer; ThBt : TBtThread; OpenNGcnt : integer; OpenMsecCnt : integer; Counter : integer; TTScnt : integer; BtDeviceHead : string; // uses ... Androidapi.JNIBridge, AndroidApi.JNI.Media; ToneGenerator: JToneGenerator; const // SPP(Serial Port Profile) による通信のUUID ServiceUUID = '{00001101-0000-1000-8000-00805F9B34FB}'; thdTHSTART = 1000; thdTHTERM = 2000; cmdSCCREATE = 200; cmdSCCONNECT = 201; cmdSCNG = 202; CO_ON = claLime; CO_OFF = claGray; CO_NG = claRed; implementation uses Androidapi.JNI.JavaTypes, FMX.Helpers.Android {$IF CompilerVersion >= 27.0} , Androidapi.Helpers {$ENDIF} ; {$R *.fmx} // n の k 乗 (Math ユニット不要) function IntPower(n, k : integer):integer; var i : integer; begin result := 1; for i := 1 to k do result := result * n; end; // ----------------------------------------------------------------------------- // Bluetooth を Open し、接続する procedure TBtThread.BtOpen; var ABluetoothManager : TBluetoothManager; APairedDevices : TBluetoothDeviceList; ADevice : TBluetoothDevice; idx, i : integer; begin GThdMODE := thdTHSTART; try try ABluetoothManager := TBluetoothManager.Current; if ABluetoothManager.ConnectionState = TBluetoothConnectionState.Connected then begin // 過去にペアリングされたデバイスの一覧から、ターゲット を探す APairedDevices := ABluetoothManager.GetPairedDevices; if APairedDevices.Count > 0 then begin idx := -1; for i := 0 to APairedDevices.Count -1 do begin Synchronize(procedure() begin with Form4.ComboBox3 do begin BeginUpdate; Items.Add(APairedDevices[i].DeviceName ); EndUpdate; end; end); if (BTDeviceHead = APairedDevices[i].DeviceName) then begin Synchronize(procedure() begin with Form4.ComboBox3 do begin ItemIndex := i; end; end); idx := i; //break; // リストアップを続ける end; end; if idx >= 0 then begin ADevice := APairedDevices[idx]; if ADevice <> nil then begin ASocket := ADevice.CreateClientSocket(StringToGUID(ServiceUUID), False); if ASocket <> nil then begin GCMDMODE := cmdSCCREATE; // 接続 ASocket.Connect; if ASocket.Connected then GCMDMODE := cmdSCCONNECT; end; end; end; end; end; except on E : Exception do begin GCMDMODE := cmdSCNG; end; end; finally // 明示的にスレッドを終了(破棄される) // スレッド実行中にアプリを終了した時エラーになるため Terminate; WaitFor; FreeAndNil(ThBt); GThdMODE := thdTHTERM; end; end; constructor TBtThread.Create; begin // スレッドを生成、直ちに実行 inherited Create(False); // スレッド終了時、スレッドオブジェクトを破棄 FreeOnTerminate := True; end; procedure TBtThread.Execute; begin BtOpen; end; // ----------------------------------------------------------------------------- procedure TForm4.InitTTS; begin tts := TJTextToSpeech.JavaClass.init(TAndroidHelper.Context, ttsListener); end; procedure TForm4.SpeakOut(const s : string); var text : JString; begin text := StringToJString(s); tts.speak(text, TJTextToSpeech.JavaClass.QUEUE_FLUSH, nil); end; { TForm4.TttsOnInitListener } constructor TForm4.TttsOnInitListener.Create(AParent: TForm4); begin inherited Create; FParent := AParent end; procedure TForm4.TttsOnInitListener.onInit(status: Integer); var Result : Integer; begin if (status = TJTextToSpeech.JavaClass.SUCCESS) then begin //result := FParent.tts.setLanguage(TJLocale.JavaClass.US); result := FParent.tts.setLanguage(TJLocale.JavaClass.JAPAN); if (result = TJTextToSpeech.JavaClass.LANG_MISSING_DATA) or (result = TJTextToSpeech.JavaClass.LANG_NOT_SUPPORTED) then ShowMessage('This Language is not supported'); end else ShowMessage('Initilization Failed!'); end; constructor TForm4.Create(AOwner: TComponent); begin inherited; ttsListener := TttsOnInitListener.Create(self); end; destructor TForm4.Destroy; begin if Assigned(tts) then begin tts.stop; tts.shutdown; tts := nil; end; end; // ----------------------------------------------------------------------------- function ASocketReceiveData(ASocket: TBluetoothSocket; ATimeout: Cardinal): string; // IM920 からの応答 var AData : TBytes; ReadData : TBytes; i : integer; res : string; Ticks : Cardinal; idx : integer; loop : boolean; cnt : integer; begin res := ''; cnt := 0; SetLength(ReadData, 1024); idx := 0; Ticks := TThread.GetTickCount; loop := True; while loop and (cnt < 500) do begin Sleep(1); AData := ASocket.ReceiveData; if Length(AData) > 0 then begin for i := 0 to Length(AData) - 1 do begin ReadData[idx] := AData[i]; Inc(idx); // 終端 LF まで読む if (AData[i] = $0A) {or (AData[i] = $03)} or (idx >= 1024) then begin loop := False; break; end; end; end; Inc(cnt); if loop then loop := TThread.GetTickCount - Ticks < ATimeout; end; SetLength(ReadData, idx); res := TEncoding.ANSI.GetString(ReadData); result := Trim(res); // 制御コードを含まない end; procedure TForm4.Pwm1Click(Sender: TObject); // Pwm1, 2 出力 var AData : TBytes; res, cmd, s : string; ATimeout: Cardinal; d : double; lbl : TLabel; btn : TButton; i, m, n : integer; begin Label3.Text := ''; if (ASocket <> nil) and ASocket.Connected then begin ATimeout := 250; Timer1.Enabled := False; Sleep(500); btn := Sender as TButton; if btn = Pwm1 then lbl := Label8 else lbl := Label19; s := btn.Text; s := StringReplace(s, ' ', '', [rfReplaceAll]); d := StrToFloatDef(lbl.Text, 0); m := Trunc(d / 5.0 * 255); if m < 0 then m := 0; if m > 255 then m := 255; s := s + IntToHex(m, 2); cmd := 'TXDA'; for i := 1 to Length(s) do cmd := cmd + IntToHex(Ord(s[i-1]), 2); // Windows と異なる AData := TEncoding.ANSI.GetBytes(cmd + #13#10); // 送信 ASocket.SendData(AData); // 受信 for i := 0 to 2 do begin res := ASocketReceiveData(ASocket, ATimeout); if Length(res) > 2 then break; end; res := StringReplace(res, ',', '', [rfReplaceAll]); Label3.Text := res; n := Pos(':', res); if n > 0 then begin s := Copy(res, n + 1); if Pos('50574D4F4B', s) > 0 then begin // 'PWMOK' n := StrToIntDef(Copy(s, 11, 2), 0); if (n = 1) or (n = 2) then ToneGenerator.startTone(TJToneGenerator.JavaClass.TONE_PROP_ACK) else ToneGenerator.startTone(TJToneGenerator.JavaClass.TONE_PROP_NACK); end; end; Timer1.Enabled := True; end; end; procedure TForm4.Button2Click(Sender: TObject); // 接続先保存 var IniFile: TMemIniFile; begin IniFile := TMemIniFile.Create(System.IOUtils.TPath.Combine( System.IOUtils.TPath.GetDocumentsPath, 'IM920And.ini'), TEncoding.UTF8); with IniFile do begin try with ComboBox3 do begin if ItemIndex >= 0 then begin WriteString('Target', 'DeviceName', Items[ItemIndex]); ShowMessage('接続先: ' + Items[ItemIndex] + 'を保存しました.' + #13#10 + '次回起動時から有効になります.' + #13#10 + 'このアプリを再起動して下さい.'); end else ShowMessage('接続先が選択されていません.'); end; IniFile.UpdateFile; finally Free; end; end; end; procedure TForm4.Button3Click(Sender: TObject); // RY 2 個 ON/ OFF/ PLS var btn : TButton; cmd, s, rcv : string; i, n : integer; AData : TBytes; ATimeout : Cardinal; v : integer; lbl : TLabel; begin Label3.Text := ''; Atimeout := 250; if (ASocket <> nil) and ASocket.Connected then begin Timer1.Enabled := False; Sleep(500); btn := Sender as TButton; s := btn.Text; s := StringReplace(s, ' ', '', [rfReplaceAll]); cmd := 'TXDA'; for i := 1 to Length(s) do cmd := cmd + IntToHex(Ord(s[i - 1]), 2); // Windows と異なる AData := TEncoding.ANSI.GetBytes(cmd + #13#10); // 送信 ASocket.SendData(AData); // 受信 for i := 0 to 2 do begin rcv := ASocketReceiveData(ASocket, ATimeout); if Length(rcv) > 2 then break; end; rcv := StringReplace(rcv, ',', '', [rfReplaceAll]); Label3.Text := rcv; n := Pos(':', rcv); if n > 0 then begin cmd := Copy(rcv, n + 1, 8); if cmd = '5259514B' then begin // 'RYOK' s := Copy(rcv, n + 8 + 1); // スイッチの状態 v := StrToIntDef(Copy(s, 3), -1); for i := 0 to 3 do begin lbl := FindComponent('Label' + IntToStr(i + 22)) as TLabel; if lbl <> nil then begin if v < 0 then begin lbl.FontColor := CO_NG; end else begin if v and IntPower(2, i) > 0 then begin if lbl.FontColor <> CO_ON then lbl.FontColor := CO_ON; end else begin if lbl.FontColor <> CO_OFF then lbl.FontColor := CO_OFF; end; end; end; end; // リレー操作のアンサーバック v := StrToIntDef(Copy(s, 1, 2), 0); if (v = 1) or (v = 2) then ToneGenerator.startTone(TJToneGenerator.JavaClass.TONE_PROP_ACK) else ToneGenerator.startTone(TJToneGenerator.JavaClass.TONE_PROP_NACK); end; end; Timer1.Enabled := True; end; end; procedure TForm4.FormCreate(Sender: TObject); var IniFile: TMemIniFile; // uses .... System.IniFiles; begin Label6.Text := ''; Label7.Text := ''; Label3.Text := ''; Label1.Text := ''; // 縦画面に固定 Application.FormFactor.Orientations := [TFormOrientation.Portrait, TFormOrientation.InvertedPortrait]; // use ..... System.IOUtils; IniFile := TMemIniFile.Create(System.IOUtils.TPath.Combine( System.IOUtils.TPath.GetDocumentsPath, 'IM920And.ini'), TEncoding.UTF8); with IniFile do begin try BtDeviceHead := ReadString('Target', 'DeviceName', 'RN42-C7CF'); finally Free; end; end; // TTS InitTTS; // ブザー ToneGenerator := TJToneGenerator.JavaClass.init( TJAudioManager.JavaClass.STREAM_ALARM, TJToneGenerator.JavaClass.MAX_VOLUME); // Bruetooth スレッド Timer1.Interval := 10; Timer1.Enabled := True; ThBt := TBtThread.Create; end; procedure TForm4.FormDestroy(Sender: TObject); // フォーム破棄 begin if ASocket <> nil then begin ASocket.Close; ASocket.Free; ASocket := nil; end; end; procedure TForm4.Timer1Timer(Sender: TObject); // インターバルタイマー var ATimeout : Cardinal; AData : TBytes; i, j, n, m : integer; s, cmd, rcv : string; Ticks : Cardinal; v : integer; sv : string; lbl : TLabel; begin if not ((GCMDMODE = cmdSCCONNECT) and ASocket.Connected) then begin Inc(OpenMsecCnt); Label7.Text := IntToStr(OpenMsecCnt * 10) + ' msec'; // エラー if GCMDMODE = cmdSCNG then begin Timer1.Enabled := False; ShowMessage(BTDeviceHead + ' 接続エラーです.'); end; if OpenMsecCnt >= 200 then begin Timer1.Enabled := False; ShowMessage(BTDeviceHead + ' 接続できませんでした.'); end; end; if (GCMDMODE = cmdSCCONNECT) and ASocket.Connected then begin Timer1.Interval := 500; Timer1.Enabled := False; Ticks := TThread.GetTickCount; ATimeout := 250; // DI x 4 点 + AI x 4 点の値を取得 s := 'SARD'; cmd := 'TXDA'; for i := 1 to Length(s) do cmd := cmd + IntToHex(Ord(s[i - 1]), 2); // Windows と異なる AData := TEncoding.ANSI.GetBytes(cmd + #13#10); // 送信 ASocket.SendData(AData); // 受信 for i := 0 to 2 do begin rcv := ASocketReceiveData(ASocket, ATimeout); if Length(rcv) > 2 then break; end; rcv := StringReplace(rcv, ',', '', [rfReplaceAll]); Label1.Text := rcv; n := Pos(':', rcv); if n > 0 then begin cmd := Copy(rcv, n + 1, 8); s := Copy(rcv, n + 8 + 1); if cmd = '53415244' then begin // 'SARD' v := StrToIntDef('$' + Copy(s, 1, 2), -1); // スイッチ入力(4 点) Label22 ~ 25 の色を変える for j := 0 to 3 do begin lbl := FindComponent('Label' + IntToStr(j + 22)) as TLabel; if lbl <> nil then begin if v < 0 then begin lbl.FontColor := CO_NG; end else begin if v and IntPower(2, j) > 0 then begin swAryNew[j] := True; if lbl.FontColor <> CO_ON then lbl.FontColor := CO_ON; end else begin swAryNew[j] := False; if lbl.FontColor <> CO_OFF then lbl.FontColor := CO_OFF; end; end; end; end; m := (Length(s) - 2) div 4; v := 0; // アナログ入力(4 点)Label26~29 の Text を変える for j := 0 to m - 1 do begin sv := Copy(s, 3 + j * 4, 4); if sv <> '' then v := StrToIntDef('$' + sv, 0); lbl := FindComponent('Label' + IntToStr(j + 26)) as TLabel; if lbl <> nil then lbl.Text := Format('%.2f', [(v / 1023) * 1.1]); end; end; end; Label7.Text := IntToStr(TThread.GetTickCount - Ticks) + ' msec'; // テキストスピーチ for i := 0 to 3 do begin s := ''; if not swAryOld[i] and swAryNew[i] then s := 'SW ' + IntToStr(i + 1) + ' オン' + #13 else if swAryOld[i] and not swAryNew[i] then s := 'SW ' + IntToStr(i + 1) + ' オフ' + #13; if s <> '' then SpeakOut(s); end; swAryOld := swAryNew; Timer1.Enabled := True; end; end; procedure TForm4.updn1Click(Sender: TObject); // PWM UP/DOWN var rect : TRectangle; n : integer; s : string; lbl : TLabel; d : double; begin rect := Sender as TRectangle; s := rect.Name; n := StrToIntDef(Copy(s, Length(s)), 0); if n > 0 then begin if n <= 4 then lbl := Label8 else lbl := Label19; d := StrToFloatDef(lbl.Text, 0); case n of 1, 5 : d := d + 0.1; 2, 6 : d := d - 0.1; 3, 7 : d := d + 1; 4, 8 : d := d - 1; end; if d < 0 then d := 0; if d > 5 then d := 5; lbl.Text := Format('%.1f', [d]); end; end; end.
//IM920 で Serial I/O // 2019/07/24 by f.izawa // 2019/07/29 RY ON/OFF のアンサーに、SW 入力の状態監視を追加 // 2019/08/04 RY_PLS のアンサーを即座に返すに変更 // 2019/08/05 RY のピン番号を 11, 12 に変更 #include <SoftwareSerial.h> // Pin 8 ~ 11 は、IM920 にて使用 #define rxPin 8 #define txPin 9 #define busyPin 10 // DI x 4 点 byte swPins[] = {2, 3, 4, 7}; // DO x 2 点 byte ryPins[] = {11,12}; // PWM x 2 点 byte pwmPins[] = {5, 6}; String rcvStr; String cmdStr; byte busy; // set up a new serial port SoftwareSerial IM920Serial = SoftwareSerial(rxPin, txPin); // // n の k 乗 int intPow(int n, int k){ int res = 1; for (int i = 0; i < k; i++) res *= n; return res; } // // 桁そろえ(文字列の前に"0"を追加) void strDigits(String &str, int digits){ int len = str.length(); if (len < digits){ for (int i = len; i < digits; i++){ str = "0" + str; } } } // int hexToInt(String hex){ char buf[5]; hex.getBytes(buf, 5); // char[] に return (int)strtol(buf, NULL, 16); // HexToInt } // void setup() { Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } Serial.println("IM920 Serial!"); // ピンモード設定 pinMode(busyPin, INPUT); for (int i = 0; i < sizeof(swPins); i++) pinMode(swPins[i], INPUT_PULLUP); // プルアップ for (int i = 0; i < sizeof(ryPins); i++) pinMode(ryPins[i], OUTPUT); // 出力をLOWに for (int i = 0; i < sizeof(ryPins); i++) digitalWrite(ryPins[i], LOW); // アナログ入力基準電圧(内部 1.1V) analogReference(INTERNAL); for (int i = 0; i < 4; i++) analogRead(i); // PWM 初期化 for (int i = 0; i < sizeof(pwmPins); i++) analogWrite(pwmPins[i], 0); // シリアル初期化 IM920Serial.begin(19200); } // // void loop() { // 受信 if (IM920Serial.available()) { rcvStr = IM920Serial.readStringUntil(0x0A); rcvStr.trim(); Serial.println(rcvStr); // 受信文字列をそのまま表示 rcvStr.replace(",", ""); // "," を削除 int idx = rcvStr.indexOf(":"); if (idx >= 0){ rcvStr = rcvStr.substring(idx + 1); // ":"以降の文字列 int n = int(rcvStr.length() / 2); //繰り返し回数 String s, sval; long m; cmdStr = ""; for (int i = 0; i < n; i++){ // 2文字ずつ取り出し数値に変換 m = hexToInt(rcvStr.substring(i * 2, i * 2 + 2)); cmdStr += char(m); // ascii コードを文字に変換 } // 送信側での 16 進変換前のコマンド文字列 Serial.println(cmdStr); // コマンドを表示 if (cmdStr == "SWRD"){ // デジタル 4 点 // busy 解除待ち do { busy = digitalRead(busyPin); } while (busy != 0); // SW1 ~ 4 の状態を集計 int sw = 0; for (int i = 0; i < sizeof(swPins); i++){ if (digitalRead(swPins[i]) == LOW) sw = sw + intPow(2, i); } sval = String(sw, HEX); strDigits(sval, 2); // 桁合わせ s = "TXDA53575244" + sval + "\r\n"; // "SWRD" IM920Serial.print(s); delay(30); } else if (cmdStr == "ADRD"){ // アナログ 4 点 // busy 解除待ち do { busy = digitalRead(busyPin); } while (busy != 0); s = "TXDA41445244"; // "ADRD" // A0 ~ A1 の状態を集計 for (int i = 0; i < 4; i++){ sval = String(analogRead(i), HEX); strDigits(sval, 4); s += sval; } s += "\r\n"; IM920Serial.print(s); delay(30); } else if (cmdStr == "SARD"){ // デジタル 4 点 + アナログ 4 点 // busy 解除待ち do { busy = digitalRead(busyPin); } while (busy != 0); s = "TXDA53415244"; // "SARD" // SW1 ~ 4 の状態を集計 int sw = 0; for (int i = 0; i < sizeof(swPins); i++){ if (digitalRead(swPins[i]) == LOW) sw += intPow(2, i); } sval = String(sw, HEX); strDigits(sval, 2); s += sval; //sval = ""; // A0 ~ A3 の状態を集計 for (int i = 0; i < 4; i++){ sval = String(analogRead(i), HEX); strDigits(sval, 4); s += sval; } s += "\r\n"; IM920Serial.print(s); delay(30); } else { // RY ON/OFF s = cmdStr.substring(0,2); if (s == "RY"){ // ex : "RY1ON", "RY2PLS", "RY2OFF" s = cmdStr.charAt(2); int ryNo = s.toInt(); if (ryNo >= 1 && ryNo <= 2){ if (cmdStr.indexOf("PLS") > 0){ digitalWrite(ryPins[ryNo - 1], HIGH); // 2019/08/04 コメントアウト //delay(500); //digitalWrite(ryPins[ryNo - 1], LOW); } else { int val = LOW; if (cmdStr.indexOf("ON") > 0) val = HIGH; digitalWrite(ryPins[ryNo - 1], val); } // busy 解除待ち do { busy = digitalRead(busyPin); } while (busy != 0); s = "TXDA5259514B"; // "RYOK"; if (ryNo == 1) s += "01"; else s += "02"; // SW1 ~ 4 の状態を集計 int sw = 0; for (int i = 0; i < sizeof(swPins); i++){ if (digitalRead(swPins[i]) == LOW) sw += intPow(2, i); } sval = String(sw, HEX); strDigits(sval, 2); s += (sval + "\r\n"); IM920Serial.print(s); delay(30); // 2019/08/04 追加 if (cmdStr.indexOf("PLS") > 0){ delay(470); digitalWrite(ryPins[ryNo - 1], LOW); } } } else if (s == "PW"){ // ex "PWM1FF" s = cmdStr.charAt(3); int pwmNo = s.toInt(); // 1 or 2 if (pwmNo == 1 || pwmNo == 2){ m = hexToInt(cmdStr.substring(4)); if (m > 255) m = 255; analogWrite(pwmPins[pwmNo - 1], m); // busy 解除待ち do { busy = digitalRead(busyPin); } while (busy != 0); s = "TXDA50574D4F4B"; // "PWMOK"; if (pwmNo == 1) s += "01"; else s += "02"; s += "\r\n"; IM920Serial.print(s); delay(30); } } } } } delay(1); }