手元で使っているArduino UNO R4は「お求めやすい」Minimaです。WiFiの搭載はありません。そこで2番目のUARTを介して伝統的なUSBシリアルインタフェースFT232RLを経由してホストPCへ接続。ホスト上で動作するPythonスクリプトでラズパイ上で動いているMQTTブローカからの指令を得ることに。
※「モダンOSのお砂場」投稿順Indexはこちら
※Arduino IDE上で「スケッチ」形式のソースからFreeRTOSを使って実験してみてます。ターゲット機はArduino UNO R4 Minima。泣く子も黙る?ArmコアのルネサスRA4M1マイコン搭載機です。
動作はこんな感じ
サーバー機と化しているラズパイ3機のネットワークに接続しているパソコンでもスマホでもブラウザを開いてNodeREDダッシュボード画面にアクセスすればこんな感じです。
ステータスがRunningとなっていれば、PC側の中継ソフトと「握れた」状態なので、下の方にあるRGBのスイッチをONしたりOFFしたりすると、ああら不思議、Arduino UNO R4に接続してあるLEDが点灯したり消灯したりします。こんな感じ。
NodeRED側の設定
上流のNodeRED側は、以下の別シリーズで「やっつけ」てありました。
ブロックを積みながら(136) Node-RED、PCをMQTT接続、マイコン接続の準備ね
上記ではパソコンまでで止まっており、3個のLEDに対応したスイッチになっていなかったので、以下のNodeREDフローのように追加してあります。
やっていることは簡単で、MQTTのトピック PC/R に紐づけられているダッシュボードスイッチがONになったらpayloadに大文字の「R」を送り、OFFになったら小文字の「r」を送るというだけのもの。以下PC/G、PG/Bも同文であります。
「中継」パソコンの設定
上記により、MQTTブローカにパブリッシュされた情報は、中継機となっているパソコン上で走っているPython3スクリプトによって受信され、FT232RLに向かって送出されます。中継しているPython3スクリプトが以下に。
#! /usr/bin/python3 import paho.mqtt.client as mqtt import serial import time def on_connect(client, userdata, flags, rc): print("Connected with result code " + str(rc)) client.subscribe("PC/R") client.subscribe("PC/G") client.subscribe("PC/B") def on_message(client, userdata, msg): print(msg.topic + " " + str(msg.payload)) ser.write(msg.payload) client = mqtt.Client() client.on_connect = on_connect client.on_message = on_message client.connect("192.168.2.121", 1883, 60) client.loop_start() client.publish("PC/Status", "Running") ser = serial.Serial('COM3',9600,timeout=None) def main(): global client counter = 0 try: while True: client.publish("PC/Count", str(counter)) counter += 1 time.sleep(5) except KeyboardInterrupt: print("Terminated by KBD interrupt.") client.publish("PC/Status", "STOP") client.loop_stop() ser.close() if __name__ == '__main__': main()
Arduino UNO R4側
前回のチョイ変ですが、Arduino UNO R4側のハード回路図が以下に。
既に前回記事で、FT232RLから受信した「コマンド」文字をOLEDに表示するところまでやってあったので、今回は受信「コマンド」に応じてLEDを点灯したり消灯したりする制御を追加するだけです。ソースが以下に。
#include <Arduino_FreeRTOS.h> #include <U8x8lib.h> #define AWAITMAX1 (10) #define AWAITMAX2 (2000) #define BuzzorON (128) #define BuzzorOFF (0) #define BuzzorWait (3000) #define ON (1) #define OFF (0) U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE); TaskHandle_t oled_task, task1, task2, task3; const uint8_t queueSize = 1; QueueHandle_t msgQueue1, msgQueue2; int msg1, msg2; int ad22100pin = A3; int ad22100val = 0; int buzzor = 6; int RED = 5; int BLUE = 4; int GREEN = 3; int rCount = 0; int getTemperature() { ad22100val = analogRead(ad22100pin); return -(562650-2000*ad22100val)/9207; } void initLEDs() { pinMode(RED, OUTPUT); digitalWrite(RED, OFF); pinMode(BLUE, OUTPUT); digitalWrite(BLUE, OFF); pinMode(GREEN, OUTPUT); digitalWrite(GREEN, OFF); } void setLEDs(char command) { switch (command) { case 'R': digitalWrite(RED, ON); break; case 'G': digitalWrite(GREEN, ON); break; case 'B': digitalWrite(BLUE, ON); break; case 'r': digitalWrite(RED, OFF); break; case 'g': digitalWrite(GREEN, OFF); break; case 'b': digitalWrite(BLUE, OFF); break; default: xTaskNotifyGive(task3); } } void setup() { Serial.begin(115200); while (!Serial) { } Serial1.begin(9600); while (!Serial1) { } u8x8.begin(); u8x8.setPowerSave(0); initLEDs(); msgQueue1 = xQueueCreate(queueSize, sizeof(int)); msgQueue2 = xQueueCreate(queueSize, sizeof(int)); auto const rc_oled = xTaskCreate ( oled_thread_func, static_cast<const char*>("OLED Thread"), 512 / 4, nullptr, 1, &oled_task ); if (rc_oled != pdPASS) { Serial.println("Failed to create 'OLED' thread."); return; } auto const rc_task1 = xTaskCreate ( task1_func, static_cast<const char*>("Task1"), 512 / 4, nullptr, 1, &task1 ); if (rc_task1 != pdPASS) { Serial.println("Failed to create 'task1' thread"); return; } auto const rc_task2 = xTaskCreate ( task2_func, static_cast<const char*>("Task2"), 512 / 4, nullptr, 1, &task2 ); if (rc_task2 != pdPASS) { Serial.println("Failed to create 'task2' thread"); return; } auto const rc_task3 = xTaskCreate ( task3_func, static_cast<const char*>("Task3"), 512 / 4, nullptr, 1, &task3 ); if (rc_task3 != pdPASS) { Serial.println("Failed to create 'task3' thread"); return; } u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.setInverseFont(1); u8x8.drawString(0,0,"TASK->OLED"); u8x8.setInverseFont(0); u8x8.drawString(0,1,"TASK1: "); u8x8.drawString(0,2,"TEMP : "); Serial.println("Starting scheduler ..."); vTaskStartScheduler(); for( ;; ); /* Never! */ } /* NEVER CALLED! */ void loop() { Serial.println(millis()); vTaskDelay(configTICK_RATE_HZ); } void oled_thread_func(void *pvParameters) { char buf[16]; int ts1, ts2; while (1) { if (xQueueReceive(msgQueue1, (void *)&ts1, 0) == pdTRUE) { sprintf(buf, "%c", ts1); u8x8.drawString(7,1,buf); } if (xQueueReceive(msgQueue2, (void *)&ts2, 0) == pdTRUE) { sprintf(buf, "%d", ts2); u8x8.drawString(7,2,buf); } sprintf(buf, "%d", rCount); u8x8.drawString(0,3,buf); u8x8.refreshDisplay(); taskYIELD(); } } void task1_func(void *pvParameters) { int count = 0; while (1) { msg1 = 0x20; while(Serial1.available()){ delay(2); msg1 = (int)Serial1.read(); if (xQueueSend(msgQueue1, (void*)&msg1, 2) == pdTRUE) { rCount++; } setLEDs((char)(msg1 & 0xFF)); } vTaskDelay(AWAITMAX1); } } void task2_func(void *pvParameters) { int ecount = 0; while (1) { msg2 = getTemperature(); if (msg2 > 40) { xTaskNotifyGive(task3); } if (xQueueSend(msgQueue2, (void*)&msg2, 2) != pdTRUE) { ecount++; } vTaskDelay(AWAITMAX2); } } void task3_func(void *pvParameters) { while (1) { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); analogWrite(buzzor, BuzzorON); vTaskDelay(BuzzorWait); analogWrite(buzzor, BuzzorOFF); } }
これにてWebブラウザ画面からArduino UNO R4に接続されたLEDを点灯/消灯できるようになりました。当たり前か。