1 デジタルピン10本のON/OFFが出来ること
2 現在のON/OFF状態を取得できること
3 アナログピン6本の電圧が計れること
4 パソコンから通信を確認できること
この4項目の事柄が出来ればだいぶ色々な制御に使えそうです。
他に必要だと思える機能として、
パソコンとの通信のエラー保護(シリアル通信のパリティと命令を2重で送る事でエラーの確立を非常に下げる事が出来そうです)
パソコンの故障などで通信が出来ない場合(通信途絶後60秒でON/OFFを初期状態に戻してOFF側に持ってゆく)
以上の機能が必要だと考えられます。
▼書き換えプロトコルを考える
1 デジタルピン10本のON/OFFが出来ること
これを実現するに当たって、次のようなコマンドを考えました
w0000000000[CR][LF]
先頭にwを付ける事により書き込みコマンドを表します。
その後ろにはデジタルピン2番から12番までの10ビットをASCII文字の0/1で表現し最後にCRLFを取り付けます。
この命令を2回送信することによりデジタルピンの出力が書き換えられて、書き換え完了後にはOK、書き換え失敗時にはNGの文字を返します。
w1000000000[CR][LF]
この文字列を送るとデジタルピン2番がONになり、他のピンはOFFになります。
w0000000001[CR][LF]
この文字列では11番がONになり、他のピンはOFFになります。
w1111111111[CR][LF]
2番から11番まですべてONになります
▼読み出しプロトコルを考える
2 現在のON/OFF状態を取得できること
実現するに当たって、次のコマンドを考えました。
r[CR][LF]
この命令を2回送信することにより、現在のON/OFF状態が返ってきます。
通信エラーを回避するために2回、同じ文字列が返ってきます。
3 アナログピン6本の電圧が計れること
実現するに当たって、次のコマンドを考えました。
a[CR][LF]
この命令を2回送信することにより、現在のアナログピンA0 A1 A2 A3 A4 A5 の電圧をカンマ区切り + この命令を実行した回数 が返ってきます。
通信エラーを回避するために2回、同じ文字列が返ってきます。
4 パソコンから通信を確認できること
パソコン側ソフトを書いていて必要だと感じたのが、テストコマンド、実現するに当たって、次のコマンドを考えました。
t[CR][LF]
コマンドを送ると常に NG[cr][lf] が返ってくるコマンドを作成しました。
以上を踏まえて、実際に使用するために必要となる機能を追加して作成してみました。
パソコンからは、9600bps、偶数パリティ、ストップビット1で接続する必要があります。
#include <avr/wdt.h>
#define SYSTEM_TIMEOUT 60 //通信途絶後60秒で初期化
#define BUF_SIZE 255
void setup() {
wdt_enable(WDTO_4S); //WDTを4秒で設定し有効化
//デジタルピン2番から11番まで出力に設定
for (int i = 2; i < 12; i++) {
pinMode(i, OUTPUT);
}
Serial.begin(9600, SERIAL_8E1); //偶数パリティ
serial_wdt_reset();
}
char buff[BUF_SIZE];
char buff_old[BUF_SIZE];
char pin_status[11] = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '\0' }; //デジタルピン2番から11番の状態を保持し一度に転送
unsigned int str_pt;
unsigned long int timeout_timer;
//ピンの状態をまとめて転送
void trans_pin_status() {
int j = 0;
for (int i = 2; i < 12; i++) {
if (pin_status[j] == '1') {
digitalWrite(i, LOW); //出力先がアクティブローのため反転
} else {
digitalWrite(i, HIGH);
}
j++;
}
}
//ピンに転送前の元データを初期化する
void reset_pin_status() {
for (int i = 0; i < 10; i++) {
pin_status[i] = '0';
}
pin_status[10] = '\0';
}
unsigned long serial_wdt_counter;
//シリアル通信がSYSTEM_TIMEOUT秒停止した場合に出力をリセットする
void serial_wdt() {
unsigned long pos = ((unsigned long)millis()) - serial_wdt_counter;
if (pos > ((unsigned long)((unsigned long)SYSTEM_TIMEOUT * 1000))) {
reset_pin_status(); //タイムアウトしているため初期化
}
}
//リセットタイマーをリセットする
void serial_wdt_reset() {
serial_wdt_counter = millis();
}
void loop() {
wdt_reset(); //WDTのリセット
serial_wdt();
if (Serial.available()) {
char c = char(Serial.read());
timeout_timer = millis(); //msを取得
buff[str_pt] = c;
str_pt++;
if (c == '\n') { //LFを終端文字とする
buff[str_pt] = '\0';
if (strcmp(buff_old, buff)) {
//比較文字列と違うため保存
strcpy(buff_old, buff);
Serial.println("NG");
} else {
//比較文字列と同じため命令を実行
if (buff[0] == 'w') {
int j = 0;
for (int i = 1; i <= 10; i++) {
if (buff[i] == '1') {
pin_status[j] = '1';
} else {
pin_status[j] = '0';
}
j++;
}
Serial.println("OK");
serial_wdt_reset();
} else if (buff[0] == 'r') {
//2回出力
Serial.print("r,");
Serial.println(pin_status);
Serial.print("r,");
Serial.println(pin_status);
serial_wdt_reset();
} else if (buff[0] == 'a') {
static unsigned long c;
int _ar[6];
_ar[0] = analogRead(A0);
_ar[1] = analogRead(A1);
_ar[2] = analogRead(A2);
_ar[3] = analogRead(A3);
_ar[4] = analogRead(A4);
_ar[5] = analogRead(A5);
Serial.print("a,");
for (int i = 0; i < 6; i++) {
Serial.print(_ar[i]);
Serial.print(',');
}
Serial.println(c);
Serial.print("a,");
for (int i = 0; i < 6; i++) {
Serial.print(_ar[i]);
Serial.print(',');
}
Serial.println(c++);
serial_wdt_reset();
} else if (buff[0] == 't') {
Serial.println("NG");
}
buff_old[0] = '\0'; //命令が終了したため旧命令は破棄
}
str_pt = 0;
}
//バッファサイズを超えないようにする
if (str_pt >= BUF_SIZE) {
str_pt = 0;
}
} else {
//データを受信せずに500ms経過した場合にはタイムアウトで初期化
if (str_pt) {
if ((millis() - timeout_timer) > 500) {
str_pt = 0;
}
}
}
trans_pin_status();
}

上記プログラムをArduinoに書き込んでD2からD11番のデジタルピンにLEDを取り付けてデバックしている所です。

アクティブローのリレーモジュールに接続して動作を確認している所です。
■シリアル通信でリレーモジュールを操作するパソコンソフト
VisualBasic 6.0で適当に作りました。
プロジェクトを開いて実行すると、次のような画面が開きます。

Aの部分がデジタルピンのON/OFFに対応し色が変わり、ボタンを押すとON/OFFが切り替わります。
Bの部分が自動制御された場合のON/OFF状態を色で表します。
Cの部分がアナログピン6本のデータが表示されます。
Dの自動制御チェックボックスがありますが、ここをONにするとフォームにある自動制御表示のB部分が実際のポートA部分に転送されて制御が開始されます。
制御関数のPrivate Sub auto_control_stub() が2秒毎に実行されるようになっていますので制御したい機器に合わせて書き換えてください。
Eの部分はA命令を使用したカウンタの値を表示しています。

Form1の下の方に auto_control_stub() 部分がありますのでココを書き換えると自動制御ができるようになります。
VisualBasic6.0で作成したプログラムのダウンロードはこちら