💧 水位ノード (Pico W + JSN-SR04 超音波センサー)
10分TDMAサイクル動作 / gate_cmd デフォルトKEEP / WOR TX→NORMAL→WOR RX 2段階COMMAND送信 / NORMALモードでRESULT待機→受信後WOR RX / DATA後NORMAL→TIME受信後WOR RX
① 起動フロー
VSYS計測キャッシュ"] B --> C{"VSYS >= 4.8V?"} C -->|Yes| D["USB設定モード
process_usb_serial"] D --> E["load_config"] C -->|No| E E --> F{"skip_ap.flag
あり?"} F -->|No| G["APモード設定サーバー
3分タイムアウト"] G --> H["load_config"] F -->|Yes| H H --> I["safe_shutdown_wifi"] I --> J["initialize_e220_startup
set_lora_ch → WOR RX"] J --> K{"BASE_DIST < 0?"} K -->|Yes| L["calibrate_baseline
超音波5回平均"] M(["メインループへ"]) K -->|No| M L --> M
② メインループ — 計測・COMMAND送信フロー
MAINT"} B -->|Yes| C["safe_reboot"] B -->|No| D["set_mode_wor_rx
UART flush"] D --> E["超音波計測
get_avg_distance 5回"] E --> F{"計測成功?"} F -->|No| G["sensor_error=True
water_level=0"] F -->|Yes| H["water_level計算
level_pct計算"] I["load_state
gate_statusロード"] G --> I H --> I I --> J{"is_halt?"} J -->|Yes| K["gate_cmd = KEEP"] L{"sensor_error?"} J -->|No| L L -->|Yes| K M{"water_level
<= TH_DRY?"} L -->|No| M M -->|Yes| N["gate_cmd = OPEN"] O{"water_level
>= TH_FULL?"} M -->|No| O O -->|Yes| P["gate_cmd = CLOSE"] O -->|No| K Q["op_status = NO_ACTION"] N --> Q P --> Q K --> Q Q --> R{"is_halt?"} S(["GW報告フローへ"]) R -->|Yes| S R -->|No| T["★ 2段階COMMAND送信
set_mode_wor_tx
send_lora WAKEUP
sleep 1500ms
set_mode_normal
sleep 1500ms
send_lora COMMAND
wait_aux sleep_ms 500
★ NORMALモード維持(WOR RXに戻さない)"] T --> U["★ RESULT待機 25秒
NORMALモード ticks_ms精度"] V{"AUX=0
またはUART有?"} U --> V W{"25秒経過?"} V -->|No| W W -->|No| V W -->|Yes| X["op_status = TIMEOUT
★ set_mode_wor_rx"] X --> S V -->|Yes| Y["AUX HIGH待ち
sleep 20ms
uart.read"] Z{"RESULT 含む?"} Y --> Z Z -->|No| V AA{"AUTH_KEY
一致?"} Z -->|Yes| AA AA -->|No| V AB["op_status更新
★ gate_cmd が KEEP の場合
gate_status は上書きしない
save_state
★ set_mode_wor_rx"] AA -->|Yes| AB AB --> S
③ GW報告・TIME受信・スリープフロー
25分超 or 状態変化"} F["calc_sleep_ms
TDMAスロット計算"] B -->|No| F B -->|Yes| C["set_mode_wor_tx
send_lora DATA
★ AUX LOW確認(最大100ms)
★ wait_aux
★ sleep_ms 500ms
★ set_mode_normal
(WOR RXライトスリープ不定化リスク排除)"] C --> D["★ NORMALモードでTIME待機 15秒
ticks_ms精度
(常時受信 タイミング非依存)"] E{"TIME受信?"} D --> E E -->|Yes| E2["TH_DRY TH_FULL is_halt更新
config.json保存
RTC同期"] E3["★ set_mode_wor_rx
mins_since_last_gw_sync=0
prev状態更新
(lightsleep前のコンフィグ(DeepSleep)モードで安全リセット)"] E2 --> E3 E -->|No 15秒経過| E3 E3 --> F F --> G["gc.collect
m0 m1=HIGH設定"] G --> H{"DEBUG_MODE?"} H -->|Yes| I["time.sleep"] H -->|No| J["lightsleep
UART再初期化"] K(["ループ先頭へ"]) I --> K J --> K
🚪 水門ノード (Pico W + 2chリレー 48MHz動作)
WOR RXライトスリープ待機 / 2段階受信(WAKEUP検出→200ms待機→NORMAL切替→COMMAND/MANUAL待機10秒)/ NORMALモードでRESULT/MANUAL_RES送信→末尾set_mode_wor_rx
① 起動フロー
リレー初期化OFF"] A1 --> B["VSYS計測
WiFiチップ初期化"] B --> C{"VSYS >= 4.7V?"} C -->|Yes| D["USB設定モード
process_usb_serial"] D --> E["load_config
load_relay_config
load_state"] C -->|No| E E --> F{"skip_ap.flag?"} F -->|No| G["APモード設定サーバー
3分 or BOOTSEL"] G --> H["load_config 3回"] F -->|Yes| H H --> I["WiFiチップシャットダウン
Pin23=LOW"] I --> J["★ machine.freq 48MHz
UART再初期化"] J --> K["initialize_e220_startup
set_lora_ch → WOR RX"] K --> L["AUX割り込み設定
IRQ_FALLING"] L --> M["set_mode_wor_rx"] N(["メインループへ"]) M --> N
② メインループ — 受信待機フロー
MAINT"} B -->|Yes| C["machine.freq 125MHz
reset"] B -->|No| D{"5秒毎
manage_charging"} D --> E{"wake_flag=True
またはAUX=0?"} P{"raw あり?"} E -->|No| P E -->|Yes| F["aux.irq 割り込み無効化"] F --> G["AUX HIGH待ち
最大5秒"] G --> H["★ sleep_ms 200ms
UART flush ゴミ捨て"] H --> I["★ set_mode_normal
2段階AUX確認"] I --> J["本命パケット待機
10秒ウィンドウ"] J --> K{"AUX=0?"} K -->|Yes| L["AUX HIGH待ち
sleep 50ms"] L --> M{"★ UART有?"} K -->|No| M M -->|Yes| N{"COMMAND または MANUAL 含む?"} N -->|Yes| O["raw確定 → break"] N -->|No| Q["raw=None"] R{"10秒経過?"} Q --> R M -->|No| R R -->|No| K R -->|Yes| S["raw=None のまま"] O --> T["wake_flag=False
IRQ再有効化"] S --> T T --> P U(["パケット処理フローへ"]) P -->|Yes| U P -->|No| V["set_mode_wor_rx
UART flush"] V --> W{"uart.any?"} W -->|No| X["lightsleep 5000ms
UART再初期化"] W -->|Yes| Y["sleep_ms 10"] Z(["ループ先頭へ"]) X --> Z Y --> Z
③ パケット処理フロー(COMMAND / MANUAL)
COMMAND または MANUAL 抽出"] B --> C{"AUTH_KEY一致?"} Z_RX["★ set_mode_wor_rx
2段階AUX確認"] ZZ(["ループ先頭へ"]) C -->|No| Z_RX C -->|Yes| D{"FIELD_ID一致?"} D -->|No| Z_RX D -->|Yes| E{"l0 == MANUAL?"} E -->|Yes| F["execute_op
maintenance=True"] F --> G["★ sleep 1.5秒
受信側(GW)NORMAL確立待機"] G --> H["★ set_mode_normal
(プリアンブルなし GWがNORMALで受信)
time.sleep_ms(500)
(E220モード変更後の安定化待ち)"] I["send_lora MANUAL_RES
SUCCESS HALT ACT_ERR LOW_BATT"] H --> I E -->|No| K{"l0 == COMMAND?"} K -->|Yes| L["execute_op
maintenance=False"] L --> M["★ sleep 1.5秒
受信側(水位ノード)NORMAL確立待機"] M --> N["★ set_mode_normal
(プリアンブルなし 水位ノードがNORMALで受信)
time.sleep_ms(500)
(E220モード変更後の安定化待ち)"] N --> O["send_lora RESULT"] K -->|No| Z_RX I --> Z_RX O --> Z_RX Z_RX --> ZZ
④ execute_op 詳細
かつis_act_error?"} C -->|Yes| D["is_act_error=False
save_state NORMAL"] E{"is_halt
かつnot maintenance?"} D --> E C -->|No| E E -->|Yes| F["return HALT"] G{"is_act_error
かつnot maintenance?"} E -->|No| G G -->|Yes| H["return ACT_ERR"] I{"v_now < 11.5V?"} G -->|No| I I -->|Yes| J["is_act_error=True
return LOW_BATT"] K{"cmd == OPEN
またはCLOSE?"} I -->|No| K K -->|No| L["return SUCCESS
リレー操作なし"] K -->|Yes| M{"current_pump_status
== cmd
かつnot maintenance?"} M -->|Yes| N["return SUCCESS
リレー操作なし 即return"] M -->|No| O["drive_motor cmd
リレーON"] O --> P["10秒モーター動作
100ms毎に電圧監視"] Q{"v < 9.0V?"} P --> Q Q -->|Yes| R["is_act_error=True
return ACT_ERR"] Q -->|No| S["drive_motor STOP
current_pump_status更新
save_state"] S --> T["return SUCCESS"]
📡 ゲートウェイ (Wi-Fi版 Pico W / 125MHz)
LoRa WOR RX常時待機 / ルートA(STOP安全窓10秒/RUN 500ms分岐) / scheduler1(Wi-Fi POST) / scheduler2(LoRa ルートB) / イベントコード分離 / TIME送信はNORMAL(1200ms後) / MANUAL送信後NORMAL維持→MANUAL_RES受信後WOR RX
① 起動フロー
wlan.active"] B --> C["VSYS計測
WL_GPIO1経由"] C --> D["load_wifi_conf
load_config
load_lora_cmd_queue"] D --> E{"起動時刻チェック
定時・メンテ時間?"} E -->|No| F["process_usb_serial
VSYS>=4.6Vのみ"] E -->|Yes| G["startup_led_pattern"] F --> G G --> H["setup_wifi NTP同期"] H --> I["sync_fields GET
FIELD_CHANNELS取得"] I --> J["initialize_e220_startup
set_lora_ch → WOR RX"] J --> K["GWブート通知
CHUNK_QUEUEに積む"] K --> L{"LORA_CMD_QUEUE
空でない?"} L -->|Yes| M["manual_cmd_deadline
=now+35分"] N["set_mode_wor_rx"] L -->|No| N M --> N O(["メインループへ"]) N --> O
② メインループ全体構造
5秒毎"] C{"ルートC
manual_cmd_deadline
期限超過?"} B --> C C -->|Yes| D["★ ルートC処理
WAKEUP→1500ms
→NORMAL→1500ms
→MANUAL送信
log_event 206
★ log_event 202/205"] E{"MAINT時刻?"} D --> E C -->|No| E E -->|Yes| F["router_pwr OFF
reset"] E -->|No| G{"AUX=0
またはUART有?"} G -->|Yes| H["LoRa受信処理"] H --> I{"DATA受信?"} I -->|Yes| J["★ ルートA処理
→GW報告フローへ"] I -->|No| K{"MANUAL_RES受信?"} L["CHUNK_QUEUE urgent投入"] K -->|Yes| L M{"should_sync?
30分毎"} K -->|No| M J --> M L --> M M -->|Yes| N["BATCH_CACHE→
CHUNK_QUEUE"] O{"Scheduler1
25-30秒スロット"} M -->|No| O N --> O O -->|Yes| P{"PENDING_SYNC?"} P -->|Yes| Q["sync_fields GET"] P -->|No| R{"CHUNK_QUEUE
空でない?"} R -->|Yes| S["https_post_json_to_gas
FIELD_CACHE更新
LORA_CMD_QUEUE更新"] T{"Scheduler2
35-40秒スロット
ルートB"} R -->|No| T Q --> T S --> T O -->|No| T T -->|Yes| U["★ WAKEUP→1500ms
→NORMAL→1500ms
→MANUAL送信
★ log_event 202/205"] T -->|No| V["sleep_ms 50
gc.collect"] U --> V V --> A
③ DATA受信処理+ルートA詳細
sleep_ms 20
uart.read"] B --> C{"DATA 含む?"} Z(["戻る"]) C -->|No| Z C -->|Yes| D["field_id volt level
act_st sys_st抽出"] E{"urgent判定
op_status異常?
sys_st異常?
act_st異常?
SUCCESS OPEN CLOSE?"} D --> E E -->|urgent| F["CHUNK_QUEUE先頭挿入"] E -->|normal| G["BATCH_CACHE上書き"] H["g_res=FIELD_CACHE取得
tg_ch=FIELD_CHANNELS取得"] F --> H G --> H H --> H2["★ _time_has_stop判定
g_res末尾がSTOP?"] H2 --> I["★ sleep_ms 1200(TIME送信前待機)
★ set_mode_normal
send_lora TIME(プリアンブルなし)
★ log_event 208(STOP) または 207(RUN)
set_mode_wor_rx
(水位ノードNORMAL確立T=1310ms
TIME到達T=1490ms 余裕180ms)"] J{"LORA_CMD_QUEUE
空でない?
かつfield_id一致?"} I --> J J -->|No| Z J -->|Yes| J2{"_time_has_stop?"} J2 -->|Yes STOP| J3["★ 安全窓待機
sleep_ms 10000
水門lightsleep確認後送信"] J2 -->|No RUN| J4["E220安定待機
sleep_ms 500"] K["★ ルートA
set_mode_wor_tx
send_lora WAKEUP
sleep 1500ms
set_mode_normal
sleep 1500ms
send_lora MANUAL
★ 同一ch:NORMALモード維持
異なるch:set_lora_ch(LISTEN_CH)"] J3 --> K J4 --> K K --> L["★ log_event 202(OPEN) または 205(CLOSE)
save_lora_cmd_queue"] L --> M{"QUEUE空?"} M -->|Yes| N["manual_cmd_deadline=None"] O(["そのまま"]) M -->|No| O N --> Z O --> Z Z2(["MANUAL_RES受信後"]) Z2 --> ZR["★ set_mode_wor_rx
CHUNK_QUEUE urgent投入"] ZR --> Z
📶 ゲートウェイ (LTE版 無印Pico / 48MHz)
SIM7080G LTE制御 / APN接続 / LoRa WOR RX常時待機 / ルートA(STOP安全窓10秒/RUN 500ms分岐) / イベントコード分離 / TIME送信はNORMAL(1200ms後) / MANUAL送信後NORMAL維持→MANUAL_RES受信後WOR RX
① 起動フロー
UART0 UART1初期化
rxbuf 4096"] B --> C["load_config
load_lora_cmd_queue"] C --> D["startup_led_pattern"] D --> E["sim_power_on
LTEモジュール電源ON"] E --> F["sim_wait_ready
AT応答待ち最大20秒"] F --> G["lte_connect
APN設定→PDP→HTTP初期化"] G --> H{"LTE接続成功?"} H -->|No| I["log_event 103
リトライ"] H -->|Yes| J["log_event 102
NTP時刻同期"] J --> K["sync_fields GET
FIELD_CHANNELS取得"] K --> L["initialize_e220_startup
set_lora_ch → WOR RX"] L --> M["GWブート通知
CHUNK_QUEUEに積む"] M --> N{"LORA_CMD_QUEUE
空でない?"} N -->|Yes| O["manual_cmd_deadline
now+35分"] P(["メインループへ"]) N -->|No| P O --> P
② LTE通信フロー詳細
AT確認"] B --> C{"応答あり?"} C -->|No| D["sim_power_on
再起動シーケンス"] D --> E["lte_connect"] C -->|Yes| F["AT+SHBOD データ設定"] F --> G["AT+SHREQ POST送信
最大30秒"] G --> H{"HTTP 200?"} I["log_event 105
return None"] H -->|No| I H -->|Yes| J["AT+SHREAD
レスポンス読み出し"] J --> K{"JSON正常?"} K -->|No| I K -->|Yes| L["return レスポンス文字列"] M(["呼び出し元へ"]) L --> M A2(["lte_connect"]) --> B2["AT+CGDCONT APN設定"] B2 --> C2["AT+CNACT PDP起動
最大60秒"] C2 --> D2{"ACTIVE?"} D2 -->|No| E2["log_event 103
return False"] D2 -->|Yes| F2["AT+SHCONF HTTP設定"] F2 --> G2["AT+SHCONN 接続
最大30秒"] G2 --> H2["return True"]
③ メインループ全体構造
5秒毎"] B --> C{"AUX=0
またはUART有?"} C -->|Yes| D["LoRa受信処理
DATA or MANUAL_RES"] D --> E{"DATA受信?"} E -->|Yes| F["★ sleep_ms 1200(TIME送信前待機)
★ set_mode_normal
send_lora TIME(プリアンブルなし)
★ log_event 208/207
★ ルートA判定
STOP:安全窓10秒 RUN:500ms待機
MANUAL送信後 同一ch:NORMAL維持
★ log_event 202/205"] E -->|No| G{"MANUAL_RES?"} G -->|Yes| H["CHUNK_QUEUE urgent投入
★ set_mode_wor_rx"] I{"ルートC
manual_cmd_deadline
期限超過?"} F --> I H --> I C -->|No| I I -->|Yes| J["★ WAKEUP→1500ms
→NORMAL→1500ms
→MANUAL送信
log_event 206
★ log_event 202/205"] K{"MAINT時刻?"} I -->|No| K J --> K K -->|Yes| L["machine.reset"] M{"should_sync?
30分毎"} K -->|No| M M -->|Yes| N["BATCH_CACHE→
CHUNK_QUEUE生成"] O{"25-30秒スロット
かつCHUNK_QUEUE有?"} M -->|No| O N --> O O -->|Yes| P["https_post_json_to_gas
FIELD_CACHE更新
LORA_CMD_QUEUE更新"] Q{"35-40秒スロット
ルートB"} O -->|No| Q P --> Q Q -->|Yes| R["★ WAKEUP→1500ms
→NORMAL→1500ms
→MANUAL送信
★ log_event 202/205"] Q -->|No| S["gc.collect
sleep_ms 50"] R --> S S --> A
🌐 Cloudflare Worker プロキシサーバー
GASの302リダイレクト自動追従・CORSヘッダ付与 (POSTボディ消滅回避)
① リクエスト中継・CORS処理フロー
fetch"]) --> B{"OPTIONSメソッド?
CORSプリフライト"} B -->|Yes| C["CORSヘッダ設定
Access-Control-Allow-*"] Z(["return Response(null)"]) C --> Z B -->|No| D["GAS Web App URL構築
パラメータ引継ぎ"] D --> E["requestInit構築
redirect: 'follow' 設定
GASの302リダイレクト追従"] E --> F{"POSTメソッド?"} F -->|Yes| G["リクエストボディ読込
request.arrayBuffer"] F -->|No| H["fetch実行
中継先GASへ"] G --> H H --> I["レスポンス取得
CORSヘッダ再付与"] J(["クライアントへ返却"]) I --> J
☁️ 管理用バックエンドGAS
データ受信・モード管理・Discord通知・ダッシュボード連携
① doPost (一括データ受信・処理)
batchData"] D --> E["SYSTEMシート確認
globalMode/notify取得"] E --> F["各ノードのデータループ処理"] F --> G["processNodeData呼出"] G --> H{"異常・状態変化
あるか?"} H -->|Yes| I["アラート配列に蓄積"] H -->|No| J["次データへ"] I --> J J --> K{"全データ処理完了?"} K -->|No| F K -->|Yes| L{"bulkAlerts有?"} L -->|Yes| M["Discordへ一括通知送信
sendMessagingApiPush"] L -->|No| N["REMOTE_CMDシート確認
PENDINGコマンド抽出"] M --> N N --> O["ロック解放
finally処理"] P(["return JSON
CONFIG / COMMANDS"]) O --> P
② processNodeData (ノード個別ロジック)
フィールドモード(RUN/STOP)判定"] C --> D{"rawGate == 'KEEP'
または空文字?"} D -->|Yes| E["表示用finalGateに
前回ステータスを補完"] D -->|No| F["受信データを採用"] E --> F F --> G{"アラート判定条件"} G -->|水門開閉変化| H["Discord通知キュー追加"] G -->|センサー/動作エラー| H G -->|通信失敗・タイムアウト| H G -->|電圧・バッテリー低下| H G -->|該当なし| I["Logシート更新
上書きまたは追記"] H --> I I --> J["DASHBOARD用SSへ履歴追記
イベントコード(e_log)付与"] J --> K["最終通信時刻更新
updateLastActiveTime"] L(["return
閾値とモード(RUN/STOP)"]) K --> L
③ 定期実行 (トリガー監視)
monitorCommunicationHeartbeat"]) --> B["SYSTEMシート確認
RUNモード時のみ実行"] B --> C["ID_LIST走査"] C --> D{"最終通信から
90分以上経過?"} D -->|No| E["次ノードへ"] D -->|Yes| F{"現在OFFLINE?"} F -->|Yes| E F -->|No| G["Discordへ通信途絶アラート送信"] G --> H["状態をOFFLINEに変更
LogシートにTIMEOUT行追記"] H --> E E --> I{"走査完了?"} I -->|No| C J(["終了"]) I -->|Yes| J
💻 メインダッシュボード (index.html)
フロントエンドUI・Cloudflareプロキシ連携・USBシリアル設定・遠隔操作
① データ読み込み・描画 (loadData)
action=getDataForWeb"] C --> D["Cloudflare Worker経由で
GASからJSON取得"] D --> E{"取得成功?"} E -->|No| F["UI: エラー詳細表示
デプロイURL確認喚起"] E -->|Yes| G["renderData(data)"] G --> H["システム設定反映
RUN/STOP一括切替ボタン"] H --> I["Logデータループ
カードUI生成"] I --> J["個別モード/全体モード考慮
フィールド状況・電圧描画"] J --> K["履歴タブ・グラフ描画
Chart.js更新"] L(["描画完了"]) K --> L
② 遠隔操作予約 (sendRemoteCommand)
ボタンクリック"]) --> B{"fieldMode == 'STOP'?"} B -->|No| C["警告アラート表示
『RUN中は操作不可』
処理中断"] B -->|Yes| D["確認ダイアログ表示"] D -->|Cancel| Z(["中断"]) D -->|OK| E["UI: 処理中オーバーレイ"] E --> F["callGasApi
action=sendRemoteCmd"] F --> G{"GAS登録成功?"} G -->|Yes| H["完了アラート表示"] G -->|No| I["エラーアラート表示"] H --> J(["オーバーレイ非表示"]) I --> J
📊 管理履歴閲覧 (GAS / スマホ対応HTML)
履歴データ供給・自動アーカイブ処理・イベントログ翻訳
① History取得 (GAS側・UI連携)
(スマホ対応版)返却"] B --> C(["UI: ブラウザ読込
loadData()実行"]) C --> D["google.script.run
getDataForWeb()呼出"] D --> E["GAS: Historyシート検索
直近4500行取得"] E --> F["最新状態マップ作成
logData生成"] F --> G["JSONとしてUIへ返却"] G --> H["UI: renderData()"] H --> I["カードUI生成
履歴リスト生成"] I --> J["Chart.js グラフ生成
開/閉イベントにマーカー付与"] K(["描画完了"]) J --> K
② 保守機能 (自動アーカイブ / イベント翻訳)
archiveHistorySheet"]) --> B["Historyシート
最終行数確認"] B --> C{"行数 > 50000?"} C -->|No| Z(["終了"]) C -->|Yes| D["既存シート走査
CopiedHistory-(X) の
最大インデックス検索"] D --> E["シート複製・リネーム退避"] E --> F["元のHistoryシートの
データ範囲クリア(1行目残す)"] F --> Z A2(["カスタム関数
GET_EVENT_LOG"]) --> B2["対象IDでHistory検索"] B2 --> C2["P列(e_log)カンマ分割"] C2 --> D2["EVENT_DICT辞書参照
101→LTE初期化
202→MANUAL送信など"] D2 --> E2["翻訳後文字列と日時を
配列として返却"]