トイレの自動点灯照明はメーカーに関わらず明るさの判定/点灯時間/モーションセンサーによる制御がイマイチでどうもしっくりこない。特に便秘ぎみで力んでもがいているとき不意打ちで消灯されてしまうと一気に集中力が途切れてしまう。あぅ~もうちょっとだったのに...って感じ。(-_-;) 手や体を揺すったりして消灯を回避しようとするのだがそうすると逆に集中できなくなったりもする。この問題?を解決するためにトイレ用の自動照明を作りたいとは思っていたのだがセンサーをどうするかでず~~~と迷っていた。
まず、便座に座っている状態を検出する必要がある。自動洗浄トイレであれば便座スイッチなるものが内蔵されているはずなのでそれを利用する方法もあるが製品依存になるし配線が出てしまい見た目にスマートでないのでボツだ。便座に座っている人間を感知するには市販されている超音波/赤外線/レーダーセンサーなどが使えそうだがトイレというデリケートな空間でもあるので見た目に怪しさを感じるゴツいものやデカいもの、カメラのように見える物などは使いたくはない。で、最近になってこれはと思うセンサーを見つけてしまった。
試してみたところ便座に座っている状態が完璧に検出できることが確認できた。30㎝ちょい程度が安定して認識できる距離かな。小型で組み込みもしやすそうだったので早速回路を設計してみた。
CPUはESP8266(WROOM02)を使い、TPLinkのHS105を直接制御してトイレ照明を点灯させる仕様だ。
ちなみに光センサーはATtiny10+LEDの組み合わせ。部品代は40円以下と超格安で感度もいいので使い始めるとやめられなくなる。かっぱえびせんみたいなセンサーだ。(笑)
【仕様】
当初はトイレ専用で考えていたが、ハンダこて、アイロン、ヒーター等の消し忘れ防止などにも使えそうなので汎用的な仕様で考えてみた。
・オン制限時間が設定可能。※オン制限時間を設定した場合の電源オンは手動スイッチのみとなり、オン後の時間延長はセンサーで可能。
・オフ遅延時間が設定可能。
・光センサーのモードと閾値が設定可能。
・PIR/DDSセンサーの有効無効が設定可能
・手動オン/オフが可能。
【回路図】
CPUのプルアップ/プルダウンを有効活用した結果、抵抗は1個だけとなった。コンデンサも可能な限り少なくなるよう考えてみた。
【設置したところ】
PIRセンサーが入口に対し真横を向いてるためトイレの入り口付近の検出がうまくできない。それを解決するため超小型パラボラ型リフレクターを3Dプリンターで作ってみた。適当な計算により設計したパラボラの表面に熱反射材として百均のアルミテープを張っただけのお粗末なものであるがトイレの入り口の外側でも感知できるほど確かな効果が実感できる。
ちなみにPIRセンサーの代わりに検出範囲の広いドップラーセンサーを使うという方法もあるがトレイ室内に風や振動等で揺れたり動いたりする物があったりトイレのドアをガタつかせただけでも反応してしまうことに注意が必要だ。
センサーは、便座を閉めたときより上側、腰付近を狙って設置すると良いだろう。
【天井裏に設置するリモートスイッチ(TPLink-HS105)】
我が家はダウンライトで天井裏にHS105を設置することになる。簡単に設置できるようにするため事前に次のようなものを用意して置いた。あとは配線を差し込むだけでOKだ。
※電気工事士の資格のない人は安全のためにも業者にお願いするべし。
【センサー設定ページ】
ライトセンサーのFeedbackは、トイレ照明制御などのように電源オンによる照明の点灯がライトセンサーに反映されてしまうような場合にチェックする。チェックすると電源オン時はライトセンサーが無視されるようになる。
【修正履歴】
2021-12-01
照明スイッチをオフにする微妙なタイミングでHS105とのオンオフ状態が合わなくなることがあるため、HS105の制御が1秒間隔で3回連続で成功するまで通信を繰り返すように修正。
2021-11-17
照明スイッチをオフにするとHS105の電源も切れてしまい電源のオンオフ状態が合わなくなってしまうため、HS105との通信エラー時に電源状態を不明(POWER_UNKNOWN)に設定するようにした。これで通信再開後に電源のオンオフ状態の同期がとれるようになる。
2021-10-10
照明が点灯しっぱなしになる現象が発生。HS105との通信エラーによるものと予想し通信が成功するまで1秒間隔でリトライするようにしてみた。
【ファームウェア】
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 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
/* esp8266_switch.ino - Sensor Power Switch for ESP8266 Copyright (c) 2021 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* ------------------------------------ Arduino 1.8.15 ------------------------------------ esp8266 by ESP8266 Community Version 3.0.2 ------------------------------------ [Board Settings] Board: "Generic ESP8266 Module" Builtin Led: "0" Upload Speed: "115200" CPU Frequency: "80MHz" Crystal Frequency: "26MHz" Flash Size: "4MB (FS:2MB OTA:~1019KB)" Flash Mode: "DOUT (compatible)" Flash Frequency: "40MHz" Reset Method: "no dtr, no_sync" Debug Port: "Disabled" Debug Level: "None" lwIP Variant: "v2 Higher Bandwidth" VTables: "Flash" C++ Exceptions: "Disabled (new aborts on oom)" Stack Protection: "Disabled" Erase Flash: "Only Sketch" Espressif FW: "nonos-sdk 2.2.1+100 (190703)" SSL Support: "All SSL ciphers (most compatible)" MMU: "32KB cache + 32KB IRAM (balanced)" Non-32-Bit Access: "Use pgm_read and macros for IRAM/PROGMEN" */ //#define LEDSENSOR_DEBUG #include <stdint.h> #include <stdbool.h> #include <stdlib.h> #include "espwnet.h" #include "ledsensor.h" #include "ledswuart.h" #include "pololudds.h" #include "WiFiUdp.h" #include "TPLinkSmartPlug.h" #define CONFIGURE_FILE "sensor.conf" #define POWER_RETRY_INTERVAL 1000 // ms #define POWER_OFF_DELAY 3 // min #define LIGHT_THRESHOLD 15000 // light sensor threshold #define PIN_PWR 0 // power switch #define PIN_LDS 4 // light detection sensor #define PIN_DDS 5 // digital distance sensor #define PIN_PIR 16 // PIR Sensor typedef enum { LIGHT_MODE_DISABLE, LIGHT_MODE_DARK, LIGHT_MODE_BRIGHT, } LIGHT_MODE; typedef enum { POWER_OFF, POWER_ON, POWER_UNKNOWN = -1, } POWER_STATUS; static ESPConf conf; static LEDSWUART light(PIN_LDS); static PololuDDS distance(PIN_DDS); static TPLinkSmartPlug smartplug; static WiFiClient client; static WiFiUDP udp; static uint32_t timelimit; static uint32_t offdelay; static uint32_t lightmode; static uint32_t threshold; static bool feedback; static bool pirmode; static bool ddsmode; static uint32_t start0; static uint32_t start1; static uint32_t start2; static uint32_t success; static uint32_t pushed; static POWER_STATUS request = POWER_OFF; static POWER_STATUS status = POWER_UNKNOWN; static void configure(void) { conf.setPropertyInt("smartplug", "timelimit", 0); conf.setPropertyInt("smartplug", "offdelay" , POWER_OFF_DELAY); conf.setPropertyInt("light" , "mode" , LIGHT_MODE_DARK); conf.setPropertyInt("light" , "threshold", LIGHT_THRESHOLD); conf.setPropertyInt("light" , "feedback" , 1); conf.setPropertyInt("motion" , "pir" , 1); conf.setPropertyInt("motion" , "dds" , 1); conf.load(CONFIGURE_FILE); timelimit = atof(conf.getProperty("smartplug", "timelimit")) * (60 * 1000); offdelay = atof(conf.getProperty("smartplug", "offdelay" )) * (60 * 1000); lightmode = conf.getPropertyInt("light" , "mode" ); threshold = conf.getPropertyInt("light" , "threshold"); feedback = conf.getPropertyInt("light" , "feedback" ); pirmode = conf.getPropertyInt("motion" , "pir" ); ddsmode = conf.getPropertyInt("motion" , "dds" ); } static void onSensorPage(void) { String html; ESPWeb.client().setNoDelay(true); // // Save Properties and Restart // if (ESPWeb.arg("apply").equals("apply")) { conf.clear(); conf.setProperty("smartplug", "address" , ESPWeb.arg(F("smartplug_address" )).c_str()); conf.setProperty("smartplug", "timelimit", ESPWeb.arg(F("smartplug_timelimit")).c_str()); conf.setProperty("smartplug", "offdelay" , ESPWeb.arg(F("smartplug_offdelay" )).c_str()); conf.setProperty("light" , "mode" , ESPWeb.arg(F("light_mode" )).c_str()); conf.setProperty("light" , "threshold", ESPWeb.arg(F("light_threshold" )).c_str()); conf.setProperty("light" , "feedback" , ESPWeb.arg(F("light_feedback" )).c_str()); conf.setProperty("motion" , "pir" , ESPWeb.arg(F("motion_pir" )).c_str()); conf.setProperty("motion" , "dds" , ESPWeb.arg(F("motion_dds" )).c_str()); conf.save(CONFIGURE_FILE); ESPWNet.onRestartPage("/sensor"); } // // Edit Properties // html = F("<html lang='en'>"); html += F("<head><meta http-equiv='content-type' content='text/html; charset=utf-8'>"); html += F("<meta http-equiv='content-style-type' content='text/css'><style type='text/css'><!--"); html += F("table{border-collapse: collapse}th{background-color: #cccccc; border: solid thin #FFFFFF; padding: 2pt; width: 10em; text-align: left;}"); html += F("td{background-color: #eeeeee; border: solid thin #FFFFFF; padding: 2pt;}--></style><title>Curtain</title></head>"); html += F("<body><form action='/sensor' method='post'><h3>Smartplug (HS105)</h3><table><tbody>"); html += F("<tr><th>Address</th><td><input required type='text' name='smartplug_address' value='"); html += conf.getProperty("smartplug", "address"); html += F("'></td></tr><tr><th>Time Limit</th><td><input required type='number' style='text-align:right' name='smartplug_timelimit' step='0.1' min='0' max='999.9' value='"); html += conf.getProperty("smartplug", "timelimit"); html += F("'> min</td></tr><tr><th>Off Delay</th><td><input required type='number' style='text-align:right' name='smartplug_offdelay' step='0.1' min='0' max='999.9' value='"); html += conf.getProperty("smartplug", "offdelay"); html += F("'> min</td></tr></tbody></table><h3>Light Sensor</h3><table><tbody>"); html += F("<tr><th>Mode</th><td><select required name='light_mode'>"); int mode = conf.getPropertyInt("light", "mode"); html += F("<option value='"); html += LIGHT_MODE_DISABLE; html += F("'"); if (mode == LIGHT_MODE_DISABLE) html += F(" selected"); html += F(">disable<option value='"); html += LIGHT_MODE_DARK; html += F("'"); if (mode == LIGHT_MODE_DARK) html += F(" selected"); html += F(">dark<option value='"); html += LIGHT_MODE_BRIGHT; html += F("'"); if (mode == LIGHT_MODE_BRIGHT) html += F(" selected"); html += F(">bright</select></td></tr><tr><th>Threshold</th><td><input required type='number' style='text-align:right' name='light_threshold' min='0' max='65536' value='"); html += conf.getProperty("light", "threshold"); html += F("'> ("); html += light.handle(); html += F(")</td></tr><tr><th>Feedback</th><td><input type='checkbox' name='light_feedback' value='1'"); if (conf.getPropertyInt("light", "feedback")) html += F(" checked"); html += F("></td></tr></tbody></table><H3>Motion Sensor</h3><table><tbody>"); html += F("<tr><th>PIR</th><td><input type='checkbox' name='motion_pir' value='1'"); if (conf.getPropertyInt("motion", "pir")) html += F(" checked"); html += F("></td></tr><tr><th>DDS</th><td><input type='checkbox' name='motion_dds' value='1'"); if (conf.getPropertyInt("motion", "dds")) html += F(" checked"); html += F("></td></tr></tbody></table><p><input type='submit' name='apply' value='apply'></p></form></body></html>"); ESPWeb.send(ESPWNET_HTTP_STATUS_OK, F(ESPWNET_HTML_CONTENT_TYPE), html); } static void power_handle(void) { smartplug.handle(); if (status != request) { uint32_t now = millis(); if (now - start2 >= POWER_RETRY_INTERVAL) { start2 = now; if (!smartplug.setRelayState(request == POWER_ON)) { status = POWER_UNKNOWN; success = 0; } else if (++success >= 3) status = request; } } } static void power(POWER_STATUS req) { start2 = millis() - POWER_RETRY_INTERVAL; request = req; success = 0; } static void control(void) { uint32_t now = millis(); uint16_t lds = light.handle(); bool pwr = false; bool off = false; // // manual operation // if (!digitalRead(PIN_PWR)) { if (!pushed) pushed = (now ? now : -1); } else if (pushed) { uint32_t t = now - pushed; if (t < 20) ; // chattering else if (t < 3000) pwr = true; // power on (short press) else off = true; // power off (long press) pushed = 0; } // // sensor detection // if (!pwr && !off && (!timelimit || (request == POWER_ON))) { if (pirmode && digitalRead(PIN_PIR)) pwr = true; if (ddsmode && distance.detect()) pwr = true; if (!feedback || (request == POWER_OFF)) { switch (lightmode) { case LIGHT_MODE_DARK: if (lds < threshold) pwr = false; else if (!pirmode && !ddsmode) pwr = true; break; case LIGHT_MODE_BRIGHT: if (lds >= threshold) pwr = false; else if (!pirmode && !ddsmode) pwr = true; break; default: break; } } } // // power control // if (pwr) { start1 = now; if (request == POWER_OFF) { start0 = now; power(POWER_ON); } } else if (request == POWER_ON) { if (off || (now - start1 >= offdelay) || (timelimit && (now - start0 >= timelimit))) power(POWER_OFF); } } void setup() { // init serial Serial.begin(115200); // init GPIO pinMode(PIN_PWR, INPUT_PULLUP); pinMode(PIN_PIR, INPUT_PULLDOWN_16); // load configurations configure(); // init smartplug client.setNoDelay(true); smartplug.begin(client, udp); smartplug.setTarget(conf.getProperty("smartplug", "address")); // init light sensor light.begin(38400); // init distance sensor distance.begin(); // init espwnet ESPWeb.on("/sensor", [](){onSensorPage();}); ESPWNet.addHtmlRootLink("/sensor", "Sensor"); ESPWNet.begin(); power(POWER_OFF); } void loop() { if (ESPWNet.handle()) power_handle(); control(); } |
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 |
/* pololudds.h - Pololu Digital Distance Sensor Library for ESP8266/ESP32 Copyright (c) 2021 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __POLOLUDDS_H #define __POLOLUDDS_H class PololuDDS { private: int _pin; uint16_t _distance; uint32_t _changed; static void IRAM_ATTR onPinChanged0(PololuDDS *inst) { inst->onPinChanged(); } void IRAM_ATTR onPinChanged(void) { uint32_t t = micros(); if (digitalRead(_pin) == LOW) { uint32_t d = t - _changed; if ((d < 1000) || (d > 1850)) d = 0; _distance = d; } _changed = t; } public: PololuDDS(int pin) : _pin(pin) { } virtual ~PololuDDS(void) { } void begin(void) { pinMode(_pin, INPUT); _changed = micros(); attachInterruptArg(_pin, reinterpret_cast<void (*)(void*)>(onPinChanged0), this, CHANGE); } void end(void) { detachInterrupt(_pin); } uint16_t distance(void) { return _distance * 3 / 4; } bool detect(void) { return _distance; } }; #endif |
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 |
/* ledsensor.h - Optical Detect Sensor Library for ATtiny10 LED Sensor Copyright (c) 2021 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LEDSENSOR_H #define __LEDSENSOR_H #include <stdlib.h> #include <stdint.h> #define LEDSENSOR_LPF 5 // % class LEDSensorBase { private: uint32_t _start; uint16_t _result; uint8_t _filter; uint8_t _count; char _buffer[8]; uint8_t _id; protected: void filter(uint8_t lpf = LEDSENSOR_LPF) { _filter = lpf; } public: LEDSensorBase(uint8_t id = 0) : _start(0) , _result(0) , _filter(LEDSENSOR_LPF) , _count(0) , _id(id) { } virtual ~LEDSensorBase(void) { } uint8_t id(void) { return _id; } virtual void begin(uint32_t baudrate, uint8_t lpf = LEDSENSOR_LPF) = 0; uint16_t handle(void) { for (int c; (c = read()) >= 0; ) { if (c == '\n') { char *endptr; _buffer[_count] = 0; _count = 0; uint32_t val = strtoul(_buffer, &endptr, 16); if ((endptr == _buffer + 4) && (*endptr == '\r')) { // low pass filter _result += (int32_t)(val - _result) * _filter / 100; #ifdef LEDSENSOR_DEBUG Serial.printf("light[%d] = %d\r\n", _id, _result); } else Serial.printf("light[%d] = error.\r\n", _id); #else } #endif } else if (_count < sizeof(_buffer)) _buffer[_count++] = c; } return _result; } virtual int read(void) = 0; virtual void led(bool on) { (void)on; } }; template<typename UART_T, UART_T& UART, uint8_t ID = 1> class LEDSensor : public LEDSensorBase { public: LEDSensor(void) : LEDSensorBase(ID) { } virtual ~LEDSensor(void) { } void begin(uint32_t baudrate, uint8_t lpf = LEDSENSOR_LPF) override { filter(lpf); UART.begin(baudrate); } int read(void) override { return UART.read(); } }; #endif |
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 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
/* ledswuart.h - Software UART Library for ESP8266/ESP32 Baudrate: ESP8266( 80MHz): Max 230400 bps: (Caution: irregular error occurs) ESP32 (240MHz): Max 57600 bps: (Caution: irregular error occurs) Copyright (c) 2021 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LEDSWUART_H #define __LEDSWUART_H #include <stdint.h> #include <stdbool.h> #include "ledsensor.h" #include "Arduino.h" #define LEDSWUART_NOPIN 0xFF #define LEDSWUART_CLOCK_WIDTH(b) (F_CPU / (b)) #define LEDSWUART_FRAME_NBITS 9 // start(1) + data(8) #if defined(ESP32) #define _DI() portDISABLE_INTERRUPTS() #define _EI() portENABLE_INTERRUPTS() #else #define _DI() noInterrupts() #define _EI() interrupts() #endif class LEDSWUART : public LEDSensorBase { private: uint32_t _bitwidth; uint32_t _frmwidth; uint32_t _endclock; uint32_t _bitclock; uint32_t _bitcount; uint32_t _bitlevel; uint32_t _bitshift; uint8_t _rxputpos; uint8_t _rxgetpos; uint8_t _rxbuffer[256]; // must be 256. uint8_t _rxpin; uint8_t _txpin; bool _rxenable; static void IRAM_ATTR onRxPinChange0(LEDSWUART *instance) { instance->onRxPinChange(ESP.getCycleCount()); } void IRAM_ATTR onRxPinChange(uint32_t clock) { if (!_rxenable) return; if (_bitcount == 0) { _bitclock = clock; _endclock = clock + _frmwidth; _bitcount = 1; _bitlevel = 0; } else { int32_t t = clock - _bitclock + (_bitwidth >> 1); while ((t -= _bitwidth) > 0) { _bitshift = (_bitshift >> 1) | _bitlevel; if (_bitcount++ == LEDSWUART_FRAME_NBITS) { if ((uint8_t)(_rxputpos + 1) != _rxgetpos) _rxbuffer[_rxputpos++] = _bitshift; _bitcount = 0; // start next frame if ((int32_t)(clock - _endclock) >= 0) onRxPinChange(clock); return; } } _bitclock = clock; _bitlevel ^= 0x80; } } void finish(void) { // disable interrupt _DI(); // finish to receiving if (_bitcount && ((int32_t)(ESP.getCycleCount() - _endclock) >= 0)) onRxPinChange(_endclock - 1); // enable interrupt _EI(); } public: LEDSWUART(uint8_t rxpin, uint8_t txpin = LEDSWUART_NOPIN) : LEDSensorBase() , _bitcount(0) , _rxputpos(0) , _rxgetpos(0) , _rxpin(rxpin) , _txpin(txpin) , _rxenable(true) { } virtual ~LEDSWUART(void) { } void begin(uint32_t baudrate, uint8_t lpf = LEDSENSOR_LPF) override { filter(lpf); _bitwidth = LEDSWUART_CLOCK_WIDTH(baudrate); _frmwidth = _bitwidth * (LEDSWUART_FRAME_NBITS + 1); _bitcount = 0; _rxputpos = 0; _rxgetpos = 0; _rxenable = true; if (_rxpin != LEDSWUART_NOPIN) { digitalWrite(_rxpin, LOW); pinMode(_rxpin, INPUT_PULLUP); attachInterruptArg(_rxpin, reinterpret_cast<void (*)(void*)>(onRxPinChange0), this, CHANGE); } if (_txpin != LEDSWUART_NOPIN) { digitalWrite(_txpin, HIGH); pinMode(_txpin, OUTPUT); } } void end(void) { if (_rxpin != LEDSWUART_NOPIN) { detachInterrupt(_rxpin); _rxpin = LEDSWUART_NOPIN; } if (_txpin != LEDSWUART_NOPIN) { pinMode(_txpin, INPUT); digitalWrite(_txpin, LOW); _txpin = LEDSWUART_NOPIN; } } size_t available(void) { finish(); int remain = _rxputpos - _rxgetpos; return remain >= 0 ? remain : remain + sizeof(_rxbuffer); } int read(void) override { return (available() ? _rxbuffer[_rxgetpos++] : -1); } size_t read(uint8_t *buf, size_t len) { size_t cnt = available(); if (cnt < len) len = cnt; else cnt = len; while (len--) *buf++ = _rxbuffer[_rxgetpos++]; return cnt; } void write(uint8_t b) { if (_txpin != LEDSWUART_NOPIN) { // disable interrupt _DI(); // start bit uint32_t clock = ESP.getCycleCount(); digitalWrite(_txpin, LOW); while (ESP.getCycleCount() - clock < _bitwidth) continue; // data bit for (uint8_t i = 1; i; i <<= 1) { digitalWrite(_txpin, b & i); clock += _bitwidth; while (ESP.getCycleCount() - clock < _bitwidth) continue; } // stop bit digitalWrite(_txpin, HIGH); clock += _bitwidth; while (ESP.getCycleCount() - clock < _bitwidth) continue; // enable interrupt _EI(); // spacing to next frame clock += _bitwidth; while (ESP.getCycleCount() - clock < _bitwidth) continue; } } void write(const uint8_t *buf, size_t len) { while (len--) write(*buf++); } void led(bool on) override { if ((_rxpin != LEDSWUART_NOPIN) && (_rxenable == on)) { if (on) { // LED on _rxenable = false; pinMode(_rxpin, OUTPUT); } else { // LED off digitalWrite(_rxpin, HIGH); pinMode(_rxpin, INPUT_PULLUP); digitalWrite(_rxpin, LOW); _rxenable = true; } } } }; #endif |
【参照ライブラリ】
ESP8266/ESP32用のネットワーク管理ライブラリ
ATtiny10で光センサーモジュールを作ってみた。
TP-LINK WiFiスマートプラグをESP32(Arduino)から制御する