電池稼働させたかったので電力消耗を抑える必要があり、ULPで実装。初代ESPを使っていたので最近の情報が違うことが多く苦労する。
初代ESP32は platform = espressif32@5.4.0 にする必要あり。また *.S ファイルによるアセンブラからの実装も不可。なんとかFSMマクロで作成して、とりあえずトイレに光センサーでロギング&Zabbixへ送信できるようにしたものをトイレへ設置


センサー値が移動平均を大きく乖離した時に発動する例
I_MOVI(R1, adc_no), // [0] //処理中ADCを保存
I_MOVI(R2, MYULP_ADC_EVENT_SOURCE), //
I_ST(R1, R2, 0),
I_DELAY(20), // 一発目は捨て
I_ADC(R0, 0, adc_no), // ADC
I_STAGE_RST(), // カウンタリセット [5]
I_MOVI(R3, 0), // 累計地 R3 クリア
I_DELAY(20), // -4 <--
I_ADC(R0, 0, adc_no), // -3
I_ADDR(R3, R3, R0), // -2
I_STAGE_INC(1), // -1 [10]
I_JUMPS(-4, 4, JUMPS_LT), I_RSHI(R3, R3, 2),
// VALUE の書き込み
I_MOVR(R0, R3), // R0 <- R3 へ退避
I_MOVI(R2, addr_adc_value), // 値保存
I_ST(R0, R2, 0), // [15]
// 移動平均(4ビットシフトなので16回)
I_MOVI(R1, addr_avg_value), I_LD(R2, R1, 0), // R2 = avg
//
// avg>>4
//
I_RSHI(R3, R2, 4), // R3 = avg/16
//
// current>>4
//
I_RSHI(R0, R0, 4), // R0 = current/16
//
// avg = avg - avg/16
//
I_SUBR(R2, R2, R3),
//
// avg = avg + current/16
//
I_ADDR(R2, R2, R0),
//
// avg保存
//
I_MOVI(R1, addr_adc_value), // [20]
I_LD(R0, R1, 0), // R0 <-- 計測値復帰
I_MOVI(R1, addr_avg_value),
I_LD(R2, R1, 0), // R2 .. 平均値
I_MOVI(R1, addr_threshold),
I_LD(R3, R1, 0), // R3 <-- MYULP_ADC_THRESHOLD [25]
I_ADDR(R1, R2, R3), // R1 <-- 平均値R2 + 閾値R3 許容値境地最大
I_SUBR(R0, R0, R1), // R0 <- R0 - R1
I_BGE(3,0x7fff), // I_BL(2,1) は 許容値最大のほうが大きい
I_WAKE(),
//I_HALT(),
I_BXI(start_addr + 37), // [30]
I_MOVI(R1, addr_adc_value),
I_LD(R0, R1, 0), // R0 <-- 計測値復帰
I_SUBR(R1, R2, R3), // R1 <-- 平均値R2 - 閾値R3 許容値境地最小
I_SUBR(R0, R1, R0), // R0 <- R1 - R0
I_BGE(2,0x7fff), // I_BL(2,1) は 許容値最小のほうが小さい
I_WAKE(),
I_HALT() // [37]
照度センサーなどで、日中の明るさが断続的に変わる場合でも、光の変化を検知したい場合。
16回移動平均値を常に計算してそこから閾値分ずれた時点でWAKEさせる。
ULP:センサー閾値を動的に変化させつつ、で以上、以下を切り替える例
// 指定されたADCチャンネルから値を読み込む (R0に結果が入る)
I_MOVI(R1, adc_no), // [0] //処理中ADCを保存
I_MOVI(R2, MYULP_ADC_EVENT_SOURCE), //
I_ST(R1, R2, 0),
I_DELAY(20), // 一発目は捨て
I_ADC(R0, 0, adc_no), // ADC
I_STAGE_RST(), // カウンタリセット [5]
I_MOVI(R3, 0), // 累計地 R3 クリア
I_DELAY(20), // -4 <--
I_ADC(R0, 0, adc_no), // -3
I_ADDR(R3, R3, R0), // -2
I_STAGE_INC(1), // -1 [10]
I_JUMPS(-4, 4, JUMPS_LT),
I_RSHI(R3, R3, 2),
// VALUE の書き込み
I_MOVR(R0, R3), // R0 <- R3 へ退避
I_MOVI(R2, addr_adc_value), // 値保存
I_ST(R0, R2, 0), // [15]
I_MOVI(R2, addr_threshold),
I_LD(R3, R2, 0), // R3 <-- MYULP_ADC_THRESHOLD
I_MOVI(R2, addr_threshold_dir),
I_LD(R1, R2, 0), // R1 <-- MYULP_ADC_THRESHOLD
I_ADDI(R1, R1, 0), // R1 <- R1+0 // 以上か以下かの判定() [20]
I_BXZI(start_addr + 26), // 🍎 🚨
//
I_SUBR(R0, R0, R3), // R0 <- R0 - R3 [25]
I_BGE(2, 0x7fff), // I_BL(2,1) は センサー値R0 の 方が大きい場合は WAKE
I_WAKE(),
// I_HALT(),
I_BXI(start_addr + 29),
//
I_SUBR(R0, R3, R0), // 🍎 R0 <- R3-R0 [26]
I_BGE(2, 0x7fff), // I_BL(2,1) は 閾値の方が大きい場合は WAKE
I_WAKE(),
I_HALT(), //[29]
スイッチ的な動きをするセンサーなどでもTTLレベル(0と4095)だけではなく中間値が必ず発生する。閾値3500以上でWAKEさせ、次のOFFは閾値500以下でWAKEさせるなどの設定が可能