chrmlinux03 が 2024年01月31日10時37分31秒 に編集
動画掲載
本文の変更
# はじめに こんにちわっ リナちゃん X@chrmlinux03 です ++このページは SPRESENSE2023コンテストで作った製品を紹介するものです 最初に [開発まとめ](https://elchika.com/article/e96011d0-d280-49e2-a68c-dd3c183a204e/) をお読みいただければ幸いです++ # はじめは トランシーバーを作る気満々だった 当初は ESPNow や WiFi の速度 と転送容量を期待していたのですが 実際に実装してみるとなんと 200byte - 3byte の 197byteがせいぜい💦 しかも 転送に1秒ほどかかるようで それでもエイヤっと組んでみるかと考えて 試しに計算してみると 10Kbyte のファイル転送に要する時間が半端ない💦 さらにこんなメッセージが @[twitter](https://twitter.com/chrmlinux03/status/1741381312282526150?s=20) # 用意するもの 👉[組込用のSPRESENSEモジュールを作ったよ](https://elchika.com/article/def1853e-f830-4693-bfee-040c9fe69d57/) ![キャプションを入力できます](https://camo.elchika.com/995e90e6dc7597f7c39673ee9dd0da4499f970f2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f65646366663161302d363531312d343235362d393633662d376139623035306361376366/) ある日 毎日欠かさず読んでいるサイトに こんなデータがあることが分かった これを使えば採寸しなくて良いのでは? ※って言うか 元旦💦 @[twitter](https://twitter.com/chrmlinux03/status/1741681939118248245?s=20) # ケース作成要件 - リチウムイオン電池を内蔵して - LEDミルフィーユを右側に搭載 - アンテナは左側上部に設置する - 画面は 2.4インチのタッチパネル - タッチパネルは天面と同じ高さ # ケースの設計 ![天面](https://camo.elchika.com/d2e795e20f411504ac7f27821dffbe30368d27a1/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f36373662623863382d653562362d343233392d616433612d343735626537666438343966/) ![LED用透明パッド](https://camo.elchika.com/4753a68641af5b8a74079b1372b7c1bd1983e64f/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f65383431303839322d323838342d346334302d623034642d616364346631396233623431/) ![側面部分](https://camo.elchika.com/5bf2d8383422abff685ccf59f028de9ebeafa5c1/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f35663764393832662d366336392d343265392d613034642d323862346163323764356434/) ![底面](https://camo.elchika.com/4af572d0d54016174515924549f8ea20d5531a07/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f34663464393733652d636535332d343733622d623330642d343162636332386131313366/) # 印刷物 ![印刷物](https://camo.elchika.com/0df22002a8aa751b77687eda3be10152678b0ebe/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38373864346139632d633739622d343839652d383830322d6137626538616332663037302f63363961373663322d626135392d346662622d396639332d313839353733636633333035/) # 実装 ``` //=================================================== /* program name : LoRaPager.ino date/author : 2024/01/23 @chrmlinux03 update/author : 2024/01/23 @chrmlinux03 ----------------------------------------------------- MainCore : 512KByte */ //=================================================== #include <spre.Tools.hpp> #include <spre.Graphics.hpp> #include <tinyLoRa.hpp> #include <tinyTouch.hpp> #include <tinyTouchVrKey.hpp> #include <spre.Neo.hpp> SpreNeo neo; TouchVrKey VrKey; tinyLoRa tLoRa; #define VERSION "v0.1" const int historyBufferSize = 5; char recvHistory[historyBufferSize][SENDBUFLEN_MAX]; int recvHistoryIndex = 0; char sendHistory[historyBufferSize][SENDBUFLEN_MAX]; int sendHistoryIndex = 0; uint8_t recvBuf[SENDBUFLEN_MAX] = {0}; int16_t recvBufLen = SENDBUFLEN_MAX; int16_t rssi; uint8_t sendBuf[SENDBUFLEN_MAX] = {0}; int16_t sendBufLen = SENDBUFLEN_MAX; enum { LED_STAT_IDLE = 0, LED_STAT_SEND, LED_STAT_RECV }; int ledStat = LED_STAT_IDLE; void addToHistory(const char* data, char historyBuffer[][SENDBUFLEN_MAX], int& historyIndex) { strncpy(historyBuffer[historyIndex], data, SENDBUFLEN_MAX); historyIndex = (historyIndex + 1) % historyBufferSize; } void sendProc(void) { tLoRa.setSendData(sendBuf, sendBufLen); tLoRa.setSendFlag(); tLoRa.sendData(); addToHistory(reinterpret_cast<char*>(sendBuf), sendHistory, sendHistoryIndex); memset(sendBuf, 0x0, sendBufLen); ledStat = LED_STAT_SEND; } void recvProc(void) { if (tLoRa.recvData()) { memset(recvBuf, 0x0, recvBufLen); tLoRa.getRecvData(recvBuf, &recvBufLen, &rssi); tLoRa.dump(recvBuf, recvBufLen); addToHistory(reinterpret_cast<char*>(recvBuf), recvHistory, recvHistoryIndex); ledStat = LED_STAT_RECV; } } void setup() { Serial.begin(115200); int8_t rotation = ROT0; bool tft24inch = true; setupGraphics(rotation); setupTouch(_w, _h, rotation, tft24inch); tLoRa.setupLoRa(LORA_NORMAL_MODE); tLoRa.setDebugPrint(false); int bw = 21, bh = 20; VrKey.begin(bw, bh, 0, _h - (bh * 5)); neo.begin(); neo.set(LEDKIND_BRESS, 0, 0, 255, 64); neo.start(); } #define IDLE_TIMEOUT 500 unsigned long lastLedStatChangeTime = 0; void ledCtrl(void) { static unsigned long lastLEDUpdateTime = 0; const unsigned long LEDUpdateInterval = 500; if (millis() - lastLEDUpdateTime >= LEDUpdateInterval) { lastLEDUpdateTime = millis(); switch (ledStat) { case LED_STAT_SEND: neo.stop(); neo.set(LEDKIND_RIGHTSTAR, 0, 64, 0, 128); neo.start(); break; case LED_STAT_RECV: neo.stop(); neo.set(LEDKIND_LEFTSTAR, 64, 0, 0, 128); neo.start(); break; default: neo.stop(); neo.set(LEDKIND_BRESS, 0, 0, 64, 64); neo.start(); break; } if (ledStat != LED_STAT_IDLE && millis() - lastLedStatChangeTime >= IDLE_TIMEOUT) { ledStat = LED_STAT_IDLE; } } } void loop(void) { ledCtrl(); neo.update(); spr.startWrite(); spr.clear(TFT_BLACK); spr.setTextColor(TFT_CYAN); spr.setCursor(0, 0); for (int i = 0; i < historyBufferSize; ++i) { int index = (recvHistoryIndex + i) % historyBufferSize; spr.printf("%s\n", recvHistory[index]); } spr.setTextColor(TFT_GREEN); spr.setCursor(0, 104); for (int i = 0; i < historyBufferSize; ++i) { int index = (sendHistoryIndex + i) % historyBufferSize; spr.printf("%s\n", sendHistory[index]); } VrKey.scan(&spr, TFT_WHITE); if (VrKey.isEnter()) { VrKey.getData(sendBuf, &sendBufLen); VrKey.resetEnter(); sendProc(); } recvProc(); static int cnt = 0; char ast[4] = "|/-\\"; spr.setTextColor(TFT_WHITE); spr.setCursor(0, _h - 8); spr.printf(" [%c] %3d fps [%s]\n", ast[cnt], getfps(), VERSION); cnt = (cnt + 1) % 4; spr.pushSprite(&lcd, 0, 0); spr.endWrite(); } ``` # 実装のポイント LEDミルフィーユの光り方を苦悩した recvFlag や sendFlag は 一瞬で変わってしまうので 一旦 ledStat で受けて その ledStat を自分自身で on/off するように構成 ``` #define IDLE_TIMEOUT 500 unsigned long lastLedStatChangeTime = 0; void ledCtrl(void) { static unsigned long lastLEDUpdateTime = 0; const unsigned long LEDUpdateInterval = 500; if (millis() - lastLEDUpdateTime >= LEDUpdateInterval) { lastLEDUpdateTime = millis(); switch (ledStat) { case LED_STAT_SEND: neo.stop(); neo.set(LEDKIND_RIGHTSTAR, 0, 64, 0, 128); neo.start(); break; case LED_STAT_RECV: neo.stop(); neo.set(LEDKIND_LEFTSTAR, 64, 0, 0, 128); neo.start(); break; default: neo.stop(); neo.set(LEDKIND_BRESS, 0, 0, 64, 64); neo.start(); break; } if (ledStat != LED_STAT_IDLE && millis() - lastLedStatChangeTime >= IDLE_TIMEOUT) { ledStat = LED_STAT_IDLE; } } } ``` この作業により以下のエフェクトが実装できた |状態|エフェクト| |-|-| |アイドル時|呼吸をするイメージで青くぼーっと光る| |受信時|流れる緑の光が飛び出すイメージ| |送信時|流れる赤の光が入ってくるイメージ|
# 動かしてみた
@[twitter](https://twitter.com/chrmlinux03/status/1752503198169063919?s=20)
# 残務と今後の考察 これ シリアル用の穴をあけてUSBマイクロの差し込み口を付けるだけで LoRa端末として良い形になりそう PC に接続するとか用途は無限大ですね # さいごに デザインは大事なんだなぁとか今更考えちゃいましたっ っていうか製品感満載٩(ˊᗜˋ*)و ++このページは SPRESENSE2023コンテストで作った部品を紹介するものです 最初に [開発まとめ](https://elchika.com/article/e96011d0-d280-49e2-a68c-dd3c183a204e/) をお読みいただければ幸いです++