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);
}