nihsokのアイコン画像
nihsok 2025年10月20日作成 © MIT
セットアップや使用方法 セットアップや使用方法 閲覧数 227
nihsok 2025年10月20日作成 © MIT セットアップや使用方法 セットアップや使用方法 閲覧数 227

雷センサーモジュールAS3935をESP32で動かす (micropython)

雷センサーモジュールAS3935をESP32で動かす (micropython)

秋月電子で販売されているAS3935搭載の雷センサーモジュールをESP32からmicropythonで操作する。

Raspberry Pi & Python(ライブラリ利用)、ESP32 & Arduinoの組み合わせはいくつか見かけたが、もっと簡素な構成を目指す。今回やることは、

1. 使用機材

ピン接続は以下。

AS3935 ESP32
SDA GPIO26 (P26)
SCL GPIO25 (P25)
IRQ GPIO12 (P12)
GND GND(P12の隣)
VDD 5V

配線は他にも考えられるが、最もすっきりするものを選んだ。

2. アンテナのチューニング

取扱説明書の記述より

  1. REG0x08[7]のDISP_LCOを1にするとIRQピンにLCO周波数が出力される
  2. このとき、REG0x03[7:6]のLCO_FDIVに基づき分割した結果が出力される。
    デフォルトはLCO_FDIV=1/16
  3. REG0x08[3:0]のTUN_CAPで追加キャパシタ容量を指定し、LCO周波数が500 kHzに最も近くなるものを選ぶ。指定範囲は16段階で、0-120 pFの8 pF刻み

この作業のためのスクリプトを作成した。

anntena_tune.py

import utime from machine import Pin,I2C,Counter,Timer paddr = 0x03 i2c = I2C(1) #check frequency division byte1 = i2c.readfrom_mem(paddr,0x03,1)[0] div = (16,32,64,124)[byte1 >> 6] if div != 16: print(f'Frequency division ratio {div} is not 16') #set display LCO on IRQ byte1 = i2c.readfrom_mem(paddr,0x08,1)[0] byte1 = (byte1 & 0b00011111) | 0b10000000 i2c.writeto_mem(paddr,0x08,bytes([byte1])) #initialize counter counter = Counter(0,Pin(12,Pin.IN)) counts = [] def callback(timer): counts.append(counter.value(0)) timer = Timer(0) #capacitance loop for i in range(16): byte1 = (byte1 & 0b11110000) | i i2c.writeto_mem(paddr,0x08,bytes([byte1])) utime.sleep(0.002) #wait 2ms counter.init() timer.init(period=1000,mode=Timer.PERIODIC,callback=callback) utime.sleep(5.5) print(i,sum(counts[1:5])*div/4*.001,counts) counts = [] #reset display LCO on IRQ i2c.writeto_mem(paddr,0x08,bytes([0b00000000]))

ESP32に備わっている周波数カウンタを使うため追加の機器は不要。結果をわかりやすくするため、周波数分割比LCO_FDIVをかけた値を表示している。

タイマーを使い1秒ごとに周波数を取得し、平均を出す。今回は5回のうち1回目は(タイマーが稼働するまでの時間を考えて)捨て、残り4回の平均を計算した。出力は以下のようになった。

0 507.88004 [31874, 31739, 31743, 31744, 31744]
1 506.24802 [47673, 31638, 31641, 31642, 31641]
2 504.59604 [47504, 31536, 31538, 31537, 31538]
3 502.984 [47348, 31436, 31436, 31437, 31437]
4 501.37604 [47192, 31335, 31336, 31336, 31337]
5 499.78804 [47047, 31235, 31237, 31237, 31238]
6 498.21604 [46898, 31137, 31139, 31139, 31139]
7 496.66804 [46752, 31041, 31042, 31042, 31042]
8 495.04404 [46602, 30939, 30940, 30941, 30941]
9 493.520032 [46455, 30844, 30845, 30846, 30845]
10 492.00404 [46315, 30749, 30751, 30750, 30751]
11 490.516032 [46172, 30656, 30657, 30658, 30658]
12 489.028 [46035, 30563, 30564, 30565, 30565]
13 487.56404 [45889, 30471, 30474, 30473, 30473]
14 486.11204 [45757, 30380, 30383, 30382, 30383]
15 484.680032 [45622, 30292, 30292, 30293, 30293]

この結果から、最も500kHzに近いTUN_CAP=5を次のステップで使う。

同じ設定での周波数のばらつき(1の位)に対し、静電容量ごとの周波数の違い(100の位)は2桁大きいので、そこまで平均をとらなくても十分ということもわかる。

この操作自体をメインプログラムに組み込み自動化することもできそうだが、機差であり一度設定すれば同じ値でよいと考えスクリプトを分けた。実際何度か試しても同じ値が選ばれる。

3. 雷センサーの運用

main.py

import utime from machine import Pin,I2C import wifi import ambient paddr = 0x03 i2c = I2C(1) #reset to default i2c.writeto_mem(paddr,0x3D,bytes([0x96])) #Initial setting print('Setting:') #Read AFE Setting, Outdoor vs. Indoor byte1 = i2c.readfrom_mem(paddr,0x00,1)[0] AFE_GB = (byte1 & 0b00111110) >> 1 if AFE_GB == 0b10010: print(' Indoor') noise_level = (28,45,62,78,95,112,130,146) elif AFE_GB == 0b01110: print(' Outdoor') noise_level = (390,630,860,1100,1140,1570,1800,2000) else: print(' Unknown AFE setting') exit() #Read Noise Floor/Watchdog Threshold byte1 = i2c.readfrom_mem(paddr,0x01,1)[0] threshold = noise_level[(byte1 & 0b01110000) >> 4] print(f' Noise floor threshold: {threshold} uVrms') WDTH = (byte1 & 0b0001111) print(f' Watchdog threshold: {WDTH}') #Read Minimum Number of Lightning Detection/SREJ byte1 = i2c.readfrom_mem(paddr,0x02,1)[0] MIN_NUM_LIGH = (1,5,9,16)[(byte1 & 0b00110000) >> 4] print(f' Minimum number of lightning: {MIN_NUM_LIGH}') SREJ = (byte1 & 0b00001111) print(f' Spike rejection: {SREJ}') #Antenna caliblation TUN_CAP = 5 byte1 = i2c.readfrom_mem(paddr,0x08,1)[0] byte1 = (byte1 & 0b11110000) | TUN_CAP i2c.writeto_mem(paddr,0x08,bytes([byte1])) #Ambient setting print(wifi.connect()) am = ambient.Ambient(CHANNEL_ID,WRITE_KEY) #On interruption def interrupt(irq): byte1 = i2c.readfrom_mem(paddr,0x03,1)[0] INT = (byte1 & 0b00001111) if INT == 0b0001: #Modify noise floor print('Noise level too high') byte1 = i2c.readfrom_mem(paddr,0x01,1)[0] NF_LEV = (byte1 & 0b01110000) >> 4 byte1 = (byte1 & 0b10001111) | (min(NF_LEV+1,7) << 4) i2c.writeto_mem(paddr,0x01,bytes([byte1])) elif INT == 0b0100: #Modify MASK_DIST print('Disturber detected') byte1 = i2c.readfrom_mem(paddr,0x03,1)[0] byte1 = byte1 | 0b00100000 i2c.writeto_mem(paddr,0x03,bytes([byte1])) elif INT == 0b1000: print('Lightning interrupt') byte1 = i2c.readfrom_mem(paddr,0x07,1)[0] DISTANCE = (byte1 & 0b00111111) byte1 = i2c.readfrom_mem(paddr,0x04,1)[0] S_LIG_L = byte1 byte1 = i2c.readfrom_mem(paddr,0x05,1)[0] S_LIG_M = byte1 byte1 = i2c.readfrom_mem(paddr,0x06,1)[0] S_LIG_MM = (byte1 & 0b00011111) print(am.send({'d1':DISTANCE,'d2':S_LIG_L,'d3':S_LIG_M,'d4':S_LIG_MM})) #IRQ setting event = Pin(12,Pin.IN) event.irq(interrupt,trigger=Pin.IRQ_RISING) #main while True: utime.sleep(1800) print(wifi.connect())

雷が検出されるとIRQピンに割り込みが発生するので、これをトリガーに以下の処理を行う。

  • REG0x03[3:0]がINT_NH (0001): 高ノイズレベル⇒ノイズフロア閾値を上げる
  • REG0x03[3:0]がINT_L (1000): 妨害信号⇒REG0x03[5]のMASK_DISTを1にしてマスク。この分岐は最初のみ
  • REG0x03[3:0]がINT_L (1000): 雷を検出⇒REG0x07[5:0]のDISTANCE(雷雲までの最短距離)、強度 (REG0x04[7:0]=S_LIG_L, REG0x05[7:0]=S_LIG_M, REG0x06[4:0]=S_LIG_MM) を取得

Wi-Fi接続に関しては過去の記事を参照

IRQの割り込みはHレベルを使いたかったのだが、micropythonではPin.IRQ_HIGH_LEVELがサポートされていなかったので代わりにPin.IRQ_RISINGを使った。

結果はAmbientにアップロードしており、https://ambidata.io/bd/board.html?id=100013 で確認できる。

3.1 秋月電子提供の説明書で省略されていた点

  1. 本家説明書p23 Direct Command:レジスタの設定をすべてデフォルトの値へとリセットするにはREG0x03C、RCOを校正するにはREG0x3Dに直接コマンドを送る。このためには、それぞれのレジスタへ0x96 (0b10010110) を書き込む。
    (秋月の説明書にはコマンドの送信先は書いてあるが、コマンドのフォーマットが書かれていない)
  2. 本家説明書p29 Watchdog threshold:REG0x01[3:0]のWDTHで閾値を設定できる。このパラメータもSREJと同様に感度へ影響する。
  3. 本家説明書p35 距離推定のリセット:雷雲までの距離の統計をリセットするには、REG0x02[6]にhigh-low-highを与える。
  4. 本家説明書p36 RC Oscillatorの校正:system RCO (SRCO; 1.1 MHz) とtimer RCO (TRCO; 32.768kHz) が搭載されており、温度変化による周波数変動は自動で補償される。
    TRCOはREG0x08[5]、SRCOはREG0x08[6]を1にするとその周波数がIRQピンに出力される。校正のためのコマンドがあるが、その精度はアンテナの共振周波数に依存するため、アンテナ調整後に発信回路の校正を行う。校正結果は揮発メモリに記録されるため、起動のたびに校正するが、温度や電圧の変動に対しては内部的に補償される。power-downモードの際にTRCOを校正するには以下の手続きを行う。
    (power-downモードはREG0x00[0]を1に変えない限りは入らないので、基本的には不要?)
    1. CALIB_RCOのコマンド送信:REG0x3Dに0x96 (0b10010110) を書き込む
    2. REG0x08[6]を1にする
    3. 2ミリ秒待つ
    4. REG0x08[6]を0にする
      校正結果はREG0x3A[7:6]とREG0x3B[7:6]に出力される。それぞれTRCOとSRCOのステータスに対応し、校正が成功していれば上位ビットが1、失敗なら下位ビットが1となる(ここは秋月の説明書のレジスタマップにも書いてある)

4. 所感

  • 雷が発生していないとちゃんと動いているかわからない。
  • IRQ割込みをトリガーとしてスリープ復帰するようにすれば待機時の消費電力を抑えられる気もするが、そのたびに毎回Wi-Fi接続していると雷の発生間隔よりも時間がかかってしまいそうなので今回は不採用。
    • 消費電力調査も今後の課題
  • 連続稼働に難があるようで、誤検出が頻出するときがある。起動時にすべての設定をリセットするようにしているので、データがおかしいときはいったん電源を差しなして対応。
    • 配線やはんだなどハードの問題の場合、通信そのものができないはずなので別の理由と考えている。
nihsokのアイコン画像
ソフトウェアがメインの電子工作が好みです。
  • nihsok さんが 2025/10/20 に 編集 をしました。 (メッセージ: 初版)
ログインしてコメントを投稿する