mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 06:39:11 +00:00
42db837cf7
git-svn-id: https://svn.fhem.de/fhem/trunk@20181 2b470e98-0d58-463d-a4d8-8e2adae1ed80
1772 lines
67 KiB
C++
Executable File
1772 lines
67 KiB
C++
Executable File
/*
|
|
* 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
|
|
20.7.19 - nicer debug output for analog leves
|
|
21.7.19 - V3.30 replace delay during analog read with millis() logic, optimize waiting times for analog read
|
|
10.8.19 - V3.32 add ICACHE_RAM_ATTR for ISRs and remove remaining long casts (bug) when handling time
|
|
12.8.19 - V3.33 fix handling of keepalive timeouts when millis wraps
|
|
V3.34 add RSSI output when devVerbose >= 5 in kealive responses
|
|
|
|
ToDo / Ideas:
|
|
make analogInterval available in Fhem
|
|
save analogInterval to Flash
|
|
detect analog Threasholds automatically and adjust over time
|
|
make wifi ssid / secret configurable
|
|
|
|
*/
|
|
|
|
/* Remove this before compiling */
|
|
/* #define TestConfig // include my SSID / secret
|
|
*/
|
|
|
|
|
|
/* 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 <EEPROM.h>
|
|
|
|
const char versionStr[] PROGMEM = "ArduCounter V3.34";
|
|
|
|
#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
|
|
uint32_t lastAnalogRead; // millis() at last analog read
|
|
uint16_t analogReadInterval = 20; // interval at which to read analog values
|
|
uint8_t analogReadState = 0; // to keep track of switching LED on/off
|
|
|
|
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 <ESP8266WiFi.h> // =============================
|
|
|
|
#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
|
|
uint16_t keepAliveTimeout = 200;
|
|
uint32_t lastKeepAlive;
|
|
#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
|
|
|
|
|
|
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);
|
|
}
|
|
bootTime = now; // with boot / reset time
|
|
bootWraps = millisWraps;
|
|
lastAnalogRead = 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();
|
|
#ifdef ESP8266
|
|
lastKeepAlive = now;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
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 ...
|
|
*/
|
|
|
|
void ICACHE_RAM_ATTR ESPISR4() { // ISR for real pin GPIO 4 / pinIndex 2
|
|
doCount(2, digitalRead(4), millis());
|
|
// called with pinIndex, level, now
|
|
}
|
|
|
|
void ICACHE_RAM_ATTR ESPISR5() { // ISR for real pin GPIO 5 / pinIndex 1
|
|
doCount(1, digitalRead(5), millis());
|
|
}
|
|
|
|
void ICACHE_RAM_ATTR ESPISR12() { // ISR for real pin GPIO 12 / pinIndex 6
|
|
doCount(6, digitalRead(12), millis());
|
|
}
|
|
|
|
void ICACHE_RAM_ATTR ESPISR13() { // ISR for real pin GPIO 13 / pinIndex 7
|
|
doCount(7, digitalRead(13), millis());
|
|
}
|
|
|
|
void ICACHE_RAM_ATTR ESPISR14() {// ISR for real pin GPIO 14 / pinIndex 5
|
|
doCount(5, digitalRead(14), millis());
|
|
}
|
|
|
|
void ICACHE_RAM_ATTR ESPISR16() { // ISR for real pin GPIO 16 / pinIndex 0
|
|
doCount(0, digitalRead(16), millis());
|
|
}
|
|
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
|
|
void PrintErrorMsg() {
|
|
Output->print(F("Error: "));
|
|
}
|
|
|
|
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 "));
|
|
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
|
|
#ifdef ARDUINO_AVR_NANO
|
|
Output->print(F("NANO"));
|
|
#else
|
|
Output->print(F("UNO"));
|
|
#endif
|
|
#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
|
|
Output->print(F("Leonardo"));
|
|
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
|
Output->print(F("Mega"));
|
|
#elif defined(ESP8266)
|
|
Output->print(F("ESP8266"));
|
|
#else
|
|
Output->print(F("UNKNOWN"));
|
|
#endif
|
|
#ifdef ARDUINO_BOARD
|
|
Output->print(F(" "));
|
|
Output->print(F(ARDUINO_BOARD));
|
|
#endif
|
|
Output->print(F(" compiled "));
|
|
Output->print(F(__DATE__ " " __TIME__));
|
|
#if defined(ESP8266)
|
|
Output->print(F(" with core version "));
|
|
Output->print(F(ARDUINO_ESP8266_RELEASE));
|
|
#endif
|
|
}
|
|
|
|
|
|
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((now - lastReport[pinIndex]) > intervalMax) {
|
|
// 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 (((now - lastReport[pinIndex]) > intervalMin)
|
|
&& (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(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 || ((now - lastDelayedTcpReports) > 30000)) {
|
|
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
|
|
}
|
|
}
|
|
showEEPROM();
|
|
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("interval expects 4 values but got "));
|
|
Output->println(size);
|
|
return;
|
|
}
|
|
if (values[0] < 1 || values[0] > 3600) {
|
|
PrintErrorMsg(); Output->println(values[0]);
|
|
return;
|
|
}
|
|
intervalMin = (uint32_t)values[0] * 1000;
|
|
|
|
if (values[1] < 1 || values[1] > 3600) {
|
|
PrintErrorMsg(); Output->println(values[1]);
|
|
return;
|
|
}
|
|
intervalMax = (uint32_t)values[1]* 1000;
|
|
|
|
if (values[2] > 3600) {
|
|
PrintErrorMsg(); Output->println(values[2]);
|
|
return;
|
|
}
|
|
intervalSml = (uint32_t)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->println(values[3]);
|
|
}
|
|
|
|
#ifdef analogIR
|
|
void thresholdCmd(uint16_t *values, uint8_t size) {
|
|
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->println(values[1]);
|
|
}
|
|
|
|
void waitCmd(uint16_t *values, uint8_t size) {
|
|
if (size < 1) { // w command gets 1 value: analogReadInterval
|
|
PrintErrorMsg();
|
|
Output->print(F("size"));
|
|
Output->println();
|
|
return;
|
|
}
|
|
if (values[0] < 1 || values[0] > 999) {
|
|
PrintErrorMsg(); Output->println(values[0]);
|
|
return;
|
|
}
|
|
analogReadInterval = (int)values[0];
|
|
|
|
Output->print(F("M analog read delay set to "));
|
|
Output->println(values[0]);
|
|
}
|
|
#endif
|
|
|
|
|
|
void devVerboseCmd(uint16_t *values, uint8_t size) {
|
|
if (size < 1) { // v command gets 1 value: verbose level
|
|
PrintErrorMsg();
|
|
Output->print(F("size"));
|
|
Output->println();
|
|
return;
|
|
}
|
|
if (values[0] > 50) {
|
|
PrintErrorMsg(); Output->println(values[0]);
|
|
return;
|
|
}
|
|
devVerbose = values[0];
|
|
Output->print(F("M devVerbose set to "));
|
|
Output->println(values[0]);
|
|
}
|
|
|
|
|
|
void keepAliveCmd(uint16_t *values, uint8_t size) {
|
|
uint32_t now = millis();
|
|
|
|
if (values[0] == 1 && size > 0) {
|
|
Output->print(F("alive"));
|
|
#ifdef ESP8266
|
|
if (devVerbose >=5) {
|
|
Output->print(F(" RSSI "));
|
|
Output->print(WiFi.RSSI());
|
|
}
|
|
|
|
if (values[0] == 1 && size > 0 && size < 3 && Client1.connected()) {
|
|
tcpMode = true;
|
|
if (size == 2) {
|
|
keepAliveTimeout = values[1]; // timeout in seconds (on ESP side we use it times 3)
|
|
} else {
|
|
keepAliveTimeout = 200; // *3*1000 gives 10 minutes if nothing sent (should not happen)
|
|
}
|
|
}
|
|
lastKeepAlive = now;
|
|
#endif
|
|
Output->println();
|
|
}
|
|
}
|
|
|
|
|
|
#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 showEEPROM() {
|
|
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
|
|
commandData[commandDataPointer] = value;
|
|
devVerboseCmd(commandData, commandDataPointer+1);
|
|
break;
|
|
#ifdef analogIR
|
|
case 'w': // wait - delay between analog reads
|
|
commandData[commandDataPointer] = value;
|
|
waitCmd(commandData, commandDataPointer+1);
|
|
break;
|
|
#endif
|
|
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((now - lastKeepAlive) > (keepAliveTimeout * 3000)) {
|
|
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;
|
|
lastKeepAlive = now;
|
|
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;
|
|
if (ledOutPin)
|
|
digitalWrite(ledOutPin, triggerState);
|
|
|
|
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
|
|
|
|
#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
|
|
}
|
|
}
|
|
|
|
void readAnalog() {
|
|
short AIndex = allowedPins[analogInPin];
|
|
if (AIndex >= 0 && activePin[AIndex] >= 0) { // analog Pin active?
|
|
uint32_t now = millis();
|
|
uint16_t interval2 = analogReadInterval + 2;
|
|
uint16_t interval3 = analogReadInterval + 4;
|
|
if ((now - lastAnalogRead) > analogReadInterval) { // time for next analog read?
|
|
switch (analogReadState) {
|
|
case 0: // initial state
|
|
digitalWrite(irOutPin, LOW); // switch IR LED is off
|
|
analogReadState = 1;
|
|
break;
|
|
case 1: // wait before measuring
|
|
if ((now - lastAnalogRead) < interval2)
|
|
return;
|
|
analogReadState = 2;
|
|
break;
|
|
case 2:
|
|
sensorValueOff = analogRead(analogInPin); // read the analog in value (off)
|
|
analogReadState = 3;
|
|
break;
|
|
case 3:
|
|
digitalWrite(irOutPin, HIGH); // turn IR LED on
|
|
analogReadState = 4;
|
|
break;
|
|
case 4: // wait again before measuring
|
|
if ((now - lastAnalogRead) < interval3)
|
|
return;
|
|
analogReadState = 5;
|
|
break;
|
|
case 5:
|
|
sensorValueOn = analogRead(analogInPin); // read the analog in value (on)
|
|
analogReadState = 0;
|
|
lastAnalogRead = now;
|
|
detectTrigger (sensorValueOn - sensorValueOff);
|
|
if (devVerbose >= 25) {
|
|
char line[20];
|
|
sprintf(line, "L%4d, %4d -> % 4d", sensorValueOn, sensorValueOff, sensorValueOn - sensorValueOff);
|
|
Output->println(line);
|
|
} else if (devVerbose >= 20) {
|
|
Output->print(F("L"));
|
|
Output->println(sensorValueOn - sensorValueOff);
|
|
}
|
|
break;
|
|
default:
|
|
analogReadState = 0;
|
|
Output->println(F("D error: wrong analog read state"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#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);
|
|
if (ledOutPin)
|
|
pinMode(ledOutPin, OUTPUT);
|
|
#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
|
|
readAnalog();
|
|
#endif
|
|
#ifdef debugPins
|
|
if (devVerbose >= 10) {
|
|
debugPinChanges();
|
|
}
|
|
#endif
|
|
if (reportDue()) {
|
|
report();
|
|
}
|
|
}
|