ハンダこてを切るのを忘れてしまい電源入れっぱなしにしてしまうことがたまにある。今年の最長記録はな~んと3日間。ヤケドや火災の原因にならなかったのが幸いではあったが、高校生の頃、自宅のコタツに入ってハンダ付けをしてる最中に眠ってしまったことがある。お腹の当たりが熱くて目が覚めたら落ちたハンダこてがコタツ布団を焼いていて煙が室内に充満し発火寸前の状態。発火前に目が覚めたおかげで大事には至らなかったが、何日も気づかないことがあったりすると本当にヒヤッとしてしまう。
切り忘れるたびにハンダこて切り忘れ防止装置なるものを作ろうと思ってしまう。が、どういう仕様にするか悩んでるうちに作ること自体を忘れてしまうという繰り返しをうん十年も続けてきたのだが、漸く仕様もきまったので早速作ってみることに。
切り忘れ防止はタイマーとPIRセンサーの組み合わせで行うことにした。作業者がハンダこての近くにいるかどうかをPIRセンサーで判断し、PIRセンサーに反応があればタイマーを延長、反応がない状況が一定時間続くと切り忘れて場所を離れたとみなし電源オフする仕様だ。そして、さらなる安全策として電源オン状態を維持できる最大時間も設定できるようにしてみた。この仕様なら、ハンダこてに限らず人間が傍にいる必要がある電気製品にも応用できるはずだ。たぶん。
また、電源のオンオフだけだと簡単すぎて面白くないのでヒーターの温度制御に利用可能なゼロクロス制御方式のパワーコントローラとしても使えるようにしてみた。WindowsやLinux(Raspberry-Pi等)からUSB経由でリアルなパワー制御が可能なので、温度センサーと組み合わせた温度管理システムが簡単に構築可能できるようになる。
今回は、箱買いしたものの全く使ってなかったパナソニックのAQ1208(生産完了品)というゼロクロスSSRを使ってみた。ヒートシンクありで定格10Aで使えるものだが自然冷却ではかなり熱くなるため空冷ファンにより強制冷却できるようにしてみた。1200Wの電気ケトルで試してみたところ4分ほど経過した時点で60℃(空冷ファンはオン状態)を超えたあたりでほぼ安定状態であったので数分程度なら1KW越えにも十分耐えられそうな感じだ。但し、80℃ぐらいしか耐えられない樹脂パーツを使って組み立てたこともあり、初期設定は50℃以上で強制空冷開始、70℃以上で電源オフする設定としている。
【回路図】
回路図右上のLED回路は、ボタン押下検出、LEDによる状態表示、それと部屋の照明のオンオフに連動した制御もやってみたくてLED発電を応用した室内光検出も可能なように一石三鳥回路を考えてみたのだが、PIC16F1455のADCの入力インピーダンスの低さにより室内光検出は無理だった。テスターではうまく検出できるのに...残念。
【ファームウェア】
プロジェクト・ダウンロード(for MPLAB-X)
※ファイル数が多いのでパワーコントール関連のもののみ掲載
※512ワードCDCブートローダー対応になっている。ぎりぎりなサイズなのでこれ以上の機能追加は無理っぽい。
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 |
/* config.h - Configuration File 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 CONFIG_H #define CONFIG_H // // define ports // #define TMP_PIN A4 #define PIR_PIN C2 #define LED_PIN C3 #define PWC_PIN C4 #define FAN_PIN C5 #define ZCSSR_ZCPIN A5 #define ZCSSR_RESOLUTION 255U #define ZCSSR_FREQUENCY 60U #endif /* CONFIG_H */ |
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 |
/* ioport.h - ioport macro 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 IOPORT_H #define IOPORT_H #include "xc.h" // // IOPORT_PULLUP/IOPORT_ANALOG values // #define DISABLE 0 #define ENABLE 1 // // IOPORT_MODE values // #define OUTPUT 0 #define INPUT 1 // // IOPORT_READ/IOPORT_WRITE values // #define LOW 0 #define HIGH 1 // // ioport macro // #define IOPORT_CONCAT(r, p) r##p #define IOPORT_MODE(pin) IOPORT_CONCAT(TRIS, pin) #define IOPORT_ANALOG(pin) IOPORT_CONCAT(ANS , pin) #define IOPORT_PULLUP(pin) IOPORT_CONCAT(WPU , pin) #define IOPORT_WRITE(pin) IOPORT_CONCAT(LAT , pin) #define IOPORT_READ(pin) (uint8_t)IOPORT_CONCAT(R, pin) #define IOPORT_CONCAT3(a, b, c) a##b##c #define IOPORT_HAS_ANS(pin) ( \ IOPORT_CONCAT3(_ANSELA_ANS, pin, _MASK) \ || IOPORT_CONCAT3(_ANSELB_ANS, pin, _MASK) \ || IOPORT_CONCAT3(_ANSELC_ANS, pin, _MASK) \ ) #endif /* IOPORT_H */ |
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 |
/* main.c - Zero Cross Power Controler 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 */ #include <stdint.h> #include <stdbool.h> #include "config.h" #include "ioport.h" #include "usbmain.h" #include "tmr0.h" #include "setting.h" #include "usbapi.h" #include "zcssr.h" #include "adc.h" #define FAN_CYCLE_TIME TMR0_MS2TICK_L(10000) // ms #define LED_BLINK_TIME TMR0_MS2TICK_L( 1000) // ms #define POWER_OFF_TIME TMR0_MS2TICK_L( 3000) // ms bool pwc_enable; int8_t pwc_temp; uint16_t amb_light; static bool btn_status; static uint16_t btn_start; static bool led_status; static uint16_t led_start; static uint16_t fan_start; static uint32_t pwc_start; static uint32_t pwc_limit; static uint32_t pwc_restart; static uint32_t pwc_timer; static uint8_t adc_ch; static uint8_t adc_sampl; static uint16_t adc_value; static uint32_t now; static void setting_init(void) { setting_load(); zcssr_power(setting.level); pwc_limit = TMR0_MS2TICK_L((uint32_t)setting.limit * 60 * 1000); pwc_timer = TMR0_MS2TICK_L((uint32_t)setting.timer * 60 * 1000); } void setting_changed(void) { setting_save(); setting_init(); } static bool btn_read(void) { bool rv; uint8_t mode = (uint8_t)IOPORT_MODE(LED_PIN); di(); IOPORT_WRITE(LED_PIN) = LOW; IOPORT_MODE(LED_PIN) = OUTPUT; __delay_us(1); IOPORT_MODE(LED_PIN) = INPUT; IOPORT_WRITE(LED_PIN) = HIGH; rv = IOPORT_READ(LED_PIN); ei(); IOPORT_MODE(LED_PIN) = mode; return rv; } static void adc_read(void) { if (adc_done()) { switch (adc_ch) { case 0: // MCP9700A (10mV/C, 500mV/0C) pwc_temp = ((int16_t)(adc_result() << 1) - 495) / 10; // next from Ambient light (LED) if ((IOPORT_MODE(LED_PIN) == INPUT) && !btn_read()) { adc_setup(ADC_FMT_RIGHT, ADC_CLK_FRC, ADC_REF_1024, ADC_TRIG_NONE); adc_mux(ADC_MUX_RC3); // ADC_MUX_RC3 --> LED_PIN adc_ch = 1; adc_sampl = 0; adc_value = 0; // discharge ADC input. btn_read(); } break; case 1: // Ambient light (LED) adc_value += adc_result(); if (++adc_sampl >= (1 << ((sizeof(adc_value) << 3) - ADC_RESOLUTION))) { amb_light = adc_value; // next from MCP9700A adc_setup(ADC_FMT_RIGHT, ADC_CLK_FRC, ADC_REF_2048, ADC_TRIG_NONE); adc_mux(ADC_MUX_RA4); adc_ch = 0; // IOPORT_ANALOG(LED_PIN) = DISABLE; IOPORT_MODE(LED_PIN) = led_status ? OUTPUT : INPUT; } break; } adc_start(); } } static void led_control(bool on) { led_status = on; led_start = now; if (!IOPORT_ANALOG(LED_PIN)) IOPORT_MODE(LED_PIN) = on ? OUTPUT : INPUT; } void ac_power(bool on) { if (!on || (pwc_temp < setting.heat)) { if (on) { if (!pwc_enable) pwc_start = now; pwc_restart = now; } else IOPORT_WRITE(PWC_PIN) = HIGH; pwc_enable = on; led_control(on); } } void setup(void) { // // Enable WDT // WDTCONbits.SWDTEN = 1; // // Enable PULLUP // OPTION_REGbits.nWPUEN = 0; // // Init GPIO // IOPORT_MODE(ZCSSR_ZCPIN) = INPUT; // IOPORT_PULLUP(TMP_PIN) = DISABLE; // IOPORT_ANALOG(PIR_PIN) = DISABLE; IOPORT_MODE(PIR_PIN) = INPUT; // IOPORT_ANALOG(LED_PIN) = DISABLE; IOPORT_WRITE(LED_PIN) = HIGH; IOPORT_MODE(LED_PIN) = INPUT; // IOPORT_WRITE(PWC_PIN) = HIGH; IOPORT_MODE(PWC_PIN) = OUTPUT; // IOPORT_WRITE(FAN_PIN) = LOW; IOPORT_MODE(FAN_PIN) = OUTPUT; // // Init ZCSSR // zcssr_init(); // // Init Setting // setting_init(); // // Init ADC // adc_enable(true); adc_setup(ADC_FMT_RIGHT, ADC_CLK_FRC, ADC_REF_2048, ADC_TRIG_NONE); adc_mux(ADC_MUX_RA4); adc_start(); // // Init variable's // now = TMR0_READ_L(); fan_start = now; led_start = now - LED_BLINK_TIME; } void loop(void) { now = TMR0_READ_L(); // // USB command Interface // usb_command(); // // Read SSR Temperature or Ambient light (LED) // adc_read(); // // Fan Control // if (TIMEUP16(now, fan_start, FAN_CYCLE_TIME)) { fan_start = now; IOPORT_WRITE(FAN_PIN) = (pwc_temp >= setting.fan ? HIGH : LOW); } // // Power Control // if (pwc_enable) { // // Overheat or Timeup // if ((pwc_temp >= setting.heat) || (pwc_timer && TIMEUP32(now, pwc_restart, pwc_timer)) || (pwc_limit && TIMEUP32(now, pwc_start, pwc_limit))) ac_power(false); else { // // Blink LED // if (TIMEUP16(now, led_start, LED_BLINK_TIME)) led_control((bool)!led_status); // // Restart Power Timer // if (setting.pir && IOPORT_READ(PIR_PIN)) ac_power(true); // // Handle Zero Cross SSR // IOPORT_WRITE(PWC_PIN) = (zcssr_handle() ? LOW : HIGH); } } // // Check Power-On Switch // if (!IOPORT_ANALOG(LED_PIN)) { bool on = btn_read(); if (on) { if (btn_status) { if (TIMEUP16(now, btn_start, POWER_OFF_TIME)) on = false; } else { btn_status = true; btn_start = now; } ac_power(on); } else btn_status = false; } // // Reset WDT // CLRWDT(); } |
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 |
/* zcssr.h - Zero Cross Power Control Library 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 ZCSSR_H #define ZCSSR_H #include <stdint.h> #include <stdbool.h> //#define ZCSSR_ZCPIN A5 // zero cross detect pin extern void zcssr_init(void); extern void zcssr_power(uint8_t value); extern bool zcssr_next(void); extern bool zcssr_handle(void); #endif /* ZCSSR_H */ |
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 |
/* zcssr.c - Zero Cross Power Control Library 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 */ #include "config.h" #include "ioport.h" #include "tmr0.h" #include "zcssr.h" #ifndef ZCSSR_FREQUENCY #define ZCSSR_FREQUENCY 60U #endif #ifndef ZCSSR_RESOLUTION #define ZCSSR_RESOLUTION 255U #endif static uint16_t _start; static uint16_t _power; static uint8_t _index; static uint8_t _zcval; static bool _state; static bool _first; void zcssr_init(void) { _start = 0; _power = 0; _index = 0; _zcval = 0; _state = false; _first = true; } void zcssr_power(uint8_t value) { _power = value ? ZCSSR_RESOLUTION * 256U / (value < ZCSSR_RESOLUTION ? value : ZCSSR_RESOLUTION) : 0U; } bool zcssr_next(void) { if (_power == 0U) return false; if (_power == 256U) return true; _index = _index < ZCSSR_RESOLUTION ? _index + 1U : 1U; return (((_index - 1U) * 256U) % _power) & ~0xFFU ? false : true; } bool zcssr_handle(void) { uint16_t n = tmr0_read(); uint16_t t = TMR0_US2TICK(1000000UL / ZCSSR_FREQUENCY / 2U); #ifdef ZCSSR_ZCPIN uint8_t val = IOPORT_READ(ZCSSR_ZCPIN); if (_zcval != val) { _zcval = val; if (val != 0U) // 1=rising edge, 0=falling edge _first = true; } #endif if (_first) { _first = false; #ifdef ZCSSR_ZCPIN _start = n - (t >> 1); #else _start = n - t; #endif } if (TIMEUP16(n, _start, t)) { _start += t; _state = zcssr_next(); } return _state; } |
【PC側ユーティリティ】
プロジェクト・ダウンロード(for Eclipse-CDT + MinGW64)
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 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
/* main.cpp - Zero Cross Power Control Utility 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 */ #include <stdlib.h> #include <stdbool.h> #include <stdint.h> #include <errno.h> #include "zcpower.h" ZCPower zcpower; bool power(bool info) { bool val = false; bool rv = zcpower.getPower(val); if (!rv) printf("[ERROR] ZCPower.getPower()\n"); else printf(info ? "power = %s\n" : "%s\n", val ? "on" : "off"); return rv; } bool pir(bool info) { bool val = false; bool rv = zcpower.getPIR(val); if (!rv) printf("[ERROR] ZCPower.getPIR()\n"); else printf(info ? "pir = %s\n" : "%s\n", val ? "on" : "off"); return rv; } bool level(bool info) { uint8_t val = 0; bool rv = zcpower.getLevel(val); if (!rv) printf("[ERROR] ZCPower.getLevel()\n"); else printf(info ? "level = %u\n" : "%u\n", val); return rv; } bool limit(bool info) { uint16_t val = 0; bool rv = zcpower.getLimit(val); if (!rv) printf("[ERROR] ZCPower.getLimit()\n"); else printf(info ? "limit = %u\n" : "%u\n", val); return rv; } bool timer(bool info) { uint16_t val = 0; bool rv = zcpower.getTimer(val); if (!rv) printf("[ERROR] ZCPower.getLimit()\n"); else printf(info ? "timer = %u\n" : "%u\n", val); return rv; } bool heat(bool info) { int8_t val = 0; bool rv = zcpower.getHeat(val); if (!rv) printf("[ERROR] ZCPower.getHeat()\n"); else printf(info ? "heat = %d\n" : "%d\n", val); return rv; } bool fan(bool info) { int8_t val = 0; bool rv = zcpower.getFan(val); if (!rv) printf("[ERROR] ZCPower.getFan()\n"); else printf(info ? "fan = %d\n" : "%d\n", val); return rv; } bool temp(bool info) { int8_t val = 0; bool rv = zcpower.getTemp(val); if (!rv) printf("[ERROR] ZCPower.getTemp()\n"); else printf(info ? "temp = %d\n" : "%d\n", val); return rv; } bool light(bool info) { uint16_t val = 0; bool rv = zcpower.getLight(val); if (!rv) printf("[ERROR] ZCPower.getLight()\n"); else printf(info ? "light = %u\n" : "%u\n", val); return rv; } void usage(void) { printf("\n"); printf("Zero Cross Power Control Utility, Version 1.0\n"); printf("Copyright (c) 2021 Sasapea's Lab. All right reserved.\n"); printf("\n"); printf("Usage: zcpower port-name command\n"); printf("\n"); printf("command: power {off|on} ... power off or on\n"); printf(" pir {off|on} ... PIR sensor off or on\n"); printf(" level {n} ... power level n (0-255)\n"); printf(" limit {n} ... power on limit timer n (0-65535) minute\n"); printf(" timer {n} ... power on timer n (0-65535) minute\n"); printf(" heat {n} ... heat temperature n (-40-125)\n"); printf(" fan {n} ... fan temperature n (-40-125)\n"); printf(" temp ... SSR temperature (-40-125)\n"); printf(" light ... ambiant light value (0-65535)\n"); printf(" info ... display all values\n"); printf(" reset ... reset\n"); printf(" bootloader ... start bootloader mode\n"); printf("\n"); exit(EXIT_FAILURE); } int main(int argc, char **argv) { int help; if (argc < 3) usage(); if (zcpower.open(argv[1]) < 0) { printf("[ERROR] ZCPower.open(\"%s\"): %s\n", argv[1], strerror(errno)); usage(); } for (int i = 2; i < argc; help = false) { char *p = argv[i++]; help = true; if (strcmp(p, "bootloader") == 0) { zcpower.reset(0xDFBE); // start bootloader mode help = false; break; } else if (strcmp(p, "reset") == 0) { zcpower.reset(); help = false; break; } else if (strcmp(p, "power") == 0) { bool val; if (i < argc) { p = argv[i++]; if (strcmp(p, "off") == 0) val = false; else if (strcmp(p, "on") == 0) val = true; else break; if (!zcpower.setPower(val)) { printf("[ERROR] ZCPower.setPower(%s)\n", val ? "true" : "false"); break; } } if (!power(false)) break; } else if (strcmp(p, "pir") == 0) { bool val; if (i < argc) { p = argv[i++]; if (strcmp(p, "off") == 0) val = false; else if (strcmp(p, "on") == 0) val = true; else break; if (!zcpower.setPIR(val)) { printf("[ERROR] ZCPower.setPIR(%u)\n", val); break; } } if (!pir(false)) break; } else if (strcmp(p, "level") == 0) { uint8_t val; if (i < argc) { p = argv[i++]; val = strtol(p, &p, 10); if (*p) break; if (!zcpower.setLevel(val)) { printf("[ERROR] ZCPower.setLevel(%u)\n", val); break; } } if (!level(false)) break; } else if (strcmp(p, "limit") == 0) { uint16_t val; if (i < argc) { p = argv[i++]; val = strtol(p, &p, 10); if (*p) break; if (!zcpower.setLimit(val)) { printf("[ERROR] ZCPower.setLimit(%u)\n", val); break; } } if (!limit(false)) break; } else if (strcmp(p, "timer") == 0) { uint16_t val; if (i < argc) { p = argv[i++]; val = strtol(p, &p, 10); if (*p) break; if (!zcpower.setTimer(val)) { printf("[ERROR] ZCPower.setLimit(%u)\n", val); break; } } if (!timer(false)) break; } else if (strcmp(p, "heat") == 0) { int8_t val; if (i < argc) { p = argv[i++]; val = strtol(p, &p, 10); if (*p) break; if (!zcpower.setHeat(val)) { printf("[ERROR] ZCPower.setHeat(%d)\n", val); break; } } if (!heat(false)) break; } else if (strcmp(p, "fan") == 0) { int8_t val; if (i < argc) { p = argv[i++]; val = strtol(p, &p, 10); if (*p) break; if (!zcpower.setFan(val)) { printf("[ERROR] ZCPower.setFan(%d)\n", val); break; } } if (!fan(false)) break; } else if (strcmp(p, "temp") == 0) { if (!temp(false)) break; } else if (strcmp(p, "light") == 0) { if (!light(false)) break; } else if (strcmp(p, "info") == 0) { if (!power(true)) break; if (!pir(true)) break; if (!level(true)) break; if (!limit(true)) break; if (!timer(true)) break; if (!heat(true)) break; if (!fan(true)) break; if (!temp(true)) break; if (!light(true)) break; } else break; } if (zcpower.close() < 0) printf("[ERROR] ZCPower.close()\n"); if (help) usage(); return 0; } |
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 |
/* zcpower.h - Zero Cross Power Control Library 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 _ZCPOWER_H #define _ZCPOWER_H #include <stdint.h> #include <stdbool.h> #include "serial.h" // // Device Command // #define ZCPOWER_RESET 0x00 #define ZCPOWER_POWER_ENABLE 0x01 // 0 - 1 #define ZCPOWER_POWER_LEVEL 0x02 // 0 - 255 #define ZCPOWER_POWER_LIMIT 0x03 // 0 - 65535 min #define ZCPOWER_POWER_TIMER 0x04 // 0 - 65535 min #define ZCPOWER_PIR_ENABLE 0x05 // 0 - 1 #define ZCPOWER_TEMP_HEAT 0x06 // -40 - 125 #define ZCPOWER_TEMP_FAN 0x07 // -40 - 125 #define ZCPOWER_TEMP_DATA 0x08 // -40 - 125 #define ZCPOWER_LIGHT_DATA 0x09 // 0 - 1023 class ZCPower : public Serial { private: uint8_t _count; struct { uint8_t len; uint8_t cmd; union { struct { uint16_t val; } reset; struct { bool val; } power; struct { uint8_t val; } level; struct { uint16_t val; } limit; struct { uint16_t val; } timer; struct { bool val; } pir; struct { int8_t val; } heat; struct { int8_t val; } fan; struct { int8_t val; } temp; struct { uint16_t val; } light; } arg; }__attribute__((packed)) _packet; // Hide method ssize_t read(void *buf, size_t len) override { return Serial::read (buf, len); } // Hide method ssize_t write(const void *buf, size_t len) override { return Serial::write (buf, len); } bool invoke(uint8_t size = 0) { uint8_t cmd = _packet.cmd; if (write (&_packet, sizeof(_packet.len) + _packet.len) == (ssize_t) (sizeof(_packet.len) + _packet.len)) { _count = 0; while (read ((uint8_t *) &_packet + _count, 1) == 1) { if (++_count < sizeof(_packet.len)) continue; if (_count == sizeof(_packet.len) + _packet.len) return (_packet.len >= sizeof(_packet.cmd) + size) && (_packet.cmd == cmd); // invalid packet size ? if (_count == sizeof(_packet)) _count = 0; } } return false; } public: ZCPower(void) : _count(0) { } virtual ~ZCPower(void) override { } void reset(uint16_t mode = 0) { _packet.len = sizeof(_packet.cmd) + sizeof(_packet.arg.reset); _packet.cmd = ZCPOWER_RESET; _packet.arg.reset.val = mode; invoke(); } bool getPower(bool &val) { _packet.len = sizeof(_packet.cmd); _packet.cmd = ZCPOWER_POWER_ENABLE; if (!invoke(sizeof(_packet.arg.power))) return false; val = _packet.arg.power.val; return true; } bool setPower(bool val) { _packet.len = sizeof(_packet.cmd) + sizeof(_packet.arg.power); _packet.cmd = ZCPOWER_POWER_ENABLE; _packet.arg.power.val = val; return invoke(); } bool getLevel(uint8_t &val) { _packet.len = sizeof(_packet.cmd); _packet.cmd = ZCPOWER_POWER_LEVEL; if (!invoke(sizeof(_packet.arg.level))) return false; val = _packet.arg.level.val; return true; } bool setLevel(uint8_t val) { _packet.len = sizeof(_packet.cmd) + sizeof(_packet.arg.level); _packet.cmd = ZCPOWER_POWER_LEVEL; _packet.arg.level.val = val; return invoke(); } bool getLimit(uint16_t &val) { _packet.len = sizeof(_packet.cmd); _packet.cmd = ZCPOWER_POWER_LIMIT; if (!invoke(sizeof(_packet.arg.limit))) return false; val = _packet.arg.limit.val; return true; } bool setLimit(uint16_t val) { _packet.len = sizeof(_packet.cmd) + sizeof(_packet.arg.limit); _packet.cmd = ZCPOWER_POWER_LIMIT; _packet.arg.limit.val = val; return invoke(); } bool getTimer(uint16_t &val) { _packet.len = sizeof(_packet.cmd); _packet.cmd = ZCPOWER_POWER_TIMER; if (!invoke(sizeof(_packet.arg.timer))) return false; val = _packet.arg.timer.val; return true; } bool setTimer(uint16_t val) { _packet.len = sizeof(_packet.cmd) + sizeof(_packet.arg.timer); _packet.cmd = ZCPOWER_POWER_TIMER; _packet.arg.timer.val = val; return invoke(); } bool getPIR(bool &val) { _packet.len = sizeof(_packet.cmd); _packet.cmd = ZCPOWER_PIR_ENABLE; if (!invoke(sizeof(_packet.arg.pir))) return false; val = _packet.arg.pir.val; return true; } bool setPIR(bool val) { _packet.len = sizeof(_packet.cmd) + sizeof(_packet.arg.pir); _packet.cmd = ZCPOWER_PIR_ENABLE; _packet.arg.pir.val = val; return invoke(); } bool getHeat(int8_t &val) { _packet.len = sizeof(_packet.cmd); _packet.cmd = ZCPOWER_TEMP_HEAT; if (!invoke(sizeof(_packet.arg.heat))) return false; val = _packet.arg.heat.val; return true; } bool setHeat(int8_t val) { _packet.len = sizeof(_packet.cmd) + sizeof(_packet.arg.heat); _packet.cmd = ZCPOWER_TEMP_HEAT; _packet.arg.heat.val = val; return invoke(); } bool getFan(int8_t &val) { _packet.len = sizeof(_packet.cmd); _packet.cmd = ZCPOWER_TEMP_FAN; if (!invoke(sizeof(_packet.arg.fan))) return false; val = _packet.arg.fan.val; return true; } bool setFan(int8_t val) { _packet.len = sizeof(_packet.cmd) + sizeof(_packet.arg.fan); _packet.cmd = ZCPOWER_TEMP_FAN; _packet.arg.fan.val = val; return invoke(); } bool getTemp(int8_t &val) { _packet.len = sizeof(_packet.cmd); _packet.cmd = ZCPOWER_TEMP_DATA; if (!invoke(sizeof(_packet.arg.temp))) return false; val = _packet.arg.temp.val; return true; } bool getLight(uint16_t &val) { _packet.len = sizeof(_packet.cmd); _packet.cmd = ZCPOWER_LIGHT_DATA; if (!invoke(sizeof(_packet.arg.light))) return false; val = _packet.arg.light.val; return true; } }; #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 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 |
/* serial.h - Serial I/O Library 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 _SERIAL_H #define _SERIAL_H #include <stdio.h> #include <stdint.h> #include <string.h> #include <fcntl.h> #ifdef __linux__ #include <unistd.h> #include <termios.h> #include <sys/time.h> #include <sys/types.h> #else #include <windows.h> #if SIZE_MAX == UINT64_MAX #define ssize_t long long #else #define ssize_t int #endif #endif typedef enum { SERIAL_FRAME_5N1, SERIAL_FRAME_6N1, SERIAL_FRAME_7N1, SERIAL_FRAME_8N1, SERIAL_FRAME_5N2, SERIAL_FRAME_6N2, SERIAL_FRAME_7N2, SERIAL_FRAME_8N2, SERIAL_FRAME_5O1, SERIAL_FRAME_6O1, SERIAL_FRAME_7O1, SERIAL_FRAME_8O1, SERIAL_FRAME_5O2, SERIAL_FRAME_6O2, SERIAL_FRAME_7O2, SERIAL_FRAME_8O2, SERIAL_FRAME_5E1, SERIAL_FRAME_6E1, SERIAL_FRAME_7E1, SERIAL_FRAME_8E1, SERIAL_FRAME_5E2, SERIAL_FRAME_6E2, SERIAL_FRAME_7E2, SERIAL_FRAME_8E2, } SERIAL_FRAME; typedef enum { SERIAL_FLOW_NONE, SERIAL_FLOW_RTSCTS, SERIAL_FLOW_XONOFF, } SERIAL_FLOW; #define SERIAL_FLOW_XONOFF_START 0x11 // DC1 #define SERIAL_FLOW_XONOFF_STOP 0x13 // DC3 #define SERIAL_FLOW_XONOFF_LIMIT 256 class Serial { private: int _fd; #ifdef __linux__ struct termios _tcattr; struct timeval _timeout; #endif void config(unsigned long speed, SERIAL_FRAME frame, SERIAL_FLOW flow, unsigned long timeout) { #ifdef __linux__ struct termios tcattr; tcgetattr(_fd, &tcattr); _tcattr= tcattr; cfmakeraw(&tcattr); speed = cfgetospeed(&tcattr); int cflags = tcattr.c_cflag & (CSIZE | CSTOPB | PARENB | PARODD); tcattr.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD); switch (speed) { case 50: speed = B50; break; case 75: speed = B75; break; case 110: speed = B110; break; case 134: speed = B134; break; case 150: speed = B150; break; case 200: speed = B200; break; case 300: speed = B300; break; case 600: speed = B600; break; case 1200: speed = B1200; break; case 1800: speed = B1800; break; case 2400: speed = B2400; break; case 4800: speed = B4800; break; case 9600: speed = B9600; break; case 19200: speed = B19200; break; case 38400: speed = B38400; break; case 57600: speed = B57600; break; case 115200: speed = B115200; break; case 230400: speed = B230400; break; case 460800: speed = B460800; break; case 500000: speed = B500000; break; case 576000: speed = B576000; break; case 921600: speed = B921600; break; case 1000000: speed = B1000000; break; case 1152000: speed = B1152000; break; case 1500000: speed = B1500000; break; case 2000000: speed = B2000000; break; case 2500000: speed = B2500000; break; case 3000000: speed = B3000000; break; case 3500000: speed = B3500000; break; case 4000000: speed = B4000000; break; } switch (frame) { case SERIAL_FRAME_5N1: cflags = CS5; break; case SERIAL_FRAME_6N1: cflags = CS6; break; case SERIAL_FRAME_7N1: cflags = CS7; break; case SERIAL_FRAME_8N1: cflags = CS8; break; case SERIAL_FRAME_5N2: cflags = CS5 | CSTOPB; break; case SERIAL_FRAME_6N2: cflags = CS6 | CSTOPB; break; case SERIAL_FRAME_7N2: cflags = CS7 | CSTOPB; break; case SERIAL_FRAME_8N2: cflags = CS8 | CSTOPB; break; case SERIAL_FRAME_5O1: cflags = CS5 | PARENB | PARODD; break; case SERIAL_FRAME_6O1: cflags = CS6 | PARENB | PARODD; break; case SERIAL_FRAME_7O1: cflags = CS7 | PARENB | PARODD; break; case SERIAL_FRAME_8O1: cflags = CS8 | PARENB | PARODD; break; case SERIAL_FRAME_5O2: cflags = CS5 | CSTOPB | PARENB | PARODD; break; case SERIAL_FRAME_6O2: cflags = CS6 | CSTOPB | PARENB | PARODD; break; case SERIAL_FRAME_7O2: cflags = CS7 | CSTOPB | PARENB | PARODD; break; case SERIAL_FRAME_8O2: cflags = CS8 | CSTOPB | PARENB | PARODD; break; case SERIAL_FRAME_5E1: cflags = CS5 | PARENB; break; case SERIAL_FRAME_6E1: cflags = CS6 | PARENB; break; case SERIAL_FRAME_7E1: cflags = CS7 | PARENB; break; case SERIAL_FRAME_8E1: cflags = CS8 | PARENB; break; case SERIAL_FRAME_5E2: cflags = CS5 | CSTOPB | PARENB; break; case SERIAL_FRAME_6E2: cflags = CS6 | CSTOPB | PARENB; break; case SERIAL_FRAME_7E2: cflags = CS7 | CSTOPB | PARENB; break; case SERIAL_FRAME_8E2: cflags = CS8 | CSTOPB | PARENB; break; } cfsetspeed(&tcattr, speed); tcattr.c_cflag = (tcattr.c_cflag & ~(CSIZE | CSTOPB | PARENB | PARODD)) | cflags; #ifdef CRTSCTS if (flow == SERIAL_FLOW_RTSCTS) tcattr.c_iflag |= CRTSCTS; else tcattr.c_iflag &= ~CRTSCTS; #endif if (flow == SERIAL_FLOW_XONOFF) tcattr.c_iflag |= IXON; else tcattr.c_iflag &= ~IXON; tcattr.c_cc[VSTART] = SERIAL_FLOW_XONOFF_START; tcattr.c_cc[VSTOP] = SERIAL_FLOW_XONOFF_STOP; tcattr.c_oflag &= ~OPOST; tcflush(_fd, TCIOFLUSH); tcsetattr(_fd, TCSANOW, &tcattr); _timeout.tv_sec = (timeout / 1000); _timeout.tv_usec = (timeout % 1000) * 1000; #else DCB dcb; COMMTIMEOUTS tov; HANDLE hSerial = (HANDLE) _get_osfhandle(_fd); memset(&dcb, 0, sizeof(DCB)); dcb.DCBlength = sizeof(DCB); GetCommState(hSerial, &dcb); dcb.BaudRate = speed; dcb.ByteSize = (frame & 3) + 5; dcb.StopBits = (frame >> 1) & 2; dcb.Parity = (frame >> 3); dcb.fDtrControl = (flow == SERIAL_FLOW_RTSCTS ? DTR_CONTROL_HANDSHAKE : DTR_CONTROL_ENABLE); dcb.fRtsControl = (flow == SERIAL_FLOW_RTSCTS ? RTS_CONTROL_HANDSHAKE : DTR_CONTROL_ENABLE); dcb.fOutxCtsFlow = (flow == SERIAL_FLOW_RTSCTS); dcb.fOutxDsrFlow = (flow == SERIAL_FLOW_RTSCTS); dcb.fOutX = (flow == SERIAL_FLOW_XONOFF); dcb.fInX = (flow == SERIAL_FLOW_XONOFF); dcb.XonLim = SERIAL_FLOW_XONOFF_LIMIT; dcb.XoffLim = SERIAL_FLOW_XONOFF_LIMIT; dcb.XonChar = SERIAL_FLOW_XONOFF_START; dcb.XoffChar = SERIAL_FLOW_XONOFF_STOP; dcb.fTXContinueOnXoff = TRUE; dcb.fDsrSensitivity = FALSE; dcb.fErrorChar = FALSE; dcb.fNull = FALSE; dcb.fAbortOnError = FALSE; dcb.fBinary = TRUE; SetCommState(hSerial, &dcb); PurgeComm(hSerial, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); memset(&tov, 0, sizeof(tov)); tov.ReadTotalTimeoutConstant = timeout; tov.WriteTotalTimeoutConstant = timeout; SetCommTimeouts(hSerial, &tov); #endif } public: Serial(void) : _fd(-1) { } virtual ~Serial(void) { close(); } virtual int open(const char *device, unsigned long speed = 115200, SERIAL_FRAME frame = SERIAL_FRAME_8N1, SERIAL_FLOW flow = SERIAL_FLOW_NONE, unsigned long timeout = 1000) { char path[32]; #ifdef __linux__ snprintf(path, sizeof(path), "/dev/%s", device); if ((_fd = ::open(path, O_RDWR | O_NOCTTY)) >= 0) #else snprintf (path, sizeof(path), "\\\\.\\%s", device); if ((_fd = ::open(path, O_RDWR | _O_BINARY)) >= 0) #endif config(speed, frame, flow, timeout); return _fd < 0 ? _fd : 0; } virtual int close(void) { int rv = 0; if (_fd >= 0) { #ifdef __linux__ tcsetattr(_fd, TCSANOW, &_tcattr); #endif rv = ::close(_fd); _fd = -1; } return rv; } virtual ssize_t read(void *buf, size_t len) { ssize_t ret; size_t cnt; for (ret = cnt = 0; cnt < len; ++cnt) { #ifdef __linux__ fd_set rdfds; FD_ZERO(&rdfds); FD_SET(_fd, &rdfds); ret = select(_fd + 1, &rdfds, NULL, NULL, &_timeout); if (ret <= 0) break; #endif ret = ::read(_fd, (uint8_t *)buf + cnt, 1); if (ret <= 0) break; } return ret ? ret : cnt; } virtual ssize_t write(const void *buf, size_t len) { #ifdef __linux__ fd_set wrfds; FD_ZERO(&wrfds); FD_SET(_fd, &wrfds); int ret = select(_fd + 1, NULL, &wrfds, NULL, &_timeout); if (ret <= 0) return rv; #endif return ::write(_fd, buf, len); } }; #endif |