冷却用のUSB扇風機を温度制御したくてサーモスイッチを探してみたが、デカくて12V仕様のものばかりだったので自作してみた。
簡単な制御なので手持ちのATtiny85を使うことにしたが、毎度のことながらピン数が少ないので悩ましいことが多い。まず、アップデートをどうするかが問題。MCUに外部回路を接続するとISPが使えなくなる。ICソケットは耐久性がない。digisparkのようにV-USB方式にするとピンが2本も占有されてしまう。あっちをたてればこっちがたたずみたいな状況に陥るが、そこはなんとか妥協点を見つけるしかない。
今回は、V-USB方式で開発してみることにしたが、回路的には、ツェナーダイオード(3.6V)2本、1.5K抵抗1本と68Ω抵抗2本、ブートローダー(micronucleus)書き込み済みのATtiny85が必要なだけ。
【回路図】
※ J3はアップデート時のみ両方ショート。通常時はD+側のみオープンにする。
※ 複数温度センサー対応なので外部温度センサーに合わせて実装部品を変えること。
※ LMT01のExamplesを見てR5を100Kにしたところ電圧的に心もとない。そのままで行くならMCUの入力プルアップ抵抗を併用すべし。って、だったら無くてもいいんじゃね?...た、確かに。(笑)
【完成した基板】
【2018-03-15 USB機器との間に入れるタイプも作ってみた。こっちのほうが便利そう】
このレベルのモジュールを中国で作って売るなら100円以下になりそうな気がする。って、そんなことはどうでもいいんだけど、タクトスイッチと共有できるようにUSB端子をジャンパーで切り離せるようにしたことでオオハマリ...切り離すとなぜかMCUが動作しなくなるのだ。
原因は、USBのD-側をMCUに接続しておかないとV-USB内で永久ループしてしまうことであったが、そんな仕様になってるなんて知るはずもなく思いっきり悩んでしまった。orz
教訓!! 切り離し出来るのはD+側だけ。D-側は常に接続しておくべし。
【仕様】
・開発環境は、Arduino “Digispark (Default – 16.5mhz)”
・V-USB(micronucleus)によるアップデート機能
・温度センサーは、MCU内蔵、LMT01、LM35、LM60、LM61の5種類をサポート。
・センサー自動検出(MCU内蔵, LMT01, LM??)
・センサー手動選択(LM35/60/61)
・温度閾値設定(-30 – +80)
・温度補正値設定(-30 – +30)
・接点出力モード(NO/NC) Max AC-DC 40V, 2A
・接点制御間隔(10秒固定)
・設定初期化
・温度表示(1秒毎計測、Simple Moving Averageにより直近の10データを平均化)
・各種設定値表示
・LED表示方式(点滅回数方式、モールス信号方式)
なお、MCU内蔵センサーは個体差が激しく±20℃もの誤差のものがあったり自己発熱によるドリフトが大きかったり環境温度追従性も良くないのであまりお勧めではないが補正すれば使えなくもないし外部センサーなしの最小部品で構成できるので精度が必要とされないのであれば選択余地はあるかもしれない。そんなセンサーにも対応するべく温度補正範囲が(-30 – +30)になってしまったというかなんか補正とかいうレベルじゃねー。(笑)
データ表示はLED表示器?で行う仕様なのでモールス信号が読めない人は無理せず点滅回数方式をどうぞ。
【参考情報】
ESP-WROOM-32でモールス信号発生器を作ってみた。
micronucleus (firmware/releases/t85_default.hex)
【Arduinoコンパイル結果】
最大6012バイトのフラッシュメモリのうち、スケッチが4952バイト(82%)を使っています。
グローバル変数は100バイトのRAMを使用しています。
【ファームウェア】
digispark-coreと共存しつつも、H/W直接操作で実現している機能。
・※12bit Counter (Timer0 + USI-Counter) … analogWrite()と競合
・1.1 or 2.56V ADC reference Voltage … analogRead()と競合
・1ms core interrupt の利用
※あえて12bit Counterを使う必要もなかったが、なんとなく...
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 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 |
/////////////////////////////////////////////////////////////////////////////////////// // // // Tiny Thermo Switch Controller // // // // Author : Sasapea's Lab. // // Release: 2018-02-08. Version 1.00 // // // // power-on button operations (first LED on (3sec) operation) // // // // [+] and [-] ... setting reset. // // [+] ... select thermo switch type. (0=normally open, 1=normally close) // // [-] ... select display type. (0=blink, 1=morse) // // // // normal button oerations // // // // [+] and [-] ... select LMXX sensor. (0=LM35, 1=LM60, 2=LM61) // // [+] ... *1 ++threshold. // // [-] ... *1 --threshold. // // [+] ... *2 ++calibrate. // // [-] ... *2 --calibrate. // // *1 short time press. < 3sec // // *2 long time press. >=3sec (repeated) // // // /////////////////////////////////////////////////////////////////////////////////////// #include <Arduino.h> #include <avr/eeprom.h> #include <avr/wdt.h> #include "PinEdge.h" #include "Average.h" #include "BlinkCode.h" #include "MorseCode.h" // // gpio pin assign // #define GPIO_THERMO_SW PB0 #define GPIO_BOARD_LED PB1 #define GPIO_SENSOR_PIN PB2 #define GPIO_DW_BTN PB3 #define GPIO_UP_BTN PB4 #define GPIO_RESET PB5 // not used. // // ADC channel define // #define ADC0 ((0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0)) // PB5 #define ADC1 ((0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (1 << MUX0)) // PB2 #define ADC2 ((0 << MUX3) | (0 << MUX2) | (1 << MUX1) | (0 << MUX0)) // PB4 #define ADC3 ((0 << MUX3) | (0 << MUX2) | (1 << MUX1) | (1 << MUX0)) // PB3 #define ADC4 ((1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (1 << MUX0)) // MCU Internal Temperature Sensor #define ADC_MUX_MASK ADC4 #define ADC_REFS_VCC (0 << REFS2) | (0 << REFS1) | (0 << REFS0) #define ADC_REFS_110 (0 << REFS2) | (1 << REFS1) | (0 << REFS0) #define ADC_REFS_256 (1 << REFS2) | (1 << REFS1) | (0 << REFS0) #define ADC_REFS_MASK (1 << REFS2) | (1 << REFS1) | (1 << REFS0) #if GPIO_SENSOR_PIN == PB5 #define ADCX ADC0 #elif GPIO_SENSOR_PIN == PB2 #define ADCX ADC1 #elif GPIO_SENSOR_PIN == PB4 #define ADCX ADC2 #elif GPIO_SENSOR_PIN == PB3 #define ADCX ADC3 #else #define ADCX ADC4 #endif // // USI counter mask // #define USICNT_MASK ((1 << USICNT3) | (1 << USICNT2) | (1 << USICNT1) | (1 << USICNT0)) // // sensor type // #define SENSOR_MCU 0 #define SENSOR_LMT01 1 #define SENSOR_LMXX 2 #define SENSOR_LM35 0 #define SENSOR_LM60 1 #define SENSOR_LM61 2 #define SENSOR_MAX SENSOR_LM61 // // limit define // #define THRESHOLD_MAX +80 #define THRESHOLD_MIN -30 #define CALIBRATE_MAX +30 #define CALIBRATE_MIN -30 // // setting mode // #define SETTING_NONE 0 #define SETTING_SENSOR 1 // // timer setting // #define THERMO_CTRL_INTERVAL 10000 // ms #define SENSOR_READ_INTERVAL 1000 // ms #define OPTIONS_SETTING_TIME 3000 // ms #define OPTIONS_SETTING_DISP 4 // // display mode // #define BLINKCODE_MODE 0 #define MORSECODE_MODE 1 // // EEPROM values // #define OFFSETOF(t, f) ((uint8_t *)&((t *)NULL)->f) typedef struct { uint8_t size; int8_t threshold; int8_t calibrate[SENSOR_LMXX + 1]; uint8_t switch_1b; uint8_t sensor_lm; uint8_t disp_mode; } options_t; // // option variables // options_t options = {sizeof(options_t), 30, {0}, 0, SENSOR_LM35, BLINKCODE_MODE}; // // working variables // volatile uint16_t adc_value; volatile uint16_t t0_counter; volatile uint16_t lmt01_tmp; volatile uint16_t lmt01_save; volatile uint16_t lmt01_pulses; uint8_t sensor_type; uint16_t sensor_read; uint16_t thermo_ctrl; uint16_t setting_start; uint8_t setting_mode; uint8_t setting_disp; int8_t setting_value; PinEdge<GPIO_UP_BTN+1> pin_edge; Average<int8_t, int16_t, uint8_t, 10> temperature; BlinkCode<220> blink; MorseCode<160> morse; ISR(USI_OVF_vect) { USISR |= (1 << USIOIF); t0_counter += 0x1000; } void t0_init() { // // stop Timer0 // GTCCR |= (1 << TSM) | (1 << PSR0); // // Setup Timer-0 (CTC Mode) // TCCR0A = (0 << COM0A1) | (0 << COM0A0) | (0 << COM0B1) | (0 << COM0B0) | (1 << WGM01) | (0 << WGM00); TCCR0B = (0 << FOC0A) | (0 << FOC0B) | (0 << WGM02) | (1 << CS02) | (1 << CS01) | (0 << CS00); // T0 Pin Low Edge. TCNT0 = 0; OCR0A = 0xFF; OCR0B = 0; TIMSK &= ~((1 << OCIE0A) | (1 << OCIE0B) | (1 << TOIE0)); TIFR |= (1 << OCF0A) | (1 << OCF0B) | (1 << TOV0); // // setup USI-COUNTER // USISR = (1 << USIOIF); USICR = (0 << USISIE) | (1 << USIOIE) | (0 << USIWM1) | (0 << USIWM0) | (0 << USICS1) | (1 << USICS0) | (0 << USICLK) | (0 << USITC); // // reset Overflow Counter // t0_counter = 0; // // start Timer0 // GTCCR &= ~(1 << TSM); } uint16_t t0_read() { union { uint8_t b[2]; uint16_t w; } x; uint8_t c; do { c = USISR; x.w = t0_counter; x.b[0] = TCNT0; } while ((c ^ USISR) & USICNT_MASK); x.b[1] |= (c & USICNT_MASK); return x.w; } ISR(TIM1_COMPA_vect) { uint16_t cnt = t0_read(); if (lmt01_tmp != cnt) lmt01_tmp = cnt; else if (lmt01_save != cnt) { lmt01_pulses = cnt - lmt01_save; lmt01_save = cnt; } } void timer1_init() { // // using digispark core timer (1ms) // set the interrupt timing to the middle of the digispark core interrupt timing // TIMSK &= ~(1 << OCIE1A); TCCR1 &= ~((1 << COM1A1) | (1 << COM1A0)); OCR1A = (OCR1C >> 1); TIFR |= (1 << OCF1A); TIMSK |= (1 << OCIE1A); } ISR(ADC_vect) { adc_value = ADC & 0x3FF; } void adc_start() { ADCSRA |= (1 << ADSC); } void adc_wait() { while (ADCSRA & (1 << ADSC)); } void adc_init(uint8_t mux, uint8_t refs) { refs &= ADC_REFS_MASK; mux &= ADC_MUX_MASK; switch (mux) { case ADC0: DIDR0 = (1 << ADC0D); break; case ADC1: DIDR0 = (1 << ADC1D); break; case ADC2: DIDR0 = (1 << ADC2D); break; case ADC3: DIDR0 = (1 << ADC3D); break; default: case ADC4: DIDR0 = 0; break; } ADMUX = (0 << ADLAR) | refs | mux; ADCSRB = (0 << BIN) | (0 << ACME) | (0 << IPR) | (0 << ADTS2) | (0 << ADTS1) | (0 << ADTS0); ADCSRA = (1 << ADEN) | (1 << ADSC) | (0 << ADATE) | (0 << ADIF) | (1 << ADIE) | (1 << ADPS2) | (0 << ADPS1) | (0 << ADPS0); // CLK/16=1MHz adc_wait(); // discard the first time read delay(1); // wait for referrence voltage stabled } int8_t read_temperature() { int16_t val; switch (sensor_type) { // // MCU Internal Temperature Sensor [-40 - +85], 0 = 275 ADC (1 ADC/C) // default: case SENSOR_MCU: val = adc_value; adc_start(); return val - 275; // // LMT01 [-50 - +150]. 0 = 800 pulses (16 pulses/C) // case SENSOR_LMT01: cli(); val = lmt01_pulses; sei(); return (val >> 4) - 50; // // LM35/60/61 (1 ADC = 2.5mV) // case SENSOR_LMXX: switch (options.sensor_lm) { // // LM35 [0 - +110], 0 = 0mV, 10mV/C (4 ADC/C) // default: case SENSOR_LM35: val = adc_value; adc_start(); return val >> 2; // // LM60 [-40 - +125], 0 = 424mV, 6.25mV/C (10/25 ADC/C) // case SENSOR_LM60: val = adc_value; adc_start(); return ((val + 1) * 10 / 25) - 68; // // LM61 [-30 - +100], 0 = 600mV, 10mV/C (4 ADC/C) // case SENSOR_LM61: val = adc_value; adc_start(); return (val >> 2) - 60; } break; } } void setup() { // // enable interrupt // sei(); // // init gpio mode // pinMode(GPIO_THERMO_SW , OUTPUT); pinMode(GPIO_BOARD_LED , OUTPUT); pinMode(GPIO_SENSOR_PIN, INPUT_PULLUP); pinMode(GPIO_DW_BTN , INPUT_PULLUP); pinMode(GPIO_UP_BTN , INPUT_PULLUP); // // init gpio edge state // pin_edge.init(GPIO_UP_BTN, GPIO_UP_BTN); pin_edge.init(GPIO_DW_BTN, GPIO_DW_BTN); // // power-up button control // digitalWrite(GPIO_BOARD_LED, HIGH); setting_start = (uint16_t)millis(); do { pin_edge.detect(GPIO_UP_BTN); pin_edge.detect(GPIO_DW_BTN); } while ((uint16_t)millis() - setting_start < OPTIONS_SETTING_TIME); digitalWrite(GPIO_BOARD_LED, LOW); // // reset eeprom values (press DW and UP button to reset) // if (!pin_edge.state(GPIO_UP_BTN) && !pin_edge.state(GPIO_DW_BTN)) { eeprom_update_block(&options, 0, sizeof(options)); for (uint8_t i = 0; i < 10; ++i) { digitalWrite(GPIO_BOARD_LED, HIGH); delay(50); digitalWrite(GPIO_BOARD_LED, LOW); delay(50); } } else { // // init eeprom values // if (eeprom_read_byte(OFFSETOF(options_t, size)) != options.size) { eeprom_update_block(&options, 0, sizeof(options)); eeprom_busy_wait(); } // // read eeprom values // else eeprom_read_block(&options, 0, sizeof(options)); // // setting output mode // if (!pin_edge.state(GPIO_UP_BTN)) { setting_disp = OPTIONS_SETTING_DISP; eeprom_update_byte(OFFSETOF(options_t, switch_1b), setting_value = (options.switch_1b ^= 1)); } // // setting display mode // else if (!pin_edge.state(GPIO_DW_BTN)) { setting_disp = OPTIONS_SETTING_DISP; eeprom_update_byte(OFFSETOF(options_t, disp_mode), setting_value = (options.disp_mode ^= 1)); } } // // init gpio edge state // pin_edge.init(GPIO_UP_BTN, GPIO_UP_BTN, HIGH); pin_edge.init(GPIO_DW_BTN, GPIO_DW_BTN, HIGH); // // init LMT01 pulse counter // t0_init(); // // wait for LMT01 pulse output interval // delay(100); // // LMT01 // if (t0_read() >= 15) { sensor_type = SENSOR_LMT01; // // setup timer interrupt for LMT01 pulse counter // timer1_init(); // // wait for LMT01 pulse output interval // delay(100); } #if ADCX != ADC4 // // LM35/60/61 // else if (analogRead(ADCX) < (4 * 1024 / 5)) { sensor_type = SENSOR_LMXX; adc_init(ADCX, ADC_REFS_256); } #endif // // none Sensors. use MCU Internal Temperature Sensor // else { sensor_type = SENSOR_MCU; adc_init(ADC4, ADC_REFS_110); } // // init sensor pin mode // if (sensor_type != SENSOR_LMT01) { pinMode(GPIO_SENSOR_PIN, INPUT); digitalWrite(GPIO_SENSOR_PIN, LOW); // for Digispark BUG ? } // // start delay // delay(1000); // // read sensor data // temperature.add(read_temperature()); // // init timer's // sensor_read = thermo_ctrl = (uint16_t)millis(); thermo_ctrl -= THERMO_CTRL_INTERVAL; // // display start // blink.start(); morse.start(); // // wdt enable // wdt_enable(WDTO_1S); } void loop() { uint16_t now = (uint16_t)millis(); // // read sensor // if (now - sensor_read >= SENSOR_READ_INTERVAL) { sensor_read += SENSOR_READ_INTERVAL; temperature.add(read_temperature()); } // // temperature switch control // if (now - thermo_ctrl >= THERMO_CTRL_INTERVAL) { thermo_ctrl += THERMO_CTRL_INTERVAL; digitalWrite(GPIO_THERMO_SW, temperature.average() + options.calibrate[sensor_type] < options.threshold ? options.switch_1b : !options.switch_1b); } // // -- threshold (short time press) // switch (pin_edge.detect(GPIO_DW_BTN)) { case LOW: setting_start = now; setting_disp = 0; break; case HIGH: if (!setting_disp) { setting_disp = OPTIONS_SETTING_DISP; if (options.threshold > THRESHOLD_MIN) --options.threshold; eeprom_update_byte(OFFSETOF(options_t, threshold), setting_value = options.threshold); } break; } // // ++ threshold (short time press) // switch (pin_edge.detect(GPIO_UP_BTN)) { case LOW: setting_start = now; setting_disp = 0; break; case HIGH: if (!setting_disp) { setting_disp = OPTIONS_SETTING_DISP; if (options.threshold < THRESHOLD_MAX) ++options.threshold; eeprom_update_byte(OFFSETOF(options_t, threshold), setting_value = options.threshold); } break; } // // option settings // if ((pin_edge.state(GPIO_DW_BTN) == LOW) || (pin_edge.state(GPIO_UP_BTN) == LOW)) { uint16_t t = now - setting_start; // // display stop // blink.stop(); morse.stop(); // // sensor type setting (simultaneously press DW and UP button) // if ((pin_edge.state(GPIO_DW_BTN) == LOW) && (pin_edge.state(GPIO_UP_BTN) == LOW)) setting_mode = SETTING_SENSOR; // // option setting notify blink // digitalWrite(GPIO_BOARD_LED, (t >= OPTIONS_SETTING_TIME - 100) && (t < OPTIONS_SETTING_TIME)); // // option settings // if (t >= OPTIONS_SETTING_TIME) { setting_start += OPTIONS_SETTING_TIME; // // change of sensor type // if (setting_mode == SETTING_SENSOR) { setting_disp = OPTIONS_SETTING_DISP; if (++options.sensor_lm > SENSOR_MAX) options.sensor_lm = 0; eeprom_update_byte(OFFSETOF(options_t, sensor_lm), setting_value = options.sensor_lm); } // // -- calibrate (long time press) // else if (!pin_edge.state(GPIO_DW_BTN)) { setting_disp = OPTIONS_SETTING_DISP; if (options.calibrate[sensor_type] > CALIBRATE_MIN) --options.calibrate[sensor_type]; eeprom_update_byte(OFFSETOF(options_t, calibrate[sensor_type]), setting_value = options.calibrate[sensor_type]); } // // ++ calibrate (long time press) // else if (!pin_edge.state(GPIO_UP_BTN)) { setting_disp = OPTIONS_SETTING_DISP; if (options.calibrate[sensor_type] < CALIBRATE_MAX) ++options.calibrate[sensor_type]; eeprom_update_byte(OFFSETOF(options_t, calibrate[sensor_type]), setting_value = options.calibrate[sensor_type]); } } } else { // // reset setting mode // setting_mode = SETTING_NONE; // // display start // if (setting_disp == OPTIONS_SETTING_DISP) { --setting_disp; blink.start(); morse.start(); } } // // blink code display // if (options.disp_mode == BLINKCODE_MODE) { if (blink.next()) { if (setting_disp) { --setting_disp; blink.printInt(setting_value); } else blink.printInt(temperature.average() + options.calibrate[sensor_type]); } digitalWrite(GPIO_BOARD_LED, blink.handle()); } // // morse code display // else { if (morse.next()) { if (setting_disp) { --setting_disp; morse.printInt<int8_t>(setting_value); } else morse.printInt<int8_t>(temperature.average() + options.calibrate[sensor_type]); } digitalWrite(GPIO_BOARD_LED, morse.handle()); } // // wdt reset // wdt_reset(); } |
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 |
#ifndef _AVERAGE_H #define _AVERAGE_H #include <string.h> // // Simple Moving Average // template <typename V, typename T, typename C, C N> class Average { private: V _data[N]; T _total; C _index; C _count; public: Average() { clear(); } void add(V value) { _total -= _data[_index]; _total += _data[_index] = value; if (++_index >= N) _index = 0; if (_count < N) ++_count; } void clear() { memset(_data, 0, sizeof(_data)); _total = _index = _count = 0; } bool full() { return N == _count; } bool cyclic() { return _index == 0; } C size() { return N; } C count() { return _count; } T total() { return _total; } V average() { return _count ? (_total + (_count >> 1)) / _count : 0; } V first() { if (_count == 0) return 0; C i = _index + N - _count; if (i >= N) i -= N; return _data[i]; } V last() { return _count == 0 ? 0 : _data[(_index > 0 ? _index : N) - 1]; } T range(V *min, V *max) { T rv = 0; if (_count != 0) { C i = (_index > 0 ? _index : N); rv += (*min = *max = _data[--i]); for (uint8_t j = 1; j < _count; ++j) { if (i == 0) i = N; V v = _data[--i]; if (v < *min) *min = v; if (v > *max) *max = v; rv += v; } } return rv; } }; #endif // _AVERAGE_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 |
#ifndef _PINEDGE_H #define _PINEDGE_H #define PIN_UNCHANGED 0xFF template <uint8_t N, uint16_t CHATTERING = 40> class PinEdge { private: uint8_t _pins[N]; uint8_t _flags[(N + 7) >> 3]; uint8_t _state[(N + 7) >> 3]; uint8_t _check[(N + 7) >> 3]; uint16_t _timer; public: PinEdge() { memset(_pins , 0xFF, sizeof(_pins )); memset(_flags, 0x00, sizeof(_flags)); memset(_state, 0x00, sizeof(_state)); memset(_check, 0x00, sizeof(_check)); _timer = 0; } void init(uint8_t id, uint8_t pin, uint8_t inival = PIN_UNCHANGED) { if ((id < N) && (pin != 0xFF)) { uint8_t bit = _BV(id & 7); uint8_t off = id >> 3; _check[off] &= ~bit; if (digitalRead(_pins[id] = pin)) { _flags[off] |= bit; _state[off] |= bit; } else { _flags[off] &= ~bit; _state[off] &= ~bit; } if (inival == HIGH) _state[off] |= bit; else if (inival == LOW) _state[off] &= ~bit; } } uint8_t state(uint8_t id) { if ((id < N) && (_pins[id] != 0xFF)) return _state[id >> 3] & _BV(id & 7) ? HIGH : LOW; return PIN_UNCHANGED; } uint8_t detect(uint8_t id) { if ((id < N) && (_pins[id] != 0xFF)) { uint16_t now = (uint16_t)millis(); uint8_t bit = _BV(id & 7); uint8_t off = id >> 3; if ((_flags[off] ^ (digitalRead(_pins[id]) ? bit : 0)) & bit) { _flags[off] ^= bit; _check[off] |= bit; _timer = now; } else if ((_check[off] & bit) && ((now - _timer) >= CHATTERING)) { _check[off] &= ~bit; if ((_state[off] ^ _flags[off]) & bit) return (_state[off] ^= bit) & bit ? HIGH : LOW; } } return PIN_UNCHANGED; } }; #endif // _PINEDGE_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 |
#ifndef _BLINKCODE_H #define _BLINKCODE_H // // Blink Code Display // template <uint16_t DOT = 220> class BlinkCode { private: uint16_t _start; uint16_t _nspan; uint16_t _elaps; bool _ncall; bool _output; int8_t _state; int8_t _data; public: BlinkCode() { _nspan = 0; start(); } void printInt(int8_t value) { if ((_state == 0) || (_state == 3)) { _state = 0; _data = value; } } void start() { _ncall = false; _output = false; _state = 3; } void stop() { if (_state != -1) { if ((_state != 0) && (_state != 3)) _nspan = DOT * 7; _state = -1; _output = false; } } bool next() { uint16_t now = (uint16_t)millis(); _ncall = true; if ((_elaps = now - _start) >= _nspan) { if (_nspan) _start += _nspan; else _start = now; return (_state == 0) || (_state == 3); } return false; } bool handle() { if (!_ncall) next(); _ncall = false; if (_elaps >= _nspan) { if (!_output) { switch (_state) { case 0: if (_data <= 0) { _output = true; _state = 2; _data = -_data; _nspan = DOT >> 2; break; } case 1: if (_data < 10) { _data -= 1; _nspan = DOT; } else { _data -= 10; _nspan = DOT * 3; } case 2: _output = true; _state = 1; break; case 3: _nspan = 0; break; } } else { _output = false; if (_state == 1) { if (_data) _nspan = DOT; else { _state = 3; _nspan = DOT * 7; } } } } return _output; } }; #endif // _BLINKCODE_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 |
#ifndef _MORSECODE_H #define _MORSECODE_H #include <string.h> #ifdef __AVR_ARCH__ #include <avr/pgmspace.h> #else #define PROGMEM #define pgm_read_byte_near(p) *((uint8_t *)(p)) #endif static const uint8_t PROGMEM ASCII6[] = { B10000110, B11110010, B00000000, B10001100, B00000001, B00000000, B00000000, B10000000 }; static const uint8_t PROGMEM ASCII[] = { B00000000, B10101100, B01001000, B00000000, B00010010, B00000000, B01000101, B01111000, // 20-27 .!".$.&' B10110101, B10110100, B00000000, B01010101, B11001100, B10000100, B01010100, B10010000, // 28-2F ().+,-./ B11111101, B01111101, B00111101, B00011101, B00001101, B00000101, B10000101, B11000101, // 30-37 01234567 B11100101, B11110101, B11100000, B10101000, B00000000, B10001101, B00000000, B00110000, // 38-3F 89:;.=.? B01101000, B01000010, B10000100, B10100100, B10000011, B00000001, B00100100, B11000011, // 40-47 @ABCDEFG B00000100, B00000010, B01110100, B10100011, B01000100, B11000010, B10000010, B11100011, // 48-4F HIJKLMNO B01100100, B11010100, B01000011, B00000011, B10000001, B10000011, B00010100, B01100011, // 50-57 PQRSTUVW B10010100, B10110100, B11000100, B00000000, B00000000, B00000000, B00000000, B00110100 // 58-5F XYZ...._ }; static const uint8_t PROGMEM KANA6[] = { B00010010, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000 }; static const uint8_t PROGMEM KANA[] = { B00000000, B01010000, B00000000, B00000000, B01010100, B00000000, B01110100, B00000000, // A0-A7 ・。・・、・ヲ・ B01001101, B00000000, B01100101, B00000000, B00000000, B00000000, B00000000, B00000000, // A8-AF ヰ・ヱ・・・・・ B00000000, B11011101, B01000010, B00100011, B01000101, B10111101, B01000100, B10100101, // B0-B7 ・アイウエオカキ B00010100, B10110100, B11110100, B10101101, B11010101, B11101101, B01110101, B11100100, // B8-BF クケコサシスセソ B10000010, B00100100, B01100100, B01011101, B00100101, B01000011, B10100100, B00000100, // C0-C7 タチツテトナニヌ B11010100, B00110100, B10000100, B11001101, B11000100, B00000001, B10000011, B10010100, // C8-CF ネノハヒフヘホマ B00101101, B10000001, B10001101, B10010101, B01100011, B10011101, B11000010, B00000011, // D0-D7 ミムメモヤユヨラ B11000011, B10110101, B11100011, B01010100, B10100011, B01010101, B00000010, B00110101, // D8-DF リルレロワン゛゜ }; template <uint16_t DOT = 80, uint8_t SIZE = 16> class MorseCode { private: uint16_t _start; uint16_t _nspan; uint16_t _elaps; bool _ncall; bool _output; uint8_t _mclen; uint8_t _mcbit; uint8_t _index; uint8_t _count; uint8_t _buffer[SIZE]; protected: bool ismorsechar(uint8_t c) { if ((c >= 0x61) && (c <= 0x7a)) c -= 0x20; if ((c >= 0x20) && (c <= 0x5F)) { c -= 0x20; _mcbit = pgm_read_byte_near(ASCII + c); if (c == ('$' - 0x20)) _mclen = 7; else if (pgm_read_byte_near(ASCII6 + (c >> 3)) & (1 << (c & 0x07))) _mclen = 6; else _mclen = _mcbit & 0x07; return _mclen != 0; } else if ((c >= 0xA0) && (c <= 0xDF)) { c -= 0xA0; _mcbit = pgm_read_byte_near(KANA + c); if (pgm_read_byte_near(KANA6 + (c >> 3)) & (1 << (c & 0x07))) _mclen = 6; else _mclen = _mcbit & 0x07; return _mclen != 0; } return false; } uint8_t code(bool space) { bool brk = false; while (_index < _count) { uint8_t c = _buffer[_index++]; if (c == ' ') brk = space; else if (ismorsechar(c)) { if (brk) { --_index; _mclen = 0; return 2; } return 1; } } return _index = _count = 0; } void signal() { _output = true; _nspan = (_mcbit & 0x80 ? DOT * 3 : DOT); _mcbit <<= 1; _mclen--; } public: MorseCode() { _nspan = 0; start(); } void printStr(char *str) { if (_index == 0) { if ((_count = strlen(str)) > SIZE) _count = SIZE; memcpy(_buffer, str, _count); } } template <typename T> void printInt(T value) { if (_index == 0) { uint8_t i = (value < 0); uint8_t j = 0; if (i) { value = -value; _buffer[j++] = '-'; } do _buffer[j++] = (uint8_t)((value % 10) | '0'); while (value /= 10); _count = j--; while (i < j) { uint8_t x = _buffer[i]; _buffer[i++] = _buffer[j]; _buffer[j--] = x; } } } void start() { _ncall = false; _output = false; _mclen = 0; _index = 0; _count = 0; } void stop() { if (_index != (uint8_t)-1) { if (_index) _nspan = DOT * 7; _index = (uint8_t)-1; _output = false; } } bool next() { uint16_t now = (uint16_t)millis(); _ncall = true; if ((_elaps = now - _start) >= _nspan) { if (_nspan) _start += _nspan; else _start = now; return _index == 0; } return false; } bool handle() { if (!_ncall) next(); _ncall = false; if (_elaps >= _nspan) { if (!_output) { _nspan = 0; if (_index != (uint8_t)-1) { if (_mclen) signal(); else if (code(false)) signal(); } } else { _output = false; if (_mclen) _nspan = DOT; // continue else if (code(true) == 1) _nspan = DOT * 3; // next code else _nspan = DOT * 7; // end of word } } return _output; } }; #endif // _MORSECODE_H |