The Negligible Lab

Astray in the forest of electrical and electronic circuits. Adrift in the gap between time and frequency domains. 独立独歩を是とする。

ULPを活用した電池駆動ESP32雨センサシステムを作る

はじめに

明けましておめでとうございます🐇 とうとう昨年(2022年)の記事がただの1件から増えることがないまま2023年を迎えてしまいました。令和も何ともう5年です。さらにはもう2月ですね。

さて,新型コロナウイルスの流行が始まってから,筆者はほぼ在宅勤務となっています。その仕事机はちょうどバルコニー*1に通じる窓の直近に位置しています。このバルコニーには洗濯物も干しているのですが,雨が降り始めてもすぐ近くに座っている筆者がそれに気付かず,洗濯物を濡らしてしまうことが多々ありました。雨滴を検知して雨の降り始めをお知らせしてくれるシステムがあれば…いや,自分で作るのだ…! そこで,雨が降り始めたらLINEに通知を送ってくれる雨センサシステムを製作します。

まずは図1,2に完成形を示します。

図1: ESP32マイコンによる雨センサシステム

図2: “素”のESP32マイコンを搭載する基板

図1に示すように,防雨ケース内にESP32マイコンと電池ボックス(eneloop × 3本)を収納し,ゴムブッシングを介して2枚の雨滴センサを外部に引き出しています。図2は図1の中に搭載しているESP32マイコンを搭載するメイン基板です。これは本ブログでおなじみの十字配線ユニバーサル基板を用いて製作しています。後述しますが,ESP32マイコンを数ヶ月単位の長時間にわたって電池駆動しようとする場合,USBシリアル変換ICや三端子レギュレータの消費電力が無視できないため,それらを搭載しない“素”のESP32マイコンを使う必要があります。ESP32マイコンに搭載されている超低消費電力(ULP)コプロセッサを使って,電池の消費を極力抑えながら雨滴センサを監視し,雨滴を検知したらメインコアを起動してLINEに通知を送るようにします。

www.youtube.com
動画1: ESP32マイコンによる雨センサシステムの動作テスト

動画1に動作テストの様子を示します。動画の開始時ではESP32のメインコアはディープスリープしています。電流計を見ると,消費電流はほとんど零に見えますね。ここで雨滴センサに水滴を垂らすとULPコプロセッサがこれを即座に検知し(実際は1秒毎に監視しています),メインコアを起こしに行きます。すると消費電流が立ち上がって変動しつつ,メインコアはWiFiに接続してLINEに通知を送ります。スマートフォンの画面にLINEの通知が現れる様子が見えますね。そしてすぐにメインコアはディープスリープへと戻ります。

本記事ではこの雨センサシステムについて書いていきます。まずはアイディア出しや試作段階で考えていたことから始め,ESP32マイコンを搭載した市販の各種ボードについて述べます。またArduino IDEからULPコプロセッサを使用する方法や,ESP32マイコンからLINEに通知を送る方法,LINE Notifyの設定について述べることにします。

目次

基本コンセプト

ハードウェア概略構成

図3にハードウェア概略構成を示します。100 VコンセントやUSB端子のない屋外で使用するため,電源としてeneloop × 3本をESP32のVCC端子に直結します*2。当初はDC-DCコンバータを使用して3.3 Vに制御された電源を供給しようと考えましたが,電池駆動できる時間を極力伸ばすためにはDC-DCコンバータの損失(特にほぼ無負荷 ~ 低負荷での)が無視できないとの考えに至り,電池直結としました。

雨を検知するため,R1 = 33 kΩの抵抗器と雨滴センサを直列接続し,その中点をESP32マイコンのGPIO34に接続してアナログ・ディジタル変換器(ADC)に入力します。

図3: 雨センサシステムのハードウェア概略構成

図4に使用した雨滴センサの写真を示します。雨滴センサは櫛状の銅箔パターンを持つただのプリント基板です。

図4: 雨滴センサ

乾燥時は雨滴センサの抵抗値が極めて大きいため,電源電圧がそのままADCに入力されます。雨滴が落ちてくると,水が僅かに電気を通すため分圧比が変わり,これをADCで検知することによって,雨の降り始めを知ることができます。ここでは雨滴が付着したか否かが判ればよいので,ADCの線形性など細かいことは気にしなくても問題ありません。

ESP32マイコンのI2Cバス*3を介して128 × 32ピクセルのモノクロOLEDディスプレイを接続しています。開発時の動作確認用ですね。このOLEDディスプレイの消費電力も電池駆動の場合には無視できないので,取り外せるようになっています。幸いなことに,OLEDディスプレイを取り外した場合においても,プログラムの方はそのまま変更する必要がありません(I2CをWRITE方向でしか使っていないためと思われます)。また,これもデバッグ用として黄色LEDをGPIO25に接続しています。

図3には図示していませんが,USBシリアル変換ボードを接続するピンヘッダを設けた他,ENピンに10 kΩのプルアップ抵抗とタクトスイッチ,Boot (GPIO0)ピンにタクトスイッチを接続してArduino IDEからプログラムを書き込めるようにしています。なお,それぞれのタクトスイッチは押した際にそのピンをGNDに落とします。

雨滴センサの監視方法とULPコプロセッサ活用

さて,雨センサシステムを作ろうと思い立った当初,Bluetooth Low Energy (BLE)を活用しようと考えていました。BLEの低消費電力という特長を活かすこと,そして筆者自身,ESP32のBluetooth機能を使ったことがなかったので勉強になるかもしれない──。

まず,ESP32のメインコアを30秒毎にディープスリープから起こして,雨滴センサからの信号をBLEでブロードキャストする方法を試しました。Raspberry PiでこのBLEパケットを拾い,条件に応じてLINEに通知を出す──というコンセプトです。しかし,メインコア起動時の平均消費電流が数十mAであるとして,メインコアを30秒毎に2秒間だけ起動させるとしても,ディープスリープ時を含めた平均消費電流が数mAに達するほど大きくなってしまいます。これでは電池駆動では1週間ももたないことが分かりました(実際,このコンセプトのプログラムを書き込んで屋外に置いてみると,約4日で電池切れとなりました)。やはりバルコニーにシステムを放置したまま数ヶ月は動いてほしいですよね…。

雨が降ってきたらすぐに通知を出したいので,ADCによる監視のインターバルはできるだけ短くすることが望ましく,例えば電池を節約するためとは言え,メインコアの起動を5分毎や10分毎などに間引いてしまっては,洗濯物を雨から守るという役割は果たせません。

そうだ…! 雨滴センサの監視は低消費電力の例えば8ピンPICマイコンに任せ,そのPICマイコンが雨滴を検知したらディープスリープさせておいたESP32をGPIOから起こしに行けば良いのではないか…? この場合,ESP32マイコンはもはやBLEではなく直接WiFiからLINEに通知を送ってしまっても良いはずです。

こんなことをTwitterにつぶやくと,有識者の方から「ESP32にはメインの2つのコアとは別に低消費電力のCPUがもう1つ搭載されているので,これを活用したらどうか」というご提案を頂きました。な,何と…! 調べてみると,超低消費電力(ultra low power, ULP)コプロセッサというものでした。

docs.espressif.com

さらに調べ進めていくと,ULPコプロセッサArduino IDEC++言語では制御できず*4,独自のアセンブリ言語を使用する必要があるようです。このアセンブリ言語についてはArduino IDEからマクロとしてプログラムできます*5。また,コンピュータとしての構成も単純なので,往年のPICアセンブラの感覚を取り戻せばそれほど難しくないのではと考えました。また,最も重要なポイントである,「ULPコプロセッサからADCは見える/使えるのか」ですが,これに関しても問題なさそうでしたので,ULPコプロセッサで雨滴センサを監視しつつ,もし雨滴を検知したらULPコプロセッサからESP32のメインコアを起こしてWiFi接続,LINE通知するというコンセプトに落ち着きました。

ULPコプロセッサの概要

ULPコプロセッサは,ESP32のスローメモリ(RTC Slow Memory)と呼ばれる領域に書き込まれたプログラムを指定した時間毎に実行できます。また,R0, R1, R2, R3の4つの32 bitレジスタを持つ他,同じくスローメモリ内のデータを取り扱うことができます。スローメモリはメインコアからも読み書き可能であり,メインコアからULPコプロセッサ用のプログラムを書き込んだり,ULPコプロセッサ-メインコア間のデータ授受が可能です。

残念ながら,ULPコプロセッサC++でプログラムすることはできず,独自のアセンブリ言語を使う必要があります。実際には,このアセンブリ言語に基づいたマクロにて機械語を生成し,これをスローメモリ上に置いた配列とすることでプログラムします。

R0 ~ R3の4つのレジスタはほぼ等価のようですが,R0がある閾値を上回ったか下回ったかで分岐するか否かを決定する条件分岐命令が用意されています。今回の目的にちょうどいいのでこれを活用することとします。

ディープスリープについて

ディープスリープとは

ESP32にはディープスリープという機能があります。メインコアの動作を停止して低消費電力で待機させることができ,タイマ,GPIOの入力変化,タッチセンサ,そしてULPコプロセッサから復帰(wake)させることができます。表1にデータシートから引用抜粋させて頂いた通り,ディープスリープ時には消費電流をμA級に節減できるとされています。ULPコプロセッサを動作させるデューティに依りますが,1%デューティであれば100 μAとなるようです。

表1: ESP32マイコンの消費電流(ESP32のデータシートより抜粋)

なお,ESP32のディープスリープについてはLang-Shipさんの記事を参考にさせて頂きました。

lang-ship.com

Arduino IDEからは,wake upの条件を指定した後で,

esp_deep_sleep_start();

と1行書くだけでディープスリープに移行させることができます。

各種ESP32モジュールの比較

しかし,この「μA級の低消費電力」を実現できるのは,ESP32マイコン単体の場合です。言い換えると前述の“素”のESP32マイコンですね。筆者のようなド素人にとって,ESP32マイコン単体を載せたプリント基板を自ら設計し,PCBメーカーに発注して表面実装のはんだ付けをするといった行為は難易度が高いため,いきおい,ESP32 DevKitCなどのモジュールに頼ることになります。このようなモジュールにはUSB-シリアル変換ICや5 Vから3.3 Vに変換するための3端子レギュレータなどが搭載されているため,ESP32のメインコアをディープスリープさせたとしても,全体としての消費電流はμAまでは下がりません。したがって,乾電池やeneloopでの数ヶ月にわたるような長期間の運用は難しいと言えます。

図5: 各種ESP32モジュール(左からDevKitC-32E, 秋月電子通商AE-ESP32-WROOM-32E-MINI, 秋月電子通商ESP32-WROVER-Eピッチ変換キット)

図5に各種のESP32モジュールを示します。左から

  1. スタンダードなDevKitC-32E
  2. 秋月電子通商のAE-ESP32-WROOM-32E-MINI
  3. 同じく秋月電子通商のESP32-WROVER-Eピッチ変換キット(AE-ESP32-WROVER-E-BO)

です。1にはUSB-シリアル変換ICと3端子レギュレータが,2はUSB-シリアル変換が外付けであるもののやはり3端子レギュレータが載っています。一方,3はただ素のESP32-WROOVER-EをDIPに変換しているのみで,USB-シリアル変換も3端子レギュレータも搭載していません。このため,ディープスリープさせれば真にμA級の省電流を期待できます。

まったく高精度ではありませんが,1,2については動画1に登場する電流計,3についてはシャント抵抗器を使ってディープスリープ時の消費電流を測定してみました。ただし,1の測定についてはDevKitCそのものではなく,Amazon.co.jpで購入した安価な互換モジュールを使いました。目安程度ですが,表2に結果を示します。

表2: 各種ESP32モジュールのディープスリープ時の消費電流(目安)

No.モジュールディープスリープ時の消費電流(目安)USB-シリアル変換IC3端子レギュレータ備考
1ESP32 DevKitC-32E互換モジュール10 mAありありULP不使用
2秋月電子通商AE-ESP32-WROOM-32E-MINI5 mAなしありULP不使用
3秋月電子通商AE-ESP32-WROVER-E-BO10 μAなしなし本システム完成時のULP動作時

上記2については,三端子レギュレータ(AZ1117CH-3.3TRG1)の静止電流(quiescent current)がデータシートから4 ~ 6 mA (typ. ~ max.)と読み取れるので,妥当ではないかと考えます。いずれにしてもディープスリープ時の消費電流がmA級となってしまうことは電池駆動かつ数ヶ月にわたる長期運用を考えると許容できず,プログラム書き込み等が面倒になりますが,素のESP32が載っている3の秋月電子AE-ESP32-WROVER-E-BOを採用することとしました。また,これを使うために図2の基板を設計・製作しました。

ソフトウェア構成

全体構成

図6,7にULPとメインコアの概略フローチャートをそれぞれ示します。

図6: ULPコプロセッサの概略フローチャート

図7: メインコアの概略フローチャート

ソフトウェアの基本的な動きとしては単純で,ULPコプロセッサにて雨センサに繋がったADCを一定時間毎(1秒としました)に監視し,ヒステリシスを考慮して閾値を下回れば雨が降ったと判定し,スローメモリ内に置いたrainingフラグを立ててメインコアを起こします。同じく上回ればセンサが乾燥したと判定し,rainingフラグを下ろしてメインコアを起こします。また,ヒステリシスを考慮した閾値を跨がない場合は何もしないこととします。

メインコアの方は,起動された場合に,各種初期化処理を行った後,起動要因がULPコプロセッサでなければ「雨センサが起動しました🌟」という通知をLINEに送ります。起動要因がULPコプロセッサであれば,rainingフラグ(前述のようにスローメモリはメインコアからも読み書きできます)を確認して,フラグが立っていれば「雨が降り始めました🌧 洗濯物を確認してください👕」,フラグが立っていなければ「センサが乾燥したようです☀」という通知をLINEに送ります。

通知を送った後は,WiFiを切断し,ULPのプログラムをスローメモリに書き込んでから*6ディープスリープに入ります。なお,ディープスリープに入る前にWiFiを切断しないとULPコプロセッサのプログラムが正常に動作しませんでした。原因は残念ながら不明(筆者が分かっていないだけ)です。

メインコアのプログラムは面白いことにすべてsetup関数内で完結しており,loop関数は空っぽです(プログラムを書いても実行されません)。

ULPコプロセッサのプログラミング方法

ULPコプロセッサの利用方法については,本ブログでは何度もお世話になっているLang-Shipさんの記事を(大いに)参考にさせて頂いております。

lang-ship.com

ここでは,ULPコプロセッサにプログラムを書き込み,実行するrunULP関数を貼り付けます。冒頭,SLOW_ADC,SLOW_RAINING,SLOW_PROG_ADDRを列挙型の値として定義しています。これはスローメモリ上で変数として最初の2つを用い,3番目の番地以降にプログラムを格納することを分かり易くするための措置です。関数内ではまず,ulp_set_wakeup_period関数にてμs単位でULPコプロセッサのプログラムを実行する周期を指定します。ここではus = 1,000,000 μsとして1秒毎にULPコプロセッサのプログラムを実行します。

// Slow memory variable assignment
enum {
  SLOW_ADC,             // Previous ADC value
  SLOW_RAINING,         // Flag to indicate raining
  SLOW_PROG_ADDR        // Program start address
};

// ----------------------------------------------------------------
// ULP setting and C-macro program
// ----------------------------------------------------------------
void runULP(uint32_t us) {
  // Set ULP activation interval
  ulp_set_wakeup_period(0, us);

  // Initialize ADC for ULP: ADC1 and Channel 6 = GPIO34
  uint8_t adc_adc = 0;
  uint32_t adc_channel = 6;
  adc1_config_channel_atten((adc1_channel_t)adc_channel, ADC_ATTEN_DB_11);
  
  // ULP Program
  const ulp_insn_t ulp_prog[] = {
    I_WR_REG(RTC_GPIO_OUT_REG, LED_BIT, LED_BIT, 1),  // Turn on LED
    I_MOVI(R2, SLOW_RAINING),                         // R0 = SLOW_RAINING, read raining flag
    I_LD(R0, R2, 0),                                  // R0 = MEM[R2(SLOW_RAINING)]
    M_BGE(1, 1),                                      // Jump to Label 1 if R0 >= 1 (already raining)

    // If not raining yet
    I_ADC(R0, adc_adc, adc_channel),                  // Perform ADC, store ADC result in R0
    M_BGE(2, THRESHOLD - HYSTERESIS),                 // Jump to Label 2 if R0 (ADC result) >= THRESHOLD - HYSTERESIS

    // If rain is detected
    I_MOVI(R3, 1),                                    // R3 = 1 (raining)
    I_ST(R3, R2, 0),                                  // MEM[R2(SLOW_RAINING)] = R3 = 1
    I_MOVI(R2, SLOW_ADC),                             // R2 = SLOW_ADC (raining)
    I_ST(R0, R2, 0),                                  // MEM[R2(SLOW_ADC)] = R0
    I_WAKE(),                                         // Wake up main core
    M_BX(2),                                          // Jump to Label 2 to end program
    
    // If already raining
    M_LABEL(1),                                        // Label 1
    I_ADC(R0, adc_adc, adc_channel),                  // Perform ADC, store ADC result in R0
    M_BL(2, THRESHOLD + HYSTERESIS),                  // Jump to Label 2 if R0 (ADC result) < THRESHOLD + HYSTERESIS
        
    // If drying is detected
    I_MOVI(R3, 0),                                    // R3 = 0 (not raining, sensor being dry)
    I_ST(R3, R2, 0),                                  // MEM[R2(SLOW_RAINING)] = R3 = 0
    I_MOVI(R2, SLOW_ADC),                             // R2 = SLOW_ADC (raining)
    I_ST(R0, R2, 0),                                  // MEM[R2(SLOW_ADC)] = R0     
    I_WAKE(),                                         // Wake up main core
    M_BX(2),                                          // Jump to Label 2 to end program

    // If nothing changed
    M_LABEL(2),                                       // Label 2 
    I_WR_REG(RTC_GPIO_OUT_REG, LED_BIT, LED_BIT, 0),  // Turn off LED
    I_HALT(),                                         // Stop the program
  };
  
  // Run the program shifted backward by the number of variables
  size_t size = sizeof(ulp_prog) / sizeof(ulp_insn_t);
  ulp_process_macros_and_load(SLOW_PROG_ADDR, ulp_prog, &size);
  ulp_run(SLOW_PROG_ADDR);
}

21行目以降のulp_insn_t型の配列ulp_progをマクロで構成された値で順次初期化しています。これは,図6のフローチャートを実装したものです。例えば23行目のI_MOVI(R2, SLOW_RAINING)はR2レジスタに定数SLOW_RAINING(前述のrainingフラグを格納しているスローメモリ上の番地)を書き込みます。次の24行目のI_LD(R0, R2, 0)はR2レジスタの値にオフセットとして0を加算した(つまり,オフセットなし)値で示されるメモリ番地に格納されたデータをR0レジスタに代入する働きです。つまり,23行目と24行目で図6の上から3番目のボックスを実現しています。

ulp_progを初期化し終わったら,ulp_process_macros_and_load関数にてマクロを書き込み,ulp_run関数でULPコプロセッサのプログラムを走らせます。

ESP32からLINEに通知を送る方法

通知を送る関数

さて,図7に記載の通り,ULPから起こされたメインコアは,その際の条件に応じてLINEに通知を送ります。まず,ESP32からLINEに通知を送る関数の例を示します。なお,このプログラムは「ばすにっきTips」さんのサンプルコードをほぼそのまま使わせて頂いています。1行目にグローバル変数としてString型のLINE_NOTIFY_TOKENを定義・初期化しています。ここに次節の手順で取得したアクセストークン(または単に「トークン」)を代入しておきます。

takabus.com

String LINE_NOTIFY_TOKEN = "INPUT_YOUR_LINE_NOTIFY_ACESSS_TOKEN";

void notifyLINE(String body) { 
  // Post by httpClient
  String postUrl = "https://notify-api.line.me/api/notify";
  httpClient.begin(postUrl);
  httpClient.addHeader("Content-Type", "application/x-www-form-urlencoded");
  httpClient.addHeader("Authorization", "Bearer " + LINE_NOTIFY_TOKEN);

  // Check status code and show message via Serial
  int status_code = httpClient.POST("message=" + body);
  Serial.println(httpClient.getString());
  if (status_code == 200)
  {
    Serial.printf("[SUCCESS]LINE Notify (URL:%s)", postUrl.c_str());
  }
  else
  {
    Serial.printf("[SUCCESS]LINE Notify (URL:%s) Code:%d", postUrl.c_str(), status_code);
  }
  // End http Client
  httpClient.end();
}

String型の文字列を引数としてこの関数を呼べば,後述の手順で設定したトークルームに通知が届きます。

www.youtube.com
動画2: M5StackからLINEに通知を送るテスト

動画2にこのnotifyLINE関数を使用したテストの様子を示します。loop関数内でA,B,Cの各ボタンが押されたかどうかを確認し,ボタンが押されていればそれに応じた文字列(例えば「Aボタンが押されました🎵」)をLINEに送ります。

このArduinoスケッチについてはGitHubに置きましたので,これを改造すればM5StackやESP32マイコンから任意の文字列をLINEの通知として送れるようにできると思います。

github.com

LINE Notifyの準備

前節の関数を使用する場合,事前にLINE NotifyのAPIを使用するためのトークンを取得しておく必要があります。APIからの通知はLINEの「グループ」に対して行われるため,本記事のような目的には「自分だけがメンバーとなっているグループ」をあらかじめ作成しておく必要があります(後述の「トークンを発行する」の際に,対象となるグループを選ぶ必要があるため)。

自分だけがメンバーとなっているグループの作成

スマートフォンのLINEアプリで,グループを作成します。図8に示すようにグループを新規作成し,お友達から誰も選択しない状態で「次へ」をタップします。

 
図8: LINEグループを作る

すると,図9に示すようになグループの設定画面に移りますので,グループ名(ここでは例として「ひとりグループ」)を決めて「作成」をタップします。

図9: 作成したグループの設定

もちろん,ESP32やM5Stackからの通知(後述しますが,Raspberry Piからも当然通知を送ることができます)を他の人と共有したい場合は,その方を加えた状態でグループを作成できますし,後からメンバーを追加することもできます。

LINE Notifyのアクセストークンの取得

ESP32やM5Stack,Raspberry PiなどからHTTPを介して通知を送りたい場合,LINE Notifyのアクセストークンを取得しておく必要があります。

LINE Notifyのサイトから通常のLINEアプリに登録しているメールアドレスとパスワードでログインします。ここで,2段階認証されるはずですので,お手元に普段ご使用されているLINEアプリの入ったスマートフォンをご準備下さい。ログインしたら,右上のメニューから「マイページ」に移ります。

notify-bot.line.me

マイページに移ると,図10のような画面が現れます。筆者の場合,既に「雨センサ」というサービスを作成済みですが,初めてであればここに何も表示されないはずです。「アクセストークンの発行(開発者向け)」の下にある「トークンを発行する」のボタンをクリックすれば,図11のように,通知を送る対象のグループまたはトークルームを選択するダイアログが現れます。

図10 LINE Notifyのマイページの例

図11: 対象グループの選択画面の例

ここでは例としてトークン名を「通知のテスト」としています。また,通知を送る対象となるグループを選択します。ここでは先ほど作成した「ひとりグループ」を対象に選びますが,もちろん,新規にグループを作成しなくても,既存のグループやトークルームに通知を送ることも可能です。

図12: アクセストークンの表示の例

成功すれば,図12のようにアクセストークンが表示されます。ESP32,M5Stack,またRaspberry Piなどの自作システムから通知を送るには,このアクセストークンが必要になりますが,どうやら一度しか表示されないようですので,どこかにコピー & ペースト等して保管しておく必要があります。

 
図13: LINEグループへのLINE Notifyの招待

実際に通知を送る前に,やっておかないといけないことがあります。図13に示すように,作成しておいた「ひとりグループ」に公式アカウントのお友達としてLINE Notifyを招待しておく必要があります。こうすると,この「ひとりグループ」のトークでLINE NotifyがESP32やM5Stack,Raspberry Piから送った通知をつぶやくようになります。つぶやいたテキストの前にはトークン名,この例では「[通知のテスト]」が付きます。

通知が届いた際のイメージは付録の図16に示します(これはRaspberry Piから送信した場合の例です)。

まとめ

以上で,ULPコプロセッサを活用した電池駆動のESP32マイコンによる雨センサシステムについて述べました。ハードウェア構成,ソフトウェア構成,Line Notifyの使い方など,中途半端に多岐にわたる記述となってしまい,相変わらず拙い駄文になってしまったのではないかと反省しています。

実際に使ってみて

製作したESP32雨センサシステムをバルコニーの隅に置いて運用を開始したのが2022年11月3日です。雨滴を検知するとLINEに通知が来るようになりましたので,実際に洗濯物を雨から救うことが何度かできました*7

また,省電力性能についても確認を続けています。図14にeneloopの残電圧を示します。満充電したeneloop × 3直列の電圧は運用開始時点で3.926 Vでした*8。その後,図14のように時折電圧を測っていますが,2ヶ月以上が経過した2023年1月10日時点でもまだ3.871 Vもあり,このまま数ヶ月から1年は連続稼働できるのではないかと思われます。

図14: 3個直列にしたeneloopの残電圧

ただし,図3の回路構成を見ると分かる通り,雨滴センサが濡れていると消費電流が増えてしまうため(もし雨滴センサが0 Ωになってしまうと,33 kΩの抵抗器とeneloop × 3直列の電圧で決まる概ね100 μAが流れ続けてしまう),雨が増えてくる梅雨から夏には電池の消耗が加速すると推測します。雨滴センサとGNDの間にMOSFETを挿入して,ULPコプロセッサのプログラムが動作している間だけ電源電圧を加えるようにすれば,より低消費電力にできると考えます。もしMark IIを作るのであれば改良したいですね(5 ~ 10分毎にwake upして屋外の温湿度と気圧を測るというのもやってみたいです)。

このまま運用を続けて,適宜,eneloopの残電圧を記録していきます。

今後の課題

幸いにして,雨の降り始めを検知してLINEに通知を送るという当初の目論見は達成したように思われます。ただし,「何mm/hの雨が降った場合に検知できるのか」などの定量的な評価はまったくできていません。ちゃんとした雨量計を準備すれば比較できるのかもしれませんね。

また,より根本的な問題として,「LINEに通知を送っても,人間がそれに気付かなければ意味がない」という課題もあります。実際,これで洗濯物を濡らしてしまったこともありました。洗濯物自動取り込み装置? 雨よけの展開? アラート表示システム? これらは大掛かりな今後の課題となりそうです。

もうひとつ,ULPコプロセッサまたはメインコアのプログラムがどうも図6,7のフローチャートの意図通りに動いていないようです。本来は,いったん雨滴を検知したら「雨が降り始めました」と通知を送った後はセンサが乾燥するまでは何も通知を送らず,センサが乾燥した時点で「センサが乾燥したようです」と通知を送る動きとなります。つまり,「雨が降り始めました」という通知が2つ連続で届くことはないはずですが,実際には「センサが乾燥したようです」と通知が届くことは稀で,雨が降っている最中に「雨が降り始めました」という通知が何度も届きます。これはプログラムのバグなのか,それともULPコプロセッサとメインコアの間のデータの授受がうまくいっていないのか,要調査です。

さらに,図4の雨滴センサですが,雨が降って止んでを繰り返しているうちに,表面の銅箔パターンが酸化していわゆる緑青がびっしりと付着してしまいます。緑青は絶縁体なので,雨滴の検知への性能に影響するのかもしれません。今のところ,酷い場合には拭き取って対応していますが,そもそもこの雨滴センサは消耗品なのかもしれません。

次回は2次遅れ系のインパルス応答と減衰係数ζの関係に関する簡単な記事を書くつもりです。

付録

USBシリアル変換治具

図15にUSB-シリアル変換治具を示します。本記事では“素”のESP32マイコンを搭載したモジュールを使用したため,プログラムの書き込みのためには外部にUSB-シリアル変換モジュールが必要です。しかも,ピン配置を適当に決めてしまったため,秋月電子通商のモジュールをそのまま使うことができませんでした。そこで,秋月電子通商のFT231X USBシリアル変換モジュール(AE-FT231X)からピンソケットに信号を引き出す基板を相変わらず十字配線ユニバーサル基板で作りました。本当にこういう小さい基板をサッ*9と作ることには十字配線ユニバーサル基板は欠かせませんね。

図15: USB-シリアル変換治具

Raspberry PiからLINEに通知を送る

Raspberry PiPythonからも簡単に通知を送ることができます。最小構成のプログラムは下記のようになります。

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import requests

# LINE Notify API URL and token
URL = 'https://notify-api.line.me/api/notify'
TOKEN = 'INPUT_YOUR_LINE_NOTIFY_ACESSS_TOKEN'

# Text
text = '''
Raspberry PiからLINEに通知を送るテストです。
このようなヒアドキュメントも使えますね。
祇園精舎の鐘の声,諸行無常の響きあり。
沙羅双樹の花の色,盛者必衰の理をあらはす。
'''

# Excute notification
headers = {'Authorization': 'Bearer ' + TOKEN}
data = {'message': text}
requests.post(URL, headers = headers, data = data)

このプログラムを実行すると,図16のように通知が届きます。

図16: 通知が届いた場合の例

もちろんPythonを使えればRaspberry Piに限らず,パソコンからでも通知を送ることができます。実際,図16については,Raspberry Piからもパソコン上のJupyterLabからも同じように通知を送れることを確認しています。

*1:Eテレの番組「マチスコープ」で最近知りましたが,上部に屋根などの覆いがあるものを「ベランダ」,ないものを「バルコニー」と称するようです。

*2:満充電時には3.9 Vを超えます。ESP32マイコンの絶対最大定格である3.6 Vを超過してしまうので,本来はこのような使い方はしない方が良いと考えます。何か改善策を練っています。

*3:図示していませんがGPIO21 = SDA,GPIO22 = SCLとしています。

*4:FreeRTOSのタスクのように,関数を割り当てられれば良かったのですが…。

*5:ただし,全ての命令に対応しているわけではないようです。

*6:パワーオンリセット時以外は書き込み直す必要は本来ないはずですが,念のため書き込んでいます。

*7:ただし,ご存知のように関東の冬はひどく乾燥して雨が少ないため,活躍する機会は稀です。梅雨どきから夏場が本領発揮でしょう。

*8:HIOKI カードハイテスタ3244-60を使用して測定。

*9:「十字配線ユニバーサル基板でサッ!」CQ出版社の見出しのようですね😅