diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 84fdf3e..614fed9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,5 +13,7 @@ jobs: run: pip install wheel - name: Install PlatformIO Core run: pip install -U https://github.com/platformio/platformio/archive/v4.2.1.zip + - name: Copy generic config over + run: cp config.example.h config.h - name: Compile Command Station (AVR) run: python -m platformio run diff --git a/.gitignore b/.gitignore index f49f28f..e5404bb 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ Release/* .gcc-flags.json .pio/ .vscode/ +config.h \ No newline at end of file diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index e3d7cac..101f3f6 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -1,74 +1,161 @@ -/* - * © 2020, Chris Harlow. All rights reserved. - * - * This file is a demonstattion of setting up a DCC-EX - * Command station to support direct connection of WiThrottle devices - * such as "Engine Driver". If you contriol your layout through JMRI - * then DON'T connect throttles to this wifi, connect them to JMRI. - * - * This is just 3 statements longer than the basic setup. - * - * THIS SETUP DOES NOT APPLY TO ARDUINO UNO WITH ONLY A SINGLE SERIAL PORT. - * REFER TO SEPARATE EXAMPLE. - */ +//////////////////////////////////////////////////////////////////////////////////// +// © 2020, Chris Harlow. All rights reserved. +// +// This file is a demonstattion of setting up a DCC-EX +// Command station to support direct connection of WiThrottle devices +// such as "Engine Driver". If you contriol your layout through JMRI +// then DON'T connect throttles to this wifi, connect them to JMRI. +// +// This is just 3 statements longer than the basic setup. +// +// THIS SETUP DOES NOT APPLY TO ARDUINO UNO WITH ONLY A SINGLE SERIAL PORT. +// REFER TO SEPARATE EXAMPLE. +//////////////////////////////////////////////////////////////////////////////////// -#include "DCCEX.h" +#include "config.h" +#include "DCC.h" +#include "DIAG.h" +#include "DCCEXParser.h" +#if ENABLE_WIFI +#include "WifiInterface.h" +#endif +#if ENABLE_FREE_MEM_WARNING +#include "freeMemory.h" +int ramLowWatermark = 32767; // This figure gets overwritten dynamically in loop() +#endif -#ifdef ARDUINO_AVR_UNO - #include - SoftwareSerial Serial1(15,16); // YOU must get these pins correct to use Wifi on a UNO - #define WIFI_BAUD 9600 -#else - #define WIFI_BAUD 115200 -#endif +// this code is here to demonstrate use of the DCC API and other techniques + +// myFilter is an example of an OPTIONAL command filter used to intercept < > commands from +// the usb or wifi streamm. It demonstrates how a command may be intercepted +// or even a new command created without having to break open the API library code. +// The filter is permitted to use or modify the parameter list before passing it on to +// the standard parser. By setting the opcode to 0, the standard parser will +// just ignore the command on the assumption that you have already handled it. +// +// The filter must be enabled by calling the DCC EXParser::setFilter method, see use in setup(). +#if ENABLE_CUSTOM_FILTER +void myComandFilter(Print *stream, byte &opcode, byte ¶mCount, int p[]) +{ + (void)stream; // avoid compiler warning if we don't access this parameter + switch (opcode) + { + case '!': // Create a bespoke new command to clear all loco reminders or specific locos e.g + if (paramCount == 0) + DCC::forgetAllLocos(); + else + for (int i = 0; i < paramCount; i++) + DCC::forgetLoco(p[i]); + opcode = 0; // tell parser to ignore this command as we have done it already + break; + default: // drop through and parser will use the command unaltered. + break; + } +} + +// This is an OPTIONAL example of a HTTP filter... +// If you have configured wifi and an HTTP request is received on the Wifi connection +// it will normally be rejected 404 Not Found. + +// If you wish to handle HTTP requests, you can create a filter and ask the WifiInterface to +// call your code for each detected http request. + +void myHttpFilter(Print *stream, byte *cmd) +{ + (void)cmd; // Avoid compiler warning because this example doesnt use this parameter + + // BEWARE - As soon as you start responding, the cmd buffer is trashed! + // You must get everything you need from it before using StringFormatter::send! + + StringFormatter::send(stream, F("HTTP/1.1 200 OK\nContent-Type: text/html\nConnnection: close\n\n")); + StringFormatter::send(stream, F("This is my HTTP filter responding.
")); +} +#endif + +// Callback functions are necessary if you call any API that must wait for a response from the +// programming track. The API must return immediately otherwise other loop() functions would be blocked. +// Your callback function will be invoked when the data arrives from the prog track. +// See the DCC:getLocoId example in the setup function. +#if ENABLE_CUSTOM_CALLBACK +void myCallback(int result) +{ + DIAG(F("\n getting Loco Id callback result=%d"), result); +} +#endif // Create a serial command parser... Enables certain diagnostics and commands -// to be issued from the USB serial console +// to be issued from the USB serial console // This is NOT intended for JMRI.... -DCCEXParser serialParser; +DCCEXParser serialParser; -void setup() { +void setup() +{ // The main sketch has responsibilities during setup() - - // Responsibility 1: Start the usb connection for diagnostics + + // Responsibility 1: Start the usb connection for diagnostics // This is normally Serial but uses SerialUSB on a SAMD processor Serial.begin(115200); - // Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi - // NOTE: References to Serial1 are for the serial port used to connect - // your wifi chip/shield. - - Serial1.begin(WIFI_BAUD); - WifiInterface::setup(Serial1, F("Your network name"), F("your network password"),F("DCCEX"),3532); - - // Responsibility 3: Start the DCC engine. - // Note: this provides DCC with two motor drivers, main and prog, which handle the motor shield(s) - // Standard supported devices have pre-configured macros but custome hardware installations require - // detailed pin mappings and may also require modified subclasses of the MotorDriver to implement specialist logic. +// Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi +// NOTE: References to Serial1 are for the serial port used to connect +// your wifi chip/shield. - // STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h +// Optionally tell the command parser to use my example filter. +// This will intercept JMRI commands from both USB and Wifi +#if ENABLE_CUSTOM_FILTER + DCCEXParser::setFilter(myComandFilter); +#endif - // Optionally a Timer number (1..4) may be passed to DCC::begin to override the default Timer1 used for the - // waveform generation. e.g. DCC::begin(STANDARD_MOTOR_SHIELD,2); to use timer 2 - - DCC::begin(STANDARD_MOTOR_SHIELD); +#if ENABLE_CUSTOM_CALLBACK + // This is just for demonstration purposes + DIAG(F("\n===== DCCEX demonstrating DCC::getLocoId() call ==========\n")); + DCC::getLocoId(myCallback); // myCallback will be called with the result + DIAG(F("\n===== DCC::getLocoId has returned, but the callback wont be executed until we are in loop() ======\n")); +#endif +#if ENABLE_WIFI + Serial1.begin(WIFI_SERIAL_LINK_SPEED); + WifiInterface::setup(Serial1, F("Your network name"), F("your network password"), F("DCCEX"), 3532); +#endif + + // Responsibility 3: Start the DCC engine. + // Note: this provides DCC with two motor drivers, main and prog, which handle the motor shield(s) + // Standard supported devices have pre-configured macros but custome hardware installations require + // detailed pin mappings and may also require modified subclasses of the MotorDriver to implement specialist logic. + + // STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h + + // Optionally a Timer number (1..4) may be passed to DCC::begin to override the default Timer1 used for the + // waveform generation. e.g. DCC::begin(STANDARD_MOTOR_SHIELD,2); to use timer 2 + + DCC::begin(STANDARD_MOTOR_SHIELD); } -void loop() { +void loop() +{ // The main sketch has responsibilities during loop() - + // Responsibility 1: Handle DCC background processes // (loco reminders and power checks) - DCC::loop(); + DCC::loop(); // Responsibility 2: handle any incoming commands on USB connection serialParser.loop(Serial); - // Responsibility 3: Optionally handle any incoming WiFi traffic +// Responsibility 3: Optionally handle any incoming WiFi traffic +#if ENABLE_WIFI WifiInterface::loop(); +#endif - +// Optionally report any decrease in memory (will automatically trigger on first call) +#if ENABLE_FREE_MEM_WARNING + int freeNow = freeMemory(); + if (freeNow < ramLowWatermark) + { + ramLowWatermark = freeNow; + DIAG(F("\nFree RAM=%d\n"), ramLowWatermark); + } +#endif } diff --git a/DCCEX.h b/DCCEX.h deleted file mode 100644 index e94b193..0000000 --- a/DCCEX.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef DCCEX_h -#define DCCEX_h -#include "DCC.h" -#include "DIAG.h" -#include "DCCEXParser.h" -#include "WifiInterface.h" -#endif diff --git a/config.example.h b/config.example.h new file mode 100644 index 0000000..c0fc4d3 --- /dev/null +++ b/config.example.h @@ -0,0 +1,185 @@ +/********************************************************************** + +Config.h +COPYRIGHT (c) 2013-2016 Gregg E. Berman +COPYRIGHT (c) 2020 Fred Decker + +The configuration file for DCC++ EX Command Station + +**********************************************************************/ + +///////////////////////////////////////////////////////////////////////////////////// +// NOTE: Before connecting these boards and selecting one in this software +// check the quick install guides!!! Some of these boards require a voltage +// generating resitor on the current sense pin of the device. Failure to select +// the correct resistor could damage the sense pin on your Arduino or destroy +// the device. +// +// DEFINE MOTOR_SHIELD_TYPE ACCORDING TO THE FOLLOWING TABLE: +// +// STANDARD_MOTOR_SHIELD = ARDUINO MOTOR SHIELD (MAX 18V/2A PER CHANNEL) Arduino Motor shield Rev3 based on the L298 +// POLOLU_MOTOR_SHIELD = POLOLU MC33926 MOTOR SHIELD (MAX 28V/2.5 PER CHANNEL) Pololu MC33926 Motor Driver (shield or carrier) +// FUNDUMOTO_SHIELD = FunduMoto Motor Shield +// FIREBOX_MK1 = Firebox MK1 +// FIREBOX_MK1S = Firebox MK1S + + +#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE PROGRAM TRACK CURRENT LIMIT IN MILLIAMPS + +#define TRIP_CURRENT_PROG 250 + + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE NUMBER OF MAIN TRACK REGISTER + +#define MAX_MAIN_REGISTERS 12 + +///////////////////////////////////////////////////////////////////////////////////// +// +// NOTE: Only the Mega currently supports WiFi since space is a constraint on the Uno +// at this time. It may be implemented in the future. +// +// +// +// Enable Use of WiFI for the Command Station for DCC-EX communications + +#define ENABLE_WIFI false + +#if ENABLE_WIFI + ///////////////////////////////////////////////////////////////////////////////////// + // + // DEFINE WiFi Parameters + // + #define WIFI_SSID "" + #define WIFI_PASSWORD "" + #define WIFI_HOSTNAME "" + + + // This defines the speed at which the Arduino will communicate with the ESP8266 module. + // When using the ESP8266 on an Uno it is recommended to use 9600, for Mega2560 the + // higher speed can be used. Set this based on your ESP8266 module configuration. + // Common defaults are 9600 or 115200. + #define WIFI_SERIAL_LINK_SPEED 115200 +#endif + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE STATIC IP ADDRESS *OR* COMMENT OUT TO USE DHCP +// + +//#define IP_ADDRESS { 192, 168, 1, 200 } + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE PORT TO USE FOR ETHERNET COMMUNICATIONS INTERFACE +// +// Uncomment to use Ethernet + +// #define ETHERNET_PORT 2560 + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE MAC ADDRESS ARRAY FOR ETHERNET COMMUNICATIONS INTERFACE +// +// Uncomment to use with Ethernet Shields +// +// NOTE: This is not used with ESP8266 WiFi modules. + +// #define MAC_ADDRESS { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF } + +///////////////////////////////////////////////////////////////////////////////////// +// +// Allows using a pin as a trigger for a scope or analyzer so we can capture only +// the important parts of the data stream +// +// USE_TRIGGERPIN: Enable code that switches the trigger pin on and off at end +// of the preamble. This takes some clock cycles in the +// interrupt routine for the main track. +// USE_TRIGGERPIN_PER_BIT: As above but for every bit. This only makes sense +// if USE_TRIGGERPIN is set. +// +// The value of the TRIGGERPIN is defined in DCCppEX.h because it might +// be board specific +// +//#define USE_TRIGGERPIN +//#define USE_TRIGGERPIN_PER_BIT + +///////////////////////////////////////////////////////////////////////////////////// +// +// Define only of you need the store to EEPROM feature. This takes RAM and +// you may need to use less MAX_MAIN_REGISTERS to compensate (at least on the UNO) + +#define EESTORE + +///////////////////////////////////////////////////////////////////////////////////// +// +// This shows the status and version at startup. This takes RAM. You can comment +// this line if you need to increase MAX_MAIN_REGISTERS(at least on the UNO) + +#define SHOWCONFIG + +///////////////////////////////////////////////////////////////////////////////////// +// +// This is different from the above config display which only shows one line at startup +// This defines a pin that when jumpered to ground before powering up the Arduinio, +// will display more detailed settings for diagnostics. You must remove the jumper and +// restart the Arduino to return to normal operation + +#define SHOW_CONFIG_DETAIL_PIN A2 + +///////////////////////////////////////////////////////////////////////////////////// +// +// PREAMBLE_MAIN: Length of the preamble on the main track. Per standard this should +// be at least 14 bits but if some equipment wants to insert a RailCom +// cutout this should be at least 16 bits. +// PERAMBLE_PROG: Length of the preamble on the programming track. Per standard this +// should be at least 22 bits + +#define PREAMBLE_MAIN 16 // TODO: Finish configurable preamble code +#define PREAMBLE_PROG 22 + +///////////////////////////////////////////////////////////////////////////////////// +// +// DEFINE LCD SCREEN USAGE BY THE BASE STATION +// +// Note: This feature requires an I2C enabled LCD screen using a PCF8574 based chipset. +// or one using a Hitachi HD44780. +// +// To enable, uncomment the line below and make sure only the correct LIB_TYPE line +// is uncommented below to select the library used for your LCD backpack + +//#define ENABLE_LCD + +#ifdef ENABLE_LCD + #define LIB_TYPE_PCF8574 + //#define LIB_TYPE_I2C + // This defines the I2C address for the LCD device + #define LCD_ADDRESS 0x27 //common defaults are 0x27 and 0x3F + + // This defines the number of columns the LCD device has + #define LCD_COLUMNS 16 + + // This defines the number of lines the LCD device has + #define LCD_LINES 2 +#endif + + +///////////////////////////////////////////////////////////////////////////////////// +// +// Enable custom command filtering +#define ENABLE_CUSTOM_FILTER false + +///////////////////////////////////////////////////////////////////////////////////// +// +// Enable custom command filtering +#define ENABLE_CUSTOM_CALLBACK false + +///////////////////////////////////////////////////////////////////////////////////// +// +// Enable custom command filtering +#define ENABLE_FREE_MEM_WARNING false \ No newline at end of file