// https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/tree/master
// https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library
// https://github.com/adafruit/Adafruit_PCF8574
// https://github.com/felias-fogg/FlexWire/tree/main
// https://github.com/MicroBahner/MobaTools/tree/master
// https://arduino.stackexchange.com/questions/91149/how-to-determine-the-minimum-time-for-a-servo-to-reach-its-destination
/*
TODO: find a solution for this so we can change it and store in Eeprom
#define This_Decoder_Address 40
#define FunctionPinDcc 2 /* Inputpin DCCsignal (PD2) */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ******** REMOVE THE "//" IN THE NEXT LINE IF YOU WANT TO RUN A TEST DURING SETUP SEQUENCES
#define TESTRUN
//
// ******** REMOVE THE "//" IN THE FOLLOWING LINE TO SEND DEBUGGING INFO TO THE SERIAL OUTPUT
#define _DEBUG_
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* don't print warnings of diagnostic functions from here */
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunknown-pragmas"
# pragma GCC diagnostic ignored "-Wsign-compare"
# pragma GCC diagnostic ignored "-Wunused-function"
# pragma GCC diagnostic ignored "-Wunused-variable"
# pragma GCC diagnostic ignored "-Wnarrowing"
#define baseAddressServos 0x40 /* Base address of the SERVOS part */
#define baseAddressJuicer 0x20 /* Base address of the JUICER part */
#define baseAddressSignal 0x21 /* Base address of the SIGNAL part */
#define baseAddressInputa 0x24 /* Base address of the SWITCH part */
#define baseAddressInputb 0x26 /* Base address of the SWITCH part */
/* -------------------------------------------------------------------- */
#include <Arduino.h> /* Needed for C++ conversion of INOfile. */
/* -------------------------------------------------------------------- */
#include <avr/wdt.h> /* Needed for automatic reset functions. */
/* -------------------------------------------------------------------- */
#include <avr/io.h> /* AVR device-specific IO definitions. */
/* -------------------------------------------------------------------- */
#include <avr/interrupt.h> /* Might be necessary, for ISR handling. */
/* -------------------------------------------------------------------- */
#include <Wire.h> /* The file for the TWI (I2C) interface. */
/* -------------------------------------------------------------------- */
#include <NmraDcc.h> /* needed for computing the DCC signals */
NmraDcc Dcc ;
DCC_MSG Packet ;
#define This_Decoder_Address 9999 /* Range is 1 - 10239 ($1 - $27FF) */
#define FunctionPinDcc 2 /* Inputpin for a DCCsignal (PD2) */
#define NMRADCC_SIMPLE_RESET_CV 251
#define NMRADCC_MASTER_RESET_CV 252
volatile uint16_t myDecoderAddress;
/* --------------------------------------------------------------------- */
#include <Adafruit_PWMServoDriver.h> /* needed for driving the servos */
Adafruit_PWMServoDriver servos = Adafruit_PWMServoDriver( baseAddressServos );
#define SERVOMIN 150 /* min pulse value for full left servo throw (zero degrees) */
#define SERVOMAX 600 /* max pulse value for full right servo throw (180 degrees) */
#define SERVOOFF 4096 /* pulse value that turns the servo pin off (wear and tear) */
// #define USEC_MIN 600 /* rounded 'minimum' microsec based on minimum pulse of 150 */
// #define USEC_MAX 2400 /* rounded 'maximum' microsec based on maximum pulse of 600 */
#define SERVOFRQ 50 /* digital servos run at ~60Hz updates, analog servos ~50Hz */
/* WATCH IT: pins are numbered from 0 to 15 (so NOT from 1 to 16) */
/* -------------------------------------------------------------- */
#include <Adafruit_MCP23X17.h> /* used by JUICER and SIGNAL driving */
Adafruit_MCP23X17 juicer;
Adafruit_MCP23X17 signal;
/* WATCH IT: pins are numbered from 0 to 15 (so NOT from 1 to 16) */
/* -------------------------------------------------------------- */
#include <Adafruit_PCF8574.h> /* used by both SWITCH drivers */
Adafruit_PCF8574 inputa;
Adafruit_PCF8574 inputb;
/* WATCH IT: pins are numbered from 0 to 7 (so NOT from 1 to 8) */
/* -----------------------------------------------------------------
here we declare our own 'header' file ( optional variables ) */
// void displayText( byte menu = 1, bool clear = true, byte shift = 3, bool escape = false );
// bool scanTWIBus( byte addr = 0x10, uint8_t post_delay = 1 );
# pragma GCC diagnostic pop
/* don't print warnings of diagnostic functions till here */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* printing of debug and test options */
# if defined( _DEBUG_ ) || defined( TESTRUN )
# define _PP( a ) Serial.print( a );
# define _PL( a ) Serial.println( a );
# define _2P( a,b ) Serial.print( a, b );
# define _2L( a,b ) Serial.println( a, b );
# else
# define _PP( a )
# define _PL( a )
# define _2P( a,b )
# define _2L( a,b )
# endif
char sprintfBuffer[ 96 ] ; /* Max length for a buffer. */
bool foundEom = false ; /* Found end of messages. */
String commandString = "" ; /* String to hold incoming */
volatile unsigned long currentMillis; /* used for the timekeeping */
volatile unsigned long elapsedMillis; /* used for the timekeeping */
volatile bool checkSERVOS = false; /* */
volatile bool checkJUICER = false; /* */
volatile bool checkSIGNAL = false; /* */
volatile bool checkINPUTA = false; /* */
volatile bool checkINPUTB = false; /* */
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* ***** small macro, is used to create timed events ***** see the example below the macro ***** */
#define runEvery( n ) for ( static unsigned long lasttime; millis() - lasttime > ( unsigned long )( n ); lasttime = millis() )
/* example: runEvery( time ) { command; } check if the time (in millis) has passed and if so executes */
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
CVPair FactoryDefaultCVs [] =
{
/* this is the primary short decoder address */
{ CV_MULTIFUNCTION_PRIMARY_ADDRESS, (( This_Decoder_Address >> 0 ) & 0x7F ) + 0 },
/* these two CVs define the Long DCC Address */
{ CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, (( This_Decoder_Address >> 8 ) & 0x7F ) + 192 },
{ CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, (( This_Decoder_Address >> 0 ) & 0xFF ) + 0 },
/* this is the accessory decoder address */
{ CV_ACCESSORY_DECODER_ADDRESS_LSB, (( This_Decoder_Address >> 0 ) & 0xFF ) + 0 },
{ CV_ACCESSORY_DECODER_ADDRESS_MSB, (( This_Decoder_Address >> 8 ) ) + 0 },
/* ONLY uncomment 1 CV_29_CONFIG line below as appropriate - DEFAULT IS SHORT ADDRESS */
// { CV_29_CONFIG, 0}, /* Short Address 14 Speed Steps */
// { CV_29_CONFIG, CV29_F0_LOCATION}, /* Short Address 28/128 Speed Steps */
{ CV_29_CONFIG, CV29_EXT_ADDRESSING | CV29_F0_LOCATION}, /* Long Address 28/128 Speed Steps */
{ NMRADCC_MASTER_RESET_CV, 0},
{ 40, 2}, /* mover Lft for all servos */
{ 41, 2}, /* mover Rgt for all servos */
{ 42, 16}, /* total number of servos */
{ 45, 0}, /* 0 = do never check SERVOS */
{ 46, 0}, /* 0 = do never check JUICER */
{ 47, 0}, /* 0 = do never check SIGNAL */
{ 48, 0}, /* 0 = do never check INPUTA */
{ 49, 0}, /* 0 = do never check INPUTB */
/* SERVO 1 */
{ 50, 10}, /* angle Lft for this servo */
{ 51, 170}, /* angle Rgt for this servo */
{ 52, 0}, /* 0 SERVOS standard Lft */
{ 53, 0}, /* 0 JUICER standard Off */
{ 54, 0}, /* 0 SIGNAL standard Off */
{ 55, 10}, /* 0 SWITCH is Output */
/* SERVO 2 */
{ 60, 20}, /* angle Lft for this servo */
{ 61, 160}, /* angle Rgt for this servo */
{ 62, 0}, /* 0 SERVOS standard Lft */
{ 63, 0}, /* 0 JUICER standard Off */
{ 64, 0}, /* 0 SIGNAL standard Off */
{ 65, 10}, /* 0 SWITCH is Output */
/* SERVO 3 */
{ 70, 90}, /* angle Lft for this servo */
{ 71, 90}, /* angle Rgt for this servo */
{ 72, 0}, /* 0 SERVOS standard Lft */
{ 73, 0}, /* 0 JUICER standard Off */
{ 74, 0}, /* 0 SIGNAL standard Off */
{ 75, 10}, /* 0 SWITCH is Output */
/* SERVO 4 */
{ 80, 90}, /* angle Lft for this servo */
{ 81, 90}, /* angle Rgt for this servo */
{ 82, 0}, /* 0 SERVOS standard Lft */
{ 83, 0}, /* 0 JUICER standard Off */
{ 84, 0}, /* 0 SIGNAL standard Off */
{ 85, 10}, /* 0 SWITCH is Output */
/* SERVO 5 */
{ 90, 90}, /* angle Lft for this servo */
{ 91, 90}, /* angle Rgt for this servo */
{ 92, 0}, /* 0 SERVOS standard Lft */
{ 93, 0}, /* 0 JUICER standard Off */
{ 94, 0}, /* 0 SIGNAL standard Off */
{ 95, 10}, /* 0 SWITCH is Output */
/* SERVO 6 */
{ 100, 90}, /* angle Lft for this servo */
{ 101, 90}, /* angle Rgt for this servo */
{ 102, 0}, /* 0 SERVOS standard Lft */
{ 103, 0}, /* 0 JUICER standard Off */
{ 104, 0}, /* 0 SIGNAL standard Off */
{ 105, 10}, /* 0 SWITCH is Output */
/* SERVO 7 */
{ 110, 90}, /* angle Lft for this servo */
{ 111, 90}, /* angle Rgt for this servo */
{ 112, 0}, /* 0 SERVOS standard Lft */
{ 113, 0}, /* 0 JUICER standard Off */
{ 114, 0}, /* 0 SIGNAL standard Off */
{ 115, 10}, /* 0 SWITCH is Output */
/* SERVO 8 */
{ 120, 90}, /* angle Lft for this servo */
{ 121, 90}, /* angle Rgt for this servo */
{ 122, 0}, /* 0 SERVOS standard Lft */
{ 123, 0}, /* 0 JUICER standard Off */
{ 124, 0}, /* 0 SIGNAL standard Off */
{ 125, 10}, /* 0 SWITCH is Output */
/* SERVO 9 */
{ 130, 90}, /* angle Lft for this servo */
{ 131, 90}, /* angle Rgt for this servo */
{ 132, 0}, /* 0 SERVOS standard Lft */
{ 133, 0}, /* 0 JUICER standard Off */
{ 134, 0}, /* 0 SIGNAL standard Off */
{ 135, 10}, /* 0 SWITCH is Output */
/* SERVO 10 */
{ 140, 90}, /* angle Lft for this servo */
{ 141, 90}, /* angle Rgt for this servo */
{ 142, 0}, /* 0 SERVOS standard Lft */
{ 143, 0}, /* 0 JUICER standard Off */
{ 144, 0}, /* 0 SIGNAL standard Off */
{ 145, 10}, /* 0 SWITCH is Output */
/* SERVO 11 */
{ 150, 90}, /* angle Lft for this servo */
{ 151, 90}, /* angle Rgt for this servo */
{ 152, 0}, /* 0 SERVOS standard Lft */
{ 153, 0}, /* 0 JUICER standard Off */
{ 154, 0}, /* 0 SIGNAL standard Off */
{ 155, 10}, /* 0 SWITCH is Output */
/* SERVO 12 */
{ 160, 90}, /* angle Lft for this servo */
{ 161, 90}, /* angle Rgt for this servo */
{ 162, 0}, /* 0 SERVOS standard Lft */
{ 163, 0}, /* 0 JUICER standard Off */
{ 164, 0}, /* 0 SIGNAL standard Off */
{ 165, 10}, /* 0 SWITCH is Output */
/* SERVO 13 */
{ 170, 90}, /* angle Lft for this servo */
{ 171, 90}, /* angle Rgt for this servo */
{ 172, 0}, /* 0 SERVOS standard Lft */
{ 173, 0}, /* 0 JUICER standard Off */
{ 174, 0}, /* 0 SIGNAL standard Off */
{ 175, 10}, /* 0 SWITCH is Output */
/* SERVO 14 */
{ 180, 90}, /* angle Lft for this servo */
{ 181, 90}, /* angle Rgt for this servo */
{ 182, 0}, /* 0 SERVOS standard Lft */
{ 183, 0}, /* 0 JUICER standard Off */
{ 184, 0}, /* 0 SIGNAL standard Off */
{ 185, 10}, /* 0 SWITCH is Output */
/* SERVO 15 */
{ 190, 90}, /* angle Lft for this servo */
{ 191, 90}, /* angle Rgt for this servo */
{ 192, 0}, /* 0 SERVOS standard Lft */
{ 193, 0}, /* 0 JUICER standard Off */
{ 194, 0}, /* 0 SIGNAL standard Off */
{ 195, 10}, /* 0 SWITCH is Output */
/* SERVO 16 */
{ 200, 90}, /* angle Lft for this servo */
{ 201, 90}, /* angle Rgt for this servo */
{ 202, 0}, /* 0 SERVOS standard Lft */
{ 203, 0}, /* 0 JUICER standard Off */
{ 204, 0}, /* 0 SIGNAL standard Off */
{ 205, 10}, /* 0 SWITCH is Output */
{ 250, 0}, /* MASTER SWITCH OVER-RULE */
{ 251, 0}, /* NMRADCC_SIMPLE_RESET_CV */
{ 252, 0}, /* NMRADCC_MASTER_RESET_CV */
{ 253, 0}, /* */
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* TODO: check conversions from Angle to Pulse for every function !!! */
struct QUEUE
{
uint16_t pulseLft = SERVOMIN ; /* pulse to go full Left */
uint16_t pulseRgt = SERVOMAX ; /* pulse to go full Right */
uint16_t moveEndState = SERVOOFF ; /* this is end point move */
uint16_t actualServoP = SERVOOFF ; /* keep track of position */
uint16_t juicerTime = 1255 ; /* a juicer needs to wait */
bool moveToCenter = false ; /* makes a move to Center */
bool moveFromCntr = false ; /* moving to Left / Right */
bool currentState = false ; /* do we need attention ? */
};
QUEUE volatile *turnout_queue = new QUEUE[ 16 + 1 ]; /* 16 turnouts max [15:0] + 1 dummy */
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
volatile byte currentTurnout = 16; /* default turnout updating commands */
volatile byte positionUpdate = 2; /* turnout position update (x, c, v) */
volatile long ninety_degrees = ( ( SERVOMAX + SERVOMIN ) / 2 ) * ( ( 1000000 / SERVOFRQ ) / 4096 );
volatile uint8_t FactoryDefaultCVIndex = sizeof( FactoryDefaultCVs ) / sizeof( CVPair );
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
volatile bool TWIBusCheck = false; /* check the TWI bus for existance of devices */
bool scanTWIBus( byte addr = 0x10, uint8_t post_delay = 1 );
/* example from: https://github.com/adafruit/Adafruit_TestBed/tree/master */
/*!
@brief Perform a I2C scan for a given address on the bus
@param addr The address to look for
@param post_delay How many ms to wait after a scan
@return True if the address was found
*/
/**************************************************************************/
bool scanTWIBus( byte addr, uint8_t post_delay )
{
Wire.beginTransmission( addr );
bool found = ( Wire.endTransmission( ) == 0 );
delay( post_delay );
return found;
};
void checkTheTWIBusses()
{
TWIBusCheck = ( ( checkSERVOS ) ? scanTWIBus( baseAddressServos ) : true ) &
( ( checkJUICER ) ? scanTWIBus( baseAddressJuicer ) : true ) &
( ( checkSIGNAL ) ? scanTWIBus( baseAddressSignal ) : true ) &
( ( checkINPUTA ) ? scanTWIBus( baseAddressInputa ) : true ) &
( ( checkINPUTB ) ? scanTWIBus( baseAddressInputb ) : true ) ;
if (! TWIBusCheck ) { _PL( "Error: TWIBusCheck [I2C] - check connection(s)" ); }
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
void setsServosCorrect()
{
if ( TWIBusCheck ) /* check the TWI bus for existance of devices */
{
for ( uint8_t p = 0; p < 16; ++p )
{
if ( turnout_queue[ p ].currentState )
{
_PP( currentMillis );
_PP( " - " );
_PP( p + 1 );
_PP( " - " );
_PL( turnout_queue[ p ].actualServoP )
if ( turnout_queue[ p ].actualServoP > turnout_queue[ p ].moveEndState )
{
turnout_queue[ p ].actualServoP -= 1; /* move Lft */
}
if ( turnout_queue[ p ].actualServoP < turnout_queue[ p ].moveEndState )
{
turnout_queue[ p ].actualServoP += 1; /* move Rgt */
}
servos.setPWM( p, 0, turnout_queue[ p ].actualServoP ); /* move the servo */
if ( turnout_queue[ p ].actualServoP == turnout_queue[ p ].moveEndState )
{
turnout_queue[ p ].actualServoP = SERVOOFF; /* stop moving */
turnout_queue[ p ].currentState = false; /* clear the flag */
servos.setPWM( p, 0, SERVOOFF ); /* stop the servo */
}
}
}
}
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* inspired by https://forum.arduino.cc/t/debouncing-buttons-read-with-a-pcf-8575/1038096/14 */
volatile uint8_t inputaPreviousInput; // previous reading of buttons
volatile uint8_t inputaPreviousState; // Button previous state
volatile uint8_t inputaActualInState; // Button state (pressed, no pressed)
volatile uint8_t inputaActualToggled; // actual Toggle
volatile uint8_t inputaActualPressed; // actual Pressed
volatile uint8_t inputaActualRelease; // Actual Released
volatile uint8_t inputaStablePressed; // Stable Pressed
volatile uint8_t inputaStableRelease; // Stable Released
volatile uint8_t inputaButtonPinMask = 0b11111111; /* tells us which pins are marked as inputs (1) */
void readButtonsInputa() /* inputa = SWITCH1 = 0x24 = inputs 1 t/m 8 */
{
interrupts(); // Arduino UNO seems to require that we turn on interrupts for I2C to work!
uint8_t inputaHoldsNewInput = ~inputa.digitalReadByte() & inputaButtonPinMask;
/* this section reacts immediately to a new, different reading (changes) */
inputaActualPressed = inputaHoldsNewInput & ~inputaPreviousInput;
inputaActualRelease = ~inputaHoldsNewInput & inputaPreviousInput;
inputaActualToggled = inputaActualToggled ^ inputaActualPressed;
/* this sketch will use the upper part (the rest is for a later session) */
if ( ( inputaActualPressed ) || ( inputaActualRelease ) )
{
setTheSwitchesInputa( );
}
/* next section develops the idea of a stable press (same for 2 readings) */
// inputaStablePressed = ~( inputaHoldsNewInput ^ inputaPreviousInput );
// inputaActualInState &= ~inputaStablePressed;
// inputaActualInState |= inputaHoldsNewInput & inputaStablePressed;
/* next section might even get a stabler reading of fuzzy button presses */
// inputaStablePressed = inputaActualInState & ~inputaPreviousState;
// inputaStableRelease = ~inputaActualInState & inputaPreviousState;
/* always insert the next lines to keep the history in sync with reading */
inputaPreviousInput = inputaHoldsNewInput;
inputaPreviousState = inputaActualInState;
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
volatile uint8_t inputbPreviousInput; // previous reading of buttons
volatile uint8_t inputbPreviousState; // Button previous state
volatile uint8_t inputbActualInState; // Button state (pressed, no pressed)
volatile uint8_t inputbActualToggled; // actual Toggle
volatile uint8_t inputbActualPressed; // actual Pressed
volatile uint8_t inputbActualRelease; // Actual Released
volatile uint8_t inputbStablePressed; // Stable Pressed
volatile uint8_t inputbStableRelease; // Stable Released
volatile uint8_t inputbButtonPinMask = 0b11111111; /* tells us which pins are marked as inputs (1) */
void readButtonsInputb() /* inputb = SWITCH2 = 0x26 = inputs 9 t/m 16 */
{
interrupts(); // Arduino UNO seems to require that we turn on interrupts for I2C to work!
uint8_t inputbHoldsNewInput = ~inputb.digitalReadByte() & inputbButtonPinMask;
/* this section reacts immediately to a new, different reading (changes) */
inputbActualPressed = inputbHoldsNewInput & ~inputbPreviousInput;
inputbActualRelease = ~inputbHoldsNewInput & inputbPreviousInput;
inputbActualToggled = inputbActualToggled ^ inputbActualPressed;
/* this sketch will use the upper part (the rest is for a later session) */
if ( ( inputbActualPressed ) || ( inputbActualRelease ) )
{
setTheSwitchesInputb( );
}
/* next section develops the idea of a stable press (same for 2 readings) */
// inputbStablePressed = ~( inputbHoldsNewInput ^ inputbPreviousInput );
// inputbActualInState &= ~inputbStablePressed;
// inputbActualInState |= inputbHoldsNewInput & inputbStablePressed;
/* next section might even get a stabler reading of fuzzy button presses */
// inputbStablePressed = inputbActualInState & ~inputbPreviousState;
// inputbStableRelease = ~inputbActualInState & inputbPreviousState;
/* always insert the next lines to keep the history in sync with reading */
inputbPreviousInput = inputbHoldsNewInput;
inputbPreviousState = inputbActualInState;
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
void setTheSwitchesInputa( ) /* here the switches 1 till 8 are handled */
{
for ( uint8_t p = 0; p < 8; ++p )
{
if ( bitRead ( inputaActualPressed, p ) ) /* key pressed */
{
turnout_queue[ p + 0 ].moveEndState = turnout_queue[ p + 0 ].pulseRgt;
turnout_queue[ p + 0 ].currentState = true;
turnout_queue[ p + 0 ].actualServoP = turnout_queue[ p + 0 ].pulseLft;
}
if ( bitRead ( inputaActualRelease, p ) ) /* key released */
{
turnout_queue[ p + 0 ].moveEndState = turnout_queue[ p + 0 ].pulseLft;
turnout_queue[ p + 0 ].currentState = true;
turnout_queue[ p + 0 ].actualServoP = turnout_queue[ p + 0 ].pulseRgt;
}
}
if ( TWIBusCheck ) /* check the TWI bus for existance of devices */
{
uint8_t juicerInputaRead = juicer.readGPIOA ( );
uint8_t signalInputaRead = signal.readGPIOA ( );
for ( uint8_t p = 0; p < 8; ++p )
{
if ( bitRead ( inputaActualPressed, p ) ) /* key pressed */
{
bitSet ( juicerInputaRead, p );
bitSet ( signalInputaRead, p );
}
if ( bitRead ( inputaActualRelease, p ) ) /* key released */
{
bitClear ( juicerInputaRead, p );
bitClear ( signalInputaRead, p );
}
if ( bitRead ( inputaActualToggled, p ) ) /* key toggled */
{
bitClear ( inputaActualToggled, p ); /* clear toggled bit */
}
}
juicer.writeGPIOA ( juicerInputaRead );
signal.writeGPIOA ( signalInputaRead );
}
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
void setTheSwitchesInputb( ) /* here the switches 9 till 16 are handled */
{
for ( uint8_t p = 0; p < 8; ++p )
{
if ( bitRead ( inputbActualPressed, p ) ) /* key pressed */
{
turnout_queue[ p + 8 ].moveEndState = turnout_queue[ p + 8 ].pulseRgt;
turnout_queue[ p + 8 ].currentState = true;
turnout_queue[ p + 8 ].actualServoP = turnout_queue[ p + 8 ].pulseLft;
}
if ( bitRead ( inputbActualRelease, p ) ) /* key released */
{
turnout_queue[ p + 8 ].moveEndState = turnout_queue[ p + 8 ].pulseLft;
turnout_queue[ p + 8 ].currentState = true;
turnout_queue[ p + 8 ].actualServoP = turnout_queue[ p + 8 ].pulseRgt;
}
}
if ( TWIBusCheck ) /* check the TWI bus for existance of devices */
{
uint8_t juicerInputbRead = juicer.readGPIOB ( );
uint8_t signalInputbRead = signal.readGPIOB ( );
for ( uint8_t p = 0; p < 8; ++p )
{
if ( bitRead ( inputbActualPressed, p ) ) /* key pressed */
{
bitSet ( juicerInputbRead, p + 8 );
bitSet ( signalInputbRead, p + 8 );
}
if ( bitRead ( inputbActualRelease, p ) ) /* key released */
{
bitClear ( juicerInputbRead, p + 8 );
bitClear ( signalInputbRead, p + 8 );
}
if ( bitRead ( inputbActualToggled, p ) ) /* key toggled */
{
bitClear ( inputbActualToggled, p ); /* clear toggled bit */
}
}
juicer.writeGPIOB ( juicerInputbRead );
signal.writeGPIOB ( signalInputbRead );
}
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/*
* setServoAngle(int ang)
* gets angle in degrees and returns matching pulse value
*/
int setServoAngle( int ang ) {
int pulse = map( ang, 0, 180, SERVOMIN, SERVOMAX );
_PP( "...Angle: " ); _PP( ang );
_PP( " / Pulse: " ); _PP( pulse );
// double pulselength = 1000000 / SERVOFRQ; /* 1,000,000 us per second / 50 = 20.000 */
// _PP ( pulselength ); _PP ( " us per period = " );
// pulselength /= 4096; // 12 bits of resolution 20.000 / 4096 = 4,883
// _PP ( pulselength ); _PP ( " us per bit - " );
// _PP ( "default center: "); _PL ( ninety_degrees );
return pulse;
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* next function is doing an automatic reset of the processor after the preScaler time has elapsed */
void softwareReset( uint8_t preScaler )
{
wdt_enable( preScaler );
while( 1 ) {} // Wait for the prescaler time to expire and do an auto-reset
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* **********************************************************************************
*** menu system for setting and dumping values ***
*************************************************************************************
first menu:
FD factory defaults all settings --> writes to CVs
DD lists all data on serial monitor
DS x# default switch (1 to 16) --> display next menu
DR xx# read data from CV address xx# (0 to 255)
DW xx# x# write data x# to CV address xx# (0 to 255)
next menu:
c = center the default servo
x = adjust servo left x degrees at a time
v = adjust servo right x degrees at a time
p = invert servos setting for this switch
j = invert juicer setting for this switch
s = invert signal setting for this switch
i = invert switch setting for this switch
d = display all settings for this turnout
t = throw the switch (servo, juicer, signal)
w = write the settings in corresponding CVs --> ask for confirmation
? = don't save setting, just go back to first menu
there could be alse x+ and x-, v+ and v-
*************************************************************************************
We use a flag to make sure we don't enter the wrong function upon reveiving */
volatile bool next_menu = false ; //, do_escape = true ;
void displayText( byte menu = 0, bool clear = true, byte shift = 1, bool escape = false );
void displayText( byte menu, bool clear, byte shift, bool escape )
{
if ( clear == true) /* clear the screen and display a header text */
{
if ( escape == true )
{
_PP(F( "\e[2J\e[1;1H" )); /* send escape sequences out */
}
else
{
byte lines = constrain( shift, 1, 32 ); /* max 32 empty lines */
for ( byte p = 0; p < lines; ++p )
{
_PL(F( " " )); /* empty line for better readability */
}
}
_PL(F( "**** AtMega328 controling your turnouts ****" ));
}
#define line_00 "**** please enter a VALID selection ****"
#define line_01 "** put in one of the numbers shown @ left **"
#define line_09 " 0 write configuration variables into Eeprom "
if( menu == 1 )
{
// #define line_10 " %1u default address of the decoder is: %5u"
#define line_11 " %1u address assigned to this decoder: %5u"
#define line_12 " %1u check %6s during startup cycle: %3s"
_PL(F( " " )); /* empty line for better readability */
_PL(F( line_01 ) );
_PL(F( "----------------------------------------------" ));
sprintf( sprintfBuffer, line_11, 1, myDecoderAddress ); _PL( sprintfBuffer );
_PL(F( " " )); /* empty line for better readability */
sprintf( sprintfBuffer, line_12, 2, "SERVOS", ( checkSERVOS ? "Yes" : "No" ) ); _PL( sprintfBuffer );
sprintf( sprintfBuffer, line_12, 3, "JUICER", ( checkJUICER ? "Yes" : "No" ) ); _PL( sprintfBuffer );
sprintf( sprintfBuffer, line_12, 4, "SIGNAL", ( checkSIGNAL ? "Yes" : "No" ) ); _PL( sprintfBuffer );
sprintf( sprintfBuffer, line_12, 5, "INPUTA", ( checkINPUTA ? "Yes" : "No" ) ); _PL( sprintfBuffer );
sprintf( sprintfBuffer, line_12, 6, "INPUTB", ( checkINPUTB ? "Yes" : "No" ) ); _PL( sprintfBuffer );
_PL(F( " " )); /* empty line for better readability */
_PL(F( line_09 ) );
_PL(F( "----------------------------------------------" ));
_PL(F( "** use the [Enter] key if necessary **" ));
_PL(F( " " )); /* empty line for better readability */
}
if( menu != 1 )
{
#define line_21 " sequence number of the turnout servo:%4u"
#define line_22 " %1u adjust servo %5s %2u degrees at a time "
#define line_23 " %1u center the turnouts servo - %3u degrees"
#define line_24 " %1u invert turnouts %6s setting: %3s "
currentTurnout = menu -1; /* update the turnout number */
_PL(F( " " )); /* empty line for better readability */
_PL(F( line_01 ) );
_PL(F( "----------------------------------------------" ));
sprintf( sprintfBuffer, line_21, currentTurnout ); _PL( sprintfBuffer );
_PL(F( " " )); /* empty line for better readability */
sprintf( sprintfBuffer, line_22, 1, "left", positionUpdate ); _PL( sprintfBuffer );
// _PL(F( " 2 center the turnouts servo - 123 degrees" ));
sprintf( sprintfBuffer, line_23, 2, 123 ); _PL( sprintfBuffer );
sprintf( sprintfBuffer, line_22, 3, "right", positionUpdate ); _PL( sprintfBuffer );
_PL(F( " " )); /* empty line for better readability */
sprintf( sprintfBuffer, line_24, 4, "SERVOS", "xxx" ); _PL( sprintfBuffer );
sprintf( sprintfBuffer, line_24, 5, "JUICER", "xxx" ); _PL( sprintfBuffer );
sprintf( sprintfBuffer, line_24, 6, "SIGNAL", "xxx" ); _PL( sprintfBuffer );
sprintf( sprintfBuffer, line_24, 7, "SWITCH", "xxx" ); _PL( sprintfBuffer );
_PL(F( " " )); /* empty line for better readability */
_PL(F( " 8 throw the switch >servo >juicer >signal" ));
_PL(F( " " )); /* empty line for better readability */
_PL(F( line_09 ) );
_PL(F( "----------------------------------------------" ));
_PL(F( "** if necessary use the [Enter] key **" ));
_PL(F( " " )); /* empty line for better readability */
}
// if( next == true )
// {
// _PL( " " ); // An extra empty line for better understanding
// _PL(F( "** put in one of the following commands **" ));
// _PL(F( "----------------------------------------------" ));
// _PL(F( "x = adjust servo left x degrees at a time" ));
// _PL(F( "c = center the default servo (set with DS #)" ));
// _PL(F( "v = adjust servo right x degrees at a time" ));
// _PL( " " );
// _PL(F( "p = invert SERVOS setting for this turnout" ));
// _PL(F( "j = invert JUICER setting for this turnout" ));
// _PL(F( "s = invert SIGNAL setting for this turnout" ));
// _PL(F( "i = invert SWITCH setting for this turnout" ));
// _PL( " " );
// _PL(F( "d = display changed setting for this turnout" ));
// _PL(F( "t = throw the switch (servo, juicer, signal)" ));
// _PL(F( "w = write the settings, in corresponding CVs" ));
// _PL(F( "r = reset this turnout to: default settings" ));
// _PL(F( "----------------------------------------------" ));
// _PL(F( "** any other key discards the settings **" ));
// _PL( "" ); // An extra empty line for better understanding
// }
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
volatile byte myCurrentMenu = 1;
void computeInput( byte menu, String commandString )
{
char charOne = '!', deTected = sscanf( commandString.c_str(), "%1s", &charOne );
/* first we test on a + or - sign to swipe through the pages */
if ( charOne == 43 ) { myCurrentMenu += 1; myCurrentMenu = constrain( myCurrentMenu, 1, 17 ); displayText( myCurrentMenu, true, 99 ); return; }
if ( charOne == 45 ) { myCurrentMenu -= 1; myCurrentMenu = constrain( myCurrentMenu, 1, 17 ); displayText( myCurrentMenu, true, 99 ); return; }
if ( menu == 1 )
{
int menuOne = 10, deTected = sscanf( commandString.c_str(), "%1u", &menuOne );
switch ( menuOne )
{
case 1: /* 1 address assigned to this decoder: 99999 */
// myDecoderAddress
break;
case 2: /* 2 check SERVOS during startup cycle: Y/N */
checkSERVOS = ! checkSERVOS;
if ( checkSERVOS == true )
{
if (! servos.begin( ) )
{
checkSERVOS = false;
/* _PL( "Couldn't find SERVOS - check and reset" ); */
} else {
/* _PL( "Found SERVOS - going to the next steps" ); */
servos.setPWMFreq( SERVOFRQ );
}
}
displayText( menu, true, 99 );
break;
case 3: /* 3 check JUICER during startup cycle: Y/N */
checkJUICER = ! checkJUICER;
if ( checkJUICER == true )
{
if (! juicer.begin_I2C( baseAddressJuicer, &Wire ) )
{
checkJUICER = false;
/* _PL( "Couldn't find JUICER - check and reset" ); */
} else {
/* _PL( "Found JUICER - going to the next steps" ); */
for ( uint8_t p = 0; p < 16; ++p ) /* initialise all 16 as OUTPUT */
{
juicer.pinMode( p, OUTPUT );
}
}
}
displayText( menu, true, 99 );
break;
case 4: /* 4 check SIGNAL during startup cycle: Y/N */
checkSIGNAL = ! checkSIGNAL;
if ( checkSIGNAL == true )
{
if (! signal.begin_I2C( baseAddressSignal, &Wire ) )
{
checkSIGNAL = false;
/* _PL( "Couldn't find SIGNAL - check and reset" ); */
} else {
/* _PL( "Found SIGNAL - going to the next steps" ); */
for ( uint8_t p = 0; p < 16; ++p ) /* initialise all 16 as OUTPUT */
{
signal.pinMode( p, OUTPUT );
}
}
}
displayText( menu, true, 99 );
break;
case 5: /* 5 check INPUTA during startup cycle: Y/N */
checkINPUTA = ! checkINPUTA;
if ( checkINPUTA == true )
{
if (! inputa.begin( baseAddressInputa, &Wire ) )
{
checkINPUTA = false;
/* _PL( "Couldn't find INPUTA - check and reset" ); */
} else {
/* _PL( "Found INPUTA - going to the next steps" ); */
for ( uint8_t p = 0; p < 8; ++p ) /* initialise all 8 as INPUT */
{
inputa.pinMode( p, INPUT_PULLUP ); // INPUT );
}
}
}
displayText( menu, true, 99 );
break;
case 6: /* 6 check INPUTB during startup cycle: Y/N */
checkINPUTB = ! checkINPUTB;
if ( checkINPUTB == true )
{
if (! inputb.begin( baseAddressInputb, &Wire ) )
{
checkINPUTB = false;
/* _PL( "Couldn't find INPUTB - check and reset" ); */
} else {
/* _PL( "Found INPUTB - going to the next steps" ); */
for ( uint8_t p = 0; p < 8; ++p ) /* initialise all 8 as INPUT */
{
inputb.pinMode( p, INPUT_PULLUP ); // INPUT );
}
}
}
displayText( menu, true, 99 );
break;
case 0: /* 0 write configuration variables into Eeprom */
Dcc.setCV( 45, (checkSERVOS == true ? 45 : 0 ) );
Dcc.setCV( 46, (checkJUICER == true ? 46 : 0 ) );
Dcc.setCV( 47, (checkSIGNAL == true ? 47 : 0 ) );
Dcc.setCV( 48, (checkINPUTA == true ? 48 : 0 ) );
Dcc.setCV( 49, (checkINPUTB == true ? 49 : 0 ) );
checkSERVOS = ( ( Dcc.getCV( 45 ) == 0 ) ? false : true ); /* */
checkJUICER = ( ( Dcc.getCV( 46 ) == 0 ) ? false : true ); /* */
checkSIGNAL = ( ( Dcc.getCV( 47 ) == 0 ) ? false : true ); /* */
checkINPUTA = ( ( Dcc.getCV( 48 ) == 0 ) ? false : true ); /* */
checkINPUTB = ( ( Dcc.getCV( 49 ) == 0 ) ? false : true ); /* */
displayText( menu, true, 99 );
break;
default:
_PL(F( line_00 ) );
break;
}
return;
}
if ( menu != 1 )
{
int menuTwo = 10, deTected = sscanf( commandString.c_str(), "%1u", &menuTwo );
switch ( menuTwo )
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
displayText( menu, true, 99 );
break;
case 0: /* 0 write configuration variables into Eeprom */
// Dcc.setCV( 45, (checkSERVOS == true ? 45 : 0 ) );
// Dcc.setCV( 46, (checkJUICER == true ? 46 : 0 ) );
// Dcc.setCV( 47, (checkSIGNAL == true ? 47 : 0 ) );
// Dcc.setCV( 48, (checkINPUTA == true ? 48 : 0 ) );
// Dcc.setCV( 49, (checkINPUTB == true ? 49 : 0 ) );
// checkSERVOS = ( ( Dcc.getCV( 45 ) == 0 ) ? false : true ); /* */
// checkJUICER = ( ( Dcc.getCV( 46 ) == 0 ) ? false : true ); /* */
// checkSIGNAL = ( ( Dcc.getCV( 47 ) == 0 ) ? false : true ); /* */
// checkINPUTA = ( ( Dcc.getCV( 48 ) == 0 ) ? false : true ); /* */
// checkINPUTB = ( ( Dcc.getCV( 49 ) == 0 ) ? false : true ); /* */
displayText( menu, true, 99 );
break;
default:
_PL(F( line_00 ) );
break;
}
return;
}
// char charOne = '!', charTwo = '!'; /* first two could be letters */
// uint16_t intOne = -1, intTwo = -1; /* after that come the numbers */
// int deTected = sscanf( commandString.c_str(), "%1c%1c%u%u", &charOne, &charTwo, &intOne, &intTwo );
// #if defined( _DEBUG_ )
// memset( sprintfBuffer, 0, sizeof sprintfBuffer );
// sprintf( sprintfBuffer, "charOne: %1c - charTwo: %1c - intOne: %3u - intTwo: %3u - deTected: %3u", charOne, charTwo, intOne, intTwo, deTected );
// _PL( sprintfBuffer );
// #endif /* _DEBUG_ */
// _PP( menu ); _PL(".menu ");
// _PP( choice ); _PL(".choice ");
// _PP( byte( choice ) ); _PL(".byte ");
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
// sensVal = constrain(sensVal, 10, 150); // limits range of sensor values to between 10 and 150
// map(value, fromLow, fromHigh, toLow, toHigh)
void parseCom( String commandString )
{
commandString.trim(); /* remove spaces at both ends */
commandString.toLowerCase(); /* make it all lowercase */
char charOne = '!', charTwo = '!'; /* first two could be letters */
uint16_t intOne = -1, intTwo = -1; /* after that come the numbers */
int deTected = sscanf( commandString.c_str(), "%1c%1c%u%u", &charOne, &charTwo, &intOne, &intTwo );
#if defined( _DEBUG_ )
memset( sprintfBuffer, 0, sizeof sprintfBuffer );
sprintf( sprintfBuffer, "charOne: %1c - charTwo: %1c - intOne: %3u - intTwo: %3u - deTected: %3u", charOne, charTwo, intOne, intTwo, deTected );
_PL( sprintfBuffer );
#endif /* _DEBUG_ */
if ( ( next_menu ) & ( deTected == 1 ) ) /* single character commands of next menu */
{
switch ( charOne )
{
case 'x': /* x = adjust servo left x degrees at a time */
{
turnout_queue[ 16 ].pulseLft += positionUpdate ;
if ( turnout_queue[ 16 ].pulseLft > 180 ) { turnout_queue[ 16 ].pulseLft = 180; }
servos.setPWM( currentTurnout, 0, setServoAngle( turnout_queue[ 16 ].pulseLft ) );
sprintf( sprintfBuffer, "new servo left: %3u ", turnout_queue[ 16 ].pulseLft );
_PL( sprintfBuffer );
turnout_queue[ 16 ].currentState = true; /* something has changed */
break;
}
case 'c': /* c = center the default servo (set with DS) */
{
uint8_t servoPos = 90;
servos.setPWM( currentTurnout, 0, setServoAngle( servoPos ) );
_PL( "Set servo center ..." );
break;
}
case 'v': /* v = adjust servo right x degrees at a time */
{
turnout_queue[ 16 ].pulseRgt -= positionUpdate ;
if ( turnout_queue[ 16 ].pulseRgt < 0 ) { turnout_queue[ 16 ].pulseRgt = 0; } /* prevent overdrive */
servos.setPWM( currentTurnout, 0, setServoAngle( turnout_queue[ 16 ].pulseRgt ) );
sprintf( sprintfBuffer, "new servo right: %3u ", turnout_queue[ 16 ].pulseRgt );
_PL( sprintfBuffer );
turnout_queue[ 16 ].currentState = true; /* something has changed */
break;
}
case 'p': /* p = invert SERVOS setting for this turnout */
{
break;
}
case 'j': /* j = invert JUICER setting for this turnout */
{
break;
}
case 's': /* s = invert SIGNAL setting for this turnout */
{
break;
}
case 'i': /* i = invert SWITCH setting for this turnout */
{
break;
}
case 'd': /* d = display changed setting for this turnout */
{
break;
}
case 't': /* t = throw the switch (servo, juicer, signal) */
{
// if (inChar == 't') { //throw the turnout the other way from where it is
// // if (thrown) {
// // servos.setPWM(0, 0, setServoAngle(servoRgt));
// // Serial.println("Throw right ... ");
// // } else {
// // servos.setPWM(0, 0, setServoAngle(servoLft));
// // Serial.println("Throw left ... ");
// // }
// // thrown = !thrown; // flip the turnout direction
// }
break;
}
case 'w': /* w = write the settings, in corresponding CVs */
{
break;
}
case 'r': /* r = reset this turnout to: default settings */
{
break;
}
default: /* discard all changes */
break;
}
} /* end of single character commands */
if ( ( next_menu ) & ( deTected == 2 ) ) /* double character commands of next menu */
{
switch ( charOne )
{
case 'x': /* x = adjust servo left x degrees at a time */
{
switch ( charTwo )
{
case '+': /* x+ */
{
turnout_queue[ 16 ].pulseLft += positionUpdate ;
turnout_queue[ 16 ].pulseLft = constrain( turnout_queue[ 16 ].pulseLft, turnout_queue[ 16 ].pulseRgt, 180 );
// if ( turnout_queue[ 16 ].pulseLft > 180 ) { turnout_queue[ 16 ].pulseLft = 180; }
servos.setPWM( currentTurnout, 0, setServoAngle( turnout_queue[ 16 ].pulseLft ) );
sprintf( sprintfBuffer, "new servo left: %3u ", turnout_queue[ 16 ].pulseLft );
_PL( sprintfBuffer );
turnout_queue[ 16 ].currentState = true; /* something has changed */
break;
}
case '-': /* x- */
{
turnout_queue[ 16 ].pulseLft -= positionUpdate ;
turnout_queue[ 16 ].pulseLft = constrain( turnout_queue[ 16 ].pulseLft, turnout_queue[ 16 ].pulseRgt, 180 );
// if ( turnout_queue[ 16 ].pulseLft > 180 ) { turnout_queue[ 16 ].pulseLft = 180; }
servos.setPWM( currentTurnout, 0, setServoAngle( turnout_queue[ 16 ].pulseLft ) );
sprintf( sprintfBuffer, "new servo left: %3u ", turnout_queue[ 16 ].pulseLft );
_PL( sprintfBuffer );
turnout_queue[ 16 ].currentState = true; /* something has changed */
break;
}
default: /* discard all changes */
break;
}
break;
}
case 'v': /* v = adjust servo right x degrees at a time */
{
switch ( charTwo )
{
case '+': /* v+ */
{
turnout_queue[ 16 ].pulseRgt += positionUpdate ;
turnout_queue[ 16 ].pulseRgt = constrain( turnout_queue[ 16 ].pulseRgt, 0, turnout_queue[ 16 ].pulseLft );
// if ( turnout_queue[ 16 ].pulseRgt < 0 ) { turnout_queue[ 16 ].pulseRgt = 0; } /* prevent overdrive */
servos.setPWM( currentTurnout, 0, setServoAngle( turnout_queue[ 16 ].pulseRgt ) );
sprintf( sprintfBuffer, "new servo right: %3u ", turnout_queue[ 16 ].pulseRgt );
_PL( sprintfBuffer );
turnout_queue[ 16 ].currentState = true; /* something has changed */
break;
}
case '-': /* v- */
{
turnout_queue[ 16 ].pulseRgt -= positionUpdate ;
turnout_queue[ 16 ].pulseRgt = constrain( turnout_queue[ 16 ].pulseRgt, 0, turnout_queue[ 16 ].pulseLft );
// if ( turnout_queue[ 16 ].pulseRgt < 0 ) { turnout_queue[ 16 ].pulseRgt = 0; } /* prevent overdrive */
servos.setPWM( currentTurnout, 0, setServoAngle( turnout_queue[ 16 ].pulseRgt ) );
sprintf( sprintfBuffer, "new servo right: %3u ", turnout_queue[ 16 ].pulseRgt );
_PL( sprintfBuffer );
turnout_queue[ 16 ].currentState = true; /* something has changed */
break;
}
default: /* discard all changes */
break;
}
break;
}
default: /* discard all changes */
// next_menu = false;
break;
}
} /* end of double character commands */
if ( (! next_menu ) & ( deTected == 2 ) ) /* double character commands of first menu */
{
switch ( charOne )
{
case 'f': /* */
{
switch ( charTwo )
{
case 'd': /* FD clear everything: Factory Default */
{
softwareReset( WDTO_15MS );
break;
}
default: /* discard all changes */
break;
}
break;
}
case 'd':
{
switch ( charTwo )
{
case 'd': /* DD prints CV values: to your monitor */
{
break;
}
default: /* discard all changes */
break;
}
break;
}
default: /* discard all changes */
break;
}
} /* end of double character commands */
if (( next_menu != true ) & ( deTected == 3 )) /* triple character commands of first menu */
{
switch ( charOne )
{
case 'd':
{
switch ( charTwo )
{
case 's': /* DS set default item number ( 1-16 ): DS x# */
{
intOne = constrain( intOne, 1, 16 ); // limits range of sensor values to 1 till 16
sprintf( sprintfBuffer, "Default turnout changed to: %d", intOne );
_PL( sprintfBuffer );
break;
}
case 'r': /* DR reads a configuration variable: DR xx# */
{
intOne = constrain( intOne, 0, 255 ); /* limits range of CVs from 0 till 255 */
break;
}
default:
break;
}
break;
}
default:
break;
}
} /* end of triple character commands */
if ( (! next_menu ) & ( deTected == 4 ) ) /* quad character commands of first menu */
{
switch ( charOne )
{
case 'd':
{
switch ( charTwo )
{
case 'w': /* DW write a configuration variable: DW xx# x# */
{
/* TODO: do not use constrain on intOne, but check !!!! else values might be written to the wrong CV */
intOne = constrain( intOne, 0, 255 ); /* limits range of CVs from 0 till 255 */
intTwo = constrain( intTwo, 0, 255 ); /* limits CVvalue range from 0 till 255 */
break;
}
default:
break;
}
break;
}
default: /* discard all changes */
break;
}
} /* end of quad character commands */
// // default: /**** DEFAULT FOR THE SWITCH FUNCTION = NO ACTION ****/
// // {
// // displayText( true, false ); // Shows the standard explanation text
// // break;
// // }
// // break;
// }
// break;
// }
// if ( ( currentTurnout & 0b00001111 ) != 0) // if ( !( ( currentTurnout < 0 ) || ( currentTurnout > 15 ) ) )
// {
// turnout_queue[ 16 ].pulseLft = turnout_queue[ currentTurnout ].pulseLft;
// turnout_queue[ 16 ].pulseRgt = turnout_queue[ currentTurnout ].pulseRgt;
// turnout_queue[ 16 ].currentState = false;
// _2P( currentTurnout, DEC);
// _PP( " - " );
// _PL( com );
// switch ( com[1] ) /* com[0] = '<' or ' ' */
// {
// default: /**** DEFAULT FOR THE SWITCH FUNCTION ****/
// {
// displayText( true, false ); // Shows the standard explanation text
// break;
// }
// break;
// }
// }
// else
// {
// displayText( true, false ); // Shows the standard explanation text
// }
/* let us now reset the whole thing to start anew */
foundEom = false;
commandString.remove( 0 );
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
// bool scanTWIBus( byte addr = 0x10, uint8_t post_delay = 1 );
// /* example from: https://github.com/adafruit/Adafruit_TestBed/tree/master */
// /*!
// @brief Perform a I2C scan for a given address on the bus
// @param addr The address to look for
// @param post_delay How many ms to wait after a scan
// @return True if the address was found
// */
// /**************************************************************************/
// bool scanTWIBus( byte addr, uint8_t post_delay )
// {
// Wire.beginTransmission( addr );
// bool found = ( Wire.endTransmission( ) == 0 );
// delay( post_delay );
// return found;
// };
// void printBin(byte aByte) {
// for ( int8_t aBit = 7; aBit >= 0; --aBit )
// Serial.write(bitRead( aByte, aBit ) ? '1' : '0' );
// };
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* //////////////////////////////////////////////////////////////////////////////////////////////////////
this is the place where everything is set up */
void setup()
{
noInterrupts(); /* cli() can also be used */
#if ! defined( debug ) /* debug might be used in another program for debuggingby scope */
/* =========================================================================================
the following lines set the A0 to A3 lines as input (with a pull-up) for the interrupts */
DDRC = DDRC & 0b11110000; /* Set bits A[3-0] as inputs, leave the rest as-is */
PORTC = PORTC | 0b00001111; /* Switch bits A[3-0] to PULL-UP, leave the rest as-is */
PCICR = PCICR | 0b00000010; /* Set PCIE1, meaning enable the PCINT[14:8] interrupts */
PCMSK1 = PCMSK1 | 0b00001111; /* A0 = PCINT8, A1 = PCINT9, A2 = PCINT10, A3 = PCINT11 */
PCIFR = PCIFR | 0b00000010; /* Clear interrupt for all interrupts from port A[7:0] */
/* TODO: check these regs carefully with the sequences */
ADCSRA = ADCSRA & 0b01010000; /* disable possible analog conversions and interrupts */
ADCSRB = ADCSRB & 0b00000000; /* disable possible analog conversions and interrupts */
ACSR = ACSR & 0b11110111; /* Bit 3 – ACIE: Analog Comparator Interrupt Enable */
ACSR = ACSR | 0b10000000; /* Bit 7 – ACD: Analog Comparator Disable */
/* ====================================================================================== */
#endif /* debug */
interrupts(); /* Arduino UNO seems to require that we turn on interrupts for TWI to work */
Wire.begin( /* SDA_PIN, SCL_PIN */ ); /* start the I2C / TWI library - mandatory! */
Wire.setClock( 400000 ); /* go into 'fast 400khz I2C' mode, but watch out for this */
/* TODO: make setClock selectable via a CV ??? */
/* check if starting the Serial Interface is needed and do so if yes */
#if defined( _DEBUG_ ) || defined( TESTRUN )
// Serial.begin( 9600, SERIAL_8N1 ); /* ( 115200, SERIAL_8N1 ); */
Serial.begin( 115200, SERIAL_8N1 );
while (! Serial)
{
; // wait for Serial port to connect. Needed for native USB port.
}
Serial.flush(); // Wait for the outgoing serial data to complete.
while (Serial.available() > 0)
{
Serial.read(); // Clear the input buffer to get actual inputdata.
}
Serial.setTimeout( 1000 ); /* maximum wait time after an input */
commandString.reserve( 16 ); /* reserve 16 bytes for inputString */
#endif
/* simulate FD */
Dcc.setCV( NMRADCC_MASTER_RESET_CV, NMRADCC_MASTER_RESET_CV );
/* ===========================================================
check for setting the CVs to Factory Default during start or
when set by NMRADCC_MASTER_RESET_CV and do so if checked */
if ( Dcc.getCV( NMRADCC_MASTER_RESET_CV ) == NMRADCC_MASTER_RESET_CV )
{
_PL(F( "wait for the copy process to finish.." ) );
FactoryDefaultCVIndex = sizeof( FactoryDefaultCVs ) / sizeof( CVPair );
for ( uint8_t i = 0; i < FactoryDefaultCVIndex; ++i )
{
Dcc.setCV( FactoryDefaultCVs[ i ].CV, FactoryDefaultCVs[ i ].Value );
}
}
/* ======================================================== */
checkSERVOS = ( ( Dcc.getCV( 45 ) == 0 ) ? false : true ); /* */
checkJUICER = ( ( Dcc.getCV( 46 ) == 0 ) ? false : true ); /* */
checkSIGNAL = ( ( Dcc.getCV( 47 ) == 0 ) ? false : true ); /* */
checkINPUTA = ( ( Dcc.getCV( 48 ) == 0 ) ? false : true ); /* */
checkINPUTB = ( ( Dcc.getCV( 49 ) == 0 ) ? false : true ); /* */
/* ===========================================================
here existence of several parts of the project is tested */
if ( checkSERVOS ) /* call this if checkSERVOS true */
{
if (! servos.begin( ) )
{
_PL( "Couldn't find SERVOS - check and reset" );
// softwareReset( WDTO_4S ); /* resets every 4s */
} else {
_PL( "Found SERVOS - going to the next steps" );
servos.setPWMFreq( SERVOFRQ );
}
}
if ( checkJUICER ) /* call this if checkJUICER true */
{
if (! juicer.begin_I2C( baseAddressJuicer, &Wire ) )
{
_PL( "Couldn't find JUICER - check and reset" );
// softwareReset( WDTO_4S ); /* resets every 4s */
} else {
_PL( "Found JUICER - going to the next steps" );
for ( uint8_t p = 0; p < 16; ++p ) /* initialise all 16 as OUTPUT */
{
juicer.pinMode( p, OUTPUT );
}
}
}
if ( checkSIGNAL ) /* call this if checkSIGNAL true */
{
if (! signal.begin_I2C( baseAddressSignal, &Wire ) )
{
_PL( "Couldn't find SIGNAL - check and reset" );
// softwareReset( WDTO_4S ); /* resets every 4s */
} else {
_PL( "Found SIGNAL - going to the next steps" );
for ( uint8_t p = 0; p < 16; ++p ) /* initialise all 16 as OUTPUT */
{
signal.pinMode( p, OUTPUT );
}
}
}
if ( checkINPUTA ) /* call this if checkINPUTA true */
{
if (! inputa.begin( baseAddressInputa, &Wire ) )
{
_PL( "Couldn't find INPUTA - check and reset" );
// softwareReset( WDTO_4S ); /* resets every 4s */
} else {
_PL( "Found INPUTA - going to the next steps" );
for ( uint8_t p = 0; p < 8; ++p ) /* initialise all 8 as INPUT */
{
inputa.pinMode( p, INPUT_PULLUP ); // INPUT );
}
}
}
if ( checkINPUTB ) /* call this if checkINPUTB true */
{
if (! inputb.begin( baseAddressInputb, &Wire ) )
{
_PL( "Couldn't find INPUTB - check and reset" );
// softwareReset( WDTO_4S ); /* resets every 4s */
} else {
_PL( "Found INPUTB - going to the next steps" );
for ( uint8_t p = 0; p < 8; ++p ) /* initialise all 8 as INPUT */
{
inputb.pinMode( p, INPUT_PULLUP ); // INPUT );
}
}
}
/* ======================================================== */
// Call the DCC 'pin' and 'init' functions to enable the DCC Receivermode
Dcc.pin( digitalPinToInterrupt( FunctionPinDcc ), FunctionPinDcc, false );
Dcc.init( MAN_ID_DIY, 201, FLAGS_MY_ADDRESS_ONLY, 0 ); delay( 1000 );
//c.initAccessoryDecoder( MAN_ID_DIY, 201, FLAGS_MY_ADDRESS_ONLY, 0 );
Dcc.setCV( NMRADCC_SIMPLE_RESET_CV, 0 ); /* Reset the just_a_reset CV */
myDecoderAddress = Dcc.getAddr(); /* get the decoder address from CV */
/* TODO: */
// turnout_queue[ 0].pulseLft = 175;
// turnout_queue[ 0].pulseRgt = 475;
// turnout_queue[ 1].pulseLft = 275;
// turnout_queue[ 1].pulseRgt = 375;
// turnout_queue[ 2].pulseLft = 175;
// turnout_queue[ 2].pulseRgt = 475;
// turnout_queue[ 3].pulseLft = 175;
// turnout_queue[ 3].pulseRgt = 475;
// turnout_queue[ 4].pulseLft = 175;
// turnout_queue[ 4].pulseRgt = 475;
// turnout_queue[ 5].pulseLft = 175;
// turnout_queue[ 5].pulseRgt = 475;
// turnout_queue[ 6].pulseLft = 175;
// turnout_queue[ 6].pulseRgt = 475;
// turnout_queue[ 7].pulseLft = 175;
// turnout_queue[ 7].pulseRgt = 475;
// turnout_queue[ 8].pulseLft = 175;
// turnout_queue[ 8].pulseRgt = 475;
// turnout_queue[ 9].pulseLft = 175;
// turnout_queue[ 9].pulseRgt = 475;
// turnout_queue[10].pulseLft = 175;
// turnout_queue[10].pulseRgt = 475;
// turnout_queue[11].pulseLft = 175;
// turnout_queue[11].pulseRgt = 475;
// turnout_queue[12].pulseLft = 175;
// turnout_queue[12].pulseRgt = 475;
// turnout_queue[13].pulseLft = 175;
// turnout_queue[13].pulseRgt = 475;
// turnout_queue[14].pulseLft = 275;
// turnout_queue[14].pulseRgt = 375;
// turnout_queue[15].pulseLft = 175;
// turnout_queue[15].pulseRgt = 475;
/* check the TWIbus for existance of devices just to be sure */
// checkTheTWIBusses( );
/* set all SERVOS, JUICERs and SINALs to the SWITCHes states */
// if ( checkINPUTA ) { readButtonsInputa( ); }
// if ( checkINPUTB ) { readButtonsInputb( ); }
displayText( 1, true, 99, false ); /* finish, clear the screen */
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
void loop() {
currentMillis = millis(); /* lets keep track of the time */
Dcc.process(); /* call this for correct library operation */
// runEvery( 25 ) { if ( checkINPUTA ) { readButtonsInputa( ); } }
// runEvery( 25 ) { if ( checkINPUTB ) { readButtonsInputb( ); } }
// runEvery( 5 ) { if ( checkSERVOS ) { setsServosCorrect( ); } }
// runEvery( 5000 ) { checkTheTWIBusses( ); }
if ( Serial.available() > 0 ) /* Serial Input needs message-handling */
{
commandString = Serial.readString();
commandString.trim(); /* remove spaces at both ends */
commandString.toLowerCase(); /* make it all lowercase */
computeInput( myCurrentMenu, commandString );
}
};
/* /////////////////////////////////////////////////////////////////////////////////////////////////// */
/* */