UdonLibrary 1.0.0
機械システム研究部 C++ ライブラリ
読み取り中…
検索中…
一致する文字列を見つけられません
Im920s.hpp
[詳解]
1//
2// IM920s
3//
4// Copyright (c) 2022-2024 udonrobo
5//
6
7//
8// Sender --[UART]--> IM920 ~~[920MHz]~~> IM920 --[UART]--> Receiver
9// ^^^^^^ ^^^^^^^^
10//
11
12#pragma once
13
14#include "IIm920.hpp"
15
19
20namespace Udon
21{
22
24 class Im920s
25 : public IIm920
26 {
27 HardwareSerial& uart;
28
29 Im920Node* txNode;
30 Im920Node* rxNode;
31
34
35 uint32_t lastWaitUntilCommandAcceptMs = 0;
36 uint32_t lastRestartMs = 0;
37
38 public:
43 Im920s(HardwareSerial& uart, uint16_t nodeNum, uint8_t busyPin)
44 : uart(uart)
45 , txNode()
46 , rxNode()
47 , nodeNum(nodeNum)
48 , busyPin(busyPin)
49 {
50 }
51
55 Im920s(HardwareSerial& uart, uint16_t nodeNum)
56 : uart(uart)
57 , txNode()
58 , rxNode()
59 , nodeNum(nodeNum)
60 , busyPin()
61 {
62 }
63
67 Im920s(HardwareSerial& uart)
68 : uart(uart)
69 , txNode()
70 , rxNode()
71 , nodeNum()
72 , busyPin()
73 {
74 }
75
78 operator bool() const override { return uart && not isTimeout(700); }
79
82 bool begin(uint8_t channel);
83
85 void update();
86
88 void show() const
89 {
90 if (operator bool())
91 {
92 Serial.print("IM920s: OK ");
93 }
94 else
95 {
96 Serial.print("IM920s: NG ");
97 }
98
99 switch (getTransmitMode())
100 {
101 case TransmitMode::Send: Serial.print("SendMode "); break;
102 case TransmitMode::Receive: Serial.print("ReceiveMode "); break;
103 case TransmitMode::TwoWay: Serial.print("TwoWayMode "); break;
104 case TransmitMode::Empty: Serial.print("Empty "); break;
105 }
106 }
107
111 {
112 waitUntilCommandAccept();
113 uart.print("RDNN\r\n");
114 Serial.print(uart.readStringUntil('\n'));
115 }
116
119 void joinTx(Im920Node& node) override { txNode = &node; }
120
122 void joinRx(Im920Node& node) override { rxNode = &node; }
123
125 static int ClampChannel(int channel)
126 {
127 return constrain(channel, 1, 29);
128 }
129
130 private:
131 enum class TransmitMode
132 {
133 Send,
134 Receive,
135 TwoWay,
136 Empty,
137 };
138
140 TransmitMode getTransmitMode() const
141 {
142 if (txNode)
143 {
144 if (rxNode)
145 {
146 return TransmitMode::TwoWay;
147 }
148 else
149 {
150 return TransmitMode::Send;
151 }
152 }
153 else if (rxNode)
154 {
155 return TransmitMode::Receive;
156 }
157 else
158 {
159 return TransmitMode::Empty;
160 }
161 }
162
163 bool sendUpdate()
164 {
165 if (not nodeNum)
166 {
167 return false;
168 }
169
170 const int sendTimeMs = 4.56 + txNode->size * 0.08 + 1.00;
171 // 連続で送信する場合、52ms送信休止時間が必要 (説明書 7-3(2)送信休止時間 参照)
172
173 if (millis() - txNode->transmitMs < (unsigned long)sendTimeMs)
174 {
175 return false;
176 }
177
178 // データ送信
179 {
180 Udon::Printf(uart, "TXDU %04d ", *nodeNum);
181 // Udon::Printf(uart, "TXDA ");
182
183 Udon::BitPack(txNode->data, txNode->data + txNode->size, [this](uint8_t data)
184 { uart.write(data); });
185
186 uart.print("\r\n");
187 }
188
189 if (uart.readStringUntil('\n') == "OK\r")
190 {
191 txNode->transmitMs = millis();
192 }
193
194 return true;
195 }
196
197 bool receiveUpdate()
198 {
199 // header = [Dummy: 2byte] + [,] + [Node number: 4byte] + [,] + [RSSI: 2byte] + [:]
200 constexpr int HeaderSize = 2 + 1 + 4 + 1 + 2 + 1;
201
202 // footer = [\r] + [\n]
203 constexpr int FooterSize = 1 + 1;
204
205 // FrameSize = header + data + footer
206 const int FrameSize = HeaderSize + Udon::BitPackedSize(rxNode->size) + FooterSize;
207
208 // 受信バッファにデータがない場合は何もしない
209 if (uart.available() < FrameSize)
210 {
211 return false;
212 }
213
214 // ヘッダー読み捨て
215 for (int i = 0; i < HeaderSize; ++i)
216 {
217 uart.read();
218 }
219
220 // データ読み込み
221 if (not Udon::BitUnpack(rxNode->data, rxNode->data + rxNode->size, [this]() -> uint8_t
222 { return uart.read(); }))
223 {
224 // 先頭バイトの MSB が 1 でない場合はデータが壊れているので読み捨て
225 while (uart.available())
226 {
227 uart.read();
228 }
229 return false;
230 }
231
232 // フッター読み捨て
233 for (int i = 0; i < FooterSize; ++i)
234 {
235 uart.read();
236 }
237
238 // バッファに残っているデータを読み捨て
239 while (uart.available())
240 {
241 uart.read();
242 }
243
244 rxNode->transmitMs = millis();
245
246 return true;
247 }
248
249 void twoWayUpdate()
250 {
251 // transTime =[内部処理時間:10~20ms]+[キャリアセンス:初回5.2ms 連続通信時0.5ms]+[不要データの通信:3.2ms]+[バイトごとの送信時間:0.16ms]
252 // const double sendTime = 10.0 + 5.2 + 3.2 + txNode->size * 0.16;
253 // const double receiveTime = 10.0 + 5.2 + 3.2 + rxNode->size * 0.16;
254
255 // static uint32_t lastTransmitMs = 0;
256
257 // if (millis() - lastTransmitMs > sendTime + receiveTime && receiveUpdate())
258 // {
259 // sendUpdate();
260 // lastTransmitMs = millis();
261 // }
262 // else
263 // {
264 // if (millis() - lastTransmitMs > random(2000))
265 // {
266 // sendUpdate();
267
268 // lastTransmitMs = millis();
269 // }
270 // }
271 }
272
273 bool isTimeout(uint32_t timeoutMs) const
274 {
275 switch (getTransmitMode())
276 {
277 case TransmitMode::Send:
278 return millis() - txNode->transmitMs > timeoutMs;
279 case TransmitMode::Receive:
280 return millis() - rxNode->transmitMs > timeoutMs;
281 case TransmitMode::TwoWay:
282 return millis() - txNode->transmitMs > timeoutMs || millis() - rxNode->transmitMs > timeoutMs;
283 case TransmitMode::Empty:
284 return false;
285 default:
286 return false;
287 }
288 }
289
290 void RestartOnTimeout()
291 {
292 if (not isTimeout(2000))
293 {
294 return;
295 }
296
297 // 通信が不通になった場合はソフトウエアリセット
298 if (millis() - lastRestartMs > 2000)
299 {
300 waitUntilCommandAccept();
301 uart.print("SRST\r\n");
302 lastRestartMs = millis();
303 while (uart.available())
304 {
305 uart.read();
306 }
307 }
308 }
309
311 void waitUntilCommandAccept()
312 {
313 if (busyPin)
314 {
315 lastWaitUntilCommandAcceptMs = millis();
316
317 while (digitalRead(*busyPin)) // busyピンがHIGHの間はコマンドを受け付けない
318 {
319 delayMicroseconds(10); // チャタリング防止
320
321 if (millis() - lastWaitUntilCommandAcceptMs > 200)
322 {
323 break; // タイムアウトした場合は強制的にコマンドを受け付ける
324 }
325 }
326 }
327 else
328 {
329 delay(200); // busy pinが設定されていない場合は200ms待つ
330 }
331
332 // 待機している間に余計なデータが送られてきている可能性があるので読み捨てる
333 while (uart.available())
334 {
335 uart.read();
336 }
337 }
338 };
339
340 inline bool Im920s::begin(uint8_t channel)
341 {
342 // ボーレート設定
343 uart.begin(460800);
344
345 if (busyPin)
346 {
347 pinMode(*busyPin, INPUT);
348 }
349
350 // タイムアウト設定
351 uart.setTimeout(10);
352
353 waitUntilCommandAccept();
354 uart.print("RDVR\r\n");
355
356 uart.readStringUntil(' ');
357 uart.readStringUntil('.'); // 頭のVer.は捨てる
358 const int version = uart.readStringUntil('.').substring(0).toInt() * 100 + uart.readStringUntil('\n').substring(0).toInt();
359
360 int defaultChannel, defaultPower;
361 String defaultCharIOMode, defaultStatusOutputOnReceipt;
362 // パラメーター一括読み出し
363 waitUntilCommandAccept();
364 uart.print("RPRM\r\n");
365 if (version >= 121)
366 {
367 // ver01.21で動作確認済み
368 (void)uart.readStringUntil('\n'); // ID:
369 (void)uart.readStringUntil('\n'); // STNN:
370 (void)uart.readStringUntil('\n'); // STGN:
371 defaultChannel = uart.readStringUntil('\n').substring(5).toInt(); // STCH:
372 defaultPower = uart.readStringUntil('\n').substring(5).toInt(); // STPO:
373 (void)uart.readStringUntil('\n'); // STNM:
374 (void)uart.readStringUntil('\n'); // STTH:
375 (void)uart.readStringUntil('\n'); // STTL:
376 (void)uart.readStringUntil('\n'); // SSTM:
377 (void)uart.readStringUntil('\n'); // SWTM:
378 (void)uart.readStringUntil('\n'); // STTN:
379 (void)uart.readStringUntil('\n'); // [ENRX] DSRX スリープ
380 (void)uart.readStringUntil('\n'); // [ENAK]
381 defaultCharIOMode = uart.readStringUntil('\n'); // [ECIO] DCIO キャラクタ入出力モード
382 (void)uart.readStringUntil('\n'); // ENAD [DSAD] AD 入力モード
383 (void)uart.readStringUntil('\n'); // [DSAR] ENAR リトライ送信
384 (void)uart.readStringUntil('\n'); // ENSS [DSSS] 同期スリープ開始・解除
385 (void)uart.readStringUntil('\n'); // ESNF [DSNF] スニファモード
386 (void)uart.readStringUntil('\n'); // ENRC [DSRC] リモートコマンド受信
387 (void)uart.readStringUntil('\n'); // ETRC [DTRC] リモートコマンド送信
388 defaultStatusOutputOnReceipt = uart.readStringUntil('\n'); // [ERXI] DRXI 受信時のステータス出力
389 (void)uart.readStringUntil('\n'); // ENWR [DSWR] フラッシュメモリ書き込み許可
390 }
391 else
392 {
393 // ver01.03で動作確認済み
394 (void)uart.readStringUntil('\n'); // ID
395 (void)uart.readStringUntil('\n'); // STNN
396 (void)uart.readStringUntil('\n'); // STGN
397 defaultChannel = uart.readStringUntil('\n').substring(5).toInt(); // STCH
398 defaultPower = uart.readStringUntil('\n').substring(5).toInt(); // STPO
399 (void)uart.readStringUntil('\n'); // STNM
400 (void)uart.readStringUntil('\n'); // STTH
401 (void)uart.readStringUntil('\n'); // STTL
402 (void)uart.readStringUntil('\n'); // SSTM
403 (void)uart.readStringUntil('\n'); // SWTM
404 (void)uart.readStringUntil('\n'); // ENRX
405 (void)uart.readStringUntil('\n'); // ENAK
406 defaultCharIOMode = uart.readStringUntil('\n'); //[ECIO] DCIO
407 (void)uart.readStringUntil('\n'); // DSSS
408 (void)uart.readStringUntil('\n'); // DSNF
409 defaultStatusOutputOnReceipt = uart.readStringUntil('\n'); //[ERXI] DRXI
410 }
411
412 // 書き換え許可
413 if (defaultChannel != channel || defaultPower != 2 || defaultCharIOMode != "ECIO\r" || defaultStatusOutputOnReceipt != "ERXI\r")
414 {
415 waitUntilCommandAccept();
416 uart.print("ENWR\r\n");
417 if (uart.readStringUntil('\n') != "OK\r")
418 {
419 return false;
420 }
421 }
422 // チャンネル設定
423 if (defaultChannel != channel)
424 {
425 waitUntilCommandAccept();
426 Udon::Printf(uart, "STCH %02d\r\n", channel);
427 if (uart.readStringUntil('\n') != "OK\r")
428 {
429 return false;
430 }
431 }
432 // 送信出力設定
433 if (defaultPower != 2)
434 {
435 waitUntilCommandAccept();
436 Udon::Printf(uart, "STPO %d\r\n", 2);
437 if (uart.readStringUntil('\n') != "OK\r")
438 {
439 return false;
440 }
441 }
442 // 入出力モード設定
443 if (defaultCharIOMode != "ECIO\r")
444 {
445 waitUntilCommandAccept();
446 uart.print("ECIO\r\n");
447 if (uart.readStringUntil('\n') != "OK\r")
448 {
449 return false;
450 }
451 }
452 // STATUS出力設定
453 if (defaultStatusOutputOnReceipt != "ERXI\r")
454 {
455 waitUntilCommandAccept();
456 uart.print("ERXI\r\n");
457 if (uart.readStringUntil('\n') != "OK\r")
458 {
459 return false;
460 }
461 }
462 // 書き換え禁止
463 if (defaultChannel != channel || defaultPower != 2 || defaultCharIOMode != "ECIO\r" || defaultStatusOutputOnReceipt != "ERXI\r")
464 {
465 waitUntilCommandAccept();
466 uart.print("DSWR\r\n");
467 if (uart.readStringUntil('\n') != "OK\r")
468 {
469 return false;
470 }
471 }
472 return true;
473 }
474
475 inline void Im920s::update()
476 {
477 switch (getTransmitMode())
478 {
479 case TransmitMode::Send:
480 sendUpdate();
481 break;
482 case TransmitMode::Receive:
483 receiveUpdate();
484 break;
485 case TransmitMode::TwoWay:
486 twoWayUpdate();
487 break;
488 case TransmitMode::Empty:
489 break;
490 }
491
492 RestartOnTimeout();
493 }
494
495} // namespace Udon
IM920のインターフェース
Definition IIm920.hpp:31
IM920s送信クラス
Definition Im920s.hpp:26
void showNodeNumber()
自身のノード番号を表示
Definition Im920s.hpp:110
Im920s(HardwareSerial &uart)
受信者用コンストラクタ
Definition Im920s.hpp:67
static int ClampChannel(int channel)
IM920sLで使用可能なチャンネル数に制限をかける
Definition Im920s.hpp:125
Im920s(HardwareSerial &uart, uint16_t nodeNum, uint8_t busyPin)
送信者用コンストラクタ
Definition Im920s.hpp:43
void show() const
IM920の状態を表示
Definition Im920s.hpp:88
void joinRx(Im920Node &node) override
受信ノードを登録
Definition Im920s.hpp:122
void update()
通信更新
Definition Im920s.hpp:475
Im920s(HardwareSerial &uart, uint16_t nodeNum)
送信者用コンストラクタ
Definition Im920s.hpp:55
bool begin(uint8_t channel)
通信開始
Definition Im920s.hpp:340
void joinTx(Im920Node &node) override
送信ノードを登録
Definition Im920s.hpp:119
オプショナル型
Definition Optional.hpp:62
Definition Bit.hpp:12
void BitPack(const InputIterator begin, const InputIterator end, Functor callback)
バイト列のMSB(最上位ビット)を抽出し、7bitごとに分割してコールバック関数に渡す
Definition BitPack.hpp:23
void Printf(const char *format, Args... args)
Definition Printf.hpp:44
bool BitUnpack(OutputIterator begin, OutputIterator end, Functor callback)
7bit分割されたデータを結合する
Definition BitPack.hpp:70
constexpr size_t BitPackedSize(size_t size)
ビットパックされたデータのサイズを取得する
Definition BitPack.hpp:132
IM920ノード
Definition IIm920.hpp:20
uint32_t transmitMs
Definition IIm920.hpp:23
uint8_t * data
Definition IIm920.hpp:21
uint8_t size
Definition IIm920.hpp:22