diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp
index 5b43017..47551b2 100644
--- a/CommandDistributor.cpp
+++ b/CommandDistributor.cpp
@@ -1,3 +1,21 @@
+/*
+ * © 2020,Gregor Baues, Chris Harlow. All rights reserved.
+ *
+ * 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 .
+ */
#include
#include "CommandDistributor.h"
#include "WiThrottle.h"
diff --git a/CommandDistributor.h b/CommandDistributor.h
index e27b60e..93e4d7c 100644
--- a/CommandDistributor.h
+++ b/CommandDistributor.h
@@ -1,3 +1,21 @@
+/*
+ * © 2020,Gregor Baues, Chris Harlow. All rights reserved.
+ *
+ * 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 .
+ */
#ifndef CommandDistributor_h
#define CommandDistributor_h
#include "DCCEXParser.h"
diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino
index 462bd79..9749fea 100644
--- a/CommandStation-EX.ino
+++ b/CommandStation-EX.ino
@@ -12,18 +12,6 @@
#include "config.h"
#include "DCCEX.h"
-////////////////////////////////////////////////////////////////
-//
-// 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
-
// 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.
@@ -31,45 +19,22 @@ DCCEXParser serialParser;
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()
// Responsibility 1: Start the usb connection for diagnostics
// This is normally Serial but uses SerialUSB on a SAMD processor
Serial.begin(115200);
+ DIAG(F("DCC++ EX v%S"),F(VERSION));
+
+ CONDITIONAL_LCD_START {
+ // This block is ignored if LCD not in use
+ LCD(0,F("DCC++ EX v%S"),F(VERSION));
+ LCD(1,F("Starting"));
+ }
// Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi
-#ifdef WIFI_ON
+#if WIFI_ON
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT);
#endif // WIFI_ON
@@ -83,7 +48,8 @@ void setup()
// 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);
+ DCC::begin(MOTOR_SHIELD_TYPE);
+ LCD(1,F("Ready"));
}
void loop()
@@ -101,7 +67,9 @@ void loop()
#if WIFI_ON
WifiInterface::loop();
#endif
-
+
+ LCDDisplay::loop(); // ignored if LCD not in use
+
// 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
@@ -110,7 +78,7 @@ void loop()
if (freeNow < ramLowWatermark)
{
ramLowWatermark = freeNow;
- DIAG(F("\nFree RAM=%d\n"), ramLowWatermark);
+ LCD(2,F("Free RAM=%5db"), ramLowWatermark);
}
#endif
}
diff --git a/DCC.h b/DCC.h
index 580d8b7..4bc0197 100644
--- a/DCC.h
+++ b/DCC.h
@@ -162,16 +162,5 @@ private:
#error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH AN ARDUINO UNO, NANO 328, OR ARDUINO MEGA 1280/2560
#endif
-#if ENABLE_LCD
-#include
-#if defined(LIB_TYPE_PCF8574)
-#include
-extern LiquidCrystal_PCF8574 lcdDisplay;
-#elif defined(LIB_TYPE_I2C)
-#include
-extern LiquidCrystal_I2C lcdDisplay;
-#endif
-extern bool lcdEnabled;
-#endif
#endif
diff --git a/DCCEX.h b/DCCEX.h
index 091bd47..fb4baef 100644
--- a/DCCEX.h
+++ b/DCCEX.h
@@ -11,7 +11,8 @@
#include "version.h"
#include "WifiInterface.h"
#include "EthernetInterface.h"
-
+#include "LCD_Implementation.h"
+#include "freeMemory.h"
#include
#endif
diff --git a/DIAG.h b/DIAG.h
index e0d4383..6d16f78 100644
--- a/DIAG.h
+++ b/DIAG.h
@@ -20,4 +20,5 @@
#define DIAG_h
#include "StringFormatter.h"
#define DIAG StringFormatter::diag
+#define LCD StringFormatter::lcd
#endif
diff --git a/LCDDisplay.cpp b/LCDDisplay.cpp
new file mode 100644
index 0000000..e031f10
--- /dev/null
+++ b/LCDDisplay.cpp
@@ -0,0 +1,79 @@
+/*
+ * © 2020, Chris Harlow. All rights reserved.
+ *
+ * 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 .
+ */
+
+// CAUTION: the device dependent parts of this class are created in the .ini using LCD_Implementation.h
+#include "LCDDisplay.h"
+
+ void LCDDisplay::clear() {
+ clearNative();
+ for (byte row=0;row=MAX_LCD_ROWS || hotCol>=MAX_LCD_COLS) return -1;
+ rowBuffer[hotRow][hotCol]=b;
+ hotCol++;
+ rowBuffer[hotRow][hotCol]=0;
+ return 1;
+ }
+
+ void LCDDisplay::loop() {
+ if (!lcdDisplay) return;
+ lcdDisplay->loop2(false);
+ }
+
+ LCDDisplay* LCDDisplay::loop2(bool force) {
+ if ((!force) && (millis() - lastScrollTime)< LCD_SCROLL_TIME) return NULL;
+ lastScrollTime=millis();
+ clearNative();
+ int rowFirst=nextFilledRow();
+ if (rowFirst<0)return NULL; // No filled rows
+ setRowNative(0);
+ writeNative(rowBuffer[rowFirst]);
+ for (int slot=1;slot.
+ */
+#ifndef LCDDisplay_h
+#define LCDDisplay_h
+#include
+
+// This class is created in LCDisplay_Implementation.h
+
+class LCDDisplay : public Print {
+
+ public:
+ static const int MAX_LCD_ROWS=8;
+ static const int MAX_LCD_COLS=16;
+ static const long LCD_SCROLL_TIME=3000; // 3 seconds
+
+ static LCDDisplay* lcdDisplay;
+ LCDDisplay();
+ void interfake(int p1, int p2, int p3);
+
+ // Internally handled functions
+ static void loop();
+ LCDDisplay* loop2(bool force);
+ void setRow(byte line);
+ void clear();
+
+ virtual size_t write(uint8_t b);
+ using Print::write;
+
+ private:
+ int nextFilledRow();
+
+ // Relay functions to the live driver
+ void clearNative();
+ void displayNative();
+ void setRowNative(byte line);
+ void writeNative(char * b);
+
+ unsigned long lastScrollTime=0;
+ int hotRow=0;
+ int hotCol=0;
+ int topRow=0;
+ int lcdRows;
+ void renderRow(byte row);
+ char rowBuffer[MAX_LCD_ROWS][MAX_LCD_COLS+1];
+};
+
+#endif
diff --git a/LCD_Implementation.h b/LCD_Implementation.h
new file mode 100644
index 0000000..1da344e
--- /dev/null
+++ b/LCD_Implementation.h
@@ -0,0 +1,55 @@
+/*
+ * © 2020, Chris Harlow. All rights reserved.
+ *
+ * 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 .
+ */
+
+////////////////////////////////////////////////////////////////////////////////////
+// This implementation is designed to be #included ONLY ONCE in the .ino
+//
+// It will create a driver implemntation and a shim class implementation.
+// This means that other classes can reference the shim without knowing
+// which libraray is involved.
+////////////////////////////////////////////////////////////////////////////////////
+
+#include "config.h"
+#include
+#include "LCDDisplay.h"
+
+LCDDisplay * LCDDisplay::lcdDisplay=0;
+
+// Implement the LCDDisplay shim class as a singleton.
+// Notice that the LCDDisplay class declaration (LCDDisplay.h) is independent of the library
+// but the implementation is compiled here with dependencies on LCDDriver which is
+// specific to the library in use.
+// Thats the workaround to the drivers not all implementing a common interface.
+
+#if defined(OLED_DRIVER)
+ #include "LCD_OLED.h"
+ #define CONDITIONAL_LCD_START for (LCDDisplay * dummy=new LCDDisplay();dummy!=NULL; dummy=dummy->loop2(true))
+
+
+#elif defined(LCD_DRIVER)
+ #include "LCD_LCD.h"
+ #define CONDITIONAL_LCD_START for (LCDDisplay * dummy=new LCDDisplay();dummy!=NULL; dummy=dummy->loop2(true))
+
+#else
+ #include "LCD_NONE.h"
+ #define CONDITIONAL_LCD_START if (false) /* NO LCD CONFIG */
+#endif
+
+
+
diff --git a/LCD_LCD.h b/LCD_LCD.h
new file mode 100644
index 0000000..6f88ddc
--- /dev/null
+++ b/LCD_LCD.h
@@ -0,0 +1,37 @@
+/*
+ * © 2020, Chris Harlow. All rights reserved.
+ *
+ * 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 .
+ */
+ #include
+ LiquidCrystal_I2C LCDDriver(LCD_DRIVER); // set the LCD address, cols, rows
+ // DEVICE SPECIFIC LCDDisplay Implementation for LCD_DRIVER
+ LCDDisplay::LCDDisplay() {
+ lcdDisplay=this;
+ LCDDriver.init();
+ LCDDriver.backlight();
+ interfake(LCD_DRIVER);
+ clear();
+ }
+ void LCDDisplay::interfake(int p1, int p2, int p3) {(void)p1; (void)p2; lcdRows=p3; }
+ void LCDDisplay::clearNative() {LCDDriver.clear();}
+ void LCDDisplay::setRowNative(byte row) {
+ LCDDriver.setCursor(0, row);
+ LCDDriver.print(F(" "));
+ LCDDriver.setCursor(0, row);
+ }
+ void LCDDisplay::writeNative(char * b){ LCDDriver.print(b); }
+ void LCDDisplay::displayNative() { LCDDriver.display(); }
diff --git a/LCD_NONE.h b/LCD_NONE.h
new file mode 100644
index 0000000..65e05e4
--- /dev/null
+++ b/LCD_NONE.h
@@ -0,0 +1,27 @@
+/*
+ * © 2020, Chris Harlow. All rights reserved.
+ *
+ * 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 .
+ */
+
+// dummy LCD shim to keep linker happy
+ LCDDisplay::LCDDisplay() {}
+ void LCDDisplay::interfake(int p1, int p2, int p3) {(void)p1; (void)p2; (void)p3;}
+ void LCDDisplay::setRowNative(byte row) { (void)row;}
+ void LCDDisplay::clearNative() {}
+ void LCDDisplay::writeNative(char * b){ (void)b;} //
+ void LCDDisplay::displayNative(){}
+
diff --git a/LCD_OLED.h b/LCD_OLED.h
new file mode 100644
index 0000000..efa2bc7
--- /dev/null
+++ b/LCD_OLED.h
@@ -0,0 +1,57 @@
+/*
+ * © 2020, Chris Harlow. All rights reserved.
+ *
+ * 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 .
+ */
+
+// OLED Implementation of LCDDisplay class
+// Note: this file is optionally included by LCD_Implenentation.h
+// It is NOT a .cpp file to prevent it being compiled and demanding libraraies even when not needed.
+
+#include
+Adafruit_SSD1306 LCDDriver(OLED_DRIVER);
+
+// DEVICE SPECIFIC LCDDisplay Implementation for OLED
+
+LCDDisplay::LCDDisplay() {
+ if(LCDDriver.begin(SSD1306_SWITCHCAPVCC, 0x3C) || LCDDriver.begin(SSD1306_SWITCHCAPVCC, 0x3D)) {
+ DIAG(F("\nOLED display found"));
+ delay(2000); // painful Adafruit splash pants!
+ lcdDisplay=this;
+ LCDDriver.setTextSize(1); // Normal 1:1 pixel scale
+ LCDDriver.setTextColor(SSD1306_WHITE); // Draw white text
+ interfake(OLED_DRIVER,0);
+ clear();
+ return;
+ }
+ DIAG(F("\nOLED display not found\n"));
+ }
+
+ void LCDDisplay::interfake(int p1, int p2, int p3) {(void)p1; lcdRows=p2/8; (void)p3;}
+
+ void LCDDisplay::clearNative() {LCDDriver.clearDisplay();}
+
+ void LCDDisplay::setRowNative(byte row) {
+ // Positions text write to start of row 1..n and clears previous text
+ int y=8*row;
+ LCDDriver.fillRect(0, y, LCDDriver.width(), 8, SSD1306_BLACK);
+ LCDDriver.setCursor(0, y);
+ }
+
+ void LCDDisplay::writeNative(char * b){ LCDDriver.print(b); }
+
+ void LCDDisplay::displayNative() { LCDDriver.display(); }
+
diff --git a/StringFormatter.cpp b/StringFormatter.cpp
index e99cfc3..261794a 100644
--- a/StringFormatter.cpp
+++ b/StringFormatter.cpp
@@ -31,6 +31,8 @@
#define __FlashStringHelper char
#endif
+#include "LCDDisplay.h"
+
bool Diag::ACK=false;
bool Diag::CMD=false;
bool Diag::WIFI=false;
@@ -44,6 +46,14 @@ void StringFormatter::diag( const __FlashStringHelper* input...) {
send2(diagSerial,input,args);
}
+void StringFormatter::lcd(byte row, const __FlashStringHelper* input...) {
+ if (!LCDDisplay::lcdDisplay) return;
+ LCDDisplay::lcdDisplay->setRow(row);
+ va_list args;
+ va_start(args, input);
+ send2(LCDDisplay::lcdDisplay,input,args);
+}
+
void StringFormatter::send(Print * stream, const __FlashStringHelper* input...) {
va_list args;
va_start(args, input);
@@ -56,7 +66,6 @@ void StringFormatter::send(Print & stream, const __FlashStringHelper* input...)
send2(&stream,input,args);
}
-
void StringFormatter::send2(Print * stream,const __FlashStringHelper* format, va_list args) {
// thanks to Jan Turoň https://arduino.stackexchange.com/questions/56517/formatting-strings-in-arduino-for-output
@@ -66,6 +75,13 @@ void StringFormatter::send2(Print * stream,const __FlashStringHelper* format, va
char c=pgm_read_byte_near(flash+i);
if (c=='\0') return;
if(c!='%') { stream->print(c); continue; }
+
+ bool formatContinues=false;
+ byte formatWidth=0;
+ bool formatLeft=false;
+ do {
+
+ formatContinues=false;
i++;
c=pgm_read_byte_near(flash+i);
switch(c) {
@@ -73,14 +89,34 @@ void StringFormatter::send2(Print * stream,const __FlashStringHelper* format, va
case 'c': stream->print((char) va_arg(args, int)); break;
case 's': stream->print(va_arg(args, char*)); break;
case 'e': printEscapes(stream,va_arg(args, char*)); break;
+ case 'E': printEscapes(stream,(const __FlashStringHelper*)va_arg(args, char*)); break;
case 'S': stream->print((const __FlashStringHelper*)va_arg(args, char*)); break;
- case 'd': stream->print(va_arg(args, int), DEC); break;
- case 'l': stream->print(va_arg(args, long), DEC); break;
+ case 'd': printPadded(stream,va_arg(args, int), formatWidth, formatLeft); break;
+ case 'l': printPadded(stream,va_arg(args, long), formatWidth, formatLeft); break;
case 'b': stream->print(va_arg(args, int), BIN); break;
case 'o': stream->print(va_arg(args, int), OCT); break;
case 'x': stream->print(va_arg(args, int), HEX); break;
case 'f': stream->print(va_arg(args, double), 2); break;
+ //format width prefix
+ case '-':
+ formatLeft=true;
+ formatContinues=true;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ formatWidth=formatWidth * 10 + (c-'0');
+ formatContinues=true;
+ break;
}
+ } while(formatContinues);
}
va_end(args);
}
@@ -94,6 +130,17 @@ void StringFormatter::printEscapes(Print * stream,char * input) {
}
}
+void StringFormatter::printEscapes(Print * stream, const __FlashStringHelper * input) {
+
+ if (!stream) return;
+ char* flash=(char*)input;
+ for(int i=0; ; ++i) {
+ char c=pgm_read_byte_near(flash+i);
+ printEscape(stream,c);
+ if (c=='\0') return;
+ }
+}
+
void StringFormatter::printEscape( char c) {
printEscape(diagSerial,c);
}
@@ -109,4 +156,27 @@ void StringFormatter::printEscape(Print * stream, char c) {
default: stream->print(c);
}
}
+
+
+void StringFormatter::printPadded(Print* stream, long value, byte width, bool formatLeft) {
+ if (width==0) {
+ stream->print(value, DEC);
+ return;
+ }
+
+ int digits=(value <= 0)? 1: 0; // zero and negative need extra digot
+ long v=value;
+ while (v) {
+ v /= 10;
+ digits++;
+ }
+
+ if (formatLeft) stream->print(value, DEC);
+ while(digitsprint(' ');
+ digits++;
+ }
+ if (!formatLeft) stream->print(value, DEC);
+ }
+
diff --git a/StringFormatter.h b/StringFormatter.h
index 62d2492..5cbccf1 100644
--- a/StringFormatter.h
+++ b/StringFormatter.h
@@ -27,6 +27,7 @@
#define __FlashStringHelper char
#endif
+#include "LCDDisplay.h"
class Diag {
public:
static bool ACK;
@@ -42,16 +43,19 @@ class StringFormatter
static void send(Print & serial, const __FlashStringHelper* input...);
static void printEscapes(Print * serial,char * input);
+ static void printEscapes(Print * serial,const __FlashStringHelper* input);
static void printEscape(Print * serial, char c);
// DIAG support
static Print * diagSerial;
static void diag( const __FlashStringHelper* input...);
+ static void lcd(byte row, const __FlashStringHelper* input...);
static void printEscapes(char * input);
static void printEscape( char c);
private:
static void send2(Print * serial, const __FlashStringHelper* input,va_list args);
+ static void printPadded(Print* stream, long value, byte width, bool formatLeft);
};
#endif
diff --git a/WiThrottle.cpp b/WiThrottle.cpp
index 044b00b..5c536b5 100644
--- a/WiThrottle.cpp
+++ b/WiThrottle.cpp
@@ -103,7 +103,7 @@ void WiThrottle::parse(Print & stream, byte * cmdx) {
// we have to take a copy of the cmd buffer as the reply will get built into the cmdx
byte local[150];
- for (byte i=0;i