モダン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スクリプトが以下に。

#! /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を点灯したり消灯したりする制御を追加するだけです。ソースが以下に。

#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の用語基礎知識 へ進む