静電容量式の降雨センサーは雨の降り始めはわかるが降り終わりがわからないので雨の強さや降り終わりもわかるボタン電池で動作する超低消費電力なセンサーが作れないものだろうかと考えてみた。
降雨センサーは何種類かあるがそれぞれ一長一短がある。
1.抵抗値(降り終わりがわからない、ヒーター付きは消費電力大)
2.静電容量(降り終わりがわからない、ヒーター付きは消費電力大)
3.電波系(ドップラー方式、消費電力大)
4.光学系(消費電力大)
6.音響系(雨音の検出が難しい?)
電波系や光学系は電力消費が比較的大きいため雨粒の衝突音を検出する音響系の方法が一番消費電力が低くできる可能性がありそうだ。但し、マイクでは特性が良すぎて不必要な環境音まで拾ってしまい雨粒の衝突音だけを検出するのが少々やっかいそうなので他に使えそうな方法は?と探して見つけたのがピエゾセンサー。楽器用にも使われている振動を検出するためのセンサーであるがマイクより感度は低く周波数特性も良くないが雨粒の衝突音の検出用には使えそうだし値段も安く入手もしやすいので早速試してみた。
ピエゾセンサーを一般的なオペアンプを使って試したところかなり感度がいい。大きな音や息を吹きかけたりしても反応する。少し感度が良すぎるかも?とは思ったが使えそうなことがわかったのであとは低消費電力化をどうするかだ。実験の結果、超低消費電力(uA未満)で動作するオペアンプはスルーレートが低すぎて使いものにならなかったので超低消費電力(uA未満)で動作するコンパレータのみで実験してみた。
センサーを屋外に置き実際に試してみるとアンプなしではやはり感度不足っぽい気もするがしっかり濡れるぐらいの降雨であれば検出は可能だ。あまり感度が高いと風とか飛行機とか工事の音とか様々な環境音にも反応してしまうのでこれぐらいの感度のほうがいいのかも。使い物になるかどうかは暫く使い込んでみる必要があるけれどね...
【18:06-00:37までの実測データをグラフ化】
最初の山は19時頃、次の山は00時頃で実際に雨が降っていた。風速7-8m程度の比較的強い風だったため風だけでもセンサーが反応してしまっていたが1分毎のデータなので60以下なら風の影響と考えれそう。グラフ設定がうまくできなかったけど雰囲気だけは出てるっぽいな。(笑)
【回路図】
コンパレータはTS881(210nA)を使ったのだが実測すると4-5uAも流れてた。なぜだろう?
省電力化のためコンパレータ出力はスリープ中でも動作するTWELITEのパルスカウンターでカウントしパワーオンやカウントがゼロになったときにディープスリープさせ最初のバルスでウェイクアップさせる。その後はウェイクタイマーにより1分毎にウェイクアップしながらカウントの差分をNVデータ領域にバッファリングしていきカウントがゼロになるまで繰り返す。データはバッファフルになるかカウントがゼロになった時点で送信する。
【ファームウェア(TWELITE)】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
/* main.cpp - rain sensor for NXP JN516x Series Copyright (c) 2024 Sasapea's Lab. All right reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include <string.h> #include <limits.h> #include "device.h" #define DEBUG 1 #define WPAN_ADDRESS 10 #define WPAN_PANID 0x5169 #define WPAN_CHANNEL WPan::CHANNEL_24 #define WPAN_HOST JN516x::ADDR_OTA_BRIDGE #define TX_RETRY_COUNT 3 #define TX_TIMEOUT 300 // ms #define DATA_SHIFT 0 #define MIN_PULSECOUNT 5 #define DATA_INTERVAL 60 // seconds typedef struct __attribute__((packed)) { uint8_t command[3]; uint8_t status; union { struct __attribute__((packed)) { uint8_t shift; uint8_t count; uint16_t data[5]; } pulse; uint32_t data[3]; } nvm; } packet_t; static packet_t packet = { .command = { 'P', 'C', 'D' }}; static uint32_t pulsecount; static uint8_t txstatus; void sleep(void) { // // Sleep + Wake Timer (0.64uA + 0.05uA) // if (pulsecount) { WakeTimer::start(WakeTimer::WAKE_0, DATA_INTERVAL, empty); System::sleep(System::SLEEP_OSCON_RAMOFF); } // // Deep Sleep + Wake DIO (0.1uA) // else { Gpio::wake(Gpio::DIO1, Gpio::FALLING, empty); System::sleep(System::SLEEP_DEEP); } } void WPanTransmitCallback(uint8 u8Handle, uint8 u8Status) { switch (u8Handle) { case 0x01: txstatus = u8Status; break; } } void send(uint16_t addr, void *buf, uint8_t len) { // // Setup WPan // WPan::begin(WPAN_ADDRESS, WPAN_PANID, WPAN_CHANNEL); // // Send Data // txstatus = MAC_ENUM_NO_ACK; for (size_t i = 0; (txstatus != MAC_ENUM_SUCCESS) && (i < TX_RETRY_COUNT); ++i) { WPan::transmit(addr, (uint8_t *)buf, len, 0x01 | WPAN_OPT_DUPLICATE); for (uint32_t t = System::millis(); (txstatus != MAC_ENUM_SUCCESS) && (System::millis() - t < TX_TIMEOUT); ) yield(); } // // End WPan // WPan::end(); // // Increment Packet SEQ Number // packet.nvm.pulse.shift += 0x10; } void clearNVM(bool all = false) { uint8_t b = all ? 0 : packet.nvm.pulse.shift & 0xF0; memset(&packet.nvm, 0, sizeof(packet.nvm)); packet.nvm.pulse.shift = b; } void loadNVM(void) { for (size_t i = 0; i < sizeof(packet.nvm.data) / sizeof(packet.nvm.data[0]); ++i) packet.nvm.data[i] = System::readNVData(i); } void saveNVM(void) { for (size_t i = 0; i < sizeof(packet.nvm.data) / sizeof(packet.nvm.data[0]); ++i) System::writeNVData(i, packet.nvm.data[i]); } void setup(void) { // // Power On Reset Delay // System::POWER poweron = System::powerStatus(); if ((poweron & (System::POWER_SLEEP_WAKEUP | System::POWER_DEEP_SLEEP)) == 0) System::delay(100); // // Setup Pulse Counter // uint32_t count; PulseCounter::configure(PulseCounter::COUNTER_0, true, PulseCounter::SAMPLES_0, PulseCounter::COMBINE_ON0); PulseCounter::location(PulseCounter::COUNTER_0, false); PulseCounter::read32(count); PulseCounter::start(PulseCounter::COUNTER_0); // // Read Pulse Counter // if (poweron & System::POWER_SLEEP_WAKEUP) pulsecount = count - System::readNVData(3); System::writeNVData(3, count); // // Load NV Data // loadNVM(); // // Wakeup from Deep Sleep (DIO Pin) // if (poweron & System::POWER_DEEP_SLEEP) { clearNVM(); pulsecount = 1; } // // Wakeup from Sleep (WakeTimer) // else if (poweron & System::POWER_SLEEP_WAKEUP) { uint8_t datacount = packet.nvm.pulse.count; if (pulsecount < MIN_PULSECOUNT) pulsecount = 0; if ((pulsecount == 0) || (packet.nvm.pulse.count >= sizeof(packet.nvm.pulse.data) / sizeof(packet.nvm.pulse.data[0]))) { if (datacount != 0) { packet.status = pulsecount ? 1 : 2; send(WPAN_HOST, &packet, sizeof(packet)); clearNVM(); } } if (pulsecount) { if (datacount == 0) { packet.status = 0; send(WPAN_HOST, &packet, sizeof(packet)); } uint8_t shift = 0; pulsecount >>= packet.nvm.pulse.shift & 0x0F; while (pulsecount > USHRT_MAX) { pulsecount >>= 1; ++shift; } if (shift) { packet.nvm.pulse.shift = (packet.nvm.pulse.shift & 0xF0) | ((packet.nvm.pulse.shift + shift) & 0x0F); for (uint8_t i = 0; i < packet.nvm.pulse.count; ++i) { if ((packet.nvm.pulse.data[i] >>= shift) == 0) packet.nvm.pulse.data[i] = 1; } } packet.nvm.pulse.data[packet.nvm.pulse.count++] = pulsecount ? pulsecount : 1; } } // // Power On Reset // else { clearNVM(true); } // // Save NV data // saveNVM(); } void loop(void) { sleep(); } |