【Wio Terminal】Queue and WiFi
WiFiを使う時、Queueが使えない
元来Wio Terminalには、WiFiを使いながらFreeRTOSを使えないという問題があったが、宛ら吐哺握髪の執着的調査により、これに関しては一応の解決を示した。
但し、上記事で成し得たのは単なるマルチタスクであった。然らば次に問題となるのは、マルチタスク以外の機能である。そこでQueueを試したところ、案の定、従来の手法では正常に動かなかった。
StaticQueue
探してみれば、すぐにStaticQueue
というクラスの定義が見つかる。
しかし、茲から幾許の問題が露見した。
StaticQueueの問題
現状、私の把握するものを臚列する。
- 定義以外に何もない
- 認識されない
- エラーコード
2
定義以外に何もない
マルチタスク実現の手掛かりとなったThread
は、用例が存在したこともあって扱うまでに時間を要さなかった。
しかしStaticQueue
は、先に示した定義一箇所を除き、何処にも出現しない。全く記述がない。
とは言え、その構造は比較的単純であるため、用法はソースコードを読めば分かった。
認識されない
Thread
はerpc
名前空間に定義されているため、erpc::Thread
あるいはusing namespace erpc;
と記述することで利用することができている。
#include "rpcWiFi.h"
using namespace erpc;
Thread TaskA(&ThreadA, configMAX_PRIORITIES - 10, 256, "Task A");
しかしStaticQueue
は違った。
#include "rpcWiFi.h"
erpc::StaticQueue<int, 4> queue;
Compilation error: 'StaticQueue' in namespace 'erpc' does not name a template type
曰く、これはtemplate
ではないと。この指摘は、定義に矛盾するものに見える。
試みに、指摘の通りテンプレートの記述を取り払う。
#include "rpcWiFi.h"
erpc::StaticQueue queue;
Compilation error: 'StaticQueue' in namespace 'erpc' does not name a type
曰く、type
ではないと。
'StaticQueue'はテンプレートクラスとして定義されながら、テンプレートでもなく、型でもないと。では何だというのか。
認識させる方法
定義のあるファイルを名指しすると認識する。
#include "rpcWiFi.h"
#include "erpc/erpc_static_queue.h"
erpc::StaticQueue<int, 4> queue;
2
エラーコード辛くも以上の問題を回避してきたが、完全に動作するには至らず、途中で停止するのが現状である。その際、User LEDの点滅回数から、エラーコードは2
と判明している。
malloc faliedやstack over flowなどと見える。
現況
小手先の対策により、Queueが動く様子は観測された。しかし、すぐに停止してしまう。
ソースコード1
現状、最も動作の良いものを示す。
#include "rpcWiFi.h"
#include "erpc/erpc_static_queue.h"
#define STACK_SIZE 256
#define QUEUE_SIZE 4
erpc::StaticQueue<int, QUEUE_SIZE> queue;
static void ThreadA(void* pvParameters) {
(void) pvParameters;
int iValue = 0;
while (1) {
if (queue.add(iValue)) {
Serial.printf("ThreadA\tsend: %d\n", iValue);
iValue++;
}
delay(2000);
}
}
static void ThreadB(void* pvParameters) {
(void) pvParameters;
int iValueReceived = 0;
while (1) {
Serial.printf("ThreadB\tnumber of elements in queue: %d\n", queue.size());
if (queue.get(&iValueReceived)) {
Serial.printf("ThreadB\treceive: %d\n", iValueReceived);
}
delay(1000);
}
}
void setup() {
Serial.begin(115200);
vNopDelayMS(1000);
while(!Serial);
erpc::Thread taskA(
&ThreadA,
configMAX_PRIORITIES - 10,
STACK_SIZE,
"Task A"
);
erpc::Thread taskB(
&ThreadB,
configMAX_PRIORITIES - 11,
STACK_SIZE,
"Task B"
);
erpc::Thread* tasks[] = {&taskA, &taskB};
for (erpc::Thread* pt : tasks) {
pt -> start();
}
}
void loop() {}
ThreadA send: 0
ThreadB number of elements in queue: 1
ThreadB receive: 0
この表示で停止する。
ソースコード2
上では、#define STACK_SIZE 256
にてその値を256
に設定しているが、これを変じると動作に影響が確認される。
倍の#define STACK_SIZE 512
、あるいは半分の#define STACK_SIZE 128
とした場合に於いて、実行結果は次の通りであった。
ThreadA send: 0
Mutexの検討
Seeed_Arduino_rpcUnifiedには、Mutexに関する定義もある。
StaticQueueの実体
StaticQueueの実体は、下に示す通りStaticQueue
クラスのprotected
メンバー変数であり、FreeRTOSのQueueとは独立している。
xQueueGenericSend
FreeRTOSのQueueに於ける送信の実装は、xQueueGenericSend()
にある。これを見ると、taskENTER_CRITICAL()
とtaskEXIT_CRITICAL()
によって、他の割り込みが起こらない時間を設けている。
StaticQueueにそのような実装は見えない。
排他制御
そもそも、FreeRTOS側で定義される機能(xTaskCreate()
またはvTaskStartScheduler()
)を使うことで動作が停止していた。Queueに関しても同様である。以上から、taskENTER_CRITICAL()
及びtaskEXIT_CRITICAL()
の使用は避けておき、Seeed_Arduino_rpcUnifiedに定義されている機能の中から、それらしいものを選択して試す。
#include "rpcWiFi.h"
#include "erpc/erpc_static_queue.h"
#define STACK_SIZE 256
#define QUEUE_SIZE 4
erpc::StaticQueue<int, QUEUE_SIZE> g_QUEUE;
erpc::Mutex g_MUTEX;
static void ThreadA(void* pvParameters) {
(void) pvParameters;
int iValue = 0;
bool bSucceedAddition = false;
while (1) {
{
/*
Resource Acquisition Is Initialization
GuardのconstructorがMutexのlock()に対応し、
GuardのdestructorがMutexのunlock()に対応する
*/
erpc::Mutex::Guard guardA(g_MUTEX);
bSucceedAddition = g_QUEUE.add(iValue);
}
if (bSucceedAddition) {
Serial.printf("ThreadA\tsend: %d\n", iValue);
iValue++;
}
delay(2000);
}
}
static void ThreadB(void* pvParameters) {
(void) pvParameters;
int iValueReceived = 0;
int iQueueSize = 0;
bool bSucceedAcquisition = false;
while (1) {
{
erpc::Mutex::Guard guardB1(g_MUTEX);
iQueueSize = g_QUEUE.size();
}
Serial.printf("ThreadB\tnumber of elements in queue: %d\n", iQueueSize);
{
erpc::Mutex::Guard guardB2(g_MUTEX);
bSucceedAcquisition = g_QUEUE.get(&iValueReceived);
}
if (bSucceedAcquisition) {
Serial.printf("ThreadB\treceive: %d\n", iValueReceived);
}
delay(1000);
}
}
void setup() {
Serial.begin(115200);
vNopDelayMS(1000);
while(!Serial);
erpc::Thread taskA(
&ThreadA,
configMAX_PRIORITIES - 10,
STACK_SIZE,
"Task A"
);
erpc::Thread taskB(
&ThreadB,
configMAX_PRIORITIES - 11,
STACK_SIZE,
"Task B"
);
erpc::Thread* tasks[] = {&taskA, &taskB};
for (erpc::Thread* pt : tasks) {
pt -> start();
}
}
void loop() {}
ThreadA send: 0
ThreadB number of elements in queue: 1
ThreadB receive: 0
ThreadB number of elements in queue: 0
エラーコードは4
であった。
少なくともこの記述では、停止を防ぐことはできなかった。
解決
前稿の解決法と用例を示す。
原因と対処法
諸悪の根源は、シリアル出力で用いるフォーマットであった。
🤔🤔🤔
なんで?
動作する場合
static void ThreadA(void* pvParameters) {
(void) pvParameters;
Serial.println("Thread A\tStarted");
unsigned int flag = 0;
while (1) {
Serial.println("Thread A\tHi");
Serial.print("Thread A\tflag: ");
Serial.println(flag);
flag++;
delay(1000);
}
}
停止する場合
次のように記述するときは、シリアル出力の後に停止する様子が見られる。この際、エラーコードは2
となる。
-
Serial.printf()
を使う
static void ThreadA(void* pvParameters) {
(void) pvParameters;
Serial.println("Thread A\tStarted");
unsigned int flag = 0;
while (1) {
Serial.println("Thread A\tHi");
Serial.printf("Thread A\tflag: %u\n", flag); // errorBlink(2)
flag++;
delay(1000);
}
}
-
snprintf()
を使う
static void ThreadA(void* pvParameters) {
(void) pvParameters;
Serial.println("Thread A\tStarted");
unsigned int flag = 0;
while (1) {
Serial.println("Thread A\tHi");
int printed;
char string[24];
printed = snprintf(
string,
24,
"Thread A\tflag: %u\n",
flag
);
if (printed >= 0)
Serial.println(string); // errorBlink(2)
flag++;
delay(1000);
}
}
StaticQueueの用例
この対処により、StaticQueueが動作した。
#include "rpcWiFi.h"
#include "erpc/erpc_static_queue.h"
#define STACK_SIZE 256
#define QUEUE_SIZE 4
erpc::StaticQueue<int, QUEUE_SIZE> g_QUEUE;
erpc::Mutex g_MUTEX;
static void ThreadA(void* pvParameters) {
(void) pvParameters;
Serial.println("ThreadA\tbegin");
int iValue = 0;
bool bSucceedAddition = false;
while (1) {
{
erpc::Mutex::Guard guardA(g_MUTEX);
bSucceedAddition = g_QUEUE.add(iValue);
}
if (bSucceedAddition) {
Serial.print("ThreadA\tsend: ");
Serial.println(iValue);
iValue++;
}
delay(2000);
}
}
static void ThreadB(void* pvParameters) {
(void) pvParameters;
Serial.println("ThreadB\tbegin");
int iValueReceived = 0;
int iQueueSize = 0;
bool bSucceedAcquisition = false;
while (1) {
{
erpc::Mutex::Guard guardB1(g_MUTEX);
iQueueSize = g_QUEUE.size();
}
Serial.print("ThreadB\tnumber of elements in queue: ");
Serial.println(iQueueSize);
if (iQueueSize != 0) {
erpc::Mutex::Guard guardB2(g_MUTEX);
bSucceedAcquisition = g_QUEUE.get(&iValueReceived);
} else {
bSucceedAcquisition = false;
}
if (bSucceedAcquisition) {
Serial.print("ThreadB\treceive: ");
Serial.println(iValueReceived);
}
delay(1000);
}
}
void setup() {
Serial.begin(115200);
vNopDelayMS(1000);
while(!Serial);
erpc::Thread taskA(
&ThreadA,
configMAX_PRIORITIES - 10,
STACK_SIZE,
"Task A"
);
erpc::Thread taskB(
&ThreadB,
configMAX_PRIORITIES - 11,
STACK_SIZE,
"Task B"
);
erpc::Thread* tasks[] = {&taskA, &taskB};
for (erpc::Thread* pt : tasks) {
pt -> start();
}
}
void loop() {}
ThreadA begin
ThreadA send: 0
ThreadB begin
ThreadB number of elements in queue: 1
ThreadB receive: 0
ThreadB number of elements in queue: 0
ThreadA send: 1
ThreadB number of elements in queue: 1
ThreadB receive: 1
ThreadB number of elements in queue: 0
ThreadA send: 2
ThreadB number of elements in queue: 1
ThreadB receive: 2
ThreadB number of elements in queue: 0
ThreadA send: 3
ThreadB number of elements in queue: 1
ThreadB receive: 3
ThreadB number of elements in queue: 0
ThreadA send: 4
ThreadB number of elements in queue: 1
ThreadB receive: 4
ThreadB number of elements in queue: 0
ThreadA send: 5
ThreadB number of elements in queue: 1
ThreadB receive: 5
ThreadB number of elements in queue: 0
ThreadA send: 6
ThreadB number of elements in queue: 1
ThreadB receive: 6
ThreadB number of elements in queue: 0
ThreadA send: 7
ThreadB number of elements in queue: 1
ThreadB receive: 7
ThreadB number of elements in queue: 0
ThreadA send: 8
ThreadB number of elements in queue: 1
ThreadB receive: 8
ThreadB number of elements in queue: 0
ThreadA send: 9
ThreadB number of elements in queue: 1
ThreadB receive: 9
ThreadB number of elements in queue: 0
ThreadA send: 10
ThreadB number of elements in queue: 1
ThreadB receive: 10
ThreadB number of elements in queue: 0
ThreadA send: 11
ThreadB number of elements in queue: 1
ThreadB receive: 11
ThreadB number of elements in queue: 0
ThreadA send: 12
ThreadB number of elements in queue: 1
ThreadB receive: 12
ThreadB number of elements in queue: 0
ThreadA send: 13
ThreadB number of elements in queue: 1
ThreadB receive: 13
ThreadB number of elements in queue: 0
ThreadA send: 14
ThreadB number of elements in queue: 1
ThreadB receive: 14
ThreadB number of elements in queue: 0
ThreadA send: 15
ThreadB number of elements in queue: 1
ThreadB receive: 15
ThreadB number of elements in queue: 0
ThreadA send: 16
ThreadB number of elements in queue: 1
ThreadB receive: 16
ThreadB number of elements in queue: 0
ThreadA send: 17
ThreadB number of elements in queue: 1
ThreadB receive: 17
ThreadB number of elements in queue: 0
ThreadA send: 18
ThreadB number of elements in queue: 1
ThreadB receive: 18
ThreadB number of elements in queue: 0
ThreadA send: 19
ThreadB number of elements in queue: 1
ThreadB receive: 19
ThreadB number of elements in queue: 0
ThreadA send: 20
ThreadB number of elements in queue: 1
ThreadB receive: 20
ThreadB number of elements in queue: 0
ThreadA send: 21
ThreadB number of elements in queue: 1
ThreadB receive: 21
// power off