1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-26 17:46:14 +01:00

Merge branch 'TM_nofloat' into TM-mergetest

This commit is contained in:
Harald Barth 2022-06-11 22:14:56 +02:00
commit 7fba96417f
11 changed files with 198 additions and 70 deletions

View File

@ -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; clientId<sizeof(clients); clientId++) {
if (clients[clientId]==NONE_TYPE) continue;
if ( clients[clientId]==WITHROTTLE_TYPE && !includeWithrottleClients) continue;
ring->mark(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("<H %d %d>\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("<H %d %d>\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("<l %d %d %d %l>\n"),
FLUSH
StringFormatter::send(BUFFER,F("<l %d %d %d %l>\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("<p%c%S>\nPPA%c\n"),state,reason, main?'1':'0');
FLUSH
StringFormatter::send(BUFFER,F("<p %c%S>\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);
broadcast(true);
}
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
}

View File

@ -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<<clientid
enum clientType: byte {NONE_TYPE,COMMAND_TYPE,WITHROTTLE_TYPE};
static clientType clients[8];
enum clientType: byte {NONE_TYPE,COMMAND_TYPE,WITHROTTLE_TYPE};
static void broadcastToClients(clientType type);
static StringBuffer * broadcastBufferWriter;
#ifdef CD_HANDLE_RING
static RingStream * ring;
static const byte NO_CLIENT=255;
static byte ringClient;
static clientType clients[8];
#endif
};
#endif

View File

@ -1 +1 @@
#define GITHUB_SHA "TM-PORTX-20220611"
#define GITHUB_SHA "TM-PORTX-20220611-1"

View File

@ -88,16 +88,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();
@ -213,10 +220,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) {

View File

@ -113,9 +113,9 @@ class MotorDriver {
virtual int getCurrentRaw();
virtual int getCurrentRawInInterrupt();
virtual unsigned int raw2mA( int raw);
virtual int mA2raw( unsigned int mA);
inline bool brakeCanPWM() {
return ((brakePin!=UNUSED_PIN) && (digitalPinToTimer(brakePin)));
virtual unsigned int mA2raw( unsigned int mA);
}
inline int getRawCurrentTripValue() {
return rawCurrentTripValue;
@ -142,7 +142,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;

View File

@ -21,6 +21,7 @@
#include "SerialManager.h"
#include "DCCEXParser.h"
SerialManager * SerialManager::first=NULL;
SerialManager::SerialManager(Stream * myserial) {
@ -49,11 +50,11 @@ void SerialManager::init() {
#endif
}
void SerialManager::broadcast(RingStream * ring) {
for (SerialManager * s=first;s;s=s->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() {

View File

@ -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;

45
StringBuffer.cpp Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#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;
}

38
StringBuffer.h Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef StringBuffer_h
#define StringBuffer_h
#include <Arduino.h>
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

View File

@ -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;

View File

@ -4,8 +4,10 @@
#include "StringFormatter.h"
#define VERSION "4.2.0 rc1"
#define VERSION "4.2.0 rc2"
// 4.2.0 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
// TrackManager DCC & DC up to 8 Districts Architecture