From f8a19de9fb0160ae5ee1dc908b3c6d1fb2a89d20 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 13 May 2022 01:22:00 +0200 Subject: [PATCH 1/3] tag it --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 6af3d57..1941499 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "a26d988" +#define GITHUB_SHA "TM-20220513" From 2b2012ef1d7308f8d71ba4b152ada49b6666e831 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 6 Jun 2022 17:37:23 +0100 Subject: [PATCH 2/3] float memory saver --- MotorDriver.cpp | 19 +++++++++++++------ MotorDriver.h | 11 +++++++++-- StringFormatter.cpp | 2 +- version.h | 1 + 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 92d8c88..b72cc67 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -67,16 +67,23 @@ MotorDriver::MotorDriver(VPIN power_pin, byte signal_pin, byte signal_pin2, int8 pinMode(faultPin, INPUT); } - senseFactor=sense_factor; + // This conversion performed at compile time so the remainder of the code never needs + // float calculations or libraray code. + senseFactorInternal=sense_factor * senseScale; tripMilliamps=trip_milliamps; - rawCurrentTripValue=(int)(trip_milliamps / sense_factor); + rawCurrentTripValue=mA2raw(trip_milliamps); if (currentPin==UNUSED_PIN) DIAG(F("MotorDriver ** WARNING ** No current or short detection")); - else + else { DIAG(F("MotorDriver currentPin=A%d, senseOffset=%d, rawCurrentTripValue(relative to offset)=%d"), currentPin-A0, senseOffset,rawCurrentTripValue); + // self testing diagnostic for the non-float converters... may be removed when happy + // DIAG(F("senseFactorInternal=%d raw2mA(1000)=%d mA2Raw(1000)=%d"), + // senseFactorInternal, raw2mA(1000),mA2raw(1000)); + } + // prepare values for current detection sampleDelay = 0; lastSampleTaken = millis(); @@ -164,10 +171,10 @@ int MotorDriver::getCurrentRawInInterrupt() { } unsigned int MotorDriver::raw2mA( int raw) { - return (unsigned int)(raw * senseFactor); + return (int32_t)raw * senseFactorInternal / senseScale; } -int MotorDriver::mA2raw( unsigned int mA) { - return (int)(mA / senseFactor); +unsigned int MotorDriver::mA2raw( unsigned int mA) { + return (int32_t)mA * senseScale / senseFactorInternal; } void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & result) { diff --git a/MotorDriver.h b/MotorDriver.h index 283039c..1e4081e 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -79,7 +79,7 @@ class MotorDriver { virtual int getCurrentRaw(); virtual int getCurrentRawInInterrupt(); virtual unsigned int raw2mA( int raw); - virtual int mA2raw( unsigned int mA); + virtual unsigned int mA2raw( unsigned int mA); inline bool canBrake() { return brakePin!=UNUSED_PIN; } @@ -108,7 +108,14 @@ class MotorDriver { FASTPIN fastSignalPin, fastSignalPin2, fastBrakePin,fastFaultPin; bool dualSignal; // true to use signalPin2 bool invertBrake; // brake pin passed as negative means pin is inverted - float senseFactor; + + // Raw to milliamp conversion factors avoiding float data types. + // Milliamps=rawADCreading * sensefactorInternal / senseScale + // + // senseScale is chosen as 256 to give enough scale for 2 decimal place + // raw->mA conversion with an ultra fast optimised integer multiplication + int senseFactorInternal; // set to senseFactor * senseScale + static const int senseScale=256; int senseOffset; unsigned int tripMilliamps; int rawCurrentTripValue; diff --git a/StringFormatter.cpp b/StringFormatter.cpp index 73a910f..485d8fd 100644 --- a/StringFormatter.cpp +++ b/StringFormatter.cpp @@ -104,7 +104,7 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) { 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; + //case 'f': stream->print(va_arg(args, double), 2); break; //format width prefix case '-': formatLeft=true; diff --git a/version.h b/version.h index e3ff37a..5ab7e44 100644 --- a/version.h +++ b/version.h @@ -6,6 +6,7 @@ #define VERSION "4.0.3" // 4.0.3 Track Manager additions: +// Float eliminated saving >1.5kb PROGMEM and speed. // SET_TRACK(track,mode) Functions (A-H, MAIN|PROG|DC|DCX|OFF) // New DC track function and DCX reverse polarity function // TrackManager DCC & DC up to 8 Districts Architecture From 16fafccf152f40e19c04eb37d588807a46d8e658 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 10 Jun 2022 12:22:28 +0100 Subject: [PATCH 3/3] Command Distributor Improve and split multi-language responses. Remove dependency on RingStream. --- CommandDistributor.cpp | 108 ++++++++++++++++++++++++----------------- CommandDistributor.h | 24 ++++++--- SerialManager.cpp | 9 ++-- SerialManager.h | 6 +-- StringBuffer.cpp | 45 +++++++++++++++++ StringBuffer.h | 38 +++++++++++++++ version.h | 1 + 7 files changed, 172 insertions(+), 59 deletions(-) create mode 100644 StringBuffer.cpp create mode 100644 StringBuffer.h diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 85df03b..9c3dba4 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -30,16 +30,29 @@ #include "DCC.h" #include "TrackManager.h" -#if defined(BIG_MEMORY) | defined(WIFI_ON) | defined(ETHERNET_ON) -// This section of CommandDistributor is simply not relevant on a uno or similar -const byte NO_CLIENT=255; -RingStream * CommandDistributor::ring=0; -byte CommandDistributor::ringClient=NO_CLIENT; -CommandDistributor::clientType CommandDistributor::clients[8]={ - NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE}; -RingStream * CommandDistributor::broadcastBufferWriter=new RingStream(100); +#ifdef BIG_RAM + // use a buffer to allow broadcast + #define BUFFER broadcastBufferWriter + #define FLUSH broadcastBufferWriter->flush(); + #define SHOVE(type) broadcastToClients(type); + StringBuffer * CommandDistributor::broadcastBufferWriter=new StringBuffer(); +#else + // on a UNO/NANO write direct to Serial and ignore flush/shove + #define BUFFER &Serial + #define FLUSH + #define SHOVE(type) +#endif +#ifdef CD_HANDLE_RING + // wifi or ethernet ring streams with multiple client types + RingStream * CommandDistributor::ring=0; + byte CommandDistributor::ringClient=NO_CLIENT; + CommandDistributor::clientType CommandDistributor::clients[8]={ + NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE}; + +// Parse is called by Withrottle or Ethernet interface to determine which +// protocol the client is using and call the appropriate part of dcc++Ex void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream) { ring=stream; ringClient=stream->peekTargetMark(); @@ -56,15 +69,15 @@ void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream void CommandDistributor::forget(byte clientId) { clients[clientId]=NONE_TYPE; } +#endif - -void CommandDistributor::broadcast(bool includeWithrottleClients) { - broadcastBufferWriter->write((byte)'\0'); +// This will not be called on a uno +void CommandDistributor::broadcastToClients(clientType type) { /* Boadcast to Serials */ - SerialManager::broadcast(broadcastBufferWriter); + if (type==COMMAND_TYPE) SerialManager::broadcast(broadcastBufferWriter->getString()); -#if defined(WIFI_ON) | defined(ETHERNET_ON) +#ifdef CD_HANDLE_RING // If we are broadcasting from a wifi/eth process we need to complete its output // before merging broadcasts in the ring, then reinstate it in case // the process continues to output to its client. @@ -72,49 +85,46 @@ void CommandDistributor::broadcast(bool includeWithrottleClients) { /* loop through ring clients */ for (byte clientId=0; clientIdmark(clientId); - broadcastBufferWriter->printBuffer(ring); - ring->commit(); + if (clients[clientId]==type) { + ring->mark(clientId); + ring->print(broadcastBufferWriter->getString()); + ring->commit(); + } } if (ringClient!=NO_CLIENT) ring->mark(ringClient); #endif - broadcastBufferWriter->flush(); } -#else -// For a UNO/NANO we can broadcast direct to just one Serial instead of the ring -// Redirect ring output ditrect to Serial -#define broadcastBufferWriter &Serial -// and ignore the internal broadcast call. -void CommandDistributor::broadcast(bool includeWithrottleClients) { - (void)includeWithrottleClients; -} -#endif +// Public broadcast functions below void CommandDistributor::broadcastSensor(int16_t id, bool on ) { - StringFormatter::send(broadcastBufferWriter,F("<%c %d>\n"), on?'Q':'q', id); - broadcast(false); + FLUSH + StringFormatter::send(BUFFER,F("<%c %d>\n"), on?'Q':'q', id); + SHOVE(COMMAND_TYPE) } void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) { // For DCC++ classic compatibility, state reported to JMRI is 1 for thrown and 0 for closed; // The string below contains serial and Withrottle protocols which should // be safe for both types. - StringFormatter::send(broadcastBufferWriter,F("\n"),id, !isClosed); -#if defined(WIFI_ON) | defined(ETHERNET_ON) - StringFormatter::send(broadcastBufferWriter,F("PTA%c%d\n"), isClosed?'2':'4', id); + FLUSH + StringFormatter::send(BUFFER,F("\n"),id, !isClosed); + SHOVE(COMMAND_TYPE) + +#ifdef CD_HANDLE_RING + FLUSH + StringFormatter::send(BUFFER,F("PTA%c%d\n"), isClosed?'2':'4', id); + SHOVE(WITHROTTLE_TYPE) #endif - broadcast(true); } void CommandDistributor::broadcastLoco(byte slot) { DCC::LOCO * sp=&DCC::speedTable[slot]; - StringFormatter::send(broadcastBufferWriter,F("\n"), + FLUSH + StringFormatter::send(BUFFER,F("\n"), sp->loco,slot,sp->speedCode,sp->functions); - broadcast(false); -#if defined(WIFI_ON) | defined(ETHERNET_ON) + SHOVE(COMMAND_TYPE) +#ifdef CD_HANDLE_RING WiThrottle::markForBroadcast(sp->loco); #endif } @@ -130,14 +140,24 @@ void CommandDistributor::broadcastPower() { else if (main) reason=F(" MAIN"); else if (prog) reason=F(" PROG"); else state='0'; - - StringFormatter::send(broadcastBufferWriter, - F("\nPPA%c\n"),state,reason, main?'1':'0'); - LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason); - broadcast(true); + FLUSH + StringFormatter::send(BUFFER,F("

\n"),state,reason); + SHOVE(COMMAND_TYPE) +#ifdef CD_HANDLE_RING + FLUSH + StringFormatter::send(BUFFER,F("PPA%c\n"), main?'1':'0'); + SHOVE(WITHROTTLE_TYPE) +#endif + LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason); } void CommandDistributor::broadcastText(const FSH * msg) { - StringFormatter::send(broadcastBufferWriter,F("%S"),msg); - broadcast(false); + FLUSH + StringFormatter::send(BUFFER,F("%S"),msg); + SHOVE(COMMAND_TYPE) +#ifdef CD_HANDLE_RING + FLUSH + StringFormatter::send(BUFFER,F("Hm%S\n"), msg); + SHOVE(WITHROTTLE_TYPE) +#endif } diff --git a/CommandDistributor.h b/CommandDistributor.h index a9af3bb..7da3c8f 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -23,6 +23,13 @@ #define CommandDistributor_h #include "DCCEXParser.h" #include "RingStream.h" +#include "StringBuffer.h" +#include "defines.h" + +#if WIFI_ON | ETHERNET_ON + // Command Distributor must handle a RingStream of clients + #define CD_HANDLE_RING +#endif class CommandDistributor { @@ -35,14 +42,15 @@ public : static void broadcastText(const FSH * msg); static void forget(byte clientId); private: - static void broadcast(bool includeWithrottleClients); - static RingStream * ring; - static RingStream * broadcastBufferWriter; - static byte ringClient; - - // each bit in broadcastlist = 1<next) s->broadcast2(ring); +void SerialManager::broadcast(char * stringBuffer) { + for (SerialManager * s=first;s;s=s->next) s->broadcast2(stringBuffer); } -void SerialManager::broadcast2(RingStream * ring) { - ring->printBuffer(serial); +void SerialManager::broadcast2(char * stringBuffer) { + serial->print(stringBuffer); } void SerialManager::loop() { diff --git a/SerialManager.h b/SerialManager.h index fb58cba..ad1eaf3 100644 --- a/SerialManager.h +++ b/SerialManager.h @@ -23,7 +23,7 @@ #include "Arduino.h" #include "defines.h" -#include "RingStream.h" + #ifndef COMMAND_BUFFER_SIZE #define COMMAND_BUFFER_SIZE 100 @@ -33,13 +33,13 @@ class SerialManager { public: static void init(); static void loop(); - static void broadcast(RingStream * ring); + static void broadcast(char * stringBuffer); private: static SerialManager * first; SerialManager(Stream * myserial); void loop2(); - void broadcast2(RingStream * ring); + void broadcast2(char * stringBuffer); Stream * serial; SerialManager * next; byte bufferLength; diff --git a/StringBuffer.cpp b/StringBuffer.cpp new file mode 100644 index 0000000..dd4e477 --- /dev/null +++ b/StringBuffer.cpp @@ -0,0 +1,45 @@ +/* + * © 2022 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 . + */ + +#include "StringBuffer.h" +#include "DIAG.h" + +StringBuffer::StringBuffer() { + flush(); +}; + +char * StringBuffer::getString() { + return _buffer; +} + +void StringBuffer::flush() { + _pos_write=0; + _buffer[0]='\0'; +} + +size_t StringBuffer::write(uint8_t b) { + if (_pos_write>=buffer_max) return 0; + _buffer[_pos_write] = b; + ++_pos_write; + _buffer[_pos_write]='\0'; + return 1; +} + + diff --git a/StringBuffer.h b/StringBuffer.h new file mode 100644 index 0000000..fe227fb --- /dev/null +++ b/StringBuffer.h @@ -0,0 +1,38 @@ + /* + * © 2022 Chris Harlow + * All rights reserved. + * + * 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 . + */ + +#ifndef StringBuffer_h +#define StringBuffer_h +#include + +class StringBuffer : public Print { + public: + StringBuffer(); + // Override Print default + virtual size_t write(uint8_t b); + void flush(); + char * getString(); + private: + static const int buffer_max=64; // enough for long text msgs to throttles + int16_t _pos_write; + char _buffer[buffer_max+1]; +}; + +#endif \ No newline at end of file diff --git a/version.h b/version.h index 5ab7e44..98b4317 100644 --- a/version.h +++ b/version.h @@ -6,6 +6,7 @@ #define VERSION "4.0.3" // 4.0.3 Track Manager additions: +// Broadcast improvements to separate <> and Withrottle responses // Float eliminated saving >1.5kb PROGMEM and speed. // SET_TRACK(track,mode) Functions (A-H, MAIN|PROG|DC|DCX|OFF) // New DC track function and DCX reverse polarity function