2020-07-03 18:35:02 +02:00
/*
* © 2020 , Chris Harlow . All rights reserved .
*
* This file is part of Asbelos DCC API
*
* This is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* It is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with CommandStation . If not , see < https : //www.gnu.org/licenses/>.
*/
2020-06-03 15:26:49 +02:00
/**********************************************************************
DCC + + BASE STATION supports Sensor inputs that can be connected to any Arduino Pin
not in use by this program . Sensors can be of any type ( infrared , magentic , mechanical . . . ) .
The only requirement is that when " activated " the Sensor must force the specified Arduino
Pin LOW ( i . e . to ground ) , and when not activated , this Pin should remain HIGH ( e . g . 5 V ) ,
or be allowed to float HIGH if use of the Arduino Pin ' s internal pull - up resistor is specified .
To ensure proper voltage levels , some part of the Sensor circuitry
MUST be tied back to the same ground as used by the Arduino .
The Sensor code below utilizes exponential smoothing to " de-bounce " spikes generated by
mechanical switches and transistors . This avoids the need to create smoothing circuitry
for each sensor . You may need to change these parameters through trial and error for your specific sensors .
To have this sketch monitor one or more Arduino pins for sensor triggers , first define / edit / delete
sensor definitions using the following variation of the " S " command :
< S ID PIN PULLUP > : creates a new sensor ID , with specified PIN and PULLUP
if sensor ID already exists , it is updated with specificed PIN and PULLUP
returns : < O > if successful and < X > if unsuccessful ( e . g . out of memory )
< S ID > : deletes definition of sensor ID
returns : < O > if successful and < X > if unsuccessful ( e . g . ID does not exist )
< S > : lists all defined sensors
returns : < Q ID PIN PULLUP > for each defined sensor or < X > if no sensors defined
where
ID : the numeric ID ( 0 - 32767 ) of the sensor
PIN : the arduino pin number the sensor is connected to
PULLUP : 1 = use internal pull - up resistor for PIN , 0 = don ' t use internal pull - up resistor for PIN
Once all sensors have been properly defined , use the < E > command to store their definitions to EEPROM .
If you later make edits / additions / deletions to the sensor definitions , you must invoke the < E > command if you want those
new definitions updated in the EEPROM . You can also clear everything stored in the EEPROM by invoking the < e > command .
All sensors defined as per above are repeatedly and sequentially checked within the main loop of this sketch .
If a Sensor Pin is found to have transitioned from one state to another , one of the following serial messages are generated :
< Q ID > - for transition of Sensor ID from HIGH state to LOW state ( i . e . the sensor is triggered )
< q ID > - for transition of Sensor ID from LOW state to HIGH state ( i . e . the sensor is no longer triggered )
Depending on whether the physical sensor is acting as an " event-trigger " or a " detection-sensor, " you may
decide to ignore the < q ID > return and only react to < Q ID > triggers .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-10-25 23:52:22 +01:00
# include "StringFormatter.h"
2020-06-03 15:26:49 +02:00
# include "Sensors.h"
# include "EEStore.h"
2020-08-15 12:32:32 +02:00
2020-06-03 15:26:49 +02:00
2020-10-25 23:52:22 +01:00
///////////////////////////////////////////////////////////////////////////////
//
2020-10-26 00:56:25 +01:00
// checks one defined sensors and prints _changed_ sensor state
2020-10-25 23:52:22 +01:00
// to stream unless stream is NULL in which case only internal
2020-10-26 00:56:25 +01:00
// state is updated. Then advances to next sensor which will
// be checked att next invocation.
2020-10-25 23:52:22 +01:00
//
2020-06-03 15:26:49 +02:00
///////////////////////////////////////////////////////////////////////////////
2020-10-25 23:52:22 +01:00
void Sensor : : checkAll ( Print * stream ) {
2020-06-03 15:26:49 +02:00
2020-10-26 00:56:25 +01:00
if ( firstSensor = = NULL ) return ;
if ( readingSensor = = NULL ) readingSensor = firstSensor ;
2020-10-26 09:10:45 +01:00
bool sensorstate = digitalRead ( readingSensor - > data . pin ) ;
if ( ! sensorstate = = readingSensor - > active ) { // active==true means sensorstate=0/false so sensor unchanged
// no change
if ( readingSensor - > latchdelay ! = 0 ) {
// enable if you want to debug contact jitter
//if (stream != NULL) StringFormatter::send(stream, F("JITTER %d %d\n"),
// readingSensor->latchdelay, readingSensor->data.snum);
readingSensor - > latchdelay = 0 ; // reset
}
} else if ( readingSensor - > latchdelay < 127 ) { // byte, max 255, good value unknown yet
// change but first increase anti-jitter counter
readingSensor - > latchdelay + + ;
} else {
// make the change
readingSensor - > active = ! sensorstate ;
readingSensor - > latchdelay = 0 ; // reset
2021-03-30 23:01:37 +02:00
if ( stream ! = NULL ) StringFormatter : : send ( stream , F ( " <%c %d> \n " ) , readingSensor - > active ? ' Q ' : ' q ' , readingSensor - > data . snum ) ;
2020-10-26 00:56:25 +01:00
}
2020-10-26 09:10:45 +01:00
2020-10-26 00:56:25 +01:00
readingSensor = readingSensor - > nextSensor ;
2020-09-06 13:44:19 +02:00
} // Sensor::checkAll
2020-06-03 15:26:49 +02:00
2020-10-25 23:52:22 +01:00
///////////////////////////////////////////////////////////////////////////////
//
// prints all sensor states to stream
//
///////////////////////////////////////////////////////////////////////////////
void Sensor : : printAll ( Print * stream ) {
for ( Sensor * tt = firstSensor ; tt ! = NULL ; tt = tt - > nextSensor ) {
if ( stream ! = NULL )
2021-03-30 23:01:37 +02:00
StringFormatter : : send ( stream , F ( " <%c %d> \n " ) , tt - > active ? ' Q ' : ' q ' , tt - > data . snum ) ;
2020-10-25 23:52:22 +01:00
} // loop over all sensors
} // Sensor::printAll
2020-06-03 15:26:49 +02:00
///////////////////////////////////////////////////////////////////////////////
Sensor * Sensor : : create ( int snum , int pin , int pullUp ) {
Sensor * tt ;
if ( firstSensor = = NULL ) {
firstSensor = ( Sensor * ) calloc ( 1 , sizeof ( Sensor ) ) ;
tt = firstSensor ;
} else if ( ( tt = get ( snum ) ) = = NULL ) {
tt = firstSensor ;
while ( tt - > nextSensor ! = NULL )
tt = tt - > nextSensor ;
tt - > nextSensor = ( Sensor * ) calloc ( 1 , sizeof ( Sensor ) ) ;
tt = tt - > nextSensor ;
}
if ( tt = = NULL ) return tt ; // problem allocating memory
tt - > data . snum = snum ;
tt - > data . pin = pin ;
tt - > data . pullUp = ( pullUp = = 0 ? LOW : HIGH ) ;
tt - > active = false ;
2020-10-26 09:10:45 +01:00
tt - > latchdelay = 0 ;
2020-06-03 15:26:49 +02:00
pinMode ( pin , INPUT ) ; // set mode to input
2020-08-15 12:32:32 +02:00
digitalWrite ( pin , pullUp ) ; // don't use Arduino's internal pull-up resistors for external infrared sensors --- each sensor must have its own 1K external pull-up resistor
2020-06-03 15:26:49 +02:00
return tt ;
}
///////////////////////////////////////////////////////////////////////////////
Sensor * Sensor : : get ( int n ) {
Sensor * tt ;
for ( tt = firstSensor ; tt ! = NULL & & tt - > data . snum ! = n ; tt = tt - > nextSensor ) ;
return tt ;
}
///////////////////////////////////////////////////////////////////////////////
bool Sensor : : remove ( int n ) {
Sensor * tt , * pp = NULL ;
for ( tt = firstSensor ; tt ! = NULL & & tt - > data . snum ! = n ; pp = tt , tt = tt - > nextSensor ) ;
if ( tt = = NULL ) return false ;
if ( tt = = firstSensor )
firstSensor = tt - > nextSensor ;
else
pp - > nextSensor = tt - > nextSensor ;
2020-10-26 00:56:25 +01:00
if ( readingSensor = = tt ) readingSensor = tt - > nextSensor ;
2020-06-03 15:26:49 +02:00
free ( tt ) ;
return true ;
}
///////////////////////////////////////////////////////////////////////////////
void Sensor : : load ( ) {
struct SensorData data ;
Sensor * tt ;
2021-07-27 18:39:54 +02:00
for ( uint16_t i = 0 ; i < EEStore : : eeStore - > data . nSensors ; i + + ) {
2020-06-03 15:26:49 +02:00
EEPROM . get ( EEStore : : pointer ( ) , data ) ;
tt = create ( data . snum , data . pin , data . pullUp ) ;
EEStore : : advance ( sizeof ( tt - > data ) ) ;
}
}
///////////////////////////////////////////////////////////////////////////////
void Sensor : : store ( ) {
Sensor * tt ;
tt = firstSensor ;
EEStore : : eeStore - > data . nSensors = 0 ;
while ( tt ! = NULL ) {
EEPROM . put ( EEStore : : pointer ( ) , tt - > data ) ;
EEStore : : advance ( sizeof ( tt - > data ) ) ;
tt = tt - > nextSensor ;
EEStore : : eeStore - > data . nSensors + + ;
}
}
///////////////////////////////////////////////////////////////////////////////
Sensor * Sensor : : firstSensor = NULL ;
2020-10-26 00:56:25 +01:00
Sensor * Sensor : : readingSensor = NULL ;