From d7ec186504abb8fe3365a0495480c01881ca57ff Mon Sep 17 00:00:00 2001 From: StefanStrobel <> Date: Sat, 13 Mar 2021 10:03:34 +0000 Subject: [PATCH] 98_ArduCounter.pm: remove old firmware git-svn-id: https://svn.fhem.de/fhem/trunk@23939 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/arduino/ArduCounter3.00.ino | 1720 --------------------- fhem/contrib/arduino/ArduCounter3.20.ino | 1728 ---------------------- 2 files changed, 3448 deletions(-) delete mode 100755 fhem/contrib/arduino/ArduCounter3.00.ino delete mode 100755 fhem/contrib/arduino/ArduCounter3.20.ino diff --git a/fhem/contrib/arduino/ArduCounter3.00.ino b/fhem/contrib/arduino/ArduCounter3.00.ino deleted file mode 100755 index 6c2617c1c..000000000 --- a/fhem/contrib/arduino/ArduCounter3.00.ino +++ /dev/null @@ -1,1720 +0,0 @@ -/* - * Sketch for counting impulses in a defined interval - * e.g. for power meters with an s0 interface that can be - * connected to an input of an arduino or esp8266 board - * - * the sketch uses pin change interrupts which can be anabled - * for any of the inputs on e.g. an arduino uno, jeenode, wemos d1 etc. - * - * the pin change Interrupt handling for arduinos used here - * is based on the arduino playground example on PCINT: - * http://playground.arduino.cc/Main/PcInt which is outdated. - * - * see https://github.com/GreyGnome/EnableInterrupt for a newer library (not used here) - * and also - * https://playground.arduino.cc/Main/PinChangeInterrupt - * http://www.avrfreaks.net/forum/difference-between-signal-and-isr - * - * Refer to avr-gcc header files, arduino source and atmega datasheet. - */ - -/* Arduino Uno / Nano Pin to interrupt map: - * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2 - * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0 - * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1 - */ - -/* test cmds analog ESP: - * 20v - * 17,3,0,50a - * 15,25t - * - * for ESP with D5 falling pullup 30 - * 5,2,1,30a - * 20v - * 10,20,1,1i - */ - -/* - Changes: - V1.2 - 27.10.16 - use noInterrupts in report() - - avoid reporting very short timeDiff in case of very slow impulses after a report - - now reporting is delayed if impulses happened only within in intervalSml - - reporting is also delayed if less than countMin pulses counted - - extend command "int" for optional intervalSml and countMin - 29.10.16 - allow interval Min >= Max or Sml > Min - which changes behavior to take fixed calculation interval instead of timeDiff between pulses - -> if intervalMin = intervalMax, counting will allways follow the reporting interval - 3.11.16 - more noInterrupt blocks when accessing the non uint8_t volatiles in report - V1.3 - 4.11.16 - check min pulse width and add more output, - - prefix show output with M - V1.4 - 10.11.16 - restructure add Cmd - - change syntax for specifying minPulseLengh - - res (reset) command - V1.6 - 13.12.16 - new startup message logic?, newline before first communication? - 18.12.16 - replace all code containing Strings, new communication syntax and parsing from Jeelink code - V1.7 - 2.1.17 - change message syntax again, report time as well, first and last impulse are reported - relative to start of intervall not start of reporting intervall - V1.8 - 4.1.17 - fixed a missing break in the case statement for pin definition - 5.1.17 - cleanup debug logging - 14.10.17 - fix a bug where last port state was not initialized after interrupt attached but this is necessary there - 23.11.17 - beautify code, add comments, more debugging for users with problematic pulse creation devices - 28.12.17 - better reportung of first pulse (even if only one pulse and countdiff is 0 but realdiff is 1) - 30.12.17 - rewrite PCInt, new handling of min pulse length, pulse history ring - 1.1.18 - check len in add command, allow pin 8 and 13 - 2.1.18 - add history per pin to report line, show negative starting times in show history - 3.1.18 - little reporting fix (start pos of history report) - - V2.0 - 17.1.18 - rewrite many things - use pin number instead of pcIntPinNumber as index, split interrupt handler for easier porting to ESP8266, ... - V2.23 - 10.2.18 - new commands for check alive and quit, send setup message after reboot also over tcp - remove reporting time of first pulse (now we hava history) - remove pcIntMode (is always change now) - pulse min interval is now always checked and defaults to 2 if not set - march 2018 many changes more to support ESP8266 - 7.3.18 - change pin config output, fix pullup (V2.26), store config in eeprom and read it back after boot - 22.4.18 - many changes, delay report if tcp mode and disconnected, verbose levels, ... - 13.5.18 - V2.36 Keepalive also on Arduino side - 9.12.18 - V3.0 start implementing analog input for old ferraris counters - 6.1.19 - V3.1 showIntervals in hello - 19.1.19 - V3.12 support for ESP with analog - 24.2.19 - V3.13 fix internal pin to GPIO mapping (must match ISR functions) when ESP8266 and analog support - - V3.14 added return of devVerbose upon startup - - ToDo / Ideas: - - -*/ - -/* allow printing of every pin change to Serial */ -#define debugPins 1 - -/* allow tracking of pulse lengths */ -#define pulseHistory 1 - -/* support analog input for ferraris counters with IR light hardware */ -#define analogIR 1 - -/* use a sample config at boot */ -// #define debugCfg 1 - -#include "pins_arduino.h" -#include - -const char versionStr[] PROGMEM = "ArduCounter V3.14"; -const char compile_date[] PROGMEM = __DATE__ " " __TIME__; -const char errorStr[] PROGMEM = "Error: "; - -#ifdef ARDUINO_BOARD -const char boardName1[] PROGMEM = ARDUINO_BOARD; -#endif - -#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) -#ifdef ARDUINO_AVR_NANO -const char boardName[] PROGMEM = "NANO"; -#else -const char boardName[] PROGMEM = "UNO"; -#endif -#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) -const char boardName[] PROGMEM = "Leonardo"; -#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -const char boardName[] PROGMEM = "Mega"; -#elif defined(ESP8266) -const char boardName[] PROGMEM = "ESP8266"; -#else -const char boardName[] PROGMEM = "UNKNOWN"; -#endif - -#define SERIAL_SPEED 38400 -#define MAX_INPUT_NUM 8 - -#ifdef analogIR -int sensorValueOff = 0; // value read from the photo transistor when ir LED is off -int sensorValueOn = 0; // value read from the photo transistor when ir LED is on -int analogThresholdMin = 100; // min value of analog input -int analogThresholdMax = 110; // max value of analog input - -uint8_t triggerState; // todo: use existing arrays instead - -// save measurement during same level as sum and count to get average and then put in history when doCount is called -// but how do we do this before we can detect the levels? - -#endif - -#ifdef ESP8266 // ESP variables and definitions -#include // ============================= - -const char* ssid = "MySSID"; -const char* password = "secret"; - -WiFiServer Server(80); // For ESP WiFi connection -WiFiClient Client1; // active TCP connection -WiFiClient Client2; // secound TCP connection to send reject message -boolean Client1Connected; // remember state of TCP connection -boolean Client2Connected; // remember state of TCP connection - -boolean tcpMode = false; -uint8_t delayedTcpReports = 0; // how often did we already delay reporting because tcp disconnected -uint32_t lastDelayedTcpReports = 0; // last time we delayed - -#define MAX_HIST 20 // 20 history entries for ESP boards (can be increased) - -#ifdef analogIR // code for ESP with analog pin and reflection light barrier support (test) - -#define MAX_APIN 18 -#define MAX_PIN 9 - -/* ESP8266 pins that are typically ok to use - * (some might be set to -1 (disallowed) because they are used - * as reset, serial, led or other things on most boards) - * maps printed pin numbers to sketch internal index numbers */ -short allowedPins[MAX_APIN] = - { 0, 1, 2, -1, // printed pin numbers 0,1,2 are ok to be used - -1, 5, -1, -1, // printed pin number 5 is ok to be used - -1, -1, -1, -1, // 8-11 not avaliable - -1, -1, -1, -1, // 12-15 not avaliable - -1, 8 }; // 16 not available, 17 is analog - -/* Wemos / NodeMCU Pins 3,4 and 8 (GPIO 0,2 and 15) define boot mode and therefore - * can not be used to connect to signal */ - -/* Map from sketch internal pin index to real chip IO pin number (not aPin, e.g. for ESP) - Note that the internal numbers might be different from the printed - pin numbers (e.g. pin 0 is in index 0 but real chip pin number 16! */ -short internalPins[MAX_PIN] = - { D0, D1, D2, D3, // map from internal pin Index to - D4, D5, D6, D7, // real GPIO pin numbers / defines - A0 }; // D0=16, D1=5, D2=4, D5=14, A0=17 - - - -uint8_t analogPins[MAX_PIN] = - { 0,0,0,0,1 }; // internal index 4 is analog - -const int analogInPin = A0; // Analog input pin that the photo transistor is attached to (internally number 17) -const int irOutPin = D6; // Digital output pin that the IR-LED is attached to -const int ledOutPin = D7; // Signal LED output pin - -#else // code for ESP without analog pin and reflection light barrier support (test) - - -#define MAX_APIN 8 -#define MAX_PIN 8 - -/* ESP8266 pins that are typically ok to use - * (some might be set to -1 (disallowed) because they are used - * as reset, serial, led or other things on most boards) - * maps printed pin numbers to sketch internal index numbers */ -short allowedPins[MAX_APIN] = - { 0, 1, 2, -1, // printed pin numbers 0,1,2 are ok to be used, 3 not - -1, 5, 6, 7}; // printed pin numbers 5-7 are ok to be used, 4 not, >8 not - -/* Wemos / NodeMCU Pins 3,4 and 8 (GPIO 0,2 and 15) define boot mode and therefore - * can not be used to connect to signal */ - -/* Map from sketch internal pin index to real chip IO pin number (not aPin, e.g. for ESP) - Note that the internal numbers might be different from the printed - pin numbers (e.g. pin 0 is in index 0 but real chip pin number 16! */ -short internalPins[MAX_PIN] = - { D0, D1, D2, D3, // printed pin numbers 0, 1, 2, 3 (3 should not be used and could be removed here) - D5, D5, D6, D7}; // printed pin numbers 4, 5, 6, 7 (4 should not be used and could be removed here) - // D0=16, D1=5, D2=4, D5=14, A0=17, ... - -#endif // end of ESP section without analog reading - - - - -#else // Arduino Uno or Nano variables and definitions - // ============================================= - -#define MAX_HIST 20 // 20 history entries for arduino boards - -/* arduino pins that are typically ok to use - * (some might be set to -1 (disallowed) because they are used - * as reset, serial, led or other things on most boards) - * maps printed pin numbers to sketch internal index numbers */ - -#ifdef analogIR - -/* 2 is used for IR out, 12 for signal, A7 for In */ -#define MAX_APIN 22 -#define MAX_PIN 18 - -short allowedPins[MAX_APIN] = - {-1, -1, -1, 0, /* arduino pin 0 - 3 to internal index or -1 if pin is reserved */ - 1, 2, 3, 4, /* arduino pin 4 - 7 to internal index */ - 5, 6, 7, 8, /* arduino pin 8 - 11 to internal index */ - -1, 9, 10, 11, /* arduino pin 12, 13, A0, A1 / 14, 15 to internal index or -1 if pin is reserved*/ - 12, 13, 14, 15, /* arduino pin A2 - A5 / 16 - 19 to internal index */ - 16, 17 }; /* arduino pin A6, A7 to internal index */ - -/* Map from sketch internal pin index to real chip IO pin number */ -short internalPins[MAX_PIN] = - { 3, 4, 5, 6, /* index 0 - 3 map to pins 3 - 6 */ - 7, 8, 9, 10, /* index 4 - 7 map to pins 7 - 10 */ - 11, 12, 14, 15, /* index 8 - 11 map to pins 11,13, A0 - A1 */ - 16, 17, 18, 19, /* index 12 - 15 map to pins A2 - A5 */ - 20, 21 }; /* index 16 - 17 map to pin A6, A7 */ - -uint8_t analogPins[MAX_PIN] = - { 0,0,0,0, /* everything except A7 is digital by default */ - 0,0,0,0, - 0,0,0,0, - 0,0,0,0, - 0,1 }; - -const int analogInPin = A7; // Analog input pin that the photo transistor is attached to -const int irOutPin = 2; // Digital output pin that the IR-LED is attached to -const int ledOutPin = 12; // Signal LED output pin - - -#else -/* no analog IR support -> all Nano pins including analog available für digital counting */ - -#define MAX_APIN 22 -#define MAX_PIN 20 -short allowedPins[MAX_APIN] = - {-1, -1, 0, 1, /* arduino pin 0 - 3 to internal Pin index or -1 if pin is reserved */ - 2, 3, 4, 5, /* arduino pin 4 - 7 to internal Pin index or -1 if pin is reserved */ - 6, 7, 8, 9, /* arduino pin 8 - 11 to internal Pin index or -1 if pin is reserved */ - 10, 11, 12, 13, /* arduino pin 12, 13, A0, A1 to internal Pin index or -1 if pin is reserved */ - 14, 15, 16, 17, /* arduino pin A2 - A5 / 16 - 19 to internal Pin index or -1 if pin is reserved */ - 18, 19 }; /* arduino pin A6, A7 to internal Pin index or -1 if pin is reserved */ - -/* Map from sketch internal pin index to real chip IO pin number */ -short internalPins[MAX_PIN] = - { 2, 3, 4, 5, /* index 0 - 3 map to pins 2 - 5 */ - 6, 7, 8, 9, /* index 4 - 7 map to pins 6 - 9 */ - 10, 11, 12, 13, /* index 8 - 11 map to pins 10 - 13 */ - 14, 15, 16, 17, /* index 12 - 15 map to pins A0 - A3 */ - 18, 19, 20, 21 }; /* index 16 - 19 map to pins A4 - A7 */ - -uint8_t analogPins[MAX_PIN] = - { 0,0,0,0, /* everything is digital by default */ - 0,0,0,0, - 0,0,0,0, - 0,0,0,0, - 0,0,0,0 }; - -#endif - -/* first and last pin at port PB, PC and PD for arduino uno/nano */ -uint8_t firstPin[] = {8, 14, 0}; // aPin -> allowedPins[] -> pinIndex -uint8_t lastPin[] = {13, 19, 7}; - -/* Pin change mask for each chip port on the arduino platform */ -volatile uint8_t *port_to_pcmask[] = { - &PCMSK0, - &PCMSK1, - &PCMSK2 -}; - -/* last PIN States at io port to detect individual pin changes in arduino ISR */ -volatile static uint8_t PCintLast[3]; - -#endif - - -Print *Output; // Pointer to output device (Serial / TCP connection with ESP8266) -uint32_t bootTime; -uint16_t bootWraps; // counter for millis wraps at last reset -uint16_t millisWraps; // counter to track when millis counter wraps -uint32_t lastMillis; // milis at last main loop iteration -uint8_t devVerbose; // >=10 shows pin changes, >=5 shows pin history - -#ifdef debugPins -uint8_t lastState[MAX_PIN]; // for debug output when a pin state changes -#endif - -uint32_t intervalMin = 30000; // default 30 sec - report after this time if nothing else delays it -uint32_t intervalMax = 60000; // default 60 sec - report after this time if it didin't happen before -uint32_t intervalSml = 2000; // default 2 secs - continue count if timeDiff is less and intervalMax not over -uint16_t countMin = 2; // continue counting if count is less than this and intervalMax not over - -uint32_t timeNextReport; -#ifdef ESP8266 -uint32_t expectK; -#endif - -/* index to the following arrays is the internal pin index number */ - -volatile boolean initialized[MAX_PIN]; // did we get first interrupt yet? -short activePin[MAX_PIN]; // printed arduino pin number for index if active - otherwise -1 -uint16_t pulseWidthMin[MAX_PIN]; // minimal pulse length in millis for filtering -uint8_t pulseLevel[MAX_PIN]; // start of pulse for measuring length - 0 / 1 as defined for each pin -uint8_t pullup[MAX_PIN]; // pullup configuration state - -volatile uint32_t counter[MAX_PIN]; // real pulse counter -volatile uint8_t counterIgn[MAX_PIN]; // ignored first pulse after init -volatile uint16_t rejectCounter[MAX_PIN]; // counter for rejected pulses that are shorter than pulseWidthMin -uint32_t lastCount[MAX_PIN]; // counter at last report (to get the delta count) -uint16_t lastRejCount[MAX_PIN]; // reject counter at last report (to get the delta count) - -volatile uint32_t lastChange[MAX_PIN]; // millis at last level change (for measuring pulse length) -volatile uint8_t lastLevel[MAX_PIN]; // level of input at last interrupt -volatile uint8_t lastLongLevel[MAX_PIN]; // last level that was longer than pulseWidthMin - -volatile uint32_t pulseWidthSum[MAX_PIN]; // sum of pulse lengths for average calculation -uint8_t reportSequence[MAX_PIN]; // sequence number for reports - - -#ifdef pulseHistory -volatile uint8_t histIndex; // pointer to next entry in history ring -volatile uint16_t histNextSeq; // next seq number to use -volatile uint16_t histSeq[MAX_HIST]; // history sequence number -volatile uint8_t histPin[MAX_HIST]; // pin for this entry -volatile uint8_t histLevel[MAX_HIST]; // level for this entry -volatile uint32_t histTime[MAX_HIST]; // time for this entry -volatile uint32_t histLen[MAX_HIST]; // time that this level was held -volatile char histAct[MAX_HIST]; // action (count, reject, ...) as one char -#endif - -volatile uint32_t intervalStart[MAX_PIN]; // start of an interval - typically set by first / last pulse -volatile uint32_t intervalEnd[MAX_PIN]; // end of an interval - typically set by first / last pulse -uint32_t lastReport[MAX_PIN]; // millis at last report to find out when maxInterval is over - -uint16_t commandData[MAX_INPUT_NUM]; // input data over serial port or network -uint8_t commandDataPointer = 0; // index pointer to next input value -uint16_t value; // the current value for input function - - -/* - do counting and set start / end time of interval. - reporting is not triggered from here. - - only here counter[] is modified - intervalEnd[] is set here and in report - intervalStart[] is set in case a pin was not initialized yet and in report -*/ -static void inline doCount(uint8_t pinIndex, uint8_t level, uint32_t now) { - uint32_t len = now - lastChange[pinIndex]; - char act = ' '; - -#ifdef pulseHistory - histIndex++; - if (histIndex >= MAX_HIST) histIndex = 0; - histSeq[histIndex] = histNextSeq++; - histPin[histIndex] = pinIndex; - histTime[histIndex] = lastChange[pinIndex]; - histLen[histIndex] = len; - histLevel[histIndex] = lastLevel[pinIndex]; -#endif - if (len < pulseWidthMin[pinIndex]) { // pulse was too short - lastChange[pinIndex] = now; - if (lastLevel[pinIndex] == pulseLevel[pinIndex]) { // if change to gap level - rejectCounter[pinIndex]++; // inc reject counter and set action to R (pulse too short) - act = 'R'; - } else { - act = 'X'; // set action to X (gap too short) - } - } else { - if (lastLevel[pinIndex] != pulseLevel[pinIndex]) { // edge does fit defined pulse start, level is now pulse, before it was gap - act = 'G'; // now the gap is confirmed (even if inbetween was a spike that we ignored) - } else { // edge is a change to gap, level is now gap - if (lastLongLevel[pinIndex] != pulseLevel[pinIndex]) { // last remembered valid level was also gap -> now we had valid new pulse -> count - counter[pinIndex]++; // count - intervalEnd[pinIndex] = now; // remember time of in case pulse will be the last in the interval - if (!initialized[pinIndex]) { - intervalStart[pinIndex] = now; // if this is the very first impulse on this pin -> start interval now - initialized[pinIndex] = true; // and start counting the next impulse (so far counter is 0) - counterIgn[pinIndex]++; // count as to be ignored for diff because it defines the start of the interval - } - pulseWidthSum[pinIndex] += len; // for average calculation - act = 'C'; - } else { // last remembered valid level was a pulse -> now we had another valid pulse - pulseWidthSum[pinIndex] += len; // for average calculation - act = 'P'; // pulse was already counted, only short drop inbetween - } - } - lastLongLevel[pinIndex] = lastLevel[pinIndex]; // remember this valid level as lastLongLevel - } -#ifdef pulseHistory - histAct[histIndex] = act; -#endif - lastChange[pinIndex] = now; - lastLevel[pinIndex] = level; -} - - -/* Interrupt handlers and their installation - * on Arduino and ESP8266 platforms - */ - -#ifndef ESP8266 -/* Add a pin to be handled (Arduino code) */ -uint8_t AddPinChangeInterrupt(uint8_t rPin) { - volatile uint8_t *pcmask; // pointer to PCMSK0 or 1 or 2 depending on the port corresponding to the pin - uint8_t bitM = digitalPinToBitMask(rPin); // mask to bit in PCMSK to enable pin change interrupt for this arduino pin - uint8_t port = digitalPinToPort(rPin); // port that this arduno pin belongs to for enabling interrupts - if (port == NOT_A_PORT) - return 0; - port -= 2; // from port (PB, PC, PD) to index in our array - pcmask = port_to_pcmask[port]; // point to PCMSK0 or 1 or 2 depending on the port corresponding to the pin - *pcmask |= bitM; // set the pin change interrupt mask through a pointer to PCMSK0 or 1 or 2 - PCICR |= 0x01 << port; // enable the interrupt - return 1; -} - - -/* Remove a pin to be handled (Arduino code) */ -uint8_t RemovePinChangeInterrupt(uint8_t rPin) { - volatile uint8_t *pcmask; - uint8_t bitM = digitalPinToBitMask(rPin); - uint8_t port = digitalPinToPort(rPin); - if (port == NOT_A_PORT) - return 0; - port -= 2; // from port (PB, PC, PD) to index in our array - pcmask = port_to_pcmask[port]; - *pcmask &= ~bitM; // clear the bit in the mask. - if (*pcmask == 0) { // if that's the last one, disable the interrupt. - PCICR &= ~(0x01 << port); - } - return 1; -} - - -// now set the arduino interrupt service routines and call the common handler with the port index number -ISR(PCINT0_vect) { - PCint(0); -} -ISR(PCINT1_vect) { - PCint(1); -} -ISR(PCINT2_vect) { - PCint(2); -} - -/* - common function for arduino pin change interrupt handlers. "port" is the PCINT port index (0-2) as passed from above, not PB, PC or PD which are mapped to 2-4 -*/ -static void PCint(uint8_t port) { - uint8_t bit; - uint8_t curr; - uint8_t delta; - short pinIndex; - uint32_t now = millis(); - - // get the pin states for the indicated port. - curr = *portInputRegister(port+2); // current pin states at port (add 2 to get from index to PB, PC or PD) - delta = (curr ^ PCintLast[port]) & *port_to_pcmask[port]; // xor gets bits that are different and & screens out non pcint pins - PCintLast[port] = curr; // store new pin state for next interrupt - - if (delta == 0) return; // no handled pin changed - - bit = 0x01; // start mit rightmost (least significant) bit in a port - for (uint8_t aPin = firstPin[port]; aPin <= lastPin[port]; aPin++) { // loop over each pin on the given port that changed - if (delta & bit) { // did this pin change? - pinIndex = allowedPins[aPin]; - if (pinIndex > 0) { // shound not be necessary but test anyway - doCount (pinIndex, ((curr & bit) > 0), now); // do the counting, history and so on - } - } - bit = bit << 1; // shift mask to go to next bit - } -} - - -#else -/* Add a pin to be handled (ESP8266 code) */ - -/* attachInterrupt needs to be given an individual function for each interrrupt . - * since we cant pass the pin value into the ISR or we need to use an - * internal function __attachInnterruptArg ... but then we need a fixed reference for the pin numbers ... -*/ -uint8_t AddPinChangeInterrupt(uint8_t rPin) { - switch(rPin) { - case 4: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR4, CHANGE); - break; - case 5: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR5, CHANGE); - break; - case 12: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR12, CHANGE); - break; - case 13: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR13, CHANGE); - break; - case 14: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR14, CHANGE); - break; - case 16: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR16, CHANGE); - break; - default: - PrintErrorMsg(); Output->println(F("attachInterrupt")); - } - return 1; -} - -void ESPISR4() { // ISR for real pin GPIO 4 / pinIndex 2 - doCount(2, digitalRead(4), millis()); - // called with pinIndex, level, now -} - -void ESPISR5() { // ISR for real pin GPIO 5 / pinIndex 1 - doCount(1, digitalRead(5), millis()); -} - -void ESPISR12() { // ISR for real pin GPIO 12 / pinIndex 6 - doCount(6, digitalRead(12), millis()); -} - -void ESPISR13() { // ISR for real pin GPIO 13 / pinIndex 7 - doCount(7, digitalRead(13), millis()); -} - -void ESPISR14() {// ISR for real pin GPIO 14 / pinIndex 5 - doCount(5, digitalRead(14), millis()); -} - -void ESPISR16() { // ISR for real pin GPIO 16 / pinIndex 0 - doCount(0, digitalRead(16), millis()); -} -#endif - - -void PrintErrorMsg() { - uint8_t len = strlen_P(errorStr); - char myChar; - for (unsigned char k = 0; k < len; k++) { - myChar = pgm_read_byte_near(errorStr + k); - Output->print(myChar); - } -} - - -void printVersionMsg() { - uint8_t len = strlen_P(versionStr); - char myChar; - for (unsigned char k = 0; k < len; k++) { - myChar = pgm_read_byte_near(versionStr + k); - Output->print(myChar); - } - Output->print(F(" on ")); - len = strlen_P(boardName); - for (unsigned char k = 0; k < len; k++) { - myChar = pgm_read_byte_near(boardName + k); - Output->print(myChar); - } - -#ifdef ARDUINO_BOARD - Output->print(F(" ")); - len = strlen_P(boardName1); - for (unsigned char k = 0; k < len; k++) { - myChar = pgm_read_byte_near(boardName1 + k); - Output->print(myChar); - } -#endif - - Output->print(F(" compiled ")); - len = strlen_P(compile_date); - for (unsigned char k = 0; k < len; k++) { - myChar = pgm_read_byte_near(compile_date + k); - Output->print(myChar); - } -} - - -void showIntervals() { - Output->print(F("I")); - Output->print(intervalMin / 1000); - Output->print(F(" ")); - Output->print(intervalMax / 1000); - Output->print(F(" ")); - Output->print(intervalSml / 1000); - Output->print(F(" ")); - Output->println(countMin); -} - - -#ifdef analogIR -void showThresholds() { - Output->print(F("T")); - Output->print(analogThresholdMin); - Output->print(F(" ")); - Output->println(analogThresholdMax); -} -#endif - - -void showPinConfig(short pinIndex) { - Output->print(F("P")); - Output->print(activePin[pinIndex]); - switch (pulseLevel[pinIndex]) { - case 1: Output->print(F(" rising")); break; - case 0: Output->print(F(" falling")); break; - default: Output->print(F(" -")); break; - } - if (pullup[pinIndex]) - Output->print(F(" pullup")); - Output->print(F(" min ")); - Output->print(pulseWidthMin[pinIndex]); -} - -#ifdef pulseHistory -void showPinHistory(short pinIndex, uint32_t now) { - uint8_t hi; - uint8_t start = (histIndex + 2) % MAX_HIST; - uint8_t count = 0; - uint32_t last; - boolean first = true; - - for (uint8_t i = 0; i < MAX_HIST; i++) { - hi = (start + i) % MAX_HIST; - if (histPin[hi] == pinIndex) - if (first || (last <= histTime[hi]+histLen[hi])) count++; - } - if (!count) return; - - Output->print (F("H")); // start with H - Output->print (activePin[pinIndex]); // printed pin number - Output->print (F(" ")); - for (uint8_t i = 0; i < MAX_HIST; i++) { - hi = (start + i) % MAX_HIST; - if (histPin[hi] == pinIndex) { - if (first || (last <= histTime[hi]+histLen[hi])) { - if (!first) Output->print (F(", ")); - Output->print (histSeq[hi]); // sequence - Output->print (F("s")); - Output->print ((long) (histTime[hi] - now)); // time when level started - Output->print (F("/")); - Output->print (histLen[hi]); // length - Output->print (F("@")); - Output->print (histLevel[hi]); // level (0/1) - Output->print (histAct[hi]); // action - first = false; - } - last = histTime[hi]; - } - } - Output->println(); -} -#endif - -/* - lastCount[] is only modified here (count at time of last reporting) - intervalEnd[] is modified here and in ISR - disable interrupts in critcal moments to avoid garbage in var - intervalStart[] is modified only here or for very first Interrupt in ISR -*/ -void showPinCounter(short pinIndex, boolean showOnly, uint32_t now) { - uint32_t count, countDiff, realDiff; - uint32_t startT, endT, timeDiff, widthSum; - uint16_t rejCount, rejDiff; - uint8_t countIgn; - - noInterrupts(); // copy counters while they cant be changed in isr - startT = intervalStart[pinIndex]; // start of interval (typically first pulse) - endT = intervalEnd[pinIndex]; // end of interval (last unless not enough) - count = counter[pinIndex]; // get current counter (counts all pulses - rejCount = rejectCounter[pinIndex]; - countIgn = counterIgn[pinIndex]; // pulses that mark the beginning of an interval - widthSum = pulseWidthSum[pinIndex]; - interrupts(); - - timeDiff = endT - startT; // time between first and last impulse - realDiff = count - lastCount[pinIndex]; // pulses during intervall - countDiff = realDiff - countIgn; // ignore forst pulse after device restart - rejDiff = rejCount - lastRejCount[pinIndex]; - - if (!showOnly) { // real reporting sets the interval borders new - if((long)(now - (lastReport[pinIndex] + intervalMax)) >= 0) { - // intervalMax is over - if ((countDiff >= countMin) && (timeDiff > intervalSml) && (intervalMin != intervalMax)) { - // normal procedure - noInterrupts(); // vars could be modified in ISR as well - intervalStart[pinIndex] = endT; // time of last impulse becomes first in next - interrupts(); - } else { - // nothing counted or counts happened during a fraction of intervalMin only - noInterrupts(); // vars could be modified in ISR as well - intervalStart[pinIndex] = now; // start a new interval for next report now - intervalEnd[pinIndex] = now; // no last impulse, use now instead - interrupts(); - timeDiff = now - startT; // special handling - calculation ends now - } - } else if( ((long)(now - (lastReport[pinIndex] + intervalMin)) >= 0) - && (countDiff >= countMin) && (timeDiff > intervalSml)) { - // minInterval has elapsed and other conditions are ok - noInterrupts(); // vars could be modified in ISR as well - intervalStart[pinIndex] = endT; // time of last also time of first in next - interrupts(); - } else { - return; // intervalMin and Max not over - dont report yet - } - noInterrupts(); - counterIgn[pinIndex] = 0; - pulseWidthSum[pinIndex] = 0; - interrupts(); - lastCount[pinIndex] = count; // remember current count for next interval - lastRejCount[pinIndex] = rejCount; - lastReport[pinIndex] = now; // remember when we reported -#ifdef ESP8266 - delayedTcpReports = 0; -#endif - reportSequence[pinIndex]++; - } - Output->print(F("R")); // R Report - Output->print(activePin[pinIndex]); - Output->print(F(" C")); // C - Count - Output->print(count); - Output->print(F(" D")); // D - Count Diff (without pulse that marks the begin) - Output->print(countDiff); - Output->print(F("/")); // R - real Diff for long counter - includes first after restart - Output->print(realDiff); - Output->print(F(" T")); // T - Time - Output->print(timeDiff); - Output->print(F(" N")); // N - now - Output->print((long)now); - Output->print(F(",")); - Output->print(millisWraps); - Output->print(F(" X")); // X Reject - Output->print(rejDiff); - - if (!showOnly) { - Output->print(F(" S")); // S - Sequence number - Output->print(reportSequence[pinIndex]); - } - if (countDiff > 0) { - Output->print(F(" A")); - Output->print(widthSum / countDiff); - } - Output->println(); -#ifdef ESP8266 - if (tcpMode && !showOnly) { - Serial.print(F("D reported pin ")); - Serial.print(activePin[pinIndex]); - Serial.print(F(" sequence ")); - Serial.print(reportSequence[pinIndex]); - Serial.println(F(" over tcp ")); - } -#endif - -} - - -/* - report count and time for pins that are between min and max interval -*/ - -boolean reportDue() { - uint32_t now = millis(); - boolean doReport = false; // check if report needs to be called - if((long)(now - timeNextReport) >= 0) // works fine when millis wraps. - doReport = true; // intervalMin is over - else - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) - if (activePin[pinIndex] >= 0) - if((long)(now - (lastReport[pinIndex] + intervalMax)) >= 0) - doReport = true; // active pin has not been reported for langer than intervalMax - return doReport; -} - - - -void report() { - uint32_t now = millis(); -#ifdef ESP8266 - if (tcpMode && !Client1Connected && (delayedTcpReports < 3)) { - if(delayedTcpReports == 0 || ((long)(now - (lastDelayedTcpReports + (1 * 30 * 1000))) > 0)) { - Serial.print(F("D report called but tcp is disconnected - delaying (")); - Serial.print(delayedTcpReports); - Serial.print(F(")")); - Serial.print(F(" now ")); - Serial.print(now); - Serial.print(F(" last ")); - Serial.print(lastDelayedTcpReports); - Serial.print(F(" diff ")); - Serial.println(now - lastDelayedTcpReports); - delayedTcpReports++; - lastDelayedTcpReports = now; - return; - } else return; - } -#endif - - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) { // go through all observed pins as pinIndex - if (activePin[pinIndex] >= 0) { - showPinCounter (pinIndex, false, now); // report pin counters if necessary -#ifdef pulseHistory - if (devVerbose >= 5) - showPinHistory(pinIndex, now); // show pin history if verbose >= 5 -#endif - } - } - timeNextReport = now + intervalMin; // check again after intervalMin or if intervalMax is over for a pin -} - - -/* give status report in between if requested over serial input */ -void showCmd() { - uint32_t now = millis(); - Output->print(F("M Status: ")); - printVersionMsg(); - Output->println(); - - showIntervals(); -#ifdef analogIR - showThresholds(); -#endif - Output->print(F("V")); - Output->println(devVerbose); - - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) { - if (activePin[pinIndex] >= 0) { - showPinConfig(pinIndex); - Output->print(F(", ")); - showPinCounter(pinIndex, true, now); -#ifdef pulseHistory - showPinHistory(pinIndex, now); -#endif - } - } - readFromEEPROM(); - Output->print(F("M Next report in ")); - Output->print(timeNextReport - millis()); - Output->print(F(" milliseconds")); - Output->println(); - //Output->println(F("M #end#")); -} - - -void helloCmd() { - uint32_t now = millis(); - Output->println(); - printVersionMsg(); - Output->print(F(" Hello, pins ")); - boolean first = true; - for (uint8_t aPin=0; aPin < MAX_APIN; aPin++) { - if (allowedPins[aPin] >= 0) { - if (!first) { - Output->print(F(",")); - } else { - first = false; - } - Output->print(aPin); - } - } - Output->print(F(" available")); - Output->print(F(" T")); - Output->print(now); - Output->print(F(",")); - Output->print(millisWraps); - Output->print(F(" B")); - Output->print(bootTime); - Output->print(F(",")); - Output->print(bootWraps); - - Output->println(); - showIntervals(); -#ifdef analogIR - showThresholds(); -#endif - Output->print(F("V")); - Output->println(devVerbose); - - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) { // go through all observed pins as pinIndex - if (activePin[pinIndex] >= 0) { - showPinConfig(pinIndex); - Output->println(); - } - } -} - - - -/* - handle add command. -*/ -void addCmd(uint16_t *values, uint8_t size) { - uint16_t pulseWidth; - uint32_t now = millis(); - - uint8_t aPin = values[0]; // values[0] is pin number - if (aPin >= MAX_APIN || allowedPins[aPin] < 0) { - PrintErrorMsg(); - Output->print(F("Illegal pin specification ")); - Output->println(aPin); - return; - }; - uint8_t pinIndex = allowedPins[aPin]; - uint8_t rPin = internalPins[pinIndex]; - - if (activePin[pinIndex] != aPin) { // in case this pin is not already active counting - #ifndef ESP8266 - uint8_t port = digitalPinToPort(rPin) - 2; - PCintLast[port] = *portInputRegister(port+2); - #endif - initPinVars(pinIndex, now); - activePin[pinIndex] = aPin; // save arduino pin number and flag this pin as active for reporting - } - - if (values[1] < 2 || values[1] > 3) { // values[1] is level (3->rising / 2->falling) - PrintErrorMsg(); - Output->print(F("Illegal pulse level specification for pin ")); - Output->println(aPin); - } - pulseLevel[pinIndex] = (values[1] == 3); // 2 = falling -> pulseLevel 0, 3 = rising -> pulseLevel 1 - -#ifdef analogIR - if (size > 2 && values[2] && !analogPins[pinIndex]) { -#else - if (size > 2 && values[2]) { -#endif - pinMode (rPin, INPUT_PULLUP); // values[2] is pullup flag - pullup[pinIndex] = 1; - } else { - pinMode (rPin, INPUT); - pullup[pinIndex] = 0; - } - - if (size > 3 && values[3] > 0) { // value 3 is min length - pulseWidth = values[3]; - } else { - pulseWidth = 2; - } - - /* todo: add upper and lower limits for analog pins as option here and in Fhem module */ - - pulseWidthMin[pinIndex] = pulseWidth; - -#ifdef analogIR - if (!analogPins[pinIndex]) { -#endif - if (!AddPinChangeInterrupt(rPin)) { // add Pin Change Interrupt - PrintErrorMsg(); - Output->println(F("AddInt")); - return; - } -#ifdef analogIR - } -#endif - Output->print(F("M defined ")); - showPinConfig(pinIndex); - Output->println(); -} - - -/* - handle rem command. -*/ -void removeCmd(uint16_t *values, uint8_t size) { - uint8_t aPin = values[0]; - if (size < 1 || aPin >= MAX_APIN || allowedPins[aPin] < 0) { - PrintErrorMsg(); - Output->print(F("Illegal pin specification ")); - Output->println(aPin); - return; - }; - uint8_t pinIndex = allowedPins[aPin]; - -#ifdef analogIR - if (!analogPins[pinIndex]) { -#endif -#ifdef ESP8266 - detachInterrupt(digitalPinToInterrupt(internalPins[pinIndex])); -#else - if (!RemovePinChangeInterrupt(internalPins[pinIndex])) { - PrintErrorMsg(); Output->println(F("RemInt")); - return; - } -#endif -#ifdef analogIR - } -#endif - initPinVars(pinIndex, 0); - Output->print(F("M removed ")); - Output->println(aPin); -} - - - -void intervalCmd(uint16_t *values, uint8_t size) { - /*Serial.print(F("D int ptr is ")); - Serial.println(size);*/ - if (size < 4) { // i command always gets 4 values: min, max, sml, cntMin - PrintErrorMsg(); - Output->print(F("size")); - Output->println(); - return; - } - if (values[0] < 1 || values[0] > 3600) { - PrintErrorMsg(); Output->println(values[0]); - return; - } - intervalMin = (long)values[0] * 1000; - if (millis() + intervalMin < timeNextReport) - timeNextReport = millis() + intervalMin; - - if (values[1] < 1 || values[1] > 3600) { - PrintErrorMsg(); Output->println(values[1]); - return; - } - intervalMax = (long)values[1]* 1000; - - if (values[2] > 3600) { - PrintErrorMsg(); Output->println(values[2]); - return; - } - intervalSml = (long)values[2] * 1000; - - if (values[3] > 100) { - PrintErrorMsg(); Output->println(values[3]); - return; - } - countMin = values[3]; - - Output->print(F("M intervals set to ")); - Output->print(values[0]); - Output->print(F(" ")); - Output->print(values[1]); - Output->print(F(" ")); - Output->print(values[2]); - Output->print(F(" ")); - Output->print(values[3]); - Output->println(); -} - -#ifdef analogIR -void thresholdCmd(uint16_t *values, uint8_t size) { - /*Serial.print(F("D threshold size ")); - Serial.print(size); - Serial.print(F(" v0 ")); - Serial.print(values[0]); - Serial.print(F(" v1 ")); - Serial.print(values[1]); - Serial.println();*/ - - if (size < 2) { // t command gets 2 values: min, max - PrintErrorMsg(); - Output->print(F("size")); - Output->println(); - return; - } - if (values[0] < 1 || values[0] > 1023) { - PrintErrorMsg(); Output->println(values[0]); - return; - } - analogThresholdMin = (int)values[0]; - - if (values[1] < 1 || values[1] > 1023) { - PrintErrorMsg(); Output->println(values[1]); - return; - } - analogThresholdMax = (int)values[1]; - - Output->print(F("M analog thresholds set to ")); - Output->print(values[0]); - Output->print(F(" ")); - Output->print(values[1]); - Output->println(); -} -#endif - - -void keepAliveCmd(uint16_t *values, uint8_t size) { - if (values[0] == 1 && size > 0) { - Output->println(F("alive")); - } -#ifdef ESP8266 - if (values[0] == 1 && size > 0 && size < 3 && Client1.connected()) { - tcpMode = true; - if (size == 2) { - expectK = millis() + values[1] * 2500; - } else { - expectK = millis() + 600000; // 10 Minutes if nothing sent (should not happen) - } - } -#endif -} - - -#ifdef ESP8266 -void quitCmd() { - if (Client1.connected()) { - Client1.println(F("closing connection")); - Client1.stop(); - tcpMode = false; - Serial.println(F("M TCP connection closed")); - } else { - Serial.println(F("M TCP not connected")); - } -} -#endif - - - -void updateEEPROM(int &address, byte value) { - if( EEPROM.read(address) != value){ - EEPROM.write(address, value); - } - address++; -} - - -void updateEEPROMSlot(int &address, char cmd, int v1, int v2, int v3, int v4) { - updateEEPROM(address, cmd); // I / A - updateEEPROM(address, v1 & 0xff); - updateEEPROM(address, v1 >> 8); - updateEEPROM(address, v2 & 0xff); - updateEEPROM(address, v2 >> 8); - updateEEPROM(address, v3 & 0xff); - updateEEPROM(address, v3 >> 8); - updateEEPROM(address, v4 & 0xff); - updateEEPROM(address, v4 >> 8); -} - -/* todo: include analogPins as well as analog limits in save / restore */ - -void saveToEEPROMCmd() { - int address = 0; - uint8_t slots = 1; - updateEEPROM(address, 'C'); - updateEEPROM(address, 'f'); - updateEEPROM(address, 'g'); - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) - if (activePin[pinIndex] >= 0) slots ++; -#ifdef analogIR - slots ++; -#endif - updateEEPROM(address, slots); // number of defined pins + intervall definition - updateEEPROMSlot(address, 'I', (uint16_t)(intervalMin / 1000), (uint16_t)(intervalMax / 1000), - (uint16_t)(intervalSml / 1000), (uint16_t)countMin); -#ifdef analogIR - updateEEPROMSlot(address, 'T', (uint16_t)analogThresholdMin, (uint16_t)analogThresholdMax, 0, 0); -#endif - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) - if (activePin[pinIndex] >= 0) - updateEEPROMSlot(address, 'A', (uint16_t)activePin[pinIndex], (uint16_t)(pulseLevel[pinIndex] ? 3:2), - (uint16_t)pullup[pinIndex], (uint16_t)pulseWidthMin[pinIndex]); -#ifdef ESP8266 - EEPROM.commit(); -#endif - Serial.print(F("config saved, ")); - Serial.print(slots); - Serial.print(F(", ")); - Serial.println(address); -} - - -void readFromEEPROM() { - int address = 0; - uint16_t v1, v2, v3, v4; - char cmd; - if (EEPROM.read(address) != 'C' || EEPROM.read(address+1) != 'f' || EEPROM.read(address+2) != 'g') { - Output->println(F("M no config in EEPROM")); - return; - } - address = 3; - uint8_t slots = EEPROM.read(address++); - if (slots > MAX_PIN + 2) { - Output->println(F("M illegal config in EEPROM")); - return; - } - Output->println(); - Output->print(F("M EEPROM Config: ")); - Output->print((char) EEPROM.read(0)); - Output->print((char) EEPROM.read(1)); - Output->print((char) EEPROM.read(2)); - Output->print(F(" Slots: ")); - Output->print((int) EEPROM.read(3)); - Output->println(); - for (uint8_t slot=0; slot < slots; slot++) { - cmd = EEPROM.read(address); - v1 = EEPROM.read(address+1) + (((uint16_t)EEPROM.read(address+2)) << 8); - v2 = EEPROM.read(address+3) + (((uint16_t)EEPROM.read(address+4)) << 8); - v3 = EEPROM.read(address+5) + (((uint16_t)EEPROM.read(address+6)) << 8); - v4 = EEPROM.read(address+7) + (((uint16_t)EEPROM.read(address+8)) << 8); - address = address + 9; - Output->print(F("M Slot: ")); - Output->print(cmd); - Output->print(F(" ")); - Output->print(v1); - Output->print(F(",")); - Output->print(v2); - Output->print(F(",")); - Output->print(v3); - Output->print(F(",")); - Output->print(v4); - Output->println(); - } -} - - -void restoreFromEEPROM() { - int address = 0; - if (EEPROM.read(address) != 'C' || EEPROM.read(address+1) != 'f' || EEPROM.read(address+2) != 'g') { - Serial.println(F("M no config in EEPROM")); - return; - } - address = 3; - uint8_t slots = EEPROM.read(address++); - if (slots > MAX_PIN + 1 || slots < 1) { - Serial.println(F("M illegal config in EEPROM")); - return; - } - Serial.println(F("M restoring config from EEPROM")); - char cmd; - for (uint8_t slot=0; slot < slots; slot++) { - cmd = EEPROM.read(address); - commandData[0] = EEPROM.read(address+1) + (((uint16_t)EEPROM.read(address+2)) << 8); - commandData[1] = EEPROM.read(address+3) + (((uint16_t)EEPROM.read(address+4)) << 8); - commandData[2] = EEPROM.read(address+5) + (((uint16_t)EEPROM.read(address+6)) << 8); - commandData[3] = EEPROM.read(address+7) + (((uint16_t)EEPROM.read(address+8)) << 8); - address = address + 9; - commandDataPointer = 4; - if (cmd == 'I') intervalCmd(commandData, commandDataPointer); -#ifdef analogIR - if (cmd == 'T') thresholdCmd(commandData, commandDataPointer); -#endif - if (cmd == 'A') addCmd(commandData, commandDataPointer); - } - commandDataPointer = 0; - value = 0; - for (uint8_t i=0; i < MAX_INPUT_NUM; i++) - commandData[i] = 0; - -} - - -void handleInput(char c) { - if (c == ',') { // Komma input, last value is finished - if (commandDataPointer < (MAX_INPUT_NUM - 1)) { - commandData[commandDataPointer++] = value; - value = 0; - } - } - else if ('0' <= c && c <= '9') { // digit input - value = 10 * value + c - '0'; - } - else if ('a' <= c && c <= 'z') { // letter input is command - - if (devVerbose > 0) { - commandData[commandDataPointer] = value; - Serial.print(F("D got ")); - for (short v = 0; v <= commandDataPointer; v++) { - if (v > 0) Serial.print(F(",")); - Serial.print(commandData[v]); - } - Serial.print(c); - Serial.print(F(" size ")); - Serial.print(commandDataPointer+1); - Serial.println(); - } - - switch (c) { - case 'a': // add a pin - commandData[commandDataPointer] = value; - addCmd(commandData, commandDataPointer+1); - break; - case 'd': // delete a pin - commandData[commandDataPointer] = value; - removeCmd(commandData, commandDataPointer+1); - break; - case 'e': // save to EEPROM - saveToEEPROMCmd(); - break; - case 'f': // flash ota - // OTA flash from HTTP Server - break; - case 'h': // hello - helloCmd(); - break; - case 'i': // interval - commandData[commandDataPointer] = value; - intervalCmd(commandData, commandDataPointer+1); - break; - case 'k': // keep alive - commandData[commandDataPointer] = value; - keepAliveCmd(commandData, commandDataPointer+1); - break; -#ifdef ESP8266 - case 'q': // quit - quitCmd(); - break; -#endif - case 'r': // reset - initialize(); - break; - case 's': // show - showCmd(); - break; -#ifdef analogIR - case 't': // thresholds for analog pin - commandData[commandDataPointer] = value; - thresholdCmd(commandData, commandDataPointer+1); - break; -#endif - case 'v': // dev verbose - if (value < 255) { - devVerbose = value; - Output->print(F("M devVerbose set to ")); - Output->println(value); - } else { - Output->println(F("M illegal value passed for devVerbose")); - } - break; - default: - break; - } - commandDataPointer = 0; - value = 0; - for (uint8_t i=0; i < MAX_INPUT_NUM; i++) - commandData[i] = 0; - //Serial.println(F("D End of command")); - } -} - -#ifdef debugCfg -/* do sample config so we don't need to configure pins after each reboot */ -void debugSetup() { - commandData[0] = 10; - commandData[1] = 20; - commandData[2] = 3; - commandData[3] = 0; - commandDataPointer = 4; - intervalCmd(commandData, commandDataPointer); - - commandData[0] = 1; // pin 1 - commandData[1] = 2; // falling - commandData[2] = 1; // pullup - commandData[3] = 30; // min Length - commandDataPointer = 4; - addCmd(commandData, commandDataPointer); - - commandData[0] = 2; // pin 2 - addCmd(commandData, commandDataPointer); - -/* - commandData[0] = 5; // pin 5 - addCmd(commandData, commandDataPointer); - - commandData[0] = 6; // pin 6 - addCmd(commandData, commandDataPointer); -*/ -} -#endif - - -#ifdef debugPins -void debugPinChanges() { - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) { - short aPin = activePin[pinIndex]; - if (aPin > 0) { - uint8_t rPin = internalPins[pinIndex]; - uint8_t pinState = digitalRead(rPin); - - if (pinState != lastState[pinIndex]) { - lastState[pinIndex] = pinState; - Output->print(F("M pin ")); - Output->print(aPin); - Output->print(F(" (internal ")); - Output->print(rPin); - Output->print(F(")")); - Output->print(F(" changed to ")); - Output->print(pinState); -#ifdef pulseHistory - Output->print(F(", histIdx ")); - Output->print(histIndex); -#endif - Output->print(F(", count ")); - Output->print(counter[pinIndex]); - Output->print(F(", reject ")); - Output->print(rejectCounter[pinIndex]); - Output->println(); - } - } - } -} -#endif - - -#ifdef ESP8266 -void connectWiFi() { - Client1Connected = false; - Client2Connected = false; - - // Connect to WiFi network - WiFi.mode(WIFI_STA); - delay (1000); - if (WiFi.status() != WL_CONNECTED) { - Serial.print(F("M Connecting WiFi to ")); - Serial.println(ssid); - WiFi.begin(ssid, password); // authenticate - while (WiFi.status() != WL_CONNECTED) { - Serial.print(F("M Status is ")); - switch (WiFi.status()) { - case WL_CONNECT_FAILED: - Serial.println(F("Connect Failed")); - break; - case WL_CONNECTION_LOST: - Serial.println(F("Connection Lost")); - break; - case WL_DISCONNECTED: - Serial.println(F("Disconnected")); - break; - case WL_CONNECTED: - Serial.println(F("Connected")); - break; - default: - Serial.println(WiFi.status()); - } - delay(1000); - } - Serial.println(); - Serial.print(F("M WiFi connected to ")); - Serial.println(WiFi.SSID()); - } else { - Serial.print(F("M WiFi already connected to ")); - Serial.println(WiFi.SSID()); - } - - // Start the server - Server.begin(); - Serial.println(F("M Server started")); - - // Print the IP address - Serial.print(F("M Use this IP: ")); - Serial.println(WiFi.localIP()); -} - - -void handleConnections() { - IPAddress remote; - uint32_t now = millis(); - - if (Client1Connected) { - if((long)(now - expectK) >= 0) { - Serial.println(F("M no keepalive from Client - disconnecting")); - Client1.stop(); - } - } - if (Client1.available()) { - handleInput(Client1.read()); - //Serial.println(F("M new Input over TCP")); - } - if (Client1.connected()) { - Client2 = Server.available(); - if (Client2) { - Client2.println(F("connection already busy")); - remote = Client2.remoteIP(); - Client2.stop(); - Serial.print(F("M second connection from ")); - Serial.print(remote); - Serial.println(F(" rejected")); - } - } else { - if (Client1Connected) { // client used to be connected, now disconnected - Client1Connected = false; - Output = &Serial; - Serial.println(F("M connection to client lost")); - } - Client1 = Server.available(); - if (Client1) { // accepting new connection - remote = Client1.remoteIP(); - Serial.print(F("M new connection from ")); - Serial.print(remote); - Serial.println(F(" accepted")); - Client1Connected = true; - Output = &Client1; - expectK = now + 600000; // max 10 Minutes (to be checked on Fhem module side as well - helloCmd(); // say hello to client - } - } -} -#endif - - -void handleTime() { - uint32_t now = millis(); - if (now < lastMillis) millisWraps++; - lastMillis = now; -} - - -#ifdef analogIR -void detectTrigger(int val) { - uint8_t nextState = triggerState; - if (val > analogThresholdMax) { - nextState = 1; - } else if (val < analogThresholdMin) { - nextState = 0; - } - if (nextState != triggerState) { - triggerState = nextState; -#ifdef ledOutPin - digitalWrite(ledOutPin, triggerState); -#endif -#ifdef ESP8266 - short pinIndex = 4; // a0 -#else - short pinIndex = 17; // a7 -#endif - uint32_t now = millis(); - doCount (pinIndex, triggerState, now); // do the counting, history and so on - -#ifdef debugPins - if (devVerbose >= 10) { - short pinIndex = allowedPins[analogInPin]; - short rPin = internalPins[pinIndex]; - Output->print(F("M pin ")); - Output->print(analogInPin); - Output->print(F(" ( internal ")); - Output->print(rPin); - Output->print(F(" ) ")); - Output->print(F(" to ")); - Output->print(nextState); -#ifdef pulseHistory - Output->print(F(" histIdx ")); - Output->print(histIndex); -#endif - Output->print(F(" count ")); - Output->print(counter[pinIndex]); - Output->print(F(" reject ")); - Output->print(rejectCounter[pinIndex]); - Output->println(); - } -#endif - } -} -#endif - -void initPinVars(short pinIndex, uint32_t now) { - uint8_t level = 0; - activePin[pinIndex] = -1; // inactive (-1) - initialized[pinIndex] = false; // no pulse seen yet - pulseWidthMin[pinIndex] = 0; // min pulse length - counter[pinIndex] = 0; // counter to 0 - counterIgn[pinIndex] = 0; - lastCount[pinIndex] = 0; - rejectCounter[pinIndex] = 0; - lastRejCount[pinIndex] = 0; - intervalStart[pinIndex] = now; // time vars - intervalEnd[pinIndex] = now; - lastChange[pinIndex] = now; - lastReport[pinIndex] = now; - reportSequence[pinIndex] = 0; -#ifdef analogIR - if (!analogPins[pinIndex]) { - level = digitalRead(internalPins[pinIndex]); - } -#else - level = digitalRead(internalPins[pinIndex]); -#endif - lastLevel[pinIndex] = level; -#ifdef debugPins - lastState[pinIndex] = level; // for debug output -#endif - /* todo: add analogPins, upper and lower limits for analog */ -} - - -void initialize() { - uint32_t now = millis(); - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) { - initPinVars(pinIndex, now); - } - timeNextReport = now + intervalMin; // time for first output - devVerbose = 0; -#ifndef ESP8266 - for (uint8_t port=0; port <= 2; port++) { - PCintLast[port] = *portInputRegister(port+2); // current pin states at port for PCInt handler - } -#endif -#ifdef debugCfg - debugSetup(); -#endif - restoreFromEEPROM(); - bootTime = millis(); // with boot / reset time - bootWraps = millisWraps; -#ifdef ESP8266 - expectK = now + 600000; // max 10 Minutes (to be checked on Fhem module side as well -#endif -} - - -void setup() { - Serial.begin(SERIAL_SPEED); // initialize serial -#ifdef ESP8266 - EEPROM.begin(100); -#endif - delay (500); - interrupts(); - Serial.println(); - Output = &Serial; - millisWraps = 0; - lastMillis = millis(); - initialize(); -#ifdef analogIR - pinMode(irOutPin, OUTPUT); -#ifdef ledOutPin - pinMode(ledOutPin, OUTPUT); -#endif -#endif - helloCmd(); // started message to serial -#ifdef ESP8266 - connectWiFi(); -#endif -} - - -/* - Main Loop - checks if report should be called because timeNextReport is reached - or lastReport for one pin is older than intervalMax - timeNextReport is only set here (and when interval is changed / at setup) -*/ -void loop() { - handleTime(); - if (Serial.available()) { - handleInput(Serial.read()); - } -#ifdef ESP8266 - handleConnections(); -#endif - -#ifdef analogIR - short AIndex = allowedPins[analogInPin]; - if (AIndex >= 0 && activePin[AIndex] >= 0) { - digitalWrite(irOutPin, LOW); - // wait 10 milliseconds - delay(10); - // read the analog in value: - sensorValueOff = analogRead(analogInPin); - // turn IR LED on - digitalWrite(irOutPin, HIGH); - delay(10); - // read the analog in value: - sensorValueOn = analogRead(analogInPin); - detectTrigger (sensorValueOn - sensorValueOff); - if (devVerbose >= 20) { - Output->print(F("L")); - - Output->print(sensorValueOn); - Output->print(F(",")); - Output->print(sensorValueOff); - Output->print(F("->")); - - Output->println(sensorValueOn - sensorValueOff); - } - } -#endif - -#ifdef debugPins - if (devVerbose >= 10) { - debugPinChanges(); - } -#endif - - if (reportDue()) { - report(); - } -} diff --git a/fhem/contrib/arduino/ArduCounter3.20.ino b/fhem/contrib/arduino/ArduCounter3.20.ino deleted file mode 100755 index 88a336a09..000000000 --- a/fhem/contrib/arduino/ArduCounter3.20.ino +++ /dev/null @@ -1,1728 +0,0 @@ -/* - * Sketch for counting impulses in a defined interval - * e.g. for power meters with an s0 interface that can be - * connected to an input of an arduino or esp8266 board - * - * the sketch uses pin change interrupts which can be anabled - * for any of the inputs on e.g. an arduino uno, jeenode, wemos d1 etc. - * - * the pin change Interrupt handling for arduinos used here - * is based on the arduino playground example on PCINT: - * http://playground.arduino.cc/Main/PcInt which is outdated. - * - * see https://github.com/GreyGnome/EnableInterrupt for a newer library (not used here) - * and also - * https://playground.arduino.cc/Main/PinChangeInterrupt - * http://www.avrfreaks.net/forum/difference-between-signal-and-isr - * - * Refer to avr-gcc header files, arduino source and atmega datasheet. - */ - -/* Arduino Uno / Nano Pin to interrupt map: - * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2 - * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0 - * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1 - */ - -/* test cmds analog ESP: - * 20v Verbose - * 17,3,0,50a A0, rising, no Pullup, MinLen 50 - * 15,25t Level Diff Thresholds - * - * for ESP with D5 falling pullup 30 - * 5,2,1,30a - * 20v - * 10,20,1,1i - */ - -/* - Changes: - V1.2 - 27.10.16 - use noInterrupts in report() - - avoid reporting very short timeDiff in case of very slow impulses after a report - - now reporting is delayed if impulses happened only within in intervalSml - - reporting is also delayed if less than countMin pulses counted - - extend command "int" for optional intervalSml and countMin - 29.10.16 - allow interval Min >= Max or Sml > Min - which changes behavior to take fixed calculation interval instead of timeDiff between pulses - -> if intervalMin = intervalMax, counting will allways follow the reporting interval - 3.11.16 - more noInterrupt blocks when accessing the non uint8_t volatiles in report - V1.3 - 4.11.16 - check min pulse width and add more output, - - prefix show output with M - V1.4 - 10.11.16 - restructure add Cmd - - change syntax for specifying minPulseLengh - - res (reset) command - V1.6 - 13.12.16 - new startup message logic?, newline before first communication? - 18.12.16 - replace all code containing Strings, new communication syntax and parsing from Jeelink code - V1.7 - 2.1.17 - change message syntax again, report time as well, first and last impulse are reported - relative to start of intervall not start of reporting intervall - V1.8 - 4.1.17 - fixed a missing break in the case statement for pin definition - 5.1.17 - cleanup debug logging - 14.10.17 - fix a bug where last port state was not initialized after interrupt attached but this is necessary there - 23.11.17 - beautify code, add comments, more debugging for users with problematic pulse creation devices - 28.12.17 - better reportung of first pulse (even if only one pulse and countdiff is 0 but realdiff is 1) - 30.12.17 - rewrite PCInt, new handling of min pulse length, pulse history ring - 1.1.18 - check len in add command, allow pin 8 and 13 - 2.1.18 - add history per pin to report line, show negative starting times in show history - 3.1.18 - little reporting fix (start pos of history report) - - V2.0 - 17.1.18 - rewrite many things - use pin number instead of pcIntPinNumber as index, split interrupt handler for easier porting to ESP8266, ... - V2.23 - 10.2.18 - new commands for check alive and quit, send setup message after reboot also over tcp - remove reporting time of first pulse (now we hava history) - remove pcIntMode (is always change now) - pulse min interval is now always checked and defaults to 2 if not set - march 2018 many changes more to support ESP8266 - 7.3.18 - change pin config output, fix pullup (V2.26), store config in eeprom and read it back after boot - 22.4.18 - many changes, delay report if tcp mode and disconnected, verbose levels, ... - 13.5.18 - V2.36 Keepalive also on Arduino side - 9.12.18 - V3.0 start implementing analog input for old ferraris counters - 6.1.19 - V3.1 showIntervals in hello - 19.1.19 - V3.12 support for ESP with analog - 24.2.19 - V3.13 fix internal pin to GPIO mapping (must match ISR functions) when ESP8266 and analog support - - V3.14 added return of devVerbose upon startup - 27.6.19 - V3.20 replace timeNextReport with lastReportCall to avoid problem with data tyoes on ESP - fix a bug with analog counting on the ESP - - ToDo / Ideas: - - -*/ - -/* Remove this before compiling */ -/* #define TestConfig */ - - -/* allow printing of every pin change to Serial */ -#define debugPins 1 - -/* allow tracking of pulse lengths */ -#define pulseHistory 1 - -/* support analog input for ferraris counters with IR light hardware */ -#define analogIR 1 - -/* use a sample config at boot */ -// #define debugCfg 1 - -#include "pins_arduino.h" -#include - -const char versionStr[] PROGMEM = "ArduCounter V3.20"; -const char compile_date[] PROGMEM = __DATE__ " " __TIME__; -const char errorStr[] PROGMEM = "Error: "; - -#ifdef ARDUINO_BOARD -const char boardName1[] PROGMEM = ARDUINO_BOARD; -#endif - -#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) -#ifdef ARDUINO_AVR_NANO -const char boardName[] PROGMEM = "NANO"; -#else -const char boardName[] PROGMEM = "UNO"; -#endif -#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) -const char boardName[] PROGMEM = "Leonardo"; -#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -const char boardName[] PROGMEM = "Mega"; -#elif defined(ESP8266) -const char boardName[] PROGMEM = "ESP8266"; -#else -const char boardName[] PROGMEM = "UNKNOWN"; -#endif - -#define SERIAL_SPEED 38400 -#define MAX_INPUT_NUM 8 - -#ifdef analogIR -int sensorValueOff = 0; // value read from the photo transistor when ir LED is off -int sensorValueOn = 0; // value read from the photo transistor when ir LED is on -int analogThresholdMin = 100; // min value of analog input -int analogThresholdMax = 110; // max value of analog input - -uint8_t triggerState; // todo: use existing arrays instead - -// save measurement during same level as sum and count to get average and then put in history when doCount is called -// but how do we do this before we can detect the levels? - -#endif - -#ifdef ESP8266 // ESP variables and definitions -#include // ============================= - -#ifdef TestConfig -#include "ArduCounterTestConfig.h" -#else -const char* ssid = "MySSID"; -const char* password = "secret"; -#endif - -WiFiServer Server(80); // For ESP WiFi connection -WiFiClient Client1; // active TCP connection -WiFiClient Client2; // secound TCP connection to send reject message -boolean Client1Connected; // remember state of TCP connection -boolean Client2Connected; // remember state of TCP connection - -boolean tcpMode = false; -uint8_t delayedTcpReports = 0; // how often did we already delay reporting because tcp disconnected -uint32_t lastDelayedTcpReports = 0; // last time we delayed - -#define MAX_HIST 20 // 20 history entries for ESP boards (can be increased) - -#ifdef analogIR // code for ESP with analog pin and reflection light barrier support (test) - -#define MAX_APIN 18 -#define MAX_PIN 9 - -/* ESP8266 pins that are typically ok to use - * (some might be set to -1 (disallowed) because they are used - * as reset, serial, led or other things on most boards) - * maps printed pin numbers (aPin) to sketch internal index numbers */ -short allowedPins[MAX_APIN] = // ESP 8266 with analog: - { 0, 1, 2, -1, // printed pin numbers 0,1,2 are ok to be used - -1, 5, -1, -1, // printed pin number 5 is ok to be used - -1, -1, -1, -1, // 8-11 not avaliable - -1, -1, -1, -1, // 12-15 not avaliable - -1, 8 }; // 16 not available, 17 is analog - -/* Wemos / NodeMCU Pins 3,4 and 8 (GPIO 0,2 and 15) define boot mode and therefore - * can not be used to connect to signal */ - -/* Map from sketch internal pin index to real chip IO pin number (not aPin, e.g. for ESP) - Note that the internal numbers might be different from the printed - pin numbers (e.g. pin 0 is in index 0 but real chip pin number 16! */ -short internalPins[MAX_PIN] = - { D0, D1, D2, D3, // map from internal pin Index to - D4, D5, D6, D7, // real GPIO pin numbers / defines - A0 }; // D0=16, D1=5, D2=4, D5=14, A0=17 - - - -uint8_t analogPins[MAX_PIN] = - { 0,0,0,0,0,0,0,0,1 }; // ESP pin A0 (pinIndex 8, internal 17) is analog - -const int analogInPin = A0; // Analog input pin that the photo transistor is attached to (internally number 17) -const int irOutPin = D6; // Digital output pin that the IR-LED is attached to -const int ledOutPin = D7; // Signal LED output pin - -#else // code for ESP without analog pin and reflection light barrier support - - -#define MAX_APIN 8 -#define MAX_PIN 8 - -/* ESP8266 pins that are typically ok to use - * (some might be set to -1 (disallowed) because they are used - * as reset, serial, led or other things on most boards) - * maps printed pin numbers to sketch internal index numbers */ -short allowedPins[MAX_APIN] = // ESP 8266 without analog: - { 0, 1, 2, -1, // printed pin numbers 0,1,2 are ok to be used, 3 not - -1, 5, 6, 7}; // printed pin numbers 5-7 are ok to be used, 4 not, >8 not - -/* Wemos / NodeMCU Pins 3,4 and 8 (GPIO 0,2 and 15) define boot mode and therefore - * can not be used to connect to signal */ - -/* Map from sketch internal pin index to real chip IO pin number (not aPin, e.g. for ESP) - Note that the internal numbers might be different from the printed - pin numbers (e.g. pin 0 is in index 0 but real chip pin number 16! */ -short internalPins[MAX_PIN] = - { D0, D1, D2, D3, // printed pin numbers 0, 1, 2, 3 (3 should not be used and could be removed here) - D5, D5, D6, D7}; // printed pin numbers 4, 5, 6, 7 (4 should not be used and could be removed here) - // D0=16, D1=5, D2=4, D5=14, A0=17, ... - -#endif // end of ESP section without analog reading - - - - -#else // Arduino Uno or Nano variables and definitions - // ============================================= - -#define MAX_HIST 20 // 20 history entries for arduino boards - -/* arduino pins that are typically ok to use - * (some might be set to -1 (disallowed) because they are used - * as reset, serial, led or other things on most boards) - * maps printed pin numbers to sketch internal index numbers */ - -#ifdef analogIR - -/* 2 is used for IR out, 12 for signal, A7 for In */ -#define MAX_APIN 22 -#define MAX_PIN 18 - -short allowedPins[MAX_APIN] = - {-1, -1, -1, 0, /* arduino pin 0 - 3 to internal index or -1 if pin is reserved */ - 1, 2, 3, 4, /* arduino pin 4 - 7 to internal index */ - 5, 6, 7, 8, /* arduino pin 8 - 11 to internal index */ - -1, 9, 10, 11, /* arduino pin 12, 13, A0, A1 / 14, 15 to internal index or -1 if pin is reserved*/ - 12, 13, 14, 15, /* arduino pin A2 - A5 / 16 - 19 to internal index */ - 16, 17 }; /* arduino pin A6, A7 to internal index */ - -/* Map from sketch internal pin index to real chip IO pin number */ -short internalPins[MAX_PIN] = - { 3, 4, 5, 6, /* index 0 - 3 map to pins 3 - 6 */ - 7, 8, 9, 10, /* index 4 - 7 map to pins 7 - 10 */ - 11, 12, 14, 15, /* index 8 - 11 map to pins 11,13, A0 - A1 */ - 16, 17, 18, 19, /* index 12 - 15 map to pins A2 - A5 */ - 20, 21 }; /* index 16 - 17 map to pin A6, A7 */ - -uint8_t analogPins[MAX_PIN] = - { 0,0,0,0, /* everything except Arduino A7 (pinIndex 17, internal 21) is digital by default */ - 0,0,0,0, - 0,0,0,0, - 0,0,0,0, - 0,1 }; - -const int analogInPin = A7; // Arduino analog input pin that the photo transistor is attached to (internal 21) -const int irOutPin = 2; // Digital output pin that the IR-LED is attached to -const int ledOutPin = 12; // Signal LED output pin - - -#else -/* no analog IR support -> all Nano pins including analog available für digital counting */ - -#define MAX_APIN 22 -#define MAX_PIN 20 -short allowedPins[MAX_APIN] = - {-1, -1, 0, 1, /* arduino pin 0 - 3 to internal Pin index or -1 if pin is reserved */ - 2, 3, 4, 5, /* arduino pin 4 - 7 to internal Pin index or -1 if pin is reserved */ - 6, 7, 8, 9, /* arduino pin 8 - 11 to internal Pin index or -1 if pin is reserved */ - 10, 11, 12, 13, /* arduino pin 12, 13, A0, A1 to internal Pin index or -1 if pin is reserved */ - 14, 15, 16, 17, /* arduino pin A2 - A5 / 16 - 19 to internal Pin index or -1 if pin is reserved */ - 18, 19 }; /* arduino pin A6, A7 to internal Pin index or -1 if pin is reserved */ - -/* Map from sketch internal pin index to real chip IO pin number */ -short internalPins[MAX_PIN] = - { 2, 3, 4, 5, /* index 0 - 3 map to pins 2 - 5 */ - 6, 7, 8, 9, /* index 4 - 7 map to pins 6 - 9 */ - 10, 11, 12, 13, /* index 8 - 11 map to pins 10 - 13 */ - 14, 15, 16, 17, /* index 12 - 15 map to pins A0 - A3 */ - 18, 19, 20, 21 }; /* index 16 - 19 map to pins A4 - A7 */ - -uint8_t analogPins[MAX_PIN] = - { 0,0,0,0, /* everything is digital by default */ - 0,0,0,0, - 0,0,0,0, - 0,0,0,0, - 0,0,0,0 }; - -#endif - -/* first and last pin at port PB, PC and PD for arduino uno/nano */ -uint8_t firstPin[] = {8, 14, 0}; // aPin -> allowedPins[] -> pinIndex -uint8_t lastPin[] = {13, 19, 7}; - -/* Pin change mask for each chip port on the arduino platform */ -volatile uint8_t *port_to_pcmask[] = { - &PCMSK0, - &PCMSK1, - &PCMSK2 -}; - -/* last PIN States at io port to detect individual pin changes in arduino ISR */ -volatile static uint8_t PCintLast[3]; - -#endif - - -Print *Output; // Pointer to output device (Serial / TCP connection with ESP8266) -uint32_t bootTime; -uint16_t bootWraps; // counter for millis wraps at last reset -uint16_t millisWraps; // counter to track when millis counter wraps -uint32_t lastMillis; // milis at last main loop iteration -uint8_t devVerbose; // >=10 shows pin changes, >=5 shows pin history - -#ifdef debugPins -uint8_t lastState[MAX_PIN]; // for debug output when a pin state changes -#endif - -uint32_t intervalMin = 30000; // default 30 sec - report after this time if nothing else delays it -uint32_t intervalMax = 60000; // default 60 sec - report after this time if it didin't happen before -uint32_t intervalSml = 2000; // default 2 secs - continue count if timeDiff is less and intervalMax not over -uint16_t countMin = 2; // continue counting if count is less than this and intervalMax not over - -uint32_t lastReportCall; -#ifdef ESP8266 -uint32_t expectK; -#endif - -/* index to the following arrays is the internal pin index number */ - -volatile boolean initialized[MAX_PIN]; // did we get first interrupt yet? -short activePin[MAX_PIN]; // printed arduino pin number for index if active - otherwise -1 -uint16_t pulseWidthMin[MAX_PIN]; // minimal pulse length in millis for filtering -uint8_t pulseLevel[MAX_PIN]; // start of pulse for measuring length - 0 / 1 as defined for each pin -uint8_t pullup[MAX_PIN]; // pullup configuration state - -volatile uint32_t counter[MAX_PIN]; // real pulse counter -volatile uint8_t counterIgn[MAX_PIN]; // ignored first pulse after init -volatile uint16_t rejectCounter[MAX_PIN]; // counter for rejected pulses that are shorter than pulseWidthMin -uint32_t lastCount[MAX_PIN]; // counter at last report (to get the delta count) -uint16_t lastRejCount[MAX_PIN]; // reject counter at last report (to get the delta count) - -volatile uint32_t lastChange[MAX_PIN]; // millis at last level change (for measuring pulse length) -volatile uint8_t lastLevel[MAX_PIN]; // level of input at last interrupt -volatile uint8_t lastLongLevel[MAX_PIN]; // last level that was longer than pulseWidthMin - -volatile uint32_t pulseWidthSum[MAX_PIN]; // sum of pulse lengths for average calculation -uint8_t reportSequence[MAX_PIN]; // sequence number for reports - - -#ifdef pulseHistory -volatile uint8_t histIndex; // pointer to next entry in history ring -volatile uint16_t histNextSeq; // next seq number to use -volatile uint16_t histSeq[MAX_HIST]; // history sequence number -volatile uint8_t histPin[MAX_HIST]; // pin for this entry -volatile uint8_t histLevel[MAX_HIST]; // level for this entry -volatile uint32_t histTime[MAX_HIST]; // time for this entry -volatile uint32_t histLen[MAX_HIST]; // time that this level was held -volatile char histAct[MAX_HIST]; // action (count, reject, ...) as one char -#endif - -volatile uint32_t intervalStart[MAX_PIN]; // start of an interval - typically set by first / last pulse -volatile uint32_t intervalEnd[MAX_PIN]; // end of an interval - typically set by first / last pulse -uint32_t lastReport[MAX_PIN]; // millis at last report to find out when maxInterval is over - -uint16_t commandData[MAX_INPUT_NUM]; // input data over serial port or network -uint8_t commandDataPointer = 0; // index pointer to next input value -uint16_t value; // the current value for input function - - -/* - do counting and set start / end time of interval. - reporting is not triggered from here. - - only here counter[] is modified - intervalEnd[] is set here and in report - intervalStart[] is set in case a pin was not initialized yet and in report -*/ -static void inline doCount(uint8_t pinIndex, uint8_t level, uint32_t now) { - uint32_t len = now - lastChange[pinIndex]; - char act = ' '; - -#ifdef pulseHistory - histIndex++; - if (histIndex >= MAX_HIST) histIndex = 0; - histSeq[histIndex] = histNextSeq++; - histPin[histIndex] = pinIndex; - histTime[histIndex] = lastChange[pinIndex]; - histLen[histIndex] = len; - histLevel[histIndex] = lastLevel[pinIndex]; -#endif - if (len < pulseWidthMin[pinIndex]) { // pulse was too short - lastChange[pinIndex] = now; - if (lastLevel[pinIndex] == pulseLevel[pinIndex]) { // if change to gap level - rejectCounter[pinIndex]++; // inc reject counter and set action to R (pulse too short) - act = 'R'; - } else { - act = 'X'; // set action to X (gap too short) - } - } else { - if (lastLevel[pinIndex] != pulseLevel[pinIndex]) { // edge does fit defined pulse start, level is now pulse, before it was gap - act = 'G'; // now the gap is confirmed (even if inbetween was a spike that we ignored) - } else { // edge is a change to gap, level is now gap - if (lastLongLevel[pinIndex] != pulseLevel[pinIndex]) { // last remembered valid level was also gap -> now we had valid new pulse -> count - counter[pinIndex]++; // count - intervalEnd[pinIndex] = now; // remember time of in case pulse will be the last in the interval - if (!initialized[pinIndex]) { - intervalStart[pinIndex] = now; // if this is the very first impulse on this pin -> start interval now - initialized[pinIndex] = true; // and start counting the next impulse (so far counter is 0) - counterIgn[pinIndex]++; // count as to be ignored for diff because it defines the start of the interval - } - pulseWidthSum[pinIndex] += len; // for average calculation - act = 'C'; - } else { // last remembered valid level was a pulse -> now we had another valid pulse - pulseWidthSum[pinIndex] += len; // for average calculation - act = 'P'; // pulse was already counted, only short drop inbetween - } - } - lastLongLevel[pinIndex] = lastLevel[pinIndex]; // remember this valid level as lastLongLevel - } -#ifdef pulseHistory - histAct[histIndex] = act; -#endif - lastChange[pinIndex] = now; - lastLevel[pinIndex] = level; -} - - -/* Interrupt handlers and their installation - * on Arduino and ESP8266 platforms - */ - -#ifndef ESP8266 -/* Add a pin to be handled (Arduino code) */ -uint8_t AddPinChangeInterrupt(uint8_t rPin) { - volatile uint8_t *pcmask; // pointer to PCMSK0 or 1 or 2 depending on the port corresponding to the pin - uint8_t bitM = digitalPinToBitMask(rPin); // mask to bit in PCMSK to enable pin change interrupt for this arduino pin - uint8_t port = digitalPinToPort(rPin); // port that this arduno pin belongs to for enabling interrupts - if (port == NOT_A_PORT) - return 0; - port -= 2; // from port (PB, PC, PD) to index in our array - pcmask = port_to_pcmask[port]; // point to PCMSK0 or 1 or 2 depending on the port corresponding to the pin - *pcmask |= bitM; // set the pin change interrupt mask through a pointer to PCMSK0 or 1 or 2 - PCICR |= 0x01 << port; // enable the interrupt - return 1; -} - - -/* Remove a pin to be handled (Arduino code) */ -uint8_t RemovePinChangeInterrupt(uint8_t rPin) { - volatile uint8_t *pcmask; - uint8_t bitM = digitalPinToBitMask(rPin); - uint8_t port = digitalPinToPort(rPin); - if (port == NOT_A_PORT) - return 0; - port -= 2; // from port (PB, PC, PD) to index in our array - pcmask = port_to_pcmask[port]; - *pcmask &= ~bitM; // clear the bit in the mask. - if (*pcmask == 0) { // if that's the last one, disable the interrupt. - PCICR &= ~(0x01 << port); - } - return 1; -} - - -// now set the arduino interrupt service routines and call the common handler with the port index number -ISR(PCINT0_vect) { - PCint(0); -} -ISR(PCINT1_vect) { - PCint(1); -} -ISR(PCINT2_vect) { - PCint(2); -} - -/* - common function for arduino pin change interrupt handlers. "port" is the PCINT port index (0-2) as passed from above, not PB, PC or PD which are mapped to 2-4 -*/ -static void PCint(uint8_t port) { - uint8_t bit; - uint8_t curr; - uint8_t delta; - short pinIndex; - uint32_t now = millis(); - - // get the pin states for the indicated port. - curr = *portInputRegister(port+2); // current pin states at port (add 2 to get from index to PB, PC or PD) - delta = (curr ^ PCintLast[port]) & *port_to_pcmask[port]; // xor gets bits that are different and & screens out non pcint pins - PCintLast[port] = curr; // store new pin state for next interrupt - - if (delta == 0) return; // no handled pin changed - - bit = 0x01; // start mit rightmost (least significant) bit in a port - for (uint8_t aPin = firstPin[port]; aPin <= lastPin[port]; aPin++) { // loop over each pin on the given port that changed - if (delta & bit) { // did this pin change? - pinIndex = allowedPins[aPin]; - if (pinIndex > 0) { // shound not be necessary but test anyway - doCount (pinIndex, ((curr & bit) > 0), now); // do the counting, history and so on - } - } - bit = bit << 1; // shift mask to go to next bit - } -} - - -#else -/* Add a pin to be handled (ESP8266 code) */ - -/* attachInterrupt needs to be given an individual function for each interrrupt . - * since we cant pass the pin value into the ISR or we need to use an - * internal function __attachInnterruptArg ... but then we need a fixed reference for the pin numbers ... -*/ -uint8_t AddPinChangeInterrupt(uint8_t rPin) { - switch(rPin) { - case 4: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR4, CHANGE); - break; - case 5: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR5, CHANGE); - break; - case 12: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR12, CHANGE); - break; - case 13: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR13, CHANGE); - break; - case 14: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR14, CHANGE); - break; - case 16: - attachInterrupt(digitalPinToInterrupt(rPin), ESPISR16, CHANGE); - break; - default: - PrintErrorMsg(); Output->println(F("attachInterrupt")); - } - return 1; -} - -void ESPISR4() { // ISR for real pin GPIO 4 / pinIndex 2 - doCount(2, digitalRead(4), millis()); - // called with pinIndex, level, now -} - -void ESPISR5() { // ISR for real pin GPIO 5 / pinIndex 1 - doCount(1, digitalRead(5), millis()); -} - -void ESPISR12() { // ISR for real pin GPIO 12 / pinIndex 6 - doCount(6, digitalRead(12), millis()); -} - -void ESPISR13() { // ISR for real pin GPIO 13 / pinIndex 7 - doCount(7, digitalRead(13), millis()); -} - -void ESPISR14() {// ISR for real pin GPIO 14 / pinIndex 5 - doCount(5, digitalRead(14), millis()); -} - -void ESPISR16() { // ISR for real pin GPIO 16 / pinIndex 0 - doCount(0, digitalRead(16), millis()); -} -#endif - - -void PrintErrorMsg() { - uint8_t len = strlen_P(errorStr); - char myChar; - for (unsigned char k = 0; k < len; k++) { - myChar = pgm_read_byte_near(errorStr + k); - Output->print(myChar); - } -} - - -void printVersionMsg() { - uint8_t len = strlen_P(versionStr); - char myChar; - for (unsigned char k = 0; k < len; k++) { - myChar = pgm_read_byte_near(versionStr + k); - Output->print(myChar); - } - Output->print(F(" on ")); - len = strlen_P(boardName); - for (unsigned char k = 0; k < len; k++) { - myChar = pgm_read_byte_near(boardName + k); - Output->print(myChar); - } - -#ifdef ARDUINO_BOARD - Output->print(F(" ")); - len = strlen_P(boardName1); - for (unsigned char k = 0; k < len; k++) { - myChar = pgm_read_byte_near(boardName1 + k); - Output->print(myChar); - } -#endif - - Output->print(F(" compiled ")); - len = strlen_P(compile_date); - for (unsigned char k = 0; k < len; k++) { - myChar = pgm_read_byte_near(compile_date + k); - Output->print(myChar); - } -} - - -void showIntervals() { - Output->print(F("I")); - Output->print(intervalMin / 1000); - Output->print(F(" ")); - Output->print(intervalMax / 1000); - Output->print(F(" ")); - Output->print(intervalSml / 1000); - Output->print(F(" ")); - Output->println(countMin); -} - - -#ifdef analogIR -void showThresholds() { - Output->print(F("T")); - Output->print(analogThresholdMin); - Output->print(F(" ")); - Output->println(analogThresholdMax); -} -#endif - - -void showPinConfig(short pinIndex) { - Output->print(F("P")); - Output->print(activePin[pinIndex]); - switch (pulseLevel[pinIndex]) { - case 1: Output->print(F(" rising")); break; - case 0: Output->print(F(" falling")); break; - default: Output->print(F(" -")); break; - } - if (pullup[pinIndex]) - Output->print(F(" pullup")); - Output->print(F(" min ")); - Output->print(pulseWidthMin[pinIndex]); -} - -#ifdef pulseHistory -void showPinHistory(short pinIndex, uint32_t now) { - uint8_t hi; - uint8_t start = (histIndex + 2) % MAX_HIST; - uint8_t count = 0; - uint32_t last; - boolean first = true; - - for (uint8_t i = 0; i < MAX_HIST; i++) { - hi = (start + i) % MAX_HIST; - if (histPin[hi] == pinIndex) - if (first || (last <= histTime[hi]+histLen[hi])) count++; - } - if (!count) { - // Output->println (F("M No Pin History")); - return; - } - - Output->print (F("H")); // start with H - Output->print (activePin[pinIndex]); // printed pin number - Output->print (F(" ")); - for (uint8_t i = 0; i < MAX_HIST; i++) { - hi = (start + i) % MAX_HIST; - if (histPin[hi] == pinIndex) { - if (first || (last <= histTime[hi]+histLen[hi])) { - if (!first) Output->print (F(", ")); - Output->print (histSeq[hi]); // sequence - Output->print (F("s")); - Output->print ((long) (histTime[hi] - now)); // time when level started - Output->print (F("/")); - Output->print (histLen[hi]); // length - Output->print (F("@")); - Output->print (histLevel[hi]); // level (0/1) - Output->print (histAct[hi]); // action - first = false; - } - last = histTime[hi]; - } - } - Output->println(); -} -#endif - -/* - lastCount[] is only modified here (count at time of last reporting) - intervalEnd[] is modified here and in ISR - disable interrupts in critcal moments to avoid garbage in var - intervalStart[] is modified only here or for very first Interrupt in ISR -*/ -void showPinCounter(short pinIndex, boolean showOnly, uint32_t now) { - uint32_t count, countDiff, realDiff; - uint32_t startT, endT, timeDiff, widthSum; - uint16_t rejCount, rejDiff; - uint8_t countIgn; - - noInterrupts(); // copy counters while they cant be changed in isr - startT = intervalStart[pinIndex]; // start of interval (typically first pulse) - endT = intervalEnd[pinIndex]; // end of interval (last unless not enough) - count = counter[pinIndex]; // get current counter (counts all pulses - rejCount = rejectCounter[pinIndex]; - countIgn = counterIgn[pinIndex]; // pulses that mark the beginning of an interval - widthSum = pulseWidthSum[pinIndex]; - interrupts(); - - timeDiff = endT - startT; // time between first and last impulse - realDiff = count - lastCount[pinIndex]; // pulses during intervall - countDiff = realDiff - countIgn; // ignore forst pulse after device restart - rejDiff = rejCount - lastRejCount[pinIndex]; - - if (!showOnly) { // real reporting sets the interval borders new - if((long)(now - (lastReport[pinIndex] + intervalMax)) >= 0) { - // intervalMax is over - if ((countDiff >= countMin) && (timeDiff > intervalSml) && (intervalMin != intervalMax)) { - // normal procedure - noInterrupts(); // vars could be modified in ISR as well - intervalStart[pinIndex] = endT; // time of last impulse becomes first in next - interrupts(); - } else { - // nothing counted or counts happened during a fraction of intervalMin only - noInterrupts(); // vars could be modified in ISR as well - intervalStart[pinIndex] = now; // start a new interval for next report now - intervalEnd[pinIndex] = now; // no last impulse, use now instead - interrupts(); - timeDiff = now - startT; // special handling - calculation ends now - } - } else if( ((long)(now - (lastReport[pinIndex] + intervalMin)) >= 0) - && (countDiff >= countMin) && (timeDiff > intervalSml)) { - // minInterval has elapsed and other conditions are ok - noInterrupts(); // vars could be modified in ISR as well - intervalStart[pinIndex] = endT; // time of last also time of first in next - interrupts(); - } else { - return; // intervalMin and Max not over - dont report yet - } - noInterrupts(); - counterIgn[pinIndex] = 0; - pulseWidthSum[pinIndex] = 0; - interrupts(); - lastCount[pinIndex] = count; // remember current count for next interval - lastRejCount[pinIndex] = rejCount; - lastReport[pinIndex] = now; // remember when we reported -#ifdef ESP8266 - delayedTcpReports = 0; -#endif - reportSequence[pinIndex]++; - } - Output->print(F("R")); // R Report - Output->print(activePin[pinIndex]); - Output->print(F(" C")); // C - Count - Output->print(count); - Output->print(F(" D")); // D - Count Diff (without pulse that marks the begin) - Output->print(countDiff); - Output->print(F("/")); // R - real Diff for long counter - includes first after restart - Output->print(realDiff); - Output->print(F(" T")); // T - Time - Output->print(timeDiff); - Output->print(F(" N")); // N - now - Output->print((long)now); - Output->print(F(",")); - Output->print(millisWraps); - Output->print(F(" X")); // X Reject - Output->print(rejDiff); - - if (!showOnly) { - Output->print(F(" S")); // S - Sequence number - Output->print(reportSequence[pinIndex]); - } - if (countDiff > 0) { - Output->print(F(" A")); - Output->print(widthSum / countDiff); - } - Output->println(); -#ifdef ESP8266 - if (tcpMode && !showOnly) { - Serial.print(F("D reported pin ")); - Serial.print(activePin[pinIndex]); - Serial.print(F(" sequence ")); - Serial.print(reportSequence[pinIndex]); - Serial.println(F(" over tcp ")); - } -#endif - -} - - -/* - report count and time for pins that are between min and max interval -*/ - -boolean reportDue() { - uint32_t now = millis(); - boolean doReport = false; // check if report needs to be called - if((now - lastReportCall) >= intervalMin) // works fine when millis wraps. - doReport = true; // intervalMin is over - else - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) - if (activePin[pinIndex] >= 0) - if((now - lastReport[pinIndex]) >= intervalMax) - doReport = true; // active pin has not been reported for langer than intervalMax - return doReport; -} - - - -void report() { - uint32_t now = millis(); -#ifdef ESP8266 - if (tcpMode && !Client1Connected && (delayedTcpReports < 3)) { - if(delayedTcpReports == 0 || ((long)(now - (lastDelayedTcpReports + (1 * 30 * 1000))) > 0)) { - Serial.print(F("D report called but tcp is disconnected - delaying (")); - Serial.print(delayedTcpReports); - Serial.print(F(")")); - Serial.print(F(" now ")); - Serial.print(now); - Serial.print(F(" last ")); - Serial.print(lastDelayedTcpReports); - Serial.print(F(" diff ")); - Serial.println(now - lastDelayedTcpReports); - delayedTcpReports++; - lastDelayedTcpReports = now; - return; - } else return; - } -#endif - - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) { // go through all observed pins as pinIndex - if (activePin[pinIndex] >= 0) { - showPinCounter (pinIndex, false, now); // report pin counters if necessary -#ifdef pulseHistory - if (devVerbose >= 5) - showPinHistory(pinIndex, now); // show pin history if verbose >= 5 -#endif - } - } - lastReportCall = now; // check again after intervalMin or if intervalMax is over for a pin -} - - -/* give status report in between if requested over serial input */ -void showCmd() { - uint32_t now = millis(); - Output->print(F("M Status: ")); - printVersionMsg(); - Output->println(); - - showIntervals(); -#ifdef analogIR - showThresholds(); -#endif - Output->print(F("V")); - Output->println(devVerbose); - - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) { - if (activePin[pinIndex] >= 0) { - showPinConfig(pinIndex); - Output->print(F(", ")); - showPinCounter(pinIndex, true, now); -#ifdef pulseHistory - showPinHistory(pinIndex, now); -#endif - } - } - readFromEEPROM(); - Output->print(F("M Next report in ")); - Output->print(lastReportCall + intervalMin - millis()); - Output->print(F(" milliseconds")); - Output->println(); -} - - -void helloCmd() { - uint32_t now = millis(); - Output->println(); - printVersionMsg(); - Output->print(F(" Hello, pins ")); - boolean first = true; - for (uint8_t aPin=0; aPin < MAX_APIN; aPin++) { - if (allowedPins[aPin] >= 0) { - if (!first) { - Output->print(F(",")); - } else { - first = false; - } - Output->print(aPin); - } - } - Output->print(F(" available")); - Output->print(F(" T")); - Output->print(now); - Output->print(F(",")); - Output->print(millisWraps); - Output->print(F(" B")); - Output->print(bootTime); - Output->print(F(",")); - Output->print(bootWraps); - - Output->println(); - showIntervals(); -#ifdef analogIR - showThresholds(); -#endif - Output->print(F("V")); - Output->println(devVerbose); - - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) { // go through all observed pins as pinIndex - if (activePin[pinIndex] >= 0) { - showPinConfig(pinIndex); - Output->println(); - } - } -} - - - -/* - handle add command. -*/ -void addCmd(uint16_t *values, uint8_t size) { - uint16_t pulseWidth; - uint32_t now = millis(); - - uint8_t aPin = values[0]; // values[0] is pin number - if (aPin >= MAX_APIN || allowedPins[aPin] < 0) { - PrintErrorMsg(); - Output->print(F("Illegal pin specification ")); - Output->println(aPin); - return; - }; - uint8_t pinIndex = allowedPins[aPin]; - uint8_t rPin = internalPins[pinIndex]; - - if (activePin[pinIndex] != aPin) { // in case this pin is not already active counting - #ifndef ESP8266 - uint8_t port = digitalPinToPort(rPin) - 2; - PCintLast[port] = *portInputRegister(port+2); - #endif - initPinVars(pinIndex, now); - activePin[pinIndex] = aPin; // save arduino pin number and flag this pin as active for reporting - } - - if (values[1] < 2 || values[1] > 3) { // values[1] is level (3->rising / 2->falling) - PrintErrorMsg(); - Output->print(F("Illegal pulse level specification for pin ")); - Output->println(aPin); - } - pulseLevel[pinIndex] = (values[1] == 3); // 2 = falling -> pulseLevel 0, 3 = rising -> pulseLevel 1 - -#ifdef analogIR - if (size > 2 && values[2] && !analogPins[pinIndex]) { -#else - if (size > 2 && values[2]) { -#endif - pinMode (rPin, INPUT_PULLUP); // values[2] is pullup flag - pullup[pinIndex] = 1; - } else { - pinMode (rPin, INPUT); - pullup[pinIndex] = 0; - } - - if (size > 3 && values[3] > 0) { // value 3 is min length - pulseWidth = values[3]; - } else { - pulseWidth = 2; - } - - /* todo: add upper and lower limits for analog pins as option here and in Fhem module */ - - pulseWidthMin[pinIndex] = pulseWidth; - -#ifdef analogIR - if (!analogPins[pinIndex]) { -#endif - if (!AddPinChangeInterrupt(rPin)) { // add Pin Change Interrupt - PrintErrorMsg(); - Output->println(F("AddInt")); - return; - } -#ifdef analogIR - } -#endif - Output->print(F("M defined ")); - showPinConfig(pinIndex); - Output->println(); -} - - -/* - handle rem command. -*/ -void removeCmd(uint16_t *values, uint8_t size) { - uint8_t aPin = values[0]; - if (size < 1 || aPin >= MAX_APIN || allowedPins[aPin] < 0) { - PrintErrorMsg(); - Output->print(F("Illegal pin specification ")); - Output->println(aPin); - return; - }; - uint8_t pinIndex = allowedPins[aPin]; - -#ifdef analogIR - if (!analogPins[pinIndex]) { -#endif -#ifdef ESP8266 - detachInterrupt(digitalPinToInterrupt(internalPins[pinIndex])); -#else - if (!RemovePinChangeInterrupt(internalPins[pinIndex])) { - PrintErrorMsg(); Output->println(F("RemInt")); - return; - } -#endif -#ifdef analogIR - } -#endif - initPinVars(pinIndex, 0); - Output->print(F("M removed ")); - Output->println(aPin); -} - - - -void intervalCmd(uint16_t *values, uint8_t size) { - /*Serial.print(F("D int ptr is ")); - Serial.println(size);*/ - if (size < 4) { // i command always gets 4 values: min, max, sml, cntMin - PrintErrorMsg(); - Output->print(F("size")); - Output->println(); - return; - } - if (values[0] < 1 || values[0] > 3600) { - PrintErrorMsg(); Output->println(values[0]); - return; - } - intervalMin = (long)values[0] * 1000; - - if (values[1] < 1 || values[1] > 3600) { - PrintErrorMsg(); Output->println(values[1]); - return; - } - intervalMax = (long)values[1]* 1000; - - if (values[2] > 3600) { - PrintErrorMsg(); Output->println(values[2]); - return; - } - intervalSml = (long)values[2] * 1000; - - if (values[3] > 100) { - PrintErrorMsg(); Output->println(values[3]); - return; - } - countMin = values[3]; - - Output->print(F("M intervals set to ")); - Output->print(values[0]); - Output->print(F(" ")); - Output->print(values[1]); - Output->print(F(" ")); - Output->print(values[2]); - Output->print(F(" ")); - Output->print(values[3]); - Output->println(); -} - -#ifdef analogIR -void thresholdCmd(uint16_t *values, uint8_t size) { - /*Serial.print(F("D threshold size ")); - Serial.print(size); - Serial.print(F(" v0 ")); - Serial.print(values[0]); - Serial.print(F(" v1 ")); - Serial.print(values[1]); - Serial.println();*/ - - if (size < 2) { // t command gets 2 values: min, max - PrintErrorMsg(); - Output->print(F("size")); - Output->println(); - return; - } - if (values[0] < 1 || values[0] > 1023) { - PrintErrorMsg(); Output->println(values[0]); - return; - } - analogThresholdMin = (int)values[0]; - - if (values[1] < 1 || values[1] > 1023) { - PrintErrorMsg(); Output->println(values[1]); - return; - } - analogThresholdMax = (int)values[1]; - - Output->print(F("M analog thresholds set to ")); - Output->print(values[0]); - Output->print(F(" ")); - Output->print(values[1]); - Output->println(); -} -#endif - - -void keepAliveCmd(uint16_t *values, uint8_t size) { - if (values[0] == 1 && size > 0) { - Output->println(F("alive")); - } -#ifdef ESP8266 - if (values[0] == 1 && size > 0 && size < 3 && Client1.connected()) { - tcpMode = true; - if (size == 2) { - expectK = millis() + values[1] * 2500; - } else { - expectK = millis() + 600000; // 10 Minutes if nothing sent (should not happen) - } - } -#endif -} - - -#ifdef ESP8266 -void quitCmd() { - if (Client1.connected()) { - Client1.println(F("closing connection")); - Client1.stop(); - tcpMode = false; - Serial.println(F("M TCP connection closed")); - } else { - Serial.println(F("M TCP not connected")); - } -} -#endif - - - -void updateEEPROM(int &address, byte value) { - if( EEPROM.read(address) != value){ - EEPROM.write(address, value); - } - address++; -} - - -void updateEEPROMSlot(int &address, char cmd, int v1, int v2, int v3, int v4) { - updateEEPROM(address, cmd); // I / A - updateEEPROM(address, v1 & 0xff); - updateEEPROM(address, v1 >> 8); - updateEEPROM(address, v2 & 0xff); - updateEEPROM(address, v2 >> 8); - updateEEPROM(address, v3 & 0xff); - updateEEPROM(address, v3 >> 8); - updateEEPROM(address, v4 & 0xff); - updateEEPROM(address, v4 >> 8); -} - -/* todo: include analogPins as well as analog limits in save / restore */ - -void saveToEEPROMCmd() { - int address = 0; - uint8_t slots = 1; - updateEEPROM(address, 'C'); - updateEEPROM(address, 'f'); - updateEEPROM(address, 'g'); - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) - if (activePin[pinIndex] >= 0) slots ++; -#ifdef analogIR - slots ++; -#endif - updateEEPROM(address, slots); // number of defined pins + intervall definition - updateEEPROMSlot(address, 'I', (uint16_t)(intervalMin / 1000), (uint16_t)(intervalMax / 1000), - (uint16_t)(intervalSml / 1000), (uint16_t)countMin); -#ifdef analogIR - updateEEPROMSlot(address, 'T', (uint16_t)analogThresholdMin, (uint16_t)analogThresholdMax, 0, 0); -#endif - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) - if (activePin[pinIndex] >= 0) - updateEEPROMSlot(address, 'A', (uint16_t)activePin[pinIndex], (uint16_t)(pulseLevel[pinIndex] ? 3:2), - (uint16_t)pullup[pinIndex], (uint16_t)pulseWidthMin[pinIndex]); -#ifdef ESP8266 - EEPROM.commit(); -#endif - Serial.print(F("config saved, ")); - Serial.print(slots); - Serial.print(F(", ")); - Serial.println(address); -} - - -void readFromEEPROM() { - int address = 0; - uint16_t v1, v2, v3, v4; - char cmd; - if (EEPROM.read(address) != 'C' || EEPROM.read(address+1) != 'f' || EEPROM.read(address+2) != 'g') { - Output->println(F("M no config in EEPROM")); - return; - } - address = 3; - uint8_t slots = EEPROM.read(address++); - if (slots > MAX_PIN + 2) { - Output->println(F("M illegal config in EEPROM")); - return; - } - Output->println(); - Output->print(F("M EEPROM Config: ")); - Output->print((char) EEPROM.read(0)); - Output->print((char) EEPROM.read(1)); - Output->print((char) EEPROM.read(2)); - Output->print(F(" Slots: ")); - Output->print((int) EEPROM.read(3)); - Output->println(); - for (uint8_t slot=0; slot < slots; slot++) { - cmd = EEPROM.read(address); - v1 = EEPROM.read(address+1) + (((uint16_t)EEPROM.read(address+2)) << 8); - v2 = EEPROM.read(address+3) + (((uint16_t)EEPROM.read(address+4)) << 8); - v3 = EEPROM.read(address+5) + (((uint16_t)EEPROM.read(address+6)) << 8); - v4 = EEPROM.read(address+7) + (((uint16_t)EEPROM.read(address+8)) << 8); - address = address + 9; - Output->print(F("M Slot: ")); - Output->print(cmd); - Output->print(F(" ")); - Output->print(v1); - Output->print(F(",")); - Output->print(v2); - Output->print(F(",")); - Output->print(v3); - Output->print(F(",")); - Output->print(v4); - Output->println(); - } -} - - -void restoreFromEEPROM() { - int address = 0; - if (EEPROM.read(address) != 'C' || EEPROM.read(address+1) != 'f' || EEPROM.read(address+2) != 'g') { - Serial.println(F("M no config in EEPROM")); - return; - } - address = 3; - uint8_t slots = EEPROM.read(address++); - if (slots > MAX_PIN + 1 || slots < 1) { - Serial.println(F("M illegal config in EEPROM")); - return; - } - Serial.println(F("M restoring config from EEPROM")); - char cmd; - for (uint8_t slot=0; slot < slots; slot++) { - cmd = EEPROM.read(address); - commandData[0] = EEPROM.read(address+1) + (((uint16_t)EEPROM.read(address+2)) << 8); - commandData[1] = EEPROM.read(address+3) + (((uint16_t)EEPROM.read(address+4)) << 8); - commandData[2] = EEPROM.read(address+5) + (((uint16_t)EEPROM.read(address+6)) << 8); - commandData[3] = EEPROM.read(address+7) + (((uint16_t)EEPROM.read(address+8)) << 8); - address = address + 9; - commandDataPointer = 4; - if (cmd == 'I') intervalCmd(commandData, commandDataPointer); -#ifdef analogIR - if (cmd == 'T') thresholdCmd(commandData, commandDataPointer); -#endif - if (cmd == 'A') addCmd(commandData, commandDataPointer); - } - commandDataPointer = 0; - value = 0; - for (uint8_t i=0; i < MAX_INPUT_NUM; i++) - commandData[i] = 0; - -} - - -void handleInput(char c) { - if (c == ',') { // Komma input, last value is finished - if (commandDataPointer < (MAX_INPUT_NUM - 1)) { - commandData[commandDataPointer++] = value; - value = 0; - } - } - else if ('0' <= c && c <= '9') { // digit input - value = 10 * value + c - '0'; - } - else if ('a' <= c && c <= 'z') { // letter input is command - - if (devVerbose > 0) { - commandData[commandDataPointer] = value; - Serial.print(F("D got ")); - for (short v = 0; v <= commandDataPointer; v++) { - if (v > 0) Serial.print(F(",")); - Serial.print(commandData[v]); - } - Serial.print(c); - Serial.print(F(" size ")); - Serial.print(commandDataPointer+1); - Serial.println(); - } - - switch (c) { - case 'a': // add a pin - commandData[commandDataPointer] = value; - addCmd(commandData, commandDataPointer+1); - break; - case 'd': // delete a pin - commandData[commandDataPointer] = value; - removeCmd(commandData, commandDataPointer+1); - break; - case 'e': // save to EEPROM - saveToEEPROMCmd(); - break; - case 'f': // flash ota - // OTA flash from HTTP Server - break; - case 'h': // hello - helloCmd(); - break; - case 'i': // interval - commandData[commandDataPointer] = value; - intervalCmd(commandData, commandDataPointer+1); - break; - case 'k': // keep alive - commandData[commandDataPointer] = value; - keepAliveCmd(commandData, commandDataPointer+1); - break; -#ifdef ESP8266 - case 'q': // quit - quitCmd(); - break; -#endif - case 'r': // reset - initialize(); - break; - case 's': // show - showCmd(); - break; -#ifdef analogIR - case 't': // thresholds for analog pin - commandData[commandDataPointer] = value; - thresholdCmd(commandData, commandDataPointer+1); - break; -#endif - case 'v': // dev verbose - if (value < 255) { - devVerbose = value; - Output->print(F("M devVerbose set to ")); - Output->println(value); - } else { - Output->println(F("M illegal value passed for devVerbose")); - } - break; - default: - break; - } - commandDataPointer = 0; - value = 0; - for (uint8_t i=0; i < MAX_INPUT_NUM; i++) - commandData[i] = 0; - //Serial.println(F("D End of command")); - } -} - -#ifdef debugCfg -/* do sample config so we don't need to configure pins after each reboot */ -void debugSetup() { - commandData[0] = 10; - commandData[1] = 20; - commandData[2] = 3; - commandData[3] = 0; - commandDataPointer = 4; - intervalCmd(commandData, commandDataPointer); - - commandData[0] = 1; // pin 1 - commandData[1] = 2; // falling - commandData[2] = 1; // pullup - commandData[3] = 30; // min Length - commandDataPointer = 4; - addCmd(commandData, commandDataPointer); - - commandData[0] = 2; // pin 2 - addCmd(commandData, commandDataPointer); - -/* - commandData[0] = 5; // pin 5 - addCmd(commandData, commandDataPointer); - - commandData[0] = 6; // pin 6 - addCmd(commandData, commandDataPointer); -*/ -} -#endif - - -#ifdef debugPins -void debugPinChanges() { - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) { - short aPin = activePin[pinIndex]; - if (aPin > 0) { - uint8_t rPin = internalPins[pinIndex]; - uint8_t pinState = digitalRead(rPin); - - if (pinState != lastState[pinIndex]) { - lastState[pinIndex] = pinState; - Output->print(F("M pin ")); - Output->print(aPin); - Output->print(F(" (internal ")); - Output->print(rPin); - Output->print(F(")")); - Output->print(F(" changed to ")); - Output->print(pinState); -#ifdef pulseHistory - Output->print(F(", histIdx ")); - Output->print(histIndex); -#endif - Output->print(F(", count ")); - Output->print(counter[pinIndex]); - Output->print(F(", reject ")); - Output->print(rejectCounter[pinIndex]); - Output->println(); - } - } - } -} -#endif - - -#ifdef ESP8266 -void connectWiFi() { - Client1Connected = false; - Client2Connected = false; - - // Connect to WiFi network - WiFi.mode(WIFI_STA); - delay (1000); - if (WiFi.status() != WL_CONNECTED) { - Serial.print(F("M Connecting WiFi to ")); - Serial.println(ssid); - WiFi.begin(ssid, password); // authenticate - while (WiFi.status() != WL_CONNECTED) { - Serial.print(F("M Status is ")); - switch (WiFi.status()) { - case WL_CONNECT_FAILED: - Serial.println(F("Connect Failed")); - break; - case WL_CONNECTION_LOST: - Serial.println(F("Connection Lost")); - break; - case WL_DISCONNECTED: - Serial.println(F("Disconnected")); - break; - case WL_CONNECTED: - Serial.println(F("Connected")); - break; - default: - Serial.println(WiFi.status()); - } - delay(1000); - } - Serial.println(); - Serial.print(F("M WiFi connected to ")); - Serial.println(WiFi.SSID()); - } else { - Serial.print(F("M WiFi already connected to ")); - Serial.println(WiFi.SSID()); - } - - // Start the server - Server.begin(); - Serial.println(F("M Server started")); - - // Print the IP address - Serial.print(F("M Use this IP: ")); - Serial.println(WiFi.localIP()); -} - - -void handleConnections() { - IPAddress remote; - uint32_t now = millis(); - - if (Client1Connected) { - if((long)(now - expectK) >= 0) { - Serial.println(F("M no keepalive from Client - disconnecting")); - Client1.stop(); - } - } - if (Client1.available()) { - handleInput(Client1.read()); - //Serial.println(F("M new Input over TCP")); - } - if (Client1.connected()) { - Client2 = Server.available(); - if (Client2) { - Client2.println(F("connection already busy")); - remote = Client2.remoteIP(); - Client2.stop(); - Serial.print(F("M second connection from ")); - Serial.print(remote); - Serial.println(F(" rejected")); - } - } else { - if (Client1Connected) { // client used to be connected, now disconnected - Client1Connected = false; - Output = &Serial; - Serial.println(F("M connection to client lost")); - } - Client1 = Server.available(); - if (Client1) { // accepting new connection - remote = Client1.remoteIP(); - Serial.print(F("M new connection from ")); - Serial.print(remote); - Serial.println(F(" accepted")); - Client1Connected = true; - Output = &Client1; - expectK = now + 600000; // max 10 Minutes (to be checked on Fhem module side as well - helloCmd(); // say hello to client - } - } -} -#endif - - -void handleTime() { - uint32_t now = millis(); - if (now < lastMillis) millisWraps++; - lastMillis = now; -} - - -#ifdef analogIR -void detectTrigger(int val) { - uint8_t nextState = triggerState; - if (val > analogThresholdMax) { - nextState = 1; - } else if (val < analogThresholdMin) { - nextState = 0; - } - if (nextState != triggerState) { - triggerState = nextState; -#ifdef ledOutPin - digitalWrite(ledOutPin, triggerState); -#endif - short pinIndex = allowedPins[analogInPin]; // ESP pin A0 (pinIndex 8, internal 17) or Arduino A7 (pinIndex 17, internal 21) - uint32_t now = millis(); - doCount (pinIndex, triggerState, now); // do the counting, history and so on - - if (devVerbose >= 10) { - Output->print(F("M called doCount with pinIndex ")); - Output->print(pinIndex); - Output->print(F(" and triggerState")); - Output->print(triggerState); - Output->println(); - } - -#ifdef debugPins - if (devVerbose >= 10) { - short rPin = internalPins[pinIndex]; - Output->print(F("M pin ")); - Output->print(analogInPin); - Output->print(F(" (index ")); - Output->print(pinIndex); - Output->print(F(", internal ")); - Output->print(rPin); - Output->print(F(" ) ")); - Output->print(F(" to ")); - Output->print(nextState); -#ifdef pulseHistory - Output->print(F(" histIdx ")); - Output->print(histIndex); -#endif - Output->print(F(" count ")); - Output->print(counter[pinIndex]); - Output->print(F(" reject ")); - Output->print(rejectCounter[pinIndex]); - Output->println(); - } -#endif - } -} -#endif - -void initPinVars(short pinIndex, uint32_t now) { - uint8_t level = 0; - activePin[pinIndex] = -1; // inactive (-1) - initialized[pinIndex] = false; // no pulse seen yet - pulseWidthMin[pinIndex] = 0; // min pulse length - counter[pinIndex] = 0; // counter to 0 - counterIgn[pinIndex] = 0; - lastCount[pinIndex] = 0; - rejectCounter[pinIndex] = 0; - lastRejCount[pinIndex] = 0; - intervalStart[pinIndex] = now; // time vars - intervalEnd[pinIndex] = now; - lastChange[pinIndex] = now; - lastReport[pinIndex] = now; - reportSequence[pinIndex] = 0; -#ifdef analogIR - if (!analogPins[pinIndex]) { - level = digitalRead(internalPins[pinIndex]); - } -#else - level = digitalRead(internalPins[pinIndex]); -#endif - lastLevel[pinIndex] = level; -#ifdef debugPins - lastState[pinIndex] = level; // for debug output -#endif - /* todo: add analogPins, upper and lower limits for analog */ -} - - -void initialize() { - uint32_t now = millis(); - for (uint8_t pinIndex=0; pinIndex < MAX_PIN; pinIndex++) { - initPinVars(pinIndex, now); - } - lastReportCall = now; // time for first output after intervalMin from now - devVerbose = 0; -#ifndef ESP8266 - for (uint8_t port=0; port <= 2; port++) { - PCintLast[port] = *portInputRegister(port+2); // current pin states at port for PCInt handler - } -#endif -#ifdef debugCfg - debugSetup(); -#endif - restoreFromEEPROM(); - bootTime = millis(); // with boot / reset time - bootWraps = millisWraps; -#ifdef ESP8266 - expectK = now + 600000; // max 10 Minutes (to be checked on Fhem module side as well -#endif -} - - -void setup() { - Serial.begin(SERIAL_SPEED); // initialize serial -#ifdef ESP8266 - EEPROM.begin(100); -#endif - delay (500); - interrupts(); - Serial.println(); - Output = &Serial; - millisWraps = 0; - lastMillis = millis(); - initialize(); -#ifdef analogIR - pinMode(irOutPin, OUTPUT); -#ifdef ledOutPin - pinMode(ledOutPin, OUTPUT); -#endif -#endif - helloCmd(); // started message to serial -#ifdef ESP8266 - connectWiFi(); -#endif -} - - -/* Main Loop */ -void loop() { - handleTime(); - if (Serial.available()) handleInput(Serial.read()); -#ifdef ESP8266 - handleConnections(); -#endif - -#ifdef analogIR - short AIndex = allowedPins[analogInPin]; - if (AIndex >= 0 && activePin[AIndex] >= 0) { - digitalWrite(irOutPin, LOW); - // wait 10 milliseconds - delay(10); - // read the analog in value: - sensorValueOff = analogRead(analogInPin); - // turn IR LED on - digitalWrite(irOutPin, HIGH); - delay(10); - // read the analog in value: - sensorValueOn = analogRead(analogInPin); - detectTrigger (sensorValueOn - sensorValueOff); - if (devVerbose >= 20) { - Output->print(F("L")); - - Output->print(sensorValueOn); - Output->print(F(",")); - Output->print(sensorValueOff); - Output->print(F("->")); - - Output->println(sensorValueOn - sensorValueOff); - } - } -#endif - -#ifdef debugPins - if (devVerbose >= 10) { - debugPinChanges(); - } -#endif - - if (reportDue()) { - report(); - } -}