UdonLibrary 1.0.0
機械システム研究部 C++ ライブラリ
読み取り中…
検索中…
一致する文字列を見つけられません
Im920sL.hpp
[詳解]
1//
2// IM920sL
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 Im920sL
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 Im920sL(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 Im920sL(HardwareSerial& uart, uint16_t nodeNum)
56 : uart(uart)
57 , txNode()
58 , rxNode()
59 , nodeNum(nodeNum)
60 , busyPin()
61 {
62 }
63
67 Im920sL(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("IM920sL: OK ");
93 }
94 else
95 {
96 Serial.print("IM920sL: 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, 45);
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 = 52; // 連続で送信する場合、52ms送信休止時間が必要 (説明書 7-3(2)送信休止時間 参照)
171
172 if (millis() - txNode->transmitMs < sendTimeMs)
173 {
174 return false;
175 }
176
177 // データ送信
178 {
179 Udon::Printf(uart, "TXDU %04d ", *nodeNum);
180
181 Udon::BitPack(txNode->data, txNode->data + txNode->size, [this](uint8_t data)
182 { uart.write(data); });
183
184 uart.print("\r\n");
185 }
186
187 if (uart.readStringUntil('\n') == "OK\r")
188 {
189 txNode->transmitMs = millis();
190 }
191
192 return true;
193 }
194
195 bool receiveUpdate()
196 {
197 // header = [Dummy: 2byte] + [,] + [Node number: 4byte] + [,] + [RSSI: 2byte] + [:]
198 constexpr int HeaderSize = 2 + 1 + 4 + 1 + 2 + 1;
199
200 // footer = [\r] + [\n]
201 constexpr int FooterSize = 1 + 1;
202
203 // FrameSize = header + data + footer
204 const int FrameSize = HeaderSize + Udon::BitPackedSize(rxNode->size) + FooterSize;
205
206 // 受信バッファにデータがない場合は何もしない
207 if (uart.available() < FrameSize)
208 {
209 return false;
210 }
211
212 // ヘッダー読み捨て
213 for (int i = 0; i < HeaderSize; ++i)
214 {
215 uart.read();
216 }
217
218 // データ読み込み
219 if (not Udon::BitUnpack(rxNode->data, rxNode->data + rxNode->size, [this]() -> uint8_t
220 { return uart.read(); }))
221 {
222 // 先頭バイトの MSB が 1 でない場合はデータが壊れているので読み捨て
223 while (uart.available())
224 {
225 uart.read();
226 }
227 return false;
228 }
229
230 // フッター読み捨て
231 for (int i = 0; i < FooterSize; ++i)
232 {
233 uart.read();
234 }
235
236 // バッファに残っているデータを読み捨て
237 while (uart.available())
238 {
239 uart.read();
240 }
241
242 rxNode->transmitMs = millis();
243
244 return true;
245 }
246
247 void twoWayUpdate()
248 {
249 // transTime =[内部処理時間:10~20ms]+[キャリアセンス:初回5.2ms 連続通信時0.5ms]+[不要データの通信:3.2ms]+[バイトごとの送信時間:0.16ms]
250 const double sendTime = 52.0;
251 const double receiveTime = 52.0;
252
253 static uint32_t lastTransmitMs = 0;
254
255 if (millis() - lastTransmitMs > sendTime + receiveTime && receiveUpdate())
256 {
257 sendUpdate();
258 lastTransmitMs = millis();
259 }
260 else
261 {
262 if (millis() - lastTransmitMs > (uint32_t)random(2000))
263 {
264 sendUpdate();
265 lastTransmitMs = millis();
266 }
267 }
268 }
269
270 bool isTimeout(uint32_t timeoutMs) const
271 {
272 switch (getTransmitMode())
273 {
274 case TransmitMode::Send:
275 return millis() - txNode->transmitMs > timeoutMs;
276 case TransmitMode::Receive:
277 return millis() - rxNode->transmitMs > timeoutMs;
278 case TransmitMode::TwoWay:
279 return millis() - txNode->transmitMs > timeoutMs || millis() - rxNode->transmitMs > timeoutMs;
280 case TransmitMode::Empty:
281 return false;
282 default:
283 return false;
284 }
285 }
286
287 void RestartOnTimeout()
288 {
289 if (not isTimeout(2000))
290 {
291 return;
292 }
293
294 // 通信が不通になった場合はソフトウエアリセット
295 if (millis() - lastRestartMs > 2000)
296 {
297 waitUntilCommandAccept();
298 uart.print("SRST\r\n");
299 lastRestartMs = millis();
300 while (uart.available())
301 {
302 uart.read();
303 }
304 }
305 }
306
308 void waitUntilCommandAccept()
309 {
310 if (busyPin)
311 {
312 lastWaitUntilCommandAcceptMs = millis();
313
314 while (digitalRead(*busyPin)) // busyピンがHIGHの間はコマンドを受け付けない
315 {
316 delayMicroseconds(10); // チャタリング防止
317
318 if (millis() - lastWaitUntilCommandAcceptMs > 200)
319 {
320 break; // タイムアウトした場合は強制的にコマンドを受け付ける
321 }
322 }
323 }
324 else
325 {
326 delay(200); // busy pinが設定されていない場合は200ms待つ
327 }
328
329 // 待機している間に余計なデータが送られてきている可能性があるので読み捨てる
330 while (uart.available())
331 {
332 uart.read();
333 }
334 }
335 };
336
337 inline bool Im920sL::begin(uint8_t channel)
338 {
339 // ボーレート設定
340 uart.begin(115200);
341
342 if (busyPin)
343 {
344 pinMode(*busyPin, INPUT);
345 }
346
347 // タイムアウト設定
348 uart.setTimeout(10);
349
350 // パラメーター一括読み出し
351 waitUntilCommandAccept();
352 uart.print("RPRM\r\n");
353
354 (void)uart.readStringUntil('\n'); // ID:
355 (void)uart.readStringUntil('\n'); // STNN:
356 (void)uart.readStringUntil('\n'); // STGN:
357 (void)uart.readStringUntil('\n'); // STRT:
358 const int defaultChannel = uart.readStringUntil('\n').substring(5).toInt(); // STCH:
359 const int defaultPower = uart.readStringUntil('\n').substring(5).toInt(); // STPO:
360 (void)uart.readStringUntil('\n'); // STNM:
361 (void)uart.readStringUntil('\n'); // STTH:
362 (void)uart.readStringUntil('\n'); // SRTH:
363 (void)uart.readStringUntil('\n'); // WTRT:
364 (void)uart.readStringUntil('\n'); // STTL:
365 (void)uart.readStringUntil('\n'); // STTR:
366 (void)uart.readStringUntil('\n'); // SSTM:
367 (void)uart.readStringUntil('\n'); // SWTM:
368 (void)uart.readStringUntil('\n'); // STTN:
369 (void)uart.readStringUntil('\n'); // STAT:
370 (void)uart.readStringUntil('\n'); // STTG:
371 (void)uart.readStringUntil('\n'); // [ENRX] DSRX スリープ
372 (void)uart.readStringUntil('\n'); // [ENAK]
373 const String defaultCharIOMode = uart.readStringUntil('\n'); // [ECIO] DCIO キャラクタ入出力モード
374 (void)uart.readStringUntil('\n'); // EENC [DENC] パケット暗号化
375 (void)uart.readStringUntil('\n'); // ENAD [DSAD] AD 入力モード
376 (void)uart.readStringUntil('\n'); // [DSAR] ENAR リトライ送信
377 (void)uart.readStringUntil('\n'); // ENSS [DSSS] 同期スリープ開始・解除
378 (void)uart.readStringUntil('\n'); // ESNF [DSNF] スニファモード
379 (void)uart.readStringUntil('\n'); // ENRC [DSRC] リモートコマンド受信
380 (void)uart.readStringUntil('\n'); // ETRC [DTRC] リモートコマンド送信
381 (void)uart.readStringUntil('\n'); // DORT
382 (void)uart.readStringUntil('\n'); // DADR
383 (void)uart.readStringUntil('\n'); // DOMI
384 (void)uart.readStringUntil('\n'); // ENHP
385 (void)uart.readStringUntil('\n'); // DSEF
386 const String defaultStatusOutputOnReceipt = uart.readStringUntil('\n'); // [ERXI] DRXI 受信時のステータス出力
387 (void)uart.readStringUntil('\n'); // ENWR [DSWR] フラッシュメモリ書き込み許可
388
389 // フラッシュメモリ書き込み許可
390 waitUntilCommandAccept();
391 uart.print("ENWR\r\n");
392 if (uart.readStringUntil('\n') != "OK\r")
393 {
394 return false;
395 }
396
397 Udon::Printf(uart, "STRT 1\r\n");
398 if (uart.readStringUntil('\n') != "OK\r")
399 {
400 return false;
401 }
402
403 // チャンネル設定
404 if (channel != defaultChannel)
405 {
406 waitUntilCommandAccept();
407 Udon::Printf(uart, "STCH %02d\r\n", channel);
408 if (uart.readStringUntil('\n') != "OK\r")
409 {
410 return false;
411 }
412 }
413
414 // 出力電力設定
415 if (defaultPower != 2)
416 {
417 waitUntilCommandAccept();
418 Udon::Printf(uart, "STPO %d\r\n", 2);
419 if (uart.readStringUntil('\n') != "OK\r")
420 {
421 return false;
422 }
423 }
424
425 // キャラクタ入出力モード設定
426 if (defaultCharIOMode != "ECIO\r")
427 {
428 waitUntilCommandAccept();
429 uart.print("ECIO\r\n");
430 if (uart.readStringUntil('\n') != "OK\r")
431 {
432 return false;
433 }
434 }
435
436 // 受信時のステータス出力設定
437 if (defaultStatusOutputOnReceipt != "ERXI\r")
438 {
439 waitUntilCommandAccept();
440 uart.print("ERXI\r\n");
441 if (uart.readStringUntil('\n') != "OK\r")
442 {
443 return false;
444 }
445 }
446
447 // フラッシュメモリ書き込み禁止
448 waitUntilCommandAccept();
449 uart.print("DSWR\r\n");
450 if (uart.readStringUntil('\n') != "OK\r")
451 {
452 return false;
453 }
454
455 return true;
456 }
457
458 inline void Im920sL::update()
459 {
460 switch (getTransmitMode())
461 {
462 case TransmitMode::Send:
463 sendUpdate();
464 break;
465 case TransmitMode::Receive:
466 receiveUpdate();
467 break;
468 case TransmitMode::TwoWay:
469 twoWayUpdate();
470 break;
471 case TransmitMode::Empty:
472 break;
473 }
474
475 RestartOnTimeout();
476 }
477
478} // namespace Udon
IM920のインターフェース
Definition IIm920.hpp:31
IM920sL通信クラス
Definition Im920sL.hpp:26
void joinTx(Im920Node &node) override
送信ノードを登録
Definition Im920sL.hpp:119
bool begin(uint8_t channel)
通信開始
Definition Im920sL.hpp:337
Im920sL(HardwareSerial &uart)
受信者用コンストラクタ
Definition Im920sL.hpp:67
static int ClampChannel(int channel)
IM920sLで使用可能なチャンネル数に制限をかける
Definition Im920sL.hpp:125
void joinRx(Im920Node &node) override
受信ノードを登録
Definition Im920sL.hpp:122
void show() const
IM920の状態を表示
Definition Im920sL.hpp:88
void showNodeNumber()
自身のノード番号を表示
Definition Im920sL.hpp:110
void update()
通信更新
Definition Im920sL.hpp:458
Im920sL(HardwareSerial &uart, uint16_t nodeNum, uint8_t busyPin)
送信者用コンストラクタ
Definition Im920sL.hpp:43
Im920sL(HardwareSerial &uart, uint16_t nodeNum)
送信者用コンストラクタ
Definition Im920sL.hpp:55
オプショナル型
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