モダンOSのお砂場(82)UNO R4でFreeRTOS、ブラウザからUNO R4のLEDを操作

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ダッシュボード画面にアクセスすればこんな感じです。Dashboard

ステータスがRunningとなっていれば、PC側の中継ソフトと「握れた」状態なので、下の方にあるRGBのスイッチをONしたりOFFしたりすると、ああら不思議、Arduino UNO R4に接続してあるLEDが点灯したり消灯したりします。こんな感じ。UNOR4_DUT_BBs

NodeRED側の設定

上流のNodeRED側は、以下の別シリーズで「やっつけ」てありました。

ブロックを積みながら(136) Node-RED、PCをMQTT接続、マイコン接続の準備ね

上記ではパソコンまでで止まっており、3個のLEDに対応したスイッチになっていなかったので、以下のNodeREDフローのように追加してあります。NodeRedFlow

やっていることは簡単で、MQTTのトピック PC/R に紐づけられているダッシュボードスイッチがONになったらpayloadに大文字の「R」を送り、OFFになったら小文字の「r」を送るというだけのもの。以下PC/G、PG/Bも同文であります。

「中継」パソコンの設定

上記により、MQTTブローカにパブリッシュされた情報は、中継機となっているパソコン上で走っているPython3スクリプトによって受信され、FT232RLに向かって送出されます。中継しているPython3スクリプトが以下に。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#! /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()
#! /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側のハード回路図が以下に。UNOR4_DUT_Schematic

既に前回記事で、FT232RLから受信した「コマンド」文字をOLEDに表示するところまでやってあったので、今回は受信「コマンド」に応じてLEDを点灯したり消灯したりする制御を追加するだけです。ソースが以下に。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#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); } }
#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の用語基礎知識 へ進む