1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-02-17 06:29:15 +01:00

Merge branch 'feature/config' into mergetest

Conflicts:
	platformio.ini
This commit is contained in:
Harald Barth 2020-09-26 23:28:02 +02:00
commit 527cc92eac
24 changed files with 1620 additions and 721 deletions

View File

@ -13,5 +13,7 @@ jobs:
run: pip install wheel run: pip install wheel
- name: Install PlatformIO Core - name: Install PlatformIO Core
run: pip install -U https://github.com/platformio/platformio/archive/v4.2.1.zip 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) - name: Compile Command Station (AVR)
run: python -m platformio run run: python -m platformio run

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ Release/*
.gcc-flags.json .gcc-flags.json
.pio/ .pio/
.vscode/ .vscode/
config.h

View File

@ -1,74 +1,141 @@
/* ////////////////////////////////////////////////////////////////////////////////////
* © 2020, Chris Harlow. All rights reserved. // © 2020, Chris Harlow. All rights reserved.
* //
* This file is a demonstattion of setting up a DCC-EX // This file is a demonstattion of setting up a DCC-EX
* Command station to support direct connection of WiThrottle devices // Command station with optional support for direct connection of WiThrottle devices
* such as "Engine Driver". If you contriol your layout through JMRI // such as "Engine Driver". If you contriol your layout through JMRI
* then DON'T connect throttles to this wifi, connect them to JMRI. // then DON'T connect throttles to this wifi, connect them to JMRI.
* //
* This is just 3 statements longer than the basic setup. // THE WIFI FEATURE IS NOT SUPPORTED ON ARDUINO DEVICES WITH ONLY 2KB RAM.
* ////////////////////////////////////////////////////////////////////////////////////
* THIS SETUP DOES NOT APPLY TO ARDUINO UNO WITH ONLY A SINGLE SERIAL PORT.
* REFER TO SEPARATE EXAMPLE.
*/
#include "config.h"
#include "DCCEX.h" #include "DCCEX.h"
#ifdef ARDUINO_AVR_UNO
#include <SoftwareSerial.h>
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
// Create a serial command parser... Enables certain diagnostics and commands ////////////////////////////////////////////////////////////////
// to be issued from the USB serial console //
// This is NOT intended for JMRI.... // Enables an I2C 2x24 or 4x24 LCD Screen
#if ENABLE_LCD
bool lcdEnabled = false;
#if defined(LIB_TYPE_PCF8574)
LiquidCrystal_PCF8574 lcdDisplay(LCD_ADDRESS);
#elif defined(LIB_TYPE_I2C)
LiquidCrystal_I2C lcdDisplay = LiquidCrystal_I2C(LCD_ADDRESS, LCD_COLUMNS, LCD_LINES);
#endif
#endif
DCCEXParser serialParser; // Create a serial command parser for the USB connection,
// This supports JMRI or manual diagnostics and commands
// to be issued from the USB serial console.
DCCEXParser serialParser;
void setup() { void setup()
{
////////////////////////////////////////////
//
// More display stuff. Need to put this in a .h file and make
// it a class
#if ENABLE_LCD
Wire.begin();
// Check that we can find the LCD by its address before attempting to use it.
Wire.beginTransmission(LCD_ADDRESS);
if (Wire.endTransmission() == 0)
{
lcdEnabled = true;
lcdDisplay.begin(LCD_COLUMNS, LCD_LINES);
lcdDisplay.setBacklight(255);
lcdDisplay.clear();
lcdDisplay.setCursor(0, 0);
lcdDisplay.print("DCC++ EX v");
lcdDisplay.print(VERSION);
lcdDisplay.setCursor(0, 1);
#if COMM_INTERFACE >= 1
lcdDisplay.print("IP: PENDING");
#else
lcdDisplay.print("SERIAL: READY");
#endif
#if LCD_LINES > 2
lcdDisplay.setCursor(0, 3);
lcdDisplay.print("TRACK POWER: OFF");
#endif
}
#endif
// The main sketch has responsibilities during 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 // This is normally Serial but uses SerialUSB on a SAMD processor
Serial.begin(115200); Serial.begin(115200);
// Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi // Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi
// NOTE: References to Serial1 are for the serial port used to connect // NOTE: References to Serial1 are for the serial port used to connect
// your wifi chip/shield. // 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.
// STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h #ifdef WIFI_ON
bool wifiUp = false;
const __FlashStringHelper *wifiESSID = F(WIFI_SSID);
const __FlashStringHelper *wifiPassword = F(WIFI_PASSWORD);
const __FlashStringHelper *dccex = F(WIFI_HOSTNAME);
const uint16_t port = IP_PORT;
// Optionally a Timer number (1..4) may be passed to DCC::begin to override the default Timer1 used for the Serial1.begin(WIFI_SERIAL_LINK_SPEED);
// waveform generation. e.g. DCC::begin(STANDARD_MOTOR_SHIELD,2); to use timer 2 wifiUp = WifiInterface::setup(Serial1, wifiESSID, wifiPassword, dccex, port);
#if NUM_SERIAL > 1
DCC::begin(STANDARD_MOTOR_SHIELD); if (!wifiUp)
{
Serial2.begin(WIFI_SERIAL_LINK_SPEED);
wifiUp = WifiInterface::setup(Serial2, wifiESSID, wifiPassword, dccex, port);
}
#if NUM_SERIAL > 2
if (!wifiUp)
{
Serial3.begin(WIFI_SERIAL_LINK_SPEED);
wifiUp = WifiInterface::setup(Serial3, wifiESSID, wifiPassword, dccex, port);
}
#endif // >2
#endif // >1
#endif // WIFI_ON
// 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(MOTOR_SHIELD_TYPE);
} }
void loop() { void loop()
{
// The main sketch has responsibilities during loop() // The main sketch has responsibilities during loop()
// Responsibility 1: Handle DCC background processes // Responsibility 1: Handle DCC background processes
// (loco reminders and power checks) // (loco reminders and power checks)
DCC::loop(); DCC::loop();
// Responsibility 2: handle any incoming commands on USB connection // Responsibility 2: handle any incoming commands on USB connection
serialParser.loop(Serial); serialParser.loop(Serial);
// Responsibility 3: Optionally handle any incoming WiFi traffic // Responsibility 3: Optionally handle any incoming WiFi traffic
#ifdef WIFI_ON
WifiInterface::loop(); WifiInterface::loop();
#endif
// Optionally report any decrease in memory (will automatically trigger on first call)
#if ENABLE_FREE_MEM_WARNING
static int ramLowWatermark = 32767; // replaced on first loop
int freeNow = freeMemory();
if (freeNow < ramLowWatermark)
{
ramLowWatermark = freeNow;
DIAG(F("\nFree RAM=%d\n"), ramLowWatermark);
}
#endif
} }

10
DCC.cpp
View File

@ -41,8 +41,10 @@ const byte FN_GROUP_3=0x04;
const byte FN_GROUP_4=0x08; const byte FN_GROUP_4=0x08;
const byte FN_GROUP_5=0x10; const byte FN_GROUP_5=0x10;
__FlashStringHelper* DCC::shieldName=NULL;
void DCC::begin(MotorDriver * mainDriver, MotorDriver* progDriver, byte timerNumber) { void DCC::begin(const __FlashStringHelper* motorShieldName, MotorDriver * mainDriver, MotorDriver* progDriver, byte timerNumber) {
shieldName=(__FlashStringHelper*)motorShieldName;
DCCWaveform::begin(mainDriver,progDriver, timerNumber); DCCWaveform::begin(mainDriver,progDriver, timerNumber);
} }
@ -206,6 +208,10 @@ void DCC::setProgTrackSyncMain(bool on) {
DCCWaveform::progTrackSyncMain=on; DCCWaveform::progTrackSyncMain=on;
} }
__FlashStringHelper* DCC::getMotorShieldName() {
return shieldName;
}
const ackOp PROGMEM WRITE_BIT0_PROG[] = { const ackOp PROGMEM WRITE_BIT0_PROG[] = {
BASELINE, BASELINE,
W0,WACK, W0,WACK,
@ -568,7 +574,7 @@ void DCC::ackManagerLoop(bool blocking) {
DCCWaveform::progTrack.setPowerMode(POWERMODE::ON); DCCWaveform::progTrack.setPowerMode(POWERMODE::ON);
DCCWaveform::progTrack.sentResetsSincePacket = 0; DCCWaveform::progTrack.sentResetsSincePacket = 0;
DCCWaveform::progTrack.autoPowerOff=true; DCCWaveform::progTrack.autoPowerOff=true;
return; if (!blocking) return;
} }
if (checkResets(blocking, DCCWaveform::progTrack.autoPowerOff ? 20 : 3)) return; if (checkResets(blocking, DCCWaveform::progTrack.autoPowerOff ? 20 : 3)) return;
DCCWaveform::progTrack.setAckBaseline(); DCCWaveform::progTrack.setAckBaseline();

189
DCC.h
View File

@ -24,118 +24,151 @@
typedef void (*ACK_CALLBACK)(int result); typedef void (*ACK_CALLBACK)(int result);
enum ackOp { // Program opcodes for the ack Manager enum ackOp
BASELINE, // ensure enough resets sent before starting and obtain baseline current { // Program opcodes for the ack Manager
W0,W1, // issue write bit (0..1) packet BASELINE, // ensure enough resets sent before starting and obtain baseline current
WB, // issue write byte packet W0,
VB, // Issue validate Byte packet W1, // issue write bit (0..1) packet
V0, // Issue validate bit=0 packet WB, // issue write byte packet
V1, // issue validate bit=1 packlet VB, // Issue validate Byte packet
WACK, // wait for ack (or absence of ack) V0, // Issue validate bit=0 packet
ITC1, // If True Callback(1) (if prevous WACK got an ACK) V1, // issue validate bit=1 packlet
ITC0, // If True callback(0); WACK, // wait for ack (or absence of ack)
ITCB, // If True callback(byte) ITC1, // If True Callback(1) (if prevous WACK got an ACK)
NAKFAIL, // if false callback(-1) ITC0, // If True callback(0);
FAIL, // callback(-1) ITCB, // If True callback(byte)
STARTMERGE, // Clear bit and byte settings ready for merge pass NAKFAIL, // if false callback(-1)
MERGE, // Merge previous wack response with byte value and decrement bit number (use for readimng CV bytes) FAIL, // callback(-1)
SETBIT, // sets bit number to next prog byte STARTMERGE, // Clear bit and byte settings ready for merge pass
SETCV, // sets cv number to next prog byte MERGE, // Merge previous wack response with byte value and decrement bit number (use for readimng CV bytes)
STASHLOCOID, // keeps current byte value for later SETBIT, // sets bit number to next prog byte
COMBINELOCOID, // combines current value with stashed value and returns it SETCV, // sets cv number to next prog byte
ITSKIP, // skip to SKIPTARGET if ack true STASHLOCOID, // keeps current byte value for later
SKIPTARGET=0xFF // jump to target COMBINELOCOID, // combines current value with stashed value and returns it
ITSKIP, // skip to SKIPTARGET if ack true
SKIPTARGET = 0xFF // jump to target
}; };
// Allocations with memory implications..! // Allocations with memory implications..!
// Base system takes approx 900 bytes + 8 per loco. Turnouts, Sensors etc are dynamically created // Base system takes approx 900 bytes + 8 per loco. Turnouts, Sensors etc are dynamically created
#ifdef ARDUINO_AVR_UNO #ifdef ARDUINO_AVR_UNO
const byte MAX_LOCOS=20; const byte MAX_LOCOS = 20;
#else #else
const byte MAX_LOCOS=50; const byte MAX_LOCOS = 50;
#endif #endif
class DCC
class DCC { {
public: public:
static void begin(const __FlashStringHelper *motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver, byte timerNumber = 1);
static void begin(MotorDriver * mainDriver, MotorDriver * progDriver, byte timerNumber=1);
static void loop(); static void loop();
// Public DCC API functions // Public DCC API functions
static void setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection); static void setThrottle(uint16_t cab, uint8_t tSpeed, bool tDirection);
static uint8_t getThrottleSpeed(int cab); static uint8_t getThrottleSpeed(int cab);
static bool getThrottleDirection(int cab); static bool getThrottleDirection(int cab);
static void writeCVByteMain(int cab, int cv, byte bValue); static void writeCVByteMain(int cab, int cv, byte bValue);
static void writeCVBitMain(int cab, int cv, byte bNum, bool bValue); static void writeCVBitMain(int cab, int cv, byte bNum, bool bValue);
static void setFunction( int cab, byte fByte, byte eByte); static void setFunction(int cab, byte fByte, byte eByte);
static void setFn( int cab, byte functionNumber, bool on); static void setFn(int cab, byte functionNumber, bool on);
static int changeFn( int cab, byte functionNumber, bool pressed); static int changeFn(int cab, byte functionNumber, bool pressed);
static void updateGroupflags(byte & flags, int functionNumber); static void updateGroupflags(byte &flags, int functionNumber);
static void setAccessory(int aAdd, byte aNum, bool activate) ; static void setAccessory(int aAdd, byte aNum, bool activate);
static bool writeTextPacket( byte *b, int nBytes); static bool writeTextPacket(byte *b, int nBytes);
static void setProgTrackSyncMain(bool on); // when true, prog track becomes driveable static void setProgTrackSyncMain(bool on); // when true, prog track becomes driveable
// ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1 // ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1
static void readCV(int cv, ACK_CALLBACK callback, bool blocking=false); static void readCV(int cv, ACK_CALLBACK callback, bool blocking = false);
static void readCVBit(int cv, byte bitNum, ACK_CALLBACK callback, bool blocking=false); // -1 for error static void readCVBit(int cv, byte bitNum, ACK_CALLBACK callback, bool blocking = false); // -1 for error
static void writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking=false) ; static void writeCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking = false);
static void writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking=false); static void writeCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking = false);
static void verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking=false) ; static void verifyCVByte(int cv, byte byteValue, ACK_CALLBACK callback, bool blocking = false);
static void verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking=false); static void verifyCVBit(int cv, byte bitNum, bool bitValue, ACK_CALLBACK callback, bool blocking = false);
static void getLocoId(ACK_CALLBACK callback, bool blocking=false); static void getLocoId(ACK_CALLBACK callback, bool blocking = false);
// Enhanced API functions // Enhanced API functions
static void forgetLoco(int cab); // removes any speed reminders for this loco static void forgetLoco(int cab); // removes any speed reminders for this loco
static void forgetAllLocos(); // removes all speed reminders static void forgetAllLocos(); // removes all speed reminders
static void displayCabList(Print * stream); static void displayCabList(Print *stream);
private:
struct LOCO { static __FlashStringHelper *getMotorShieldName();
int loco;
byte speedCode; private:
byte groupFlags; struct LOCO
unsigned long functions; {
int loco;
byte speedCode;
byte groupFlags;
unsigned long functions;
}; };
static byte loopStatus; static byte loopStatus;
static void setThrottle2( uint16_t cab, uint8_t speedCode); static void setThrottle2(uint16_t cab, uint8_t speedCode);
static void updateLocoReminder(int loco, byte speedCode); static void updateLocoReminder(int loco, byte speedCode);
static void setFunctionInternal( int cab, byte fByte, byte eByte); static void setFunctionInternal(int cab, byte fByte, byte eByte);
static bool issueReminder(int reg); static bool issueReminder(int reg);
static int nextLoco; static int nextLoco;
static __FlashStringHelper *shieldName;
static LOCO speedTable[MAX_LOCOS]; static LOCO speedTable[MAX_LOCOS];
static byte cv1(byte opcode, int cv); static byte cv1(byte opcode, int cv);
static byte cv2(int cv); static byte cv2(int cv);
static int lookupSpeedTable(int locoId); static int lookupSpeedTable(int locoId);
static void issueReminders(); static void issueReminders();
static void callback(int value); static void callback(int value);
// ACK MANAGER // ACK MANAGER
static ackOp const * ackManagerProg; static ackOp const *ackManagerProg;
static byte ackManagerByte; static byte ackManagerByte;
static byte ackManagerBitNum; static byte ackManagerBitNum;
static int ackManagerCv; static int ackManagerCv;
static byte ackManagerStash; static byte ackManagerStash;
static bool ackReceived; static bool ackReceived;
static ACK_CALLBACK ackManagerCallback; static ACK_CALLBACK ackManagerCallback;
static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback, bool blocking); static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback, bool blocking);
static void ackManagerLoop(bool blocking); static void ackManagerLoop(bool blocking);
static bool checkResets(bool blocking, uint8_t numResets); static bool checkResets(bool blocking, uint8_t numResets);
static const int PROG_REPEATS=8; // repeats of programming commands (some decoders need at least 8 to be reliable) static const int PROG_REPEATS = 8; // repeats of programming commands (some decoders need at least 8 to be reliable)
// NMRA codes # // NMRA codes #
static const byte SET_SPEED=0x3f; static const byte SET_SPEED = 0x3f;
static const byte WRITE_BYTE_MAIN = 0xEC; static const byte WRITE_BYTE_MAIN = 0xEC;
static const byte WRITE_BIT_MAIN = 0xE8; static const byte WRITE_BIT_MAIN = 0xE8;
static const byte WRITE_BYTE = 0x7C; static const byte WRITE_BYTE = 0x7C;
static const byte VERIFY_BYTE= 0x74; static const byte VERIFY_BYTE = 0x74;
static const byte BIT_MANIPULATE=0x78; static const byte BIT_MANIPULATE = 0x78;
static const byte WRITE_BIT=0xF0; static const byte WRITE_BIT = 0xF0;
static const byte VERIFY_BIT=0xE0; static const byte VERIFY_BIT = 0xE0;
static const byte BIT_ON=0x08; static const byte BIT_ON = 0x08;
static const byte BIT_OFF=0x00; static const byte BIT_OFF = 0x00;
}; };
#ifdef ARDUINO_AVR_MEGA // is using Mega 1280, define as Mega 2560 (pinouts and functionality are identical)
#define ARDUINO_AVR_MEGA2560
#endif
#if defined(ARDUINO_AVR_UNO)
#define ARDUINO_TYPE "UNO"
#elif defined(ARDUINO_AVR_NANO)
#define ARDUINO_TYPE "NANO"
#elif defined(ARDUINO_AVR_MEGA2560)
#define ARDUINO_TYPE "MEGA"
#elif defined(ARDUINO_ARCH_MEGAAVR)
#define ARDUINO_TYPE "UNOWIFIR2"
#else
#error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH AN ARDUINO UNO, NANO 328, OR ARDUINO MEGA 1280/2560
#endif
#if ENABLE_LCD
#include <Wire.h>
#if defined(LIB_TYPE_PCF8574)
#include <LiquidCrystal_PCF8574.h>
extern LiquidCrystal_PCF8574 lcdDisplay;
#elif defined(LIB_TYPE_I2C)
#include <LiquidCrystal_I2C.h>
extern LiquidCrystal_I2C lcdDisplay;
#endif
extern bool lcdEnabled;
#endif
#endif #endif

12
DCCEX.h
View File

@ -1,7 +1,17 @@
// This include is intended to visually simplify the .ino for the end users.
// If there were any #ifdefs required they are much better handled in here.
#ifndef DCCEX_h #ifndef DCCEX_h
#define DCCEX_h #define DCCEX_h
#include "defines.h"
#include "DCC.h" #include "DCC.h"
#include "DIAG.h" #include "DIAG.h"
#include "DCCEXParser.h" #include "DCCEXParser.h"
#include "version.h"
#include "WifiInterface.h" #include "WifiInterface.h"
#endif #include "EthernetInterface.h"
#include <Arduino.h>
#endif

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@
#include <Arduino.h> #include <Arduino.h>
typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int p[]); typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int p[]);
typedef void (*AT_COMMAND_CALLBACK)(const byte * command);
struct DCCEXParser struct DCCEXParser
{ {
@ -29,6 +30,7 @@ struct DCCEXParser
void parse(Print * stream, byte * command, bool blocking); void parse(Print * stream, byte * command, bool blocking);
void flush(); void flush();
static void setFilter(FILTER_CALLBACK filter); static void setFilter(FILTER_CALLBACK filter);
static void setAtCommandCallback(AT_COMMAND_CALLBACK filter);
static const int MAX_PARAMS=10; // Must not exceed this static const int MAX_PARAMS=10; // Must not exceed this
private: private:
@ -59,6 +61,7 @@ struct DCCEXParser
static void callback_Vbit(int result); static void callback_Vbit(int result);
static void callback_Vbyte(int result); static void callback_Vbyte(int result);
static FILTER_CALLBACK filterCallback; static FILTER_CALLBACK filterCallback;
static AT_COMMAND_CALLBACK atCommandCallback;
static void funcmap(int cab, byte value, byte fstart, byte fstop); static void funcmap(int cab, byte value, byte fstart, byte fstop);
}; };

View File

@ -30,7 +30,7 @@ const int MIN_ACK_PULSE_DURATION = 2000;
const int MAX_ACK_PULSE_DURATION = 8500; const int MAX_ACK_PULSE_DURATION = 8500;
const int PREAMBLE_BITS_MAIN = 20; const int PREAMBLE_BITS_MAIN = 16;
const int PREAMBLE_BITS_PROG = 22; const int PREAMBLE_BITS_PROG = 22;

304
EthernetInterface.cpp Normal file
View File

@ -0,0 +1,304 @@
/*
* © 2020,Gregor Baues, Chris Harlow. All rights reserved.
*
* This file is part of DCC-EX/CommandStation-EX
*
* 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/>.
*
* Ethernet Interface added by Gregor Baues
*/
#include "EthernetInterface.h"
#include "DIAG.h"
#include "StringFormatter.h"
//#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
// Support Functions
/**
* @brief Aquire IP Address from DHCP; if that fails try a statically configured address
*
* @return true
* @return false
*/
bool EthernetInterface::setupConnection()
{
singleton=this;
DIAG(F("\nInitialize Ethernet with DHCP:"));
server = EthernetServer(LISTEN_PORT); // Ethernet Server listening on default port LISTEN_PORT
ip = IPAddress(IP_ADDRESS); // init with fixed IP address needed to get to the server
connected = false; // Connection status
streamer= new MemStream(buffer, MAX_ETH_BUFFER, MAX_ETH_BUFFER, true); // streamer who writes the results to the buffer
if (Ethernet.begin(EthernetInterface::mac) == 0)
{
DIAG(F("\nFailed to configure Ethernet using DHCP ... Trying with fixed IP"));
Ethernet.begin(EthernetInterface::mac, EthernetInterface::ip); // default ip address
if (Ethernet.hardwareStatus() == EthernetNoHardware)
{
DIAG(F("\nEthernet shield was not found. Sorry, can't run without hardware. :("));
return false;
};
if (Ethernet.linkStatus() == LinkOFF)
{
DIAG(F("\nEthernet cable is not connected."));
return false;
}
}
ip = Ethernet.localIP(); // reassign the obtained ip address
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]);
DIAG(F("\nListening on port: [%d]"), port);
dnsip = Ethernet.dnsServerIP();
DIAG(F("\nDNS server IP address: [%d.%d.%d.%d] "), ip[0], ip[1], ip[2], ip[3]);
return true;
}
/**
* @brief Handles command requests recieved via UDP. UDP is a connection less, unreliable protocol as it doesn't maintain state but fast.
*
*/
void EthernetInterface::udpHandler() {
singleton->udpHandler2();
}
void EthernetInterface::udpHandler2()
{
int packetSize = Udp.parsePacket();
if (packetSize)
{
DIAG(F("\nReceived packet of size:[%d]\n"), packetSize);
IPAddress remote = Udp.remoteIP();
DIAG(F("From: [%d.%d.%d.%d:"), remote[0], remote[1], remote[2], remote[3]);
char portBuffer[6];
DIAG(F("%s]\n"), utoa(Udp.remotePort(), portBuffer, 10)); // DIAG has issues with unsigend int's so go through utoa
// read the packet into packetBufffer
Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
DIAG(F("Command: [%s]\n"), packetBuffer);
streamer->flush();
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
ethParser.parse(streamer, (byte *)packetBuffer, true); // set to true so it is sync cf. WifiInterface
if (streamer->available() == 0)
{
DIAG(F("\nNo response\n"));
}
else
{
// send the reply
DIAG(F("Response: %s\n"), (char *)buffer);
Udp.write((char *)buffer);
Udp.endPacket();
}
memset(packetBuffer, 0, UDP_TX_PACKET_MAX_SIZE); // reset PacktBuffer
return;
}
}
/**
* @brief Handles command requests recieved via TCP. Supports up to the max# of simultaneous requests which is 8. The connection gets closed as soon as we finished processing
*
*/
void EthernetInterface::tcpHandler()
{
singleton->tcpHandler2();
}
void EthernetInterface::tcpHandler2()
{
// get client from the server
EthernetClient client = getServer().accept();
// check for new client
if (client)
{
for (byte i = 0; i < MAX_SOCK_NUM; i++)
{
if (!clients[i])
{
// On accept() the EthernetServer doesn't track the client anymore
// so we store it in our client array
clients[i] = client;
break;
}
}
}
// check for incoming data from all possible clients
for (byte i = 0; i < MAX_SOCK_NUM; i++)
{
if (clients[i] && clients[i].available() > 0)
{
// read bytes from a client
int count = clients[i].read(buffer, MAX_ETH_BUFFER);
buffer[count] = '\0'; // terminate the string properly
DIAG(F("\nReceived packet of size:[%d]\n"), count);
DIAG(F("From Client #: [%d]\n"), i);
DIAG(F("Command: [%s]\n"), buffer);
// as we use buffer for recv and send we have to reset the write position
streamer->setBufferContentPosition(0, 0);
ethParser.parse(streamer, buffer, true); // set to true to that the execution in DCC is sync
if (streamer->available() == 0)
{
DIAG(F("No response\n"));
}
else
{
buffer[streamer->available()] = '\0'; // mark end of buffer, so it can be used as a string later
DIAG(F("Response: %s\n"), (char *)buffer);
if (clients[i].connected())
{
clients[i].write(buffer, streamer->available());
}
}
}
// stop any clients which disconnect
for (byte i = 0; i < MAX_SOCK_NUM; i++)
{
if (clients[i] && !clients[i].connected())
{
DIAG(F("Disconnect client #%d \n"), i);
clients[i].stop();
}
}
}
}
// Class Functions
/**
* @brief Setup Ethernet Connection
*
* @param pt Protocol used
* @param localPort Port number for the connection
*/
void EthernetInterface::setup(protocolType pt, uint16_t localPort)
{
DIAG(F("\n++++++ Ethernet Setup In Progress ++++++++\n"));
port = localPort;
if (setupConnection())
{
DIAG(F("\nProtocol: [%s]\n"), pt ? "UDP" : "TCP");
switch (pt)
{
case UDP:
{
if (Udp.begin(localPort))
{
connected = true;
protocolHandler = udpHandler;
}
else
{
DIAG(F("\nUDP client failed to start"));
connected = false;
}
break;
};
case TCP:
{
Ethernet.begin(mac, ip);
EthernetServer server(localPort);
setServer(server);
server.begin();
connected = true;
protocolHandler = tcpHandler;
break;
};
default:
{
DIAG(F("Unkown Ethernet protocol; Setup failed"));
connected = false;
return;
}
}
}
else
{
connected = false;
};
DIAG(F("\n++++++ Ethernet Setup %S ++++++++\n"), connected ? F("OK") : F("FAILED"));
};
/**
* @brief Setup Ethernet on default port and user choosen protocol
*
* @param pt Protocol UDP or TCP
*/
void EthernetInterface::setup(protocolType pt)
{
setup(pt, LISTEN_PORT);
};
/**
* @brief Ethernet setup with defaults TCP / Listen Port
*
*/
void EthernetInterface::setup()
{
setup(TCP, LISTEN_PORT);
}
/**
* @brief Main loop for the EthernetInterface
*
*/
void EthernetInterface::loop()
{
switch (Ethernet.maintain())
{
case 1:
//renewed fail
DIAG(F("\nError: renewed fail"));
break;
case 2:
//renewed success
DIAG(F("\nRenewed success: "));
ip = Ethernet.localIP(); // reassign the obtained ip address
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"),ip[0], ip[1], ip[2], ip[3]);
break;
case 3:
//rebind fail
DIAG(F("Error: rebind fail"));
break;
case 4:
//rebind success
DIAG(F("Rebind success"));
ip = Ethernet.localIP(); // reassign the obtained ip address
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]);
break;
default:
//nothing happened
break;
}
protocolHandler();
}

107
EthernetInterface.h Normal file
View File

@ -0,0 +1,107 @@
/*
* © 2020,Gregor Baues, Chris Harlow. All rights reserved.
*
* This file is part of DCC-EX/CommandStation-EX
*
*
* 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/>.
*
* Ethernet Interface added by Gregor Baues
*/
#ifndef EthernetInterface_h
#define EthernetInterface_h
#include "DCCEXParser.h"
#include "MemStream.h"
#include <Arduino.h>
#include <avr/pgmspace.h>
#include <Ethernet.h>
/* some generated mac addresses as EthernetShields don't have one by default in HW.
* Sometimes they come on a sticker on the EthernetShield then use this address otherwise
* just choose one from below or generate one yourself. Only condition is that there is no
* other device on your network with the same Mac address.
*
* 52:b8:8a:8e:ce:21
* e3:e9:73:e1:db:0d
* 54:2b:13:52:ac:0c
* c2:d8:d4:7d:7c:cb
* 86:cf:fa:9f:07:79
*/
/**
* @brief Network Configuration
*
*/
#define MAC_ADDRESS { 0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21 } // MAC address of your networking card found on the sticker on your card or take one from above
#define IP_ADDRESS 10, 0, 0, 101 // Just in case we don't get an adress from DHCP try a static one; make sure
// this one is not used elsewhere and corresponds to your network layout
#define LISTEN_PORT 3366 // default listen port for the server
#define MAX_ETH_BUFFER 250
typedef void (*HTTP_CALLBACK)(Print * stream, byte * cmd);
enum protocolType {
TCP,
UDP
};
typedef void (*protocolCallback)();
class EthernetInterface {
private:
EthernetServer server;
public:
DCCEXParser ethParser;
bool connected;
byte mac[6];
IPAddress ip;
uint16_t port;
IPAddress dnsip;
void setup(protocolType pt, uint16_t lp); // specific port nummber
void setup(protocolType pt); // uses default port number
void setup(); // all defaults (protocol/port)
protocolCallback protocolHandler;
void loop();
private:
static EthernetInterface * singleton;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; // buffer to hold incoming UDP packet,
uint8_t buffer[MAX_ETH_BUFFER]; // buffer provided to the streamer to be filled with the reply (used by TCP also for the recv)
MemStream * streamer; // streamer who writes the results to the buffer
EthernetClient clients[MAX_SOCK_NUM]; // accept up to MAX_SOCK_NUM client connections at the same time; This depends on the chipset used on the Shield
bool setupConnection();
static void udpHandler();
static void tcpHandler();
void udpHandler2();
void tcpHandler2();
EthernetUDP Udp;
EthernetServer getServer() {
return server;
};
void setServer(EthernetServer s) {
server = s;
};
};
#endif

View File

@ -24,34 +24,43 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MemStream_h #define MemStream_h
#include <inttypes.h> #include <inttypes.h>
#if defined(ARDUINO_ARCH_MEGAAVR)
#include <Arduino.h>
#else
#include <Stream.h> #include <Stream.h>
#endif
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
class MemStream : public Stream class MemStream : public Stream
{ {
private: private:
uint8_t * _buffer; uint8_t *_buffer;
const uint16_t _len; const uint16_t _len;
bool _buffer_overflow; bool _buffer_overflow;
uint16_t _pos_read; uint16_t _pos_read;
uint16_t _pos_write; uint16_t _pos_write;
bool _allowWrite; bool _allowWrite;
public: public:
// public methods // public methods
MemStream(uint8_t *buffer, const uint16_t len, uint16_t content_len = 0, bool allowWrite=true); MemStream(uint8_t *buffer, const uint16_t len, uint16_t content_len = 0, bool allowWrite = true);
~MemStream() {} ~MemStream() {}
operator const uint8_t *() const { return _buffer; } operator const uint8_t *() const { return _buffer; }
operator const char *() const { return (const char*)_buffer; } operator const char *() const { return (const char *)_buffer; }
uint16_t current_length() const { return _pos_write; } uint16_t current_length() const { return _pos_write; }
bool listen() { return true; } bool listen() { return true; }
void end() {} void end() {}
bool isListening() { return true; } bool isListening() { return true; }
bool overflow() { bool ret = _buffer_overflow; _buffer_overflow = false; return ret; } bool overflow()
{
bool ret = _buffer_overflow;
_buffer_overflow = false;
return ret;
}
int peek(); int peek();
virtual size_t write(uint8_t byte); virtual size_t write(uint8_t byte);

View File

@ -1,5 +1,8 @@
#ifndef MotorDrivers_h #ifndef MotorDrivers_h
#define MotorDrivers_h #define MotorDrivers_h
#if defined(ARDUINO_ARCH_MEGAAVR)
#include <Arduino.h>
#endif
// *** PLEASE NOTE *** THIS FILE IS **NOT** INTENDED TO BE EDITED WHEN CONFIGURING A SYSTEM. // *** PLEASE NOTE *** THIS FILE IS **NOT** INTENDED TO BE EDITED WHEN CONFIGURING A SYSTEM.
// It will be overwritten if the library is updated. // It will be overwritten if the library is updated.
@ -8,36 +11,35 @@
// A configuration defined by macro here can be used in your sketch. // A configuration defined by macro here can be used in your sketch.
// A custom hardware setup will require your sketch to create MotorDriver instances // A custom hardware setup will require your sketch to create MotorDriver instances
// similar to those defined here, WITHOUT editing this file. // similar to those defined here, WITHOUT editing this file.
const byte UNUSED_PIN = 255; const byte UNUSED_PIN = 255;
// MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte brake_pin, byte current_pin, // MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte brake_pin, byte current_pin,
// float senseFactor, unsigned int tripMilliamps, byte faultPin); // float senseFactor, unsigned int tripMilliamps, byte faultPin);
// Arduino standard Motor Shield // Arduino standard Motor Shield
#define STANDARD_MOTOR_SHIELD \ #define STANDARD_MOTOR_SHIELD F("STANDARD_MOTOR_SHIELD"), \
new MotorDriver(3 , 12, UNUSED_PIN, UNUSED_PIN, A0, 2.99, 2000, UNUSED_PIN), \ new MotorDriver(3, 12, UNUSED_PIN, UNUSED_PIN, A0, 2.99, 2000, UNUSED_PIN), \
new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, 250 , UNUSED_PIN) new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, TRIP_CURRENT_PROG, UNUSED_PIN)
// Pololu Motor Shield // Pololu Motor Shield
#define POLOLU_MOTOR_SHIELD \ #define POLOLU_MOTOR_SHIELD F("POLOLU_MOTOR_SHIELD"), \
new MotorDriver(4, 7, UNUSED_PIN, 9 , A0, 18, 2000, 12), \ new MotorDriver(4, 7, UNUSED_PIN, 9, A0, 18, 2000, 12), \
new MotorDriver(2, 8, UNUSED_PIN, 10, A1, 18, 250 , UNUSED_PIN) new MotorDriver(2, 8, UNUSED_PIN, 10, A1, 18, TRIP_CURRENT_PROG, UNUSED_PIN)
// Firebox Mk1 // Firebox Mk1
#define FIREBOX_MK1 \ #define FIREBOX_MK1 F("FIREBOX_MK1"), \
new MotorDriver(3, 6, 7, UNUSED_PIN, A5, 9.766, 5500, UNUSED_PIN), \ new MotorDriver(3, 6, 7, UNUSED_PIN, A5, 9.766, 5500, UNUSED_PIN), \
new MotorDriver(4, 8, 9, UNUSED_PIN, A1, 5.00, 250 , UNUSED_PIN) new MotorDriver(4, 8, 9, UNUSED_PIN, A1, 5.00, TRIP_CURRENT_PROG, UNUSED_PIN)
// Firebox Mk1S // Firebox Mk1S
#define FIREBOX_MK1S \ #define FIREBOX_MK1S F("FIREBOX_MK1A"), \
new MotorDriver(24, 21, 22, 25, 23, 9.766, 5500, UNUSED_PIN), \ new MotorDriver(24, 21, 22, 25, 23, 9.766, 5500, UNUSED_PIN), \
new MotorDriver(30, 27, 28, 31, 29, 5.00, 250 , UNUSED_PIN) new MotorDriver(30, 27, 28, 31, 29, 5.00, TRIP_CURRENT_PROG, UNUSED_PIN)
// FunduMoto Motor Shield // FunduMoto Motor Shield
#define FUNDUMOTO_SHIELD \ #define FUNDUMOTO_SHIELD F("FUNDUMOTO_SHIELD"), \
new MotorDriver(10 , 12, UNUSED_PIN, 9, A0, 2.99, 2000, UNUSED_PIN), \ new MotorDriver(10, 12, UNUSED_PIN, 9, A0, 2.99, 2000, UNUSED_PIN), \
new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, 250 , UNUSED_PIN) new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, TRIP_CURRENT_PROG, UNUSED_PIN)
#endif #endif

View File

@ -85,10 +85,15 @@ ISR(TIMER2_OVF_vect)
#include "ATMEGA4809/Timer.h" #include "ATMEGA4809/Timer.h"
Timer TimerA(0); Timer TimerA(1);
Timer TimerB(2);
ISR(TCA0_OVF_vect) { ISR(TIMER1_OVF_vect) {
TimerA.isrCallback(); TimerA.isrCallback();
} }
ISR(TIMER2_OVF_vect) {
TimerB.isrCallback();
}
#endif #endif

View File

@ -1,7 +1,8 @@
/* /*
© 2020, Chris Harlow. All rights reserved. © 2020, Chris Harlow. All rights reserved.
© 2020, Harald Barth.
This file is part of Asbelos DCC API This file is part of CommandStation-EX
This is free software: you can redistribute it and/or modify This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -16,10 +17,14 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with CommandStation. If not, see <https://www.gnu.org/licenses/>. along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "WifiInterface.h"
#include "WifiInterface.h" /* config.h and defines.h included here */
#include <avr/pgmspace.h>
#include "DIAG.h" #include "DIAG.h"
#include "StringFormatter.h" #include "StringFormatter.h"
#include "WiThrottle.h" #include "WiThrottle.h"
const char PROGMEM READY_SEARCH[] = "\r\nready\r\n"; const char PROGMEM READY_SEARCH[] = "\r\nready\r\n";
const char PROGMEM OK_SEARCH[] = "\r\nOK\r\n"; const char PROGMEM OK_SEARCH[] = "\r\nOK\r\n";
const char PROGMEM END_DETAIL_SEARCH[] = "@ 1000"; const char PROGMEM END_DETAIL_SEARCH[] = "@ 1000";
@ -35,30 +40,36 @@ unsigned long WifiInterface::loopTimeoutStart = 0;
int WifiInterface::datalength = 0; int WifiInterface::datalength = 0;
int WifiInterface::connectionId; int WifiInterface::connectionId;
byte WifiInterface::buffer[MAX_WIFI_BUFFER+1]; byte WifiInterface::buffer[MAX_WIFI_BUFFER+1];
MemStream WifiInterface::streamer(buffer, MAX_WIFI_BUFFER); MemStream * WifiInterface::streamer;
Stream * WifiInterface::wifiStream = NULL; Stream * WifiInterface::wifiStream = NULL;
HTTP_CALLBACK WifiInterface::httpCallback = 0; HTTP_CALLBACK WifiInterface::httpCallback = 0;
bool WifiInterface::setup(Stream & setupStream, const __FlashStringHelper* SSid, const __FlashStringHelper* password,
void WifiInterface::setup(Stream & setupStream, const __FlashStringHelper* SSid, const __FlashStringHelper* password,
const __FlashStringHelper* hostname, int port) { const __FlashStringHelper* hostname, int port) {
static uint8_t ntry = 0;
ntry++;
wifiStream = &setupStream; wifiStream = &setupStream;
DIAG(F("\n++++++ Wifi Setup In Progress ++++++++\n")); DIAG(F("\n++ Wifi Setup Try %d ++\n"), ntry);
connected = setup2( SSid, password, hostname, port); connected = setup2( SSid, password, hostname, port);
if (connected) { if (connected) {
StringFormatter::send(wifiStream, F("ATE0\r\n")); // turn off the echo StringFormatter::send(wifiStream, F("ATE0\r\n")); // turn off the echo
checkForOK(200, OK_SEARCH, true); checkForOK(200, OK_SEARCH, true);
} }
streamer=new MemStream(buffer, MAX_WIFI_BUFFER);
DIAG(F("\n++++++ Wifi Setup %S ++++++++\n"), connected ? F("OK") : F("FAILED")); parser.setAtCommandCallback(ATCommand);
DIAG(F("\n++ Wifi Setup %S ++\n"), connected ? F("OK") : F("FAILED"));
return connected;
} }
bool WifiInterface::setup2(const __FlashStringHelper* SSid, const __FlashStringHelper* password, bool WifiInterface::setup2(const __FlashStringHelper* SSid, const __FlashStringHelper* password,
const __FlashStringHelper* hostname, int port) { const __FlashStringHelper* hostname, int port) {
int ipOK = 0; bool ipOK = false;
bool oldCmd = false;
char macAddress[17]; // mac address extraction char macAddress[17]; // mac address extraction
@ -71,66 +82,99 @@ bool WifiInterface::setup2(const __FlashStringHelper* SSid, const __FlashStringH
return true; return true;
} }
StringFormatter::send(wifiStream, F("AT\r\n")); // Is something here that understands AT?
if(!checkForOK(200, OK_SEARCH, true))
return false; // No AT compatible WiFi module here
StringFormatter::send(wifiStream, F("ATE1\r\n")); // Turn on the echo, se we can see what's happening StringFormatter::send(wifiStream, F("ATE1\r\n")); // Turn on the echo, se we can see what's happening
checkForOK(2000, OK_SEARCH, true); // Makes this visible on the console checkForOK(2000, OK_SEARCH, true); // Makes this visible on the console
// Display the AT version information // Display the AT version information
StringFormatter::send(wifiStream, F("AT+GMR\r\n")); StringFormatter::send(wifiStream, F("AT+GMR\r\n"));
checkForOK(2000, OK_SEARCH, true, false); // Makes this visible on the console checkForOK(2000, OK_SEARCH, true, false); // Makes this visible on the console
delay(8000); // give a preconfigured ES8266 a chance to connect to a router StringFormatter::send(wifiStream, F("AT+CWMODE=1\r\n")); // configure as "station" = WiFi client
checkForOK(1000, OK_SEARCH, true); // Not always OK, sometimes "no change"
StringFormatter::send(wifiStream, F("AT+CIFSR\r\n"));
// looking fpr mac addr eg +CIFSR:APMAC,"be:dd:c2:5c:6b:b7" // If the source code looks unconfigured, check if the
if (checkForOK(5000, (const char*) F("+CIFSR:APMAC,\""), true,false)) { // ESP8266 is preconfigured. We check the first 13 chars
// Copy 17 byte mac address // of the password.
for (int i=0; i<17;i++) { if (strncmp_P("Your network ",(const char*)password,13) == 0) {
while(!wifiStream->available()); delay(8000); // give a preconfigured ES8266 a chance to connect to a router
macAddress[i]=wifiStream->read();
StringFormatter::printEscape(macAddress[i]); StringFormatter::send(wifiStream, F("AT+CIFSR\r\n"));
} if (checkForOK(5000, (const char*) F("+CIFSR:STAIP"), true,false))
} if (!checkForOK(1000, (const char*) F("0.0.0.0"), true,false))
char macTail[]={macAddress[9],macAddress[10],macAddress[12],macAddress[13],macAddress[15],macAddress[16],'\0'}; ipOK = true;
} else {
if (!ipOK) {
// Older ES versions have AT+CWJAP, newer ones have AT+CWJAP_CUR and AT+CWHOSTNAME
StringFormatter::send(wifiStream, F("AT+CWJAP?\r\n"));
if (checkForOK(2000, OK_SEARCH, true)) {
oldCmd=true;
while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE
// AT command early version supports CWJAP/CWSAP
if (SSid) {
StringFormatter::send(wifiStream, F("AT+CWJAP=\"%S\",\"%S\"\r\n"), SSid, password);
ipOK = checkForOK(16000, OK_SEARCH, true);
}
DIAG(F("\n**\n"));
} else {
// later version supports CWJAP_CUR
StringFormatter::send(wifiStream, F("AT+CWHOSTNAME=\"%S\"\r\n"), hostname); // Set Host name for Wifi Client
checkForOK(2000, OK_SEARCH, true); // dont care if not supported
if (checkForOK(5000, (const char*) F("+CIFSR:STAIP"), true,false)) if (SSid) {
if (!checkForOK(1000, (const char*) F("0.0.0.0"), true,false)) StringFormatter::send(wifiStream, F("AT+CWJAP_CUR=\"%S\",\"%S\"\r\n"), SSid, password);
ipOK = 1; ipOK = checkForOK(20000, OK_SEARCH, true);
}
}
delay(8000); // give a preconfigured ES8266 a chance to connect to a router
if (ipOK) {
// But we really only have the ESSID and password correct
// Let's check for IP
ipOK = false;
StringFormatter::send(wifiStream, F("AT+CIFSR\r\n"));
if (checkForOK(5000, (const char*) F("+CIFSR:STAIP"), true,false))
if (!checkForOK(1000, (const char*) F("0.0.0.0"), true,false))
ipOK = true;
}
}
}
if (!ipOK) { if (!ipOK) {
StringFormatter::send(wifiStream, F("AT+CWMODE=3\r\n")); // configure as server or access point // If we have not managed to get this going in station mode, go for AP mode
StringFormatter::send(wifiStream, F("AT+CWMODE=2\r\n")); // configure as AccessPoint.
checkForOK(1000, OK_SEARCH, true); // Not always OK, sometimes "no change" checkForOK(1000, OK_SEARCH, true); // Not always OK, sometimes "no change"
// Older ES versions have AT+CWJAP, newer ones have AT+CWJAP_CUR and AT+CWHOSTNAME // Figure out MAC addr
StringFormatter::send(wifiStream, F("AT+CWJAP?\r\n")); StringFormatter::send(wifiStream, F("AT+CIFSR\r\n"));
if (checkForOK(2000, OK_SEARCH, true)) { // looking fpr mac addr eg +CIFSR:APMAC,"be:dd:c2:5c:6b:b7"
while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE if (checkForOK(5000, (const char*) F("+CIFSR:APMAC,\""), true,false)) {
// Copy 17 byte mac address
// AT command early version supports CWJAP/CWSAP for (int i=0; i<17;i++) {
if (SSid) { while(!wifiStream->available());
StringFormatter::send(wifiStream, F("AT+CWJAP=\"%S\",\"%S\"\r\n"), SSid, password); macAddress[i]=wifiStream->read();
checkForOK(16000, OK_SEARCH, true); // can ignore failure as AP mode may still be ok StringFormatter::printEscape(macAddress[i]);
} }
DIAG(F("\n**\n")); }
char macTail[]={macAddress[9],macAddress[10],macAddress[12],macAddress[13],macAddress[15],macAddress[16],'\0'};
// establish the APname
if (oldCmd) {
while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE
StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail); StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail);
checkForOK(16000, OK_SEARCH, true); // can ignore failure as AP mode may still be ok checkForOK(16000, OK_SEARCH, true); // can ignore failure as AP mode may still be ok
} } else {
else {
// later version supports CWJAP_CUR
StringFormatter::send(wifiStream, F("AT+CWHOSTNAME=\"%S\"\r\n"), hostname); // Set Host name for Wifi Client
checkForOK(2000, OK_SEARCH, true); // dont care if not supported
if (SSid) {
StringFormatter::send(wifiStream, F("AT+CWJAP_CUR=\"%S\",\"%S\"\r\n"), SSid, password);
checkForOK(20000, OK_SEARCH, true); // can ignore failure as AP mode may still be ok
}
StringFormatter::send(wifiStream, F("AT+CWSAP_CUR=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail); StringFormatter::send(wifiStream, F("AT+CWSAP_CUR=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail);
checkForOK(20000, OK_SEARCH, true); // can ignore failure as SSid mode may still be ok checkForOK(20000, OK_SEARCH, true); // can ignore failure as SSid mode may still be ok
@ -257,15 +301,15 @@ void WifiInterface::loop() {
case 6: // reading for length case 6: // reading for length
if (ch == ':') loopstate = (datalength == 0) ? 99 : 7; // 99 is getout without reading next char if (ch == ':') loopstate = (datalength == 0) ? 99 : 7; // 99 is getout without reading next char
else datalength = datalength * 10 + (ch - '0'); else datalength = datalength * 10 + (ch - '0');
streamer.flush(); // basically sets write point at start of buffer streamer->flush(); // basically sets write point at start of buffer
break; break;
case 7: // reading data case 7: // reading data
streamer.write(ch); // NOTE: The MemStream will throw away bytes that do not fit in the buffer. streamer->write(ch); // NOTE: The MemStream will throw away bytes that do not fit in the buffer.
// This protects against buffer overflows even with things as innocent // This protects against buffer overflows even with things as innocent
// as a browser which send massive, irrlevent HTTP headers. // as a browser which send massive, irrlevent HTTP headers.
datalength--; datalength--;
if (datalength == 0) { if (datalength == 0) {
buffer[streamer.available()]='\0'; // mark end of buffer, so it can be used as a string later buffer[streamer->available()]='\0'; // mark end of buffer, so it can be used as a string later
loopstate = 99; loopstate = 99;
} }
break; break;
@ -308,8 +352,8 @@ void WifiInterface::loop() {
loopstate = 1; loopstate = 1;
} }
if (ch == 'K') { // assume its in SEND OK if (ch == 'K') { // assume its in SEND OK
if (Diag::WIFI) DIAG(F("\n\n Wifi BUSY RETRYING.. AT+CIPSEND=%d,%d\r\n"), connectionId, streamer.available()); if (Diag::WIFI) DIAG(F("\n\n Wifi BUSY RETRYING.. AT+CIPSEND=%d,%d\r\n"), connectionId, streamer->available());
StringFormatter::send(wifiStream, F("AT+CIPSEND=%d,%d\r\n"), connectionId, streamer.available()); StringFormatter::send(wifiStream, F("AT+CIPSEND=%d,%d\r\n"), connectionId, streamer->available());
loopTimeoutStart = millis(); loopTimeoutStart = millis();
loopstate = 10; // non-blocking loop waits for > before sending loopstate = 10; // non-blocking loop waits for > before sending
break; break;
@ -322,7 +366,7 @@ void WifiInterface::loop() {
// AT this point we have read an incoming message into the buffer // AT this point we have read an incoming message into the buffer
if (Diag::WIFI) DIAG(F("\n%l Wifi(%d)<-[%e]\n"), millis(),connectionId, buffer); if (Diag::WIFI) DIAG(F("\n%l Wifi(%d)<-[%e]\n"), millis(),connectionId, buffer);
streamer.setBufferContentPosition(0, 0); // reset write position to start of buffer streamer->setBufferContentPosition(0, 0); // reset write position to start of buffer
// SIDE EFFECT WARNING::: // SIDE EFFECT WARNING:::
// We know that parser will read the entire buffer before starting to write to it. // We know that parser will read the entire buffer before starting to write to it.
// Otherwise we would have to copy the buffer elsewhere and RAM is in short supply. // Otherwise we would have to copy the buffer elsewhere and RAM is in short supply.
@ -331,18 +375,18 @@ void WifiInterface::loop() {
// Intercept HTTP requests // Intercept HTTP requests
if (isHTTP()) { if (isHTTP()) {
if (httpCallback) httpCallback(&streamer, buffer); if (httpCallback) httpCallback(streamer, buffer);
else { else {
StringFormatter::send(streamer, F("HTTP/1.1 404 Not Found\nContent-Type: text/html\nConnnection: close\n\n")); StringFormatter::send(streamer, F("HTTP/1.1 404 Not Found\nContent-Type: text/html\nConnnection: close\n\n"));
StringFormatter::send(streamer, F("<html><body>This is <b>not</b> a web server.<br/></body></html>")); StringFormatter::send(streamer, F("<html><body>This is <b>not</b> a web server.<br/></body></html>"));
} }
closeAfter = true; closeAfter = true;
} }
else if (buffer[0] == '<') parser.parse(&streamer, buffer, true); // tell JMRI parser that ACKS are blocking because we can't handle the async else if (buffer[0] == '<') parser.parse(streamer, buffer, true); // tell JMRI parser that ACKS are blocking because we can't handle the async
else WiThrottle::getThrottle(connectionId)->parse(streamer, buffer); else WiThrottle::getThrottle(connectionId)->parse(*streamer, buffer);
if (streamer.available() == 0) { if (streamer->available() == 0) {
// No reply // No reply
if (closeAfter) { if (closeAfter) {
if (Diag::WIFI) DIAG(F("AT+CIPCLOSE=%d\r\n"), connectionId); if (Diag::WIFI) DIAG(F("AT+CIPCLOSE=%d\r\n"), connectionId);
@ -352,10 +396,10 @@ void WifiInterface::loop() {
return; return;
} }
// prepare to send reply // prepare to send reply
buffer[streamer.available()]='\0'; // mark end of buffer, so it can be used as a string later buffer[streamer->available()]='\0'; // mark end of buffer, so it can be used as a string later
if (Diag::WIFI) DIAG(F("%l WiFi(%d)->[%e] l(%d)\n"), millis(), connectionId, buffer, streamer.available()); if (Diag::WIFI) DIAG(F("%l WiFi(%d)->[%e] l(%d)\n"), millis(), connectionId, buffer, streamer->available());
if (Diag::WIFI) DIAG(F("AT+CIPSEND=%d,%d\r\n"), connectionId, streamer.available()); if (Diag::WIFI) DIAG(F("AT+CIPSEND=%d,%d\r\n"), connectionId, streamer->available());
StringFormatter::send(wifiStream, F("AT+CIPSEND=%d,%d\r\n"), connectionId, streamer.available()); StringFormatter::send(wifiStream, F("AT+CIPSEND=%d,%d\r\n"), connectionId, streamer->available());
loopTimeoutStart = millis(); loopTimeoutStart = millis();
loopstate = 10; // non-blocking loop waits for > before sending loopstate = 10; // non-blocking loop waits for > before sending
} }

View File

@ -1,7 +1,8 @@
/* /*
* © 2020, Chris Harlow. All rights reserved. * © 2020, Chris Harlow. All rights reserved.
* © 2020, Harald Barth.
* *
* This file is part of Asbelos DCC API * This file is part of CommandStation-EX
* *
* This is free software: you can redistribute it and/or modify * This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -16,7 +17,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>. * along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/ */
#ifndef WifiInterface_h #ifndef WifiInterface_h
#define WifiInterface_h #define WifiInterface_h
#include "DCCEXParser.h" #include "DCCEXParser.h"
@ -24,34 +24,34 @@
#include <Arduino.h> #include <Arduino.h>
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
typedef void (*HTTP_CALLBACK)(Print * stream, byte * cmd); typedef void (*HTTP_CALLBACK)(Print *stream, byte *cmd);
class WifiInterface { class WifiInterface
{
public: public:
static void setup(Stream & setupStream, const __FlashStringHelper* SSSid, const __FlashStringHelper* password, static bool setup(Stream &setupStream, const __FlashStringHelper *SSSid, const __FlashStringHelper *password,
const __FlashStringHelper* hostname, int port); const __FlashStringHelper *hostname, int port);
static void loop(); static void loop();
static void ATCommand(const byte * command); static void ATCommand(const byte *command);
static void setHTTPCallback(HTTP_CALLBACK callback); static void setHTTPCallback(HTTP_CALLBACK callback);
private: private:
static Stream * wifiStream; static Stream *wifiStream;
static DCCEXParser parser; static DCCEXParser parser;
static bool setup2( const __FlashStringHelper* SSSid, const __FlashStringHelper* password, static bool setup2(const __FlashStringHelper *SSSid, const __FlashStringHelper *password,
const __FlashStringHelper* hostname, int port); const __FlashStringHelper *hostname, int port);
static bool checkForOK(const unsigned int timeout, const char* waitfor, bool echo, bool escapeEcho=true); static bool checkForOK(const unsigned int timeout, const char *waitfor, bool echo, bool escapeEcho = true);
static bool isHTTP(); static bool isHTTP();
static HTTP_CALLBACK httpCallback; static HTTP_CALLBACK httpCallback;
static bool connected; static bool connected;
static bool closeAfter; static bool closeAfter;
static byte loopstate; static byte loopstate;
static int datalength; static int datalength;
static int connectionId; static int connectionId;
static unsigned long loopTimeoutStart; static unsigned long loopTimeoutStart;
static const byte MAX_WIFI_BUFFER=250; static const byte MAX_WIFI_BUFFER = 250;
static byte buffer[MAX_WIFI_BUFFER+1]; static byte buffer[MAX_WIFI_BUFFER + 1];
static MemStream streamer; static MemStream * streamer;
}; };
#endif #endif

106
config.example.h Normal file
View File

@ -0,0 +1,106 @@
/**********************************************************************
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 BELOW ACCORDING TO THE FOLLOWING TABLE:
//
// STANDARD_MOTOR_SHIELD : Arduino Motor shield Rev3 based on the L298 with 18V 2A per channel
// POLOLU_MOTOR_SHIELD : Pololu MC33926 Motor Driver (not recommended for prog track)
// FUNDUMOTO_SHIELD : Fundumoto Shield, no current sensing (not recommended, no short protection)
// FIREBOX_MK1 : The Firebox MK1
// FIREBOX_MK1S : The Firebox MK1S
// |
// +-----------------------v
//
#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD
/////////////////////////////////////////////////////////////////////////////////////
//
// The IP port to talk to a WIFI or Ethernet shield.
//
#define IP_PORT 2560
/////////////////////////////////////////////////////////////////////////////////////
//
// NOTE: Only supported on Arduino Mega
// Set to false if you not even want it on the Arduino Mega
//
#define ENABLE_WIFI true
/////////////////////////////////////////////////////////////////////////////////////
//
// DEFINE WiFi Parameters (only in effect if WIFI is on)
//
#define WIFI_SSID "Your network name"
#define WIFI_PASSWORD "Your network passwd"
#define WIFI_HOSTNAME "dccex"
/////////////////////////////////////////////////////////////////////////////////////
//
// DEFINE STATIC IP ADDRESS *OR* COMMENT OUT TO USE DHCP
//
//#define IP_ADDRESS { 192, 168, 1, 200 }
/////////////////////////////////////////////////////////////////////////////////////
//
// 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 }
/////////////////////////////////////////////////////////////////////////////////////
//
// 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

45
defines.h Normal file
View File

@ -0,0 +1,45 @@
/*
© 2020, Harald Barth.
This file is part of CommandStation-EX
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/>.
*/
////////////////////////////////////////////////////////////////////////////////
//
// WIFI_ON: All prereqs for running with WIFI are met
//
#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560))
#define WIFI_ON
#endif
////////////////////////////////////////////////////////////////////////////////
//
// This defines the speed at which the Arduino will communicate with the ESP8266 module.
// Currently only devices which can communicate at 115200 are supported.
//
#define WIFI_SERIAL_LINK_SPEED 115200
////////////////////////////////////////////////////////////////////////////////
//
// Figure out number of serial ports depending on hardware
//
#if (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560))
#define NUM_SERIAL 3
#endif
#ifndef NUM_SERIAL
#define NUM_SERIAL 1
#endif

42
freeMemory.cpp Normal file
View File

@ -0,0 +1,42 @@
/*
* © 2020, Harald Barth
*
* This file is part of Asbelos DCC-EX
*
* 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/>.
*/
#include "freeMemory.h"
// thanks go to https://github.com/mpflaga/Arduino-MemoryFree
#if defined(__arm__)
extern "C" char* sbrk(int);
#elif defined(__AVR__)
extern char *__brkval;
extern char *__malloc_heap_start;
#else
#error Unsupported board type
#endif
int freeMemory() {
char top;
#if defined(__arm__)
return &top - reinterpret_cast<char*>(sbrk(0));
#elif defined(__AVR__)
return __brkval ? &top - __brkval : &top - __malloc_heap_start;
#else
#error bailed out alredy above
#endif
}

View File

@ -1,20 +1,23 @@
/*
* © 2020, Harald Barth
*
* This file is part of DCC-EX
*
* 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/>.
*/
#ifndef freeMemory_h #ifndef freeMemory_h
#define freeMemory_h #define freeMemory_h
int freeMemory();
// thanks go to https://github.com/mpflaga/Arduino-MemoryFree
#ifdef __arm__
// should use uinstd.h to define sbrk but Due causes a conflict
extern "C" char* sbrk(int incr);
#else // __ARM__
extern char *__brkval;
#endif // __arm__
int freeMemory() {
char top;
#ifdef __arm__
return &top - reinterpret_cast<char*>(sbrk(0));
#else // __arm__
return __brkval ? &top - __brkval : &top - __malloc_heap_start;
#endif // __arm__
}
#endif #endif

View File

@ -1,12 +1,14 @@
ECHO ON ECHO ON
FOR /F "delims=" %%i IN ('dir %TMP%\arduino_build_* /b /ad-h /t:c /od') DO SET a=%%i FOR /F "delims=" %%i IN ('dir %TMP%\arduino_build_* /b /ad-h /t:c /od') DO SET a=%%i
echo Most recent subfolder: %a% >%TMP%\OBJDUMP_%a%.txt echo Most recent subfolder: %a% >%TMP%\OBJDUMP_%a%.txt
avr-objdump --private=mem-usage %TMP%\%a%\DCCEX.ino.elf >>%TMP%\OBJDUMP_%a%.txt SET ELF=%TMP%\%a%\CommandStation-EX.ino.elf
avr-objdump --private=mem-usage %ELF% >>%TMP%\OBJDUMP_%a%.txt
ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt
avr-objdump -x -C %TMP%\%a%\DCCEX.ino.elf | find ".text" | sort /+25 /R >>%TMP%\OBJDUMP_%a%.txt avr-objdump -x -C %ELF% | find ".text" | sort /+25 /R >>%TMP%\OBJDUMP_%a%.txt
ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt
avr-objdump -x -C %TMP%\%a%\DCCEX.ino.elf | find ".data" | sort /+25 /R >>%TMP%\OBJDUMP_%a%.txt avr-objdump -x -C %ELF% | find ".data" | sort /+25 /R >>%TMP%\OBJDUMP_%a%.txt
ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt
avr-objdump -x -C %TMP%\%a%\DCC.ino.elf | find ".bss" | sort /+25 /R >>%TMP%\OBJDUMP_%a%.txt avr-objdump -x -C %ELF% | find ".bss" | sort /+25 /R >>%TMP%\OBJDUMP_%a%.txt
notepad %TMP%\OBJDUMP_%a%.txt notepad %TMP%\OBJDUMP_%a%.txt
EXIT EXIT

View File

@ -4,9 +4,10 @@
ARDUINOBIN=$(ls -l $(type -p arduino)| awk '{print $NF ; exit 0}') ARDUINOBIN=$(ls -l $(type -p arduino)| awk '{print $NF ; exit 0}')
PATH=$(dirname "$ARDUINOBIN")/hardware/tools/avr/bin:$PATH PATH=$(dirname "$ARDUINOBIN")/hardware/tools/avr/bin:$PATH
avr-objdump --private=mem-usage /tmp/arduino_build_233823/Blinkhabaplus.ino.elf LASTBUILD=$(ls -tr /tmp/arduino_build_*/*.ino.elf | tail -1)
avr-objdump --private=mem-usage "$LASTBUILD"
for segment in .text .data .bss ; do for segment in .text .data .bss ; do
echo '++++++++++++++++++++++++++++++++++' echo '++++++++++++++++++++++++++++++++++'
avr-objdump -x -C /tmp/arduino_build_233823/Blinkhabaplus.ino.elf | awk '$2 == "'$segment'" && $3 != 0 {print $3,$2} ; $4 == "'$segment'" && $5 != 0 { print $5,$6}' | sort -r avr-objdump -x -C "$LASTBUILD" | awk '$2 == "'$segment'" && $3 != 0 {print $3,$2} ; $4 == "'$segment'" && $5 != 0 { print $5,$6}' | sort -r
done done

View File

@ -34,6 +34,7 @@ lib_deps =
DIO2 DIO2
arduino-libraries/Ethernet arduino-libraries/Ethernet
SPI SPI
mathertel/LiquidCrystal_PCF8574
[env:mega328] [env:mega328]
platform = atmelavr platform = atmelavr
@ -58,3 +59,4 @@ framework = arduino
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
DIO2 DIO2
mathertel/LiquidCrystal_PCF8574

10
version.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef version_h
#define version_h
#include "StringFormatter.h"
// const char VERSION[] PROGMEM ="0.2.0";
#define VERSION "0.2.0"
#endif