2019.01.11

VSCodeでArduino

Microsoftが作った「Visual Studio Code」arduino拡張機能を使ってみるを参考にしてVSCodeを導入してみました。

Vscode

インテリセンスを利用したコード編集が出来ることに加えて、ボードや書込装置の種類を指定したり、コンパイルしたプログラムをArduinoにアップロードしたり出来て、シリアルモニターも使用出来ますので、快適な開発環境を得ることが出来ます(ただし、Arduino IDEのインストールは必須です。またPyhton2.7Xも必要なようです)。

| | コメント (0) | トラックバック (0)

2019.01.04

ArduinoでIoT

一時期Raspberry piを使ってIoTらしきことを試していましたが、今回(といいますか今更といいますか)Arduinoを使用したIoTらしきことを試してみました。
 
Raspberry pi(3)では「標準でWiFi」が使用出来たり、「様々な開発環境」を選択したり出来るというメリットがありますが、その一方で実用性を考えた時に「処理速度が上がらない」ことや「Linux(Raspbian)上で動かすため何らかの問題が起きた時の対応にある程度の知識が必要」といったデメリットも感じていました。
 
そこで、今更ながらArduinoってどんな感じなのかちょっと試してみたところ、「標準ではWiFiどころかEthernet(有線LAN)も使えない」、開発環境はArduino IDEくらいしか選択肢がなくしかも使いやすいとは言えない」といったデメリットはありますが、「OSを介さないので余計な知識が不要」、「OSを介していないこともあってか処理速度が早い」といったメリットがあることから、メンテナンス性や処理速度から工場内などのちょっとしたIoT化にはRaspberry piよりも向いているのではないかと感じました。
#Raspberry piの場合、故障してボードを入れ替える場合にはOS(Raspbian)のインストールから始めなくてはなりませんが、Arduinoでは新しいボードにArduino IDEで該当するスケッチを書き込むだけで良いので、だいぶ敷居が低いのではと思います。
 
今回は2種類のボードを入手してその実用性を確認してみました。
今回は、アナログ入力4ポートを使用して、光センサー(CDSセル)の信号を入力してその状態をMQTTサーバーに送信する処理を行っています。
設備の操作盤に配置されている運転中や完了ランプ、判定OK/NGランプに光センサーを貼り付けて設備の起動時と完了時の時間と完了時には判定OK/NG値をMQTTサーバーに送信する「設備稼働状態監視」を想定しました。
 
 
まずはWiFi接続が可能なESP8266を使用したWeMos D1 CH340 WiFi 開発ボード ESP8266 ESP-12F
これはアマゾンで810円で購入しました。
Esp8266
#値段は安いのですが、安いなりに技適を取得していない、MACアドレスが登録されていない、ところに注意が必要です。
 
これはWiFi接続も問題なく、ボード本体だけでデータをMQTTサーバーに送信することが出来たのですが、ESP8266を使用しているための制約なのかアナログポートが1つしか使えないことが判明したため実用化は保留判定です。

次に試したのがArduino Uno互換ボードにEthernetシールドHiLetgo W5100を載せたものです
Ethernetシールドはアマゾンで940円でした(現在は1,200円のようです)。
Uno_eth
 
こちらはアナログポートがフルで使用出来、有線LANへの接続も全く問題なく動作しました。まだ長時間の動作確認は出来ていませんが、これであればEthrnetシールドは必要であるものの、充分実用化が可能だと思います。
 
尚、MQTTサーバーはWindows版のMosquittoをサービスで動かして、ScriberはLazarusを用いで作成したもので全体の動作確認を行いました。
Iotmonitor
 
 
以下が確認用のスケッチです。


/*
設備操作盤の各種ランプの点灯/消灯を光センサ(CDSセル)で読み取って、その状況を
MQTTサーバーに送信するためのプラットホーム

MQTT送信データ形式:経過時間(X.XXX秒), 完了ランプ, OKランプ, NGランプ, 運転中
ランプ, 各ランプのアナログ入力値...
各ランプは点灯=1, 消灯=0で表現する

MQTT Publisher by Ethernet shield
PubSubClientライブラリをインストールしたフォルダ内のサンプルを参考に修正

光センサ(CDSセル)の扱いについてはo0e0oさんの以下の情報を参考にしました
https://matome.naver.jp/odai/2142568740959927501

*/

#include
#include
#include

#define SGN_DONE 0 // (A0)完了ランプ
#define SGN_OK 1 // (A1)OKランプ
#define SGN_NG 2 // (A2)NGランプ
#define SGN_RUN 3 // (A3)運転中ランプ:完了ランプがない場合は代わりに使用する
#define LIGHT_ON 500 // ランプ点灯判定しきい値:実際の条件に合わせて変更が必要

// MQTTサーバーアドレス:実際の接続条件に合わせて修正が必要
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192, 168, 11, 60); // デバイスの固定IPアドレス・・・端末毎に設定する
IPAddress gateway(192,168, 11, 1); // ゲートウェイ(WiFiターミナルのIPアドレス)
IPAddress subnet(255, 255, 255, 0); // サブネットマスク
IPAddress DNS(192, 168, 11, 1); // DNS

const char* mqtt_server = "192.168.11.18";
// MQTTサーバー接続識別子:何でも良いが会社毎や工場毎で設定することを推奨
const char* clientId = "Publisher-001";
// トピック名(何でも良いが端末が増えた際に識別が容易になるようにすべき)
// 組織名や製品等でiotを変更し、端末毎にdev001を連番で変更することを推奨
const char* mqtt_id = "iot/dev001";

EthernetClient ethClient;
PubSubClient client(ethClient);

char msg[128]; // メッセージ作成バッファ

long lasttm = 0; // Publish時間計測用
long ledfrq = 0; // 内蔵LED点滅タイミング計測用
bool led = false; // 内蔵LED点滅用フラグ

int sgnl_done = 0; // 完了ランプ
int sgnl_ok = 0; // OKランプ
int sgnl_ng = 0; // NGランプ
int sgnl_run = 0; // 運転中ランプ

int done_on = 0; // ランプ点灯=1, 消灯=0
int ok_on = 0;
int ng_on = 0;
int run_on = 0;

int done_prev = 0; // 直前のランプ点灯/消灯状態(1 or 0)
int ok_prev = 0;
int ng_prev = 0;
int run_prev = 0;


// WiFi接続処理
void setup_eth() {
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);

// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}

digitalWrite(LED_BUILTIN, HIGH); // WiFiが接続されたら内蔵LEDをONにする

randomSeed(micros());

Serial.println("");
Serial.println("Ethernet connected");
Serial.println("IP address: ");
Serial.println(Ethernet.localIP());
}

// MQTTのSubScriber データ受信時にコールされる
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}

// MQTTサーバー接続処理
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect(clientId)) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish("outTopic", "hello world");
// ... and resubscribe
client.subscribe("inTopic");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 1 seconds before retrying
delay(1000);
}
}
}

// Arduino初期化
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT); // 内蔵LEDポートの初期化
digitalWrite(LED_BUILTIN, LOW); // 内蔵LEDをOFFにする

setup_eth();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}

// メイン
void loop() {
double sec;
char s[16];

if (!client.connected()) {
reconnect();
}
client.loop();

// 現在時間取得
long now = millis();
// MQTTサーバー接続が成功して正常に可動している時は内蔵LEDを点滅させる
if (now - ledfrq > 1000) {
if (led) {
digitalWrite(LED_BUILTIN, LOW); // 内蔵LEDをOFFにする
led = false;
} else {
digitalWrite(LED_BUILTIN, HIGH); // 内蔵LEDをOFFにする
led = true;
}
ledfrq = now;
}
// 光センサ値読み込み
sgnl_done = analogRead(SGN_DONE);
sgnl_ok = analogRead(SGN_OK);
sgnl_ng = analogRead(SGN_NG);
sgnl_run = analogRead(SGN_RUN);

// 光センサの入力値によって点灯ON/OFFを判定する
if (sgnl_done < LIGHT_ON){
done_on = 1;
}else{
done_on = 0;
}
if (sgnl_ok < LIGHT_ON){
ok_on = 1;
}else{
ok_on = 0;
}
if (sgnl_ng < LIGHT_ON){
ng_on = 1;
}else{
ng_on = 0;
}
if (sgnl_run < LIGHT_ON){
run_on = 1;
}else{
run_on = 0;
}
// いづれかのランプが変化(点灯or消灯)したらPublishする
if ((done_on != done_prev) | (ok_on != ok_prev) | (ng_on != ng_prev) | (run_on != run_prev)){
// Publish用のデータ文字列を作成して送信(実際の入力値も確認出来るように後ろに追加する)
sec = (now - lasttm) / 1000.0;
dtostrf(sec, -1, 2, s); // secを小数点以下2桁で文字列にする(unoでは%fが使えない)
snprintf(msg, sizeof(msg), "%s, %d, %d, %d, %d, %d, %d, %d, %d", s, done_on, ok_on, ng_on, run_on, sgnl_done, sgnl_ok, sgnl_ng, sgnl_run);
client.publish(mqtt_id, msg);
Serial.println(msg);

lasttm = now;
done_prev = done_on;
ok_prev = ok_on;
ng_prev = ng_on;
run_prev = run_on;
}
//delay(10); // delayを入れない場合は私が確認したunoの場合2~5msでループします
}

| | コメント (3) | トラックバック (0)