Joseph Halfmoon
手元で使っている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スクリプトが以下に。
import paho.mqtt.client as mqtt
def on_connect ( client, userdata, flags, rc ) :
print ( "Connected with result code " + str ( rc ))
def on_message ( client, userdata, msg ) :
print ( msg.topic + " " + str ( msg.payload ))
client.on_connect = on_connect
client.on_message = on_message
client. connect ( "192.168.2.121" , 1883 , 60 )
client. publish ( "PC/Status" , "Running" )
ser = serial. Serial ( 'COM3' , 9600 ,timeout= None )
client. publish ( "PC/Count" , str ( counter ))
except KeyboardInterrupt:
print ( "Terminated by KBD interrupt." )
client. publish ( "PC/Status" , "STOP" )
if __name__ == '__main__' :
#! /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()
#! /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>
#define BuzzorWait (3000)
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;
ad22100val = analogRead ( ad22100pin ) ;
return - ( 562650-2000*ad22100val ) /9207;
digitalWrite ( GREEN, OFF ) ;
void setLEDs ( char command ) {
digitalWrite ( GREEN, OFF ) ;
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
Serial. println ( "Failed to create 'OLED' thread." ) ;
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" ) ;
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" ) ;
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" ) ;
u8x8. setFont ( u8x8_font_chroma48medium8_r ) ;
u8x8. drawString ( 0,0, "TASK->OLED" ) ;
u8x8. drawString ( 0,1, "TASK1: " ) ;
u8x8. drawString ( 0,2, "TEMP : " ) ;
Serial. println ( "Starting scheduler ..." ) ;
Serial. println ( millis ()) ;
vTaskDelay ( configTICK_RATE_HZ ) ;
void oled_thread_func ( void *pvParameters )
if ( xQueueReceive ( msgQueue1, ( void * ) &ts1, 0 ) == pdTRUE ) {
u8x8. drawString ( 7,1,buf ) ;
if ( xQueueReceive ( msgQueue2, ( void * ) &ts2, 0 ) == pdTRUE ) {
u8x8. drawString ( 7,2,buf ) ;
sprintf ( buf, "%d" , rCount ) ;
u8x8. drawString ( 0,3,buf ) ;
void task1_func ( void *pvParameters )
while ( Serial1. available ()){
msg1 = ( int ) Serial1. read () ;
if ( xQueueSend ( msgQueue1, ( void * ) &msg1, 2 ) == pdTRUE ) {
setLEDs (( char )( msg1 & 0xFF )) ;
void task2_func ( void *pvParameters )
if ( xQueueSend ( msgQueue2, ( void * ) &msg2, 2 ) != pdTRUE ) {
void task3_func ( void *pvParameters )
ulTaskNotifyTake ( pdTRUE, portMAX_DELAY ) ;
analogWrite ( buzzor, BuzzorON ) ;
analogWrite ( buzzor, BuzzorOFF ) ;
#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);
}
}
#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を点灯/消灯できるようになりました。当たり前か。
モダンOSのお砂場(81)UNO R4でFreeRTOS、第2のUARTで指令を送る(だけ) へ戻る
モダンOSのお砂場(83)UNO R4でFreeRTOS、Renesas RA4M1の用語基礎知識 へ進む