mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 08:06:13 +01:00
Merge remote-tracking branch 'origin/nanoEvery2' into dex/unowifi
This commit is contained in:
commit
5e8fa739fc
33
.github/workflows/sha.yml
vendored
Normal file
33
.github/workflows/sha.yml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
name: SHA
|
||||
|
||||
# Run this workflow ever time code is pushed to a branch
|
||||
# other than `main` in your repository
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
# Set the job key. The key is displayed as the job name
|
||||
# when a job name is not provided
|
||||
sha:
|
||||
# Name the Job
|
||||
name: Commit SHA
|
||||
# Set the type of machine to run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Checks out a copy of your repository on the ubuntu-latest machine
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Create SHA File
|
||||
run: |
|
||||
sha=$(git rev-parse --short "$GITHUB_SHA")
|
||||
echo "#define GITHUB_SHA \"$sha\"" > GITHUB_SHA.h
|
||||
|
||||
- uses: EndBug/add-and-commit@v4 # You can change this to use a specific version
|
||||
with:
|
||||
add: 'GITHUB_SHA.h'
|
||||
message: 'Committing a SHA'
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Leave this line unchanged
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* AnalogReadFast.h
|
||||
*
|
||||
* Copyright (C) 2016 Albert van Dalen http://www.avdweb.nl
|
||||
*
|
||||
* This file is part of CommandStation.
|
||||
*
|
||||
* CommandStation 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.
|
||||
*
|
||||
* CommandStation 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 COMMANDSTATION_DCC_ANALOGREADFAST_H_
|
||||
#define COMMANDSTATION_DCC_ANALOGREADFAST_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
int inline analogReadFast(uint8_t ADCpin);
|
||||
#if defined(ARDUINO_ARCH_MEGAAVR)
|
||||
|
||||
int inline analogReadFast(uint8_t ADCpin)
|
||||
{ byte ADC0CTRLCoriginal = ADC0.CTRLC;
|
||||
ADC0.CTRLC = (ADC0CTRLCoriginal & 0b00110000) + 0b01000011;
|
||||
int adc = analogRead(ADCpin);
|
||||
ADC0.CTRLC = ADC0CTRLCoriginal;
|
||||
return adc;
|
||||
}
|
||||
#else
|
||||
int inline analogReadFast(uint8_t ADCpin)
|
||||
{ byte ADCSRAoriginal = ADCSRA;
|
||||
ADCSRA = (ADCSRA & B11111000) | 4;
|
||||
int adc = analogRead(ADCpin);
|
||||
ADCSRA = ADCSRAoriginal;
|
||||
return adc;
|
||||
}
|
||||
#endif
|
||||
#endif // COMMANDSTATION_DCC_ANALOGREADFAST_H_
|
|
@ -1,15 +1,49 @@
|
|||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// © 2020, Chris Harlow. All rights reserved.
|
||||
// DCC-EX CommandStation-EX Please see https://DCC-EX.com
|
||||
//
|
||||
// This file is a demonstattion of setting up a DCC-EX
|
||||
// Command station with optional support for direct connection of WiThrottle devices
|
||||
// such as "Engine Driver". If you contriol your layout through JMRI
|
||||
// then DON'T connect throttles to this wifi, connect them to JMRI.
|
||||
// This file is the main sketch for the Command Station.
|
||||
//
|
||||
// THE WIFI FEATURE IS NOT SUPPORTED ON ARDUINO DEVICES WITH ONLY 2KB RAM.
|
||||
// CONFIGURATION:
|
||||
// Configuration is normally performed by editing a file called config.h.
|
||||
// This file is NOT shipped with the code so that if you pull a later version
|
||||
// of the code, your configuration will not be overwritten.
|
||||
//
|
||||
// If you used the automatic installer program, config.h will have been created automatically.
|
||||
//
|
||||
// To obtain a starting copy of config.h please copy the file config.example.h which is
|
||||
// shipped with the code and may be updated as new features are added.
|
||||
//
|
||||
// If config.h is not found, config.example.h will be used with all defaults.
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "config.h"
|
||||
#if __has_include ( "config.h")
|
||||
#include "config.h"
|
||||
#else
|
||||
#warning config.h not found. Using defaults from config.example.h
|
||||
#include "config.example.h"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* © 2020,2021 Chris Harlow, Harald Barth, David Cutting,
|
||||
* Fred Decker, Gregor Baues, Anthony W - Dayton All rights reserved.
|
||||
*
|
||||
*
|
||||
* 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 "DCCEX.h"
|
||||
|
||||
// Create a serial command parser for the USB connection,
|
||||
|
@ -35,7 +69,7 @@ void setup()
|
|||
// Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi
|
||||
|
||||
#if WIFI_ON
|
||||
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT);
|
||||
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL);
|
||||
#endif // WIFI_ON
|
||||
|
||||
#if ETHERNET_ON
|
||||
|
|
11
DCC.cpp
11
DCC.cpp
|
@ -481,9 +481,14 @@ void DCC::getLocoId(ACK_CALLBACK callback, bool blocking) {
|
|||
}
|
||||
|
||||
void DCC::setLocoId(int id,ACK_CALLBACK callback, bool blocking) {
|
||||
if (id<=0 || id>9999) callback(-1);
|
||||
if (id<=127) ackManagerSetup(id,SHORT_LOCO_ID_PROG, callback, blocking);
|
||||
else ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback, blocking);
|
||||
if (id<1 || id>10239) { //0x27FF according to standard
|
||||
callback(-1);
|
||||
return;
|
||||
}
|
||||
if (id<=127)
|
||||
ackManagerSetup(id, SHORT_LOCO_ID_PROG, callback, blocking);
|
||||
else
|
||||
ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback, blocking);
|
||||
}
|
||||
|
||||
void DCC::forgetLoco(int cab) { // removes any speed reminders for this loco
|
||||
|
|
|
@ -52,7 +52,7 @@ const int HASH_KEYWORD_ETHERNET = -30767;
|
|||
const int HASH_KEYWORD_MAX = 16244;
|
||||
const int HASH_KEYWORD_MIN = 15978;
|
||||
|
||||
int DCCEXParser::stashP[MAX_PARAMS];
|
||||
int DCCEXParser::stashP[MAX_COMMAND_PARAMS];
|
||||
bool DCCEXParser::stashBusy;
|
||||
|
||||
Print *DCCEXParser::stashStream = NULL;
|
||||
|
@ -102,7 +102,7 @@ void DCCEXParser::loop(Stream &stream)
|
|||
Sensor::checkAll(&stream); // Update and print changes
|
||||
}
|
||||
|
||||
int DCCEXParser::splitValues(int result[MAX_PARAMS], const byte *cmd)
|
||||
int DCCEXParser::splitValues(int result[MAX_COMMAND_PARAMS], const byte *cmd)
|
||||
{
|
||||
byte state = 1;
|
||||
byte parameterCount = 0;
|
||||
|
@ -111,10 +111,10 @@ int DCCEXParser::splitValues(int result[MAX_PARAMS], const byte *cmd)
|
|||
bool signNegative = false;
|
||||
|
||||
// clear all parameters in case not enough found
|
||||
for (int i = 0; i < MAX_PARAMS; i++)
|
||||
for (int i = 0; i < MAX_COMMAND_PARAMS; i++)
|
||||
result[i] = 0;
|
||||
|
||||
while (parameterCount < MAX_PARAMS)
|
||||
while (parameterCount < MAX_COMMAND_PARAMS)
|
||||
{
|
||||
byte hot = *remainingCmd;
|
||||
|
||||
|
@ -161,7 +161,7 @@ int DCCEXParser::splitValues(int result[MAX_PARAMS], const byte *cmd)
|
|||
return parameterCount;
|
||||
}
|
||||
|
||||
int DCCEXParser::splitHexValues(int result[MAX_PARAMS], const byte *cmd)
|
||||
int DCCEXParser::splitHexValues(int result[MAX_COMMAND_PARAMS], const byte *cmd)
|
||||
{
|
||||
byte state = 1;
|
||||
byte parameterCount = 0;
|
||||
|
@ -169,10 +169,10 @@ int DCCEXParser::splitHexValues(int result[MAX_PARAMS], const byte *cmd)
|
|||
const byte *remainingCmd = cmd + 1; // skips the opcode
|
||||
|
||||
// clear all parameters in case not enough found
|
||||
for (int i = 0; i < MAX_PARAMS; i++)
|
||||
for (int i = 0; i < MAX_COMMAND_PARAMS; i++)
|
||||
result[i] = 0;
|
||||
|
||||
while (parameterCount < MAX_PARAMS)
|
||||
while (parameterCount < MAX_COMMAND_PARAMS)
|
||||
{
|
||||
byte hot = *remainingCmd;
|
||||
|
||||
|
@ -251,7 +251,7 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
|
|||
(void)EEPROM; // tell compiler not to warn this is unused
|
||||
if (Diag::CMD)
|
||||
DIAG(F("\nPARSING:%s\n"), com);
|
||||
int p[MAX_PARAMS];
|
||||
int p[MAX_COMMAND_PARAMS];
|
||||
while (com[0] == '<' || com[0] == ' ')
|
||||
com++; // strip off any number of < or spaces
|
||||
byte params = splitValues(p, com);
|
||||
|
@ -770,13 +770,13 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[])
|
|||
}
|
||||
|
||||
// CALLBACKS must be static
|
||||
bool DCCEXParser::stashCallback(Print *stream, int p[MAX_PARAMS])
|
||||
bool DCCEXParser::stashCallback(Print *stream, int p[MAX_COMMAND_PARAMS])
|
||||
{
|
||||
if (stashBusy )
|
||||
return false;
|
||||
stashBusy = true;
|
||||
stashStream = stream;
|
||||
memcpy(stashP, p, MAX_PARAMS * sizeof(p[0]));
|
||||
memcpy(stashP, p, MAX_COMMAND_PARAMS * sizeof(p[0]));
|
||||
return true;
|
||||
}
|
||||
void DCCEXParser::callback_W(int result)
|
||||
|
|
|
@ -34,7 +34,7 @@ struct DCCEXParser
|
|||
static void setFilter(FILTER_CALLBACK filter);
|
||||
static void setRMFTFilter(FILTER_CALLBACK filter);
|
||||
static void setAtCommandCallback(AT_COMMAND_CALLBACK filter);
|
||||
static const int MAX_PARAMS=10; // Must not exceed this
|
||||
static const int MAX_COMMAND_PARAMS=10; // Must not exceed this
|
||||
|
||||
private:
|
||||
|
||||
|
@ -42,8 +42,8 @@ struct DCCEXParser
|
|||
byte bufferLength=0;
|
||||
bool inCommandPayload=false;
|
||||
byte buffer[MAX_BUFFER+2];
|
||||
int splitValues( int result[MAX_PARAMS], const byte * command);
|
||||
int splitHexValues( int result[MAX_PARAMS], const byte * command);
|
||||
int splitValues( int result[MAX_COMMAND_PARAMS], const byte * command);
|
||||
int splitHexValues( int result[MAX_COMMAND_PARAMS], const byte * command);
|
||||
|
||||
bool parseT(Print * stream, int params, int p[]);
|
||||
bool parseZ(Print * stream, int params, int p[]);
|
||||
|
@ -55,8 +55,8 @@ struct DCCEXParser
|
|||
static bool stashBusy;
|
||||
|
||||
static Print * stashStream;
|
||||
static int stashP[MAX_PARAMS];
|
||||
bool stashCallback(Print * stream, int p[MAX_PARAMS]);
|
||||
static int stashP[MAX_COMMAND_PARAMS];
|
||||
bool stashCallback(Print * stream, int p[MAX_COMMAND_PARAMS]);
|
||||
static void callback_W(int result);
|
||||
static void callback_B(int result);
|
||||
static void callback_R(int result);
|
||||
|
|
73
DCCTimer.cpp
73
DCCTimer.cpp
|
@ -25,6 +25,21 @@
|
|||
* DCCEX works on a single timer interrupt at a regular 58uS interval.
|
||||
* The DCCWaveform class generates the signals to the motor shield
|
||||
* based on this timer.
|
||||
*
|
||||
* If the motor drivers are BOTH configured to use the correct 2 pins for the architecture,
|
||||
* (see isPWMPin() function. )
|
||||
* then this allows us to use a hardware driven pin switching arrangement which is
|
||||
* achieved by setting the duty cycle of the NEXT clock interrupt to 0% or 100% depending on
|
||||
* the required pin state. (see setPWM())
|
||||
* This is more accurate than the software interrupt but at the expense of
|
||||
* limiting the choice of available pins.
|
||||
* Fortunately, a standard motor shield on a Mega uses pins that qualify for PWM...
|
||||
* Other shields may be jumpered to PWM pins or run directly using the software interrupt.
|
||||
*
|
||||
* Because the PWM-based waveform is effectively set half a cycle after the software version,
|
||||
* it is not acceptable to drive the two tracks on different methiods or it would cause
|
||||
* problems for <1 JOIN> etc.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "DCCTimer.h"
|
||||
|
@ -35,9 +50,11 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
|||
|
||||
#ifdef ARDUINO_ARCH_MEGAAVR
|
||||
// Arduino unoWifi Rev2 and nanoEvery architectire
|
||||
|
||||
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||
interruptHandler=callback;
|
||||
noInterrupts();
|
||||
ADC0.CTRLC = (ADC0.CTRLC & 0b00110000) | 0b01000011; // speed up analogRead sample time
|
||||
TCB0.CTRLB = TCB_CNTMODE_INT_gc & ~TCB_CCMPEN_bm; // timer compare mode with output disabled
|
||||
TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc; // 8 MHz ~ 0.125 us
|
||||
TCB0.CCMP = CLOCK_CYCLES -1; // 1 tick less for timer reset
|
||||
|
@ -54,11 +71,33 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
|||
interruptHandler();
|
||||
}
|
||||
|
||||
bool DCCTimer::isPWMPin(byte pin) {
|
||||
return false; // TODO what are the relevant pins?
|
||||
}
|
||||
|
||||
void DCCTimer::setPWM(byte pin, bool high) {
|
||||
// TODO what are the relevant pins?
|
||||
}
|
||||
|
||||
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
|
||||
memcpy(mac,(void *) &SIGROW.SERNUM0,6); // serial number
|
||||
}
|
||||
|
||||
#else
|
||||
// Arduino nano, uno, mega etc
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
#define TIMER1_A_PIN 11
|
||||
#define TIMER1_B_PIN 12
|
||||
#define TIMER1_C_PIN 13
|
||||
#else
|
||||
#define TIMER1_A_PIN 9
|
||||
#define TIMER1_B_PIN 10
|
||||
#endif
|
||||
|
||||
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||
interruptHandler=callback;
|
||||
noInterrupts();
|
||||
ADCSRA = (ADCSRA & 0b11111000) | 0b00000100; // speed up analogRead sample time
|
||||
TCCR1A = 0;
|
||||
ICR1 = CLOCK_CYCLES;
|
||||
TCNT1 = 0;
|
||||
|
@ -69,4 +108,38 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
|||
|
||||
// ISR called by timer interrupt every 58uS
|
||||
ISR(TIMER1_OVF_vect){ interruptHandler(); }
|
||||
|
||||
// Alternative pin manipulation via PWM control.
|
||||
bool DCCTimer::isPWMPin(byte pin) {
|
||||
return pin==TIMER1_A_PIN
|
||||
|| pin==TIMER1_B_PIN
|
||||
#ifdef TIMER1_C_PIN
|
||||
|| pin==TIMER1_C_PIN
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
void DCCTimer::setPWM(byte pin, bool high) {
|
||||
if (pin==TIMER1_A_PIN) {
|
||||
TCCR1A |= _BV(COM1A1);
|
||||
OCR1A= high?1024:0;
|
||||
}
|
||||
else if (pin==TIMER1_B_PIN) {
|
||||
TCCR1A |= _BV(COM1B1);
|
||||
OCR1B= high?1024:0;
|
||||
}
|
||||
#ifdef TIMER1_C_PIN
|
||||
else if (pin==TIMER1_C_PIN) {
|
||||
TCCR1A |= _BV(COM1C1);
|
||||
OCR1C= high?1024:0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#include <avr/boot.h>
|
||||
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
|
||||
for (byte i=0; i<6; i++) mac[i]=boot_signature_byte_get(0x0E + i);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,9 @@ typedef void (*INTERRUPT_CALLBACK)();
|
|||
class DCCTimer {
|
||||
public:
|
||||
static void begin(INTERRUPT_CALLBACK interrupt);
|
||||
static void getSimulatedMacAddress(byte mac[6]);
|
||||
static bool isPWMPin(byte pin);
|
||||
static void setPWM(byte pin, bool high);
|
||||
private:
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma GCC optimize ("-O3")
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "DCCWaveform.h"
|
||||
|
@ -38,6 +39,9 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) {
|
|||
progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static
|
||||
mainTrack.setPowerMode(POWERMODE::OFF);
|
||||
progTrack.setPowerMode(POWERMODE::OFF);
|
||||
MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable();
|
||||
if (MotorDriver::usePWM) DIAG(F("\nWaveform using PWM pins for accuracy."));
|
||||
else DIAG(F("\nWaveform accuracy limited by signal pin configuration."));
|
||||
DCCTimer::begin(DCCWaveform::interruptHandler);
|
||||
}
|
||||
|
||||
|
@ -119,6 +123,11 @@ void DCCWaveform::checkPowerOverload() {
|
|||
case POWERMODE::ON:
|
||||
// Check current
|
||||
lastCurrent=motorDriver->getCurrentRaw();
|
||||
if (lastCurrent < 0) {
|
||||
// We have a fault pin condition to take care of
|
||||
DIAG(F("\n*** %S FAULT PIN ACTIVE TOGGLE POWER ON THIS OR BOTH TRACKS ***\n"), isMainTrack ? F("MAIN") : F("PROG"));
|
||||
lastCurrent = -lastCurrent;
|
||||
}
|
||||
if (lastCurrent <= tripValue) {
|
||||
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
||||
if(power_good_counter<100)
|
||||
|
@ -129,9 +138,9 @@ void DCCWaveform::checkPowerOverload() {
|
|||
setPowerMode(POWERMODE::OVERLOAD);
|
||||
unsigned int mA=motorDriver->raw2mA(lastCurrent);
|
||||
unsigned int maxmA=motorDriver->raw2mA(tripValue);
|
||||
DIAG(F("\n*** %S TRACK POWER OVERLOAD current=%d max=%d offtime=%l ***\n"), isMainTrack ? F("MAIN") : F("PROG"), mA, maxmA, power_sample_overload_wait);
|
||||
power_good_counter=0;
|
||||
sampleDelay = power_sample_overload_wait;
|
||||
DIAG(F("\n*** %S TRACK POWER OVERLOAD current=%d max=%d offtime=%d ***\n"), isMainTrack ? F("MAIN") : F("PROG"), mA, maxmA, sampleDelay);
|
||||
if (power_sample_overload_wait >= 10000)
|
||||
power_sample_overload_wait = 10000;
|
||||
else
|
||||
|
@ -142,6 +151,8 @@ void DCCWaveform::checkPowerOverload() {
|
|||
// Try setting it back on after the OVERLOAD_WAIT
|
||||
setPowerMode(POWERMODE::ON);
|
||||
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
||||
// Debug code....
|
||||
DIAG(F("\n*** %S TRACK POWER RESET delay=%d ***\n"), isMainTrack ? F("MAIN") : F("PROG"), sampleDelay);
|
||||
break;
|
||||
default:
|
||||
sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning.
|
||||
|
@ -198,7 +209,10 @@ void DCCWaveform::interrupt2() {
|
|||
}
|
||||
else if (packetPending) {
|
||||
// Copy pending packet to transmit packet
|
||||
for (int b = 0; b < pendingLength; b++) transmitPacket[b] = pendingPacket[b];
|
||||
// a fixed length memcpy is faster than a variable length loop for these small lengths
|
||||
// for (int b = 0; b < pendingLength; b++) transmitPacket[b] = pendingPacket[b];
|
||||
memcpy( transmitPacket, pendingPacket, sizeof(pendingPacket));
|
||||
|
||||
transmitLength = pendingLength;
|
||||
transmitRepeats = pendingRepeats;
|
||||
packetPending = false;
|
||||
|
@ -223,7 +237,7 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea
|
|||
while (packetPending);
|
||||
|
||||
byte checksum = 0;
|
||||
for (int b = 0; b < byteCount; b++) {
|
||||
for (byte b = 0; b < byteCount; b++) {
|
||||
checksum ^= buffer[b];
|
||||
pendingPacket[b] = buffer[b];
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ const int POWER_SAMPLE_OVERLOAD_WAIT = 20;
|
|||
// Number of preamble bits.
|
||||
const int PREAMBLE_BITS_MAIN = 16;
|
||||
const int PREAMBLE_BITS_PROG = 22;
|
||||
const byte MAX_PACKET_SIZE = 12;
|
||||
const byte MAX_PACKET_SIZE = 5; // NMRA standard exrtended packets
|
||||
|
||||
// The WAVE_STATE enum is deliberately numbered because a change of order would be catastrophic
|
||||
// to the transform array.
|
||||
|
|
|
@ -17,13 +17,18 @@
|
|||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "defines.h" // This should be changed to DCCEX.h when possible
|
||||
#if __has_include ( "config.h")
|
||||
#include "config.h"
|
||||
#else
|
||||
#warning config.h not found. Using defaults from config.example.h
|
||||
#include "config.example.h"
|
||||
#endif
|
||||
#include "defines.h"
|
||||
#if ETHERNET_ON == true
|
||||
#include "EthernetInterface.h"
|
||||
#include "DIAG.h"
|
||||
#include "CommandDistributor.h"
|
||||
#include "DCCTimer.h"
|
||||
|
||||
EthernetInterface * EthernetInterface::singleton=NULL;
|
||||
/**
|
||||
|
@ -45,10 +50,15 @@ void EthernetInterface::setup()
|
|||
*/
|
||||
EthernetInterface::EthernetInterface()
|
||||
{
|
||||
byte mac[]=MAC_ADDRESS;
|
||||
byte mac[6];
|
||||
DCCTimer::getSimulatedMacAddress(mac);
|
||||
DIAG(F("\n+++++ Ethernet Setup. Simulatd mac="));
|
||||
for (byte i=0;i<sizeof(mac); i++) {
|
||||
DIAG(F("%x:"),mac[i]);
|
||||
}
|
||||
DIAG(F("\n"));
|
||||
|
||||
DIAG(F("\n+++++ Ethernet Setup "));
|
||||
connected=false;
|
||||
connected=false;
|
||||
|
||||
#ifdef IP_ADDRESS
|
||||
Ethernet.begin(mac, IP_ADDRESS);
|
||||
|
@ -73,11 +83,11 @@ EthernetInterface::EthernetInterface()
|
|||
|
||||
IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address
|
||||
|
||||
server = new EthernetServer(LISTEN_PORT); // Ethernet Server listening on default port LISTEN_PORT
|
||||
server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT
|
||||
server->begin();
|
||||
|
||||
LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
|
||||
LCD(5,F("Port:%d"), LISTEN_PORT);
|
||||
LCD(5,F("Port:%d"), IP_PORT);
|
||||
|
||||
outboundRing=new RingStream(OUTBOUND_RING_SIZE);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,12 @@
|
|||
|
||||
#ifndef EthernetInterface_h
|
||||
#define EthernetInterface_h
|
||||
|
||||
#if __has_include ( "config.h")
|
||||
#include "config.h"
|
||||
#else
|
||||
#warning config.h not found. Using defaults from config.example.h
|
||||
#include "config.example.h"
|
||||
#endif
|
||||
#include "DCCEXParser.h"
|
||||
#include <Arduino.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
@ -33,11 +38,7 @@
|
|||
* @brief Network Configuration
|
||||
*
|
||||
*/
|
||||
#ifndef MAC_ADDRESS
|
||||
#error define MAC_ADDRESS in config.h
|
||||
#endif
|
||||
|
||||
#define LISTEN_PORT 2560 // default listen port for the server
|
||||
#define MAX_ETH_BUFFER 512
|
||||
#define OUTBOUND_RING_SIZE 2048
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
#define GITHUB_SHA "9db6d36"
|
||||
#define GITHUB_SHA "0228345"
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
|
||||
#ifndef LCD_Implementation_h
|
||||
#define LCD_Implementation_h
|
||||
#include "config.h"
|
||||
#include <Wire.h>
|
||||
#include "LCDDisplay.h"
|
||||
|
||||
|
|
116
MotorDriver.cpp
116
MotorDriver.cpp
|
@ -18,51 +18,72 @@
|
|||
*/
|
||||
#include <Arduino.h>
|
||||
#include "MotorDriver.h"
|
||||
#include "AnalogReadFast.h"
|
||||
#include "DCCTimer.h"
|
||||
#include "DIAG.h"
|
||||
|
||||
#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH
|
||||
#define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW
|
||||
#define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH)
|
||||
#define isLOW(fastpin) (!isHIGH(fastpin))
|
||||
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) || defined(ARDUINO_ARCH_MEGAAVR)
|
||||
#define WritePin digitalWrite
|
||||
#define ReadPin digitalRead
|
||||
#else
|
||||
// use the DIO2 libraray for much faster pin access
|
||||
#define GPIO2_PREFER_SPEED 1
|
||||
#include <DIO2.h> // use IDE menu Tools..Manage Libraries to locate and install DIO2
|
||||
#define WritePin digitalWrite2
|
||||
#define ReadPin digitalRead2
|
||||
#endif
|
||||
bool MotorDriver::usePWM=false;
|
||||
|
||||
MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin,
|
||||
byte current_pin, float sense_factor, unsigned int trip_milliamps, byte fault_pin) {
|
||||
powerPin=power_pin;
|
||||
getFastPin(F("POWER"),powerPin,fastPowerPin);
|
||||
pinMode(powerPin, OUTPUT);
|
||||
|
||||
signalPin=signal_pin;
|
||||
getFastPin(F("SIG"),signalPin,fastSignalPin);
|
||||
pinMode(signalPin, OUTPUT);
|
||||
|
||||
signalPin2=signal_pin2;
|
||||
if (signalPin2!=UNUSED_PIN) {
|
||||
dualSignal=true;
|
||||
getFastPin(F("SIG2"),signalPin2,fastSignalPin2);
|
||||
pinMode(signalPin2, OUTPUT);
|
||||
}
|
||||
else dualSignal=false;
|
||||
|
||||
brakePin=brake_pin;
|
||||
if (brake_pin!=UNUSED_PIN){
|
||||
invertBrake=brake_pin < 0;
|
||||
brakePin=invertBrake ? 0-brake_pin : brake_pin;
|
||||
getFastPin(F("BRAKE"),brakePin,fastBrakePin);
|
||||
pinMode(brakePin, OUTPUT);
|
||||
setBrake(false);
|
||||
}
|
||||
else brakePin=UNUSED_PIN;
|
||||
|
||||
currentPin=current_pin;
|
||||
senseFactor=sense_factor;
|
||||
pinMode(currentPin, INPUT);
|
||||
|
||||
faultPin=fault_pin;
|
||||
if (faultPin != UNUSED_PIN) {
|
||||
getFastPin(F("FAULT"),faultPin, 1 /*input*/, fastFaultPin);
|
||||
pinMode(faultPin, INPUT);
|
||||
}
|
||||
|
||||
senseFactor=sense_factor;
|
||||
tripMilliamps=trip_milliamps;
|
||||
rawCurrentTripValue=(int)(trip_milliamps / sense_factor);
|
||||
simulatedOverload=(int)(32000/senseFactor);
|
||||
pinMode(powerPin, OUTPUT);
|
||||
pinMode(brakePin < 0 ? -brakePin : brakePin, OUTPUT);
|
||||
setBrake(false);
|
||||
pinMode(signalPin, OUTPUT);
|
||||
if (signalPin2 != UNUSED_PIN) pinMode(signalPin2, OUTPUT);
|
||||
pinMode(currentPin, INPUT);
|
||||
if (faultPin != UNUSED_PIN) pinMode(faultPin, INPUT);
|
||||
}
|
||||
|
||||
bool MotorDriver::isPWMCapable() {
|
||||
return (!dualSignal) && DCCTimer::isPWMPin(signalPin);
|
||||
}
|
||||
|
||||
|
||||
void MotorDriver::setPower(bool on) {
|
||||
if (brakePin == -4 && on) {
|
||||
if (on) {
|
||||
// toggle brake before turning power on - resets overcurrent error
|
||||
// on the Pololu board if brake is wired to ^D2.
|
||||
setBrake(true);
|
||||
setBrake(false);
|
||||
setHIGH(fastPowerPin);
|
||||
}
|
||||
WritePin(powerPin, on ? HIGH : LOW);
|
||||
else setLOW(fastPowerPin);
|
||||
}
|
||||
|
||||
// setBrake applies brake if on == true. So to get
|
||||
|
@ -74,30 +95,35 @@ void MotorDriver::setPower(bool on) {
|
|||
// compensate for that.
|
||||
//
|
||||
void MotorDriver::setBrake(bool on) {
|
||||
bool state = on;
|
||||
byte pin = brakePin;
|
||||
if (brakePin < 0) {
|
||||
pin=-pin;
|
||||
state=!state;
|
||||
}
|
||||
WritePin(pin, state ? HIGH : LOW);
|
||||
//DIAG(F("BrakePin: %d is %d\n"), pin, ReadPin(pin));
|
||||
if (brakePin == UNUSED_PIN) return;
|
||||
if (on ^ invertBrake) setHIGH(fastBrakePin);
|
||||
else setLOW(fastBrakePin);
|
||||
}
|
||||
|
||||
void MotorDriver::setSignal( bool high) {
|
||||
WritePin(signalPin, high ? HIGH : LOW);
|
||||
if (signalPin2 != UNUSED_PIN) WritePin(signalPin2, high ? LOW : HIGH);
|
||||
if (usePWM) {
|
||||
DCCTimer::setPWM(signalPin,high);
|
||||
}
|
||||
else {
|
||||
if (high) {
|
||||
setHIGH(fastSignalPin);
|
||||
if (dualSignal) setLOW(fastSignalPin2);
|
||||
}
|
||||
else {
|
||||
setLOW(fastSignalPin);
|
||||
if (dualSignal) setHIGH(fastSignalPin2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int MotorDriver::getCurrentRaw() {
|
||||
if (faultPin != UNUSED_PIN && ReadPin(faultPin) == LOW && ReadPin(powerPin) == HIGH)
|
||||
return simulatedOverload;
|
||||
|
||||
int current = analogRead(currentPin);
|
||||
if (faultPin != UNUSED_PIN && isLOW(fastFaultPin) && isHIGH(fastPowerPin))
|
||||
return -current;
|
||||
return current;
|
||||
// IMPORTANT: This function can be called in Interrupt() time within the 56uS timer
|
||||
// The default analogRead takes ~100uS which is catastrphic
|
||||
// so analogReadFast is used here. (-2uS)
|
||||
return analogReadFast(currentPin);
|
||||
// so DCCTimer has set the sample time to be much faster.
|
||||
}
|
||||
|
||||
unsigned int MotorDriver::raw2mA( int raw) {
|
||||
|
@ -106,3 +132,15 @@ unsigned int MotorDriver::raw2mA( int raw) {
|
|||
int MotorDriver::mA2raw( unsigned int mA) {
|
||||
return (int)(mA / senseFactor);
|
||||
}
|
||||
|
||||
void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & result) {
|
||||
DIAG(F("\nMotorDriver %S Pin=%d,"),type,pin);
|
||||
uint8_t port = digitalPinToPort(pin);
|
||||
if (input)
|
||||
result.inout = portInputRegister(port);
|
||||
else
|
||||
result.inout = portOutputRegister(port);
|
||||
result.maskHIGH = digitalPinToBitMask(pin);
|
||||
result.maskLOW = ~result.maskHIGH;
|
||||
DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x\n"),port, result.inout,input,result.maskHIGH);
|
||||
}
|
||||
|
|
|
@ -18,12 +18,20 @@
|
|||
*/
|
||||
#ifndef MotorDriver_h
|
||||
#define MotorDriver_h
|
||||
#include "FSH.h"
|
||||
|
||||
// Virtualised Motor shield 1-track hardware Interface
|
||||
|
||||
#ifndef UNUSED_PIN // sync define with the one in MotorDrivers.h
|
||||
#define UNUSED_PIN 127 // inside int8_t
|
||||
#endif
|
||||
|
||||
struct FASTPIN {
|
||||
volatile uint8_t *inout;
|
||||
uint8_t maskHIGH;
|
||||
uint8_t maskLOW;
|
||||
};
|
||||
|
||||
class MotorDriver {
|
||||
public:
|
||||
MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin);
|
||||
|
@ -34,15 +42,22 @@ class MotorDriver {
|
|||
virtual unsigned int raw2mA( int raw);
|
||||
virtual int mA2raw( unsigned int mA);
|
||||
inline int getRawCurrentTripValue() {
|
||||
return rawCurrentTripValue;
|
||||
return rawCurrentTripValue;
|
||||
}
|
||||
bool isPWMCapable();
|
||||
static bool usePWM;
|
||||
|
||||
private:
|
||||
byte powerPin, signalPin, signalPin2, currentPin, faultPin;
|
||||
int8_t brakePin; // negative means pin is inverted
|
||||
void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result);
|
||||
void getFastPin(const FSH* type,int pin, FASTPIN & result) {
|
||||
getFastPin(type, pin, 0, result);
|
||||
}
|
||||
byte powerPin, signalPin, signalPin2, currentPin, faultPin, brakePin;
|
||||
FASTPIN fastPowerPin,fastSignalPin, fastSignalPin2, fastBrakePin,fastFaultPin;
|
||||
bool dualSignal; // true to use signalPin2
|
||||
bool invertBrake; // brake pin passed as negative means pin is inverted
|
||||
float senseFactor;
|
||||
unsigned int tripMilliamps;
|
||||
int rawCurrentTripValue;
|
||||
int simulatedOverload;
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
/*
|
||||
* © 2020, Chris Harlow. All rights reserved.
|
||||
* © 2020, Harald Barth.
|
||||
*
|
||||
* This file is part of Asbelos DCC API
|
||||
*
|
||||
* 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 ARDUINO_AVR_UNO_WIFI_REV2
|
||||
#include <Arduino.h>
|
||||
#include "WifiInboundHandler.h"
|
||||
#include "RingStream.h"
|
||||
|
@ -228,3 +248,5 @@ void WifiInboundHandler::purgeCurrentCIPSEND() {
|
|||
pendingCipsend=false;
|
||||
clientPendingCIPSEND=-1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ARDUINO_AVR_UNO_WIFI_REV2
|
||||
// This code is NOT compiled on a unoWifiRev2 processor which uses a different architecture
|
||||
#include "WifiInterface.h" /* config.h included there */
|
||||
#include <avr/pgmspace.h>
|
||||
#include "DIAG.h"
|
||||
|
@ -25,11 +26,9 @@
|
|||
|
||||
#include "WifiInboundHandler.h"
|
||||
|
||||
const char FLASH READY_SEARCH[] = "\r\nready\r\n";
|
||||
const char FLASH OK_SEARCH[] = "\r\nOK\r\n";
|
||||
const char FLASH END_DETAIL_SEARCH[] = "@ 1000";
|
||||
const char FLASH SEND_OK_SEARCH[] = "\r\nSEND OK\r\n";
|
||||
const char FLASH IPD_SEARCH[] = "+IPD";
|
||||
|
||||
|
||||
|
||||
const unsigned long LOOP_TIMEOUT = 2000;
|
||||
bool WifiInterface::connected = false;
|
||||
Stream * WifiInterface::wifiStream;
|
||||
|
@ -43,7 +42,7 @@ Stream * WifiInterface::wifiStream;
|
|||
//
|
||||
// Figure out number of serial ports depending on hardware
|
||||
//
|
||||
#if defined(ARDUINO_AVR_UNO)
|
||||
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
|
||||
#define NUM_SERIAL 0
|
||||
#endif
|
||||
|
||||
|
@ -59,7 +58,8 @@ bool WifiInterface::setup(long serial_link_speed,
|
|||
const FSH *wifiESSID,
|
||||
const FSH *wifiPassword,
|
||||
const FSH *hostname,
|
||||
const int port) {
|
||||
const int port,
|
||||
const byte channel) {
|
||||
|
||||
wifiSerialState wifiUp = WIFI_NOAT;
|
||||
|
||||
|
@ -74,7 +74,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
|||
|
||||
#if NUM_SERIAL > 0
|
||||
Serial1.begin(serial_link_speed);
|
||||
wifiUp = setup(Serial1, wifiESSID, wifiPassword, hostname, port);
|
||||
wifiUp = setup(Serial1, wifiESSID, wifiPassword, hostname, port, channel);
|
||||
#endif
|
||||
|
||||
// Other serials are tried, depending on hardware.
|
||||
|
@ -82,7 +82,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
|||
if (wifiUp == WIFI_NOAT)
|
||||
{
|
||||
Serial2.begin(serial_link_speed);
|
||||
wifiUp = setup(Serial2, wifiESSID, wifiPassword, hostname, port);
|
||||
wifiUp = setup(Serial2, wifiESSID, wifiPassword, hostname, port, channel);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -90,7 +90,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
|||
if (wifiUp == WIFI_NOAT)
|
||||
{
|
||||
Serial3.begin(serial_link_speed);
|
||||
wifiUp = setup(Serial3, wifiESSID, wifiPassword, hostname, port);
|
||||
wifiUp = setup(Serial3, wifiESSID, wifiPassword, hostname, port, channel);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -108,7 +108,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
|||
}
|
||||
|
||||
wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, const FSH* password,
|
||||
const FSH* hostname, int port) {
|
||||
const FSH* hostname, int port, byte channel) {
|
||||
wifiSerialState wifiState;
|
||||
static uint8_t ntry = 0;
|
||||
ntry++;
|
||||
|
@ -117,7 +117,7 @@ wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, con
|
|||
|
||||
DIAG(F("\n++ Wifi Setup Try %d ++\n"), ntry);
|
||||
|
||||
wifiState = setup2( SSid, password, hostname, port);
|
||||
wifiState = setup2( SSid, password, hostname, port, channel);
|
||||
|
||||
if (wifiState == WIFI_NOAT) {
|
||||
DIAG(F("\n++ Wifi Setup NO AT ++\n"));
|
||||
|
@ -126,7 +126,7 @@ wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, con
|
|||
|
||||
if (wifiState == WIFI_CONNECTED) {
|
||||
StringFormatter::send(wifiStream, F("ATE0\r\n")); // turn off the echo
|
||||
checkForOK(200, OK_SEARCH, true);
|
||||
checkForOK(200, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -140,7 +140,7 @@ wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, con
|
|||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
|
||||
const FSH* hostname, int port) {
|
||||
const FSH* hostname, int port, byte channel) {
|
||||
bool ipOK = false;
|
||||
bool oldCmd = false;
|
||||
|
||||
|
@ -149,136 +149,161 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
|
|||
// First check... Restarting the Arduino does not restart the ES.
|
||||
// There may alrerady be a connection with data in the pipeline.
|
||||
// If there is, just shortcut the setup and continue to read the data as normal.
|
||||
if (checkForOK(200,IPD_SEARCH, true)) {
|
||||
if (checkForOK(200,F("+IPD"), true)) {
|
||||
DIAG(F("\nPreconfigured Wifi already running with data waiting\n"));
|
||||
// loopstate=4; // carry on from correct place... or not as the case may be
|
||||
return WIFI_CONNECTED;
|
||||
}
|
||||
|
||||
StringFormatter::send(wifiStream, F("AT\r\n")); // Is something here that understands AT?
|
||||
if(!checkForOK(200, OK_SEARCH, true))
|
||||
if(!checkForOK(200, true))
|
||||
return WIFI_NOAT; // No AT compatible WiFi module here
|
||||
|
||||
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, true); // Makes this visible on the console
|
||||
|
||||
// Display the AT version information
|
||||
StringFormatter::send(wifiStream, F("AT+GMR\r\n"));
|
||||
checkForOK(2000, OK_SEARCH, true, false); // Makes this visible on the console
|
||||
checkForOK(2000, true, false); // Makes this visible on the console
|
||||
|
||||
#ifdef DONT_TOUCH_WIFI_CONF
|
||||
DIAG(F("\nDONT_TOUCH_WIFI_CONF was set: Using existing config\n"));
|
||||
#else
|
||||
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"
|
||||
checkForOK(1000, true); // Not always OK, sometimes "no change"
|
||||
|
||||
// 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, true)) {
|
||||
oldCmd=true;
|
||||
while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE
|
||||
}
|
||||
|
||||
// If the source code looks unconfigured, check if the
|
||||
// ESP8266 is preconfigured. We check the first 13 chars
|
||||
// of the SSid.
|
||||
const char *yourNetwork = "Your network ";
|
||||
if (strncmp_P(yourNetwork, (const char*)SSid, 13) == 0 || ((const char *)SSid)[0] == '\0') {
|
||||
delay(8000); // give a preconfigured ES8266 a chance to connect to a router
|
||||
// typical connect time approx 7 seconds
|
||||
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;
|
||||
} else { // Should this really be "else" here /haba
|
||||
|
||||
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
|
||||
if (strncmp_P(yourNetwork, (const char*)SSid, 13) == 0 || strncmp_P("", (const char*)SSid, 13) == 0) {
|
||||
if (strncmp_P(yourNetwork, (const char*)password, 13) == 0) {
|
||||
// If the source code looks unconfigured, check if the
|
||||
// ESP8266 is preconfigured in station mode.
|
||||
// We check the first 13 chars of the SSid and the password
|
||||
|
||||
// give a preconfigured ES8266 a chance to connect to a router
|
||||
// typical connect time approx 7 seconds
|
||||
delay(8000);
|
||||
StringFormatter::send(wifiStream, F("AT+CIFSR\r\n"));
|
||||
if (checkForOK(5000, F("+CIFSR:STAIP"), true,false))
|
||||
if (!checkForOK(1000, F("0.0.0.0"), true,false))
|
||||
ipOK = true;
|
||||
}
|
||||
} else {
|
||||
// SSID was configured, so we assume station (client) mode.
|
||||
if (oldCmd) {
|
||||
// AT command early version supports CWJAP/CWSAP
|
||||
if (SSid) {
|
||||
StringFormatter::send(wifiStream, F("AT+CWJAP=\"%S\",\"%S\"\r\n"), SSid, password);
|
||||
ipOK = checkForOK(WIFI_CONNECT_TIMEOUT, OK_SEARCH, true);
|
||||
}
|
||||
DIAG(F("\n**\n"));
|
||||
|
||||
StringFormatter::send(wifiStream, F("AT+CWJAP=\"%S\",\"%S\"\r\n"), SSid, password);
|
||||
ipOK = checkForOK(WIFI_CONNECT_TIMEOUT, true);
|
||||
} 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
|
||||
checkForOK(2000, true); // dont care if not supported
|
||||
|
||||
if (SSid) {
|
||||
StringFormatter::send(wifiStream, F("AT+CWJAP_CUR=\"%S\",\"%S\"\r\n"), SSid, password);
|
||||
ipOK = checkForOK(WIFI_CONNECT_TIMEOUT, OK_SEARCH, true);
|
||||
}
|
||||
StringFormatter::send(wifiStream, F("AT+CWJAP_CUR=\"%S\",\"%S\"\r\n"), SSid, password);
|
||||
ipOK = checkForOK(WIFI_CONNECT_TIMEOUT, true);
|
||||
}
|
||||
|
||||
if (ipOK) {
|
||||
// But we really only have the ESSID and password correct
|
||||
// Let's check for IP
|
||||
// Let's check for IP (via DHCP)
|
||||
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))
|
||||
if (checkForOK(5000, F("+CIFSR:STAIP"), true,false))
|
||||
if (!checkForOK(1000, F("0.0.0.0"), true,false))
|
||||
ipOK = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ipOK) {
|
||||
// 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"
|
||||
// StringFormatter::send(wifiStream, F("AT+RST\r\n"));
|
||||
// checkForOK(1000, true); // Not always OK, sometimes "no change"
|
||||
|
||||
int i=0;
|
||||
do {
|
||||
// configure as AccessPoint. Try really hard as this is the
|
||||
// last way out to get any Wifi connectivity.
|
||||
StringFormatter::send(wifiStream, F("AT+CWMODE=2\r\n"));
|
||||
} while (!checkForOK(1000+i*500, true) && i++<10);
|
||||
|
||||
while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE
|
||||
|
||||
// Figure out MAC addr
|
||||
StringFormatter::send(wifiStream, F("AT+CIFSR\r\n"));
|
||||
StringFormatter::send(wifiStream, F("AT+CIFSR\r\n")); // not TOMATO
|
||||
// looking fpr mac addr eg +CIFSR:APMAC,"be:dd:c2:5c:6b:b7"
|
||||
if (checkForOK(5000, (const char*) F("+CIFSR:APMAC,\""), true,false)) {
|
||||
if (checkForOK(5000, F("+CIFSR:APMAC,\""), true,false)) {
|
||||
// Copy 17 byte mac address
|
||||
for (int i=0; i<17;i++) {
|
||||
while(!wifiStream->available());
|
||||
macAddress[i]=wifiStream->read();
|
||||
StringFormatter::printEscape(macAddress[i]);
|
||||
}
|
||||
} else {
|
||||
memset(macAddress,'f',sizeof(macAddress));
|
||||
}
|
||||
char macTail[]={macAddress[9],macAddress[10],macAddress[12],macAddress[13],macAddress[15],macAddress[16],'\0'};
|
||||
|
||||
if (oldCmd) {
|
||||
while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE
|
||||
checkForOK(1000, true, false); // suck up remainder of AT+CIFSR
|
||||
|
||||
int i=0;
|
||||
do {
|
||||
if (strncmp_P(yourNetwork, (const char*)password, 13) == 0) {
|
||||
// unconfigured
|
||||
StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail);
|
||||
} else {
|
||||
// password configured by user
|
||||
StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"%s\",1,4\r\n"), macTail, password);
|
||||
}
|
||||
} while (i++<2 && !checkForOK(WIFI_CONNECT_TIMEOUT, OK_SEARCH, true)); // do twice if necessary but ignore failure as AP mode may still be ok
|
||||
} else {
|
||||
|
||||
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
|
||||
i=0;
|
||||
do {
|
||||
if (strncmp_P(yourNetwork, (const char*)password, 13) == 0) {
|
||||
// unconfigured
|
||||
StringFormatter::send(wifiStream, F("AT+CWSAP%s=\"DCCEX_%s\",\"PASS_%s\",%d,4\r\n"),
|
||||
oldCmd ? "" : "_CUR", macTail, macTail, channel);
|
||||
} else {
|
||||
// password configured by user
|
||||
StringFormatter::send(wifiStream, F("AT+CWSAP%s=\"DCCEX_%s\",\"%S\",%d,4\r\n"), oldCmd ? "" : "_CUR",
|
||||
macTail, password, channel);
|
||||
}
|
||||
} while (!checkForOK(WIFI_CONNECT_TIMEOUT, true) && i++<2); // do twice if necessary but ignore failure as AP mode may still be ok
|
||||
if (i >= 2)
|
||||
DIAG(F("\nWarning: Setting AP SSID and password failed\n")); // but issue warning
|
||||
|
||||
if (!oldCmd) {
|
||||
StringFormatter::send(wifiStream, F("AT+CIPRECVMODE=0\r\n"), port); // make sure transfer mode is correct
|
||||
checkForOK(2000, OK_SEARCH, true);
|
||||
checkForOK(2000, true);
|
||||
}
|
||||
}
|
||||
|
||||
StringFormatter::send(wifiStream, F("AT+CIPSERVER=0\r\n")); // turn off tcp server (to clean connections before CIPMUX=1)
|
||||
checkForOK(1000, OK_SEARCH, true); // ignore result in case it already was off
|
||||
checkForOK(1000, true); // ignore result in case it already was off
|
||||
|
||||
StringFormatter::send(wifiStream, F("AT+CIPMUX=1\r\n")); // configure for multiple connections
|
||||
if (!checkForOK(1000, OK_SEARCH, true)) return WIFI_DISCONNECTED;
|
||||
if (!checkForOK(1000, true)) return WIFI_DISCONNECTED;
|
||||
|
||||
StringFormatter::send(wifiStream, F("AT+CIPSERVER=1,%d\r\n"), port); // turn on server on port
|
||||
if (!checkForOK(1000, OK_SEARCH, true)) return WIFI_DISCONNECTED;
|
||||
if (!checkForOK(1000, true)) return WIFI_DISCONNECTED;
|
||||
#endif //DONT_TOUCH_WIFI_CONF
|
||||
|
||||
StringFormatter::send(wifiStream, F("AT+CIFSR\r\n")); // Display ip addresses to the DIAG
|
||||
if (!checkForOK(1000, OK_SEARCH, true, false)) return WIFI_DISCONNECTED;
|
||||
DIAG(F("\nPORT=%d\n"),port);
|
||||
if (!checkForOK(1000, F("IP,\"") , true, false)) return WIFI_DISCONNECTED;
|
||||
// Copy the IP address
|
||||
{
|
||||
const byte MAX_IP_LENGTH=15;
|
||||
char ipString[MAX_IP_LENGTH+1];
|
||||
ipString[MAX_IP_LENGTH]='\0'; // protection against missing " character on end.
|
||||
for(byte ipLen=0;ipLen<MAX_IP_LENGTH;ipLen++) {
|
||||
while(!wifiStream->available());
|
||||
int ipChar=wifiStream->read();
|
||||
StringFormatter::printEscape(ipChar);
|
||||
if (ipChar=='"') {
|
||||
ipString[ipLen]='\0';
|
||||
break;
|
||||
}
|
||||
ipString[ipLen]=ipChar;
|
||||
}
|
||||
LCD(4,F("%s"),ipString); // There is not enough room on some LCDs to put a title to this
|
||||
}
|
||||
// suck up anything after the IP.
|
||||
if (!checkForOK(1000, true, false)) return WIFI_DISCONNECTED;
|
||||
LCD(5,F("PORT=%d\n"),port);
|
||||
|
||||
return WIFI_CONNECTED;
|
||||
}
|
||||
|
@ -300,15 +325,19 @@ void WifiInterface::ATCommand(const byte * command) {
|
|||
}
|
||||
else {
|
||||
StringFormatter:: send(wifiStream, F("AT+%s\r\n"), command);
|
||||
checkForOK(10000, OK_SEARCH, true);
|
||||
checkForOK(10000, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool WifiInterface::checkForOK( const unsigned int timeout, const char * waitfor, bool echo, bool escapeEcho) {
|
||||
bool WifiInterface::checkForOK( const unsigned int timeout, bool echo, bool escapeEcho) {
|
||||
return checkForOK(timeout,F("\r\nOK\r\n"),echo,escapeEcho);
|
||||
}
|
||||
|
||||
bool WifiInterface::checkForOK( const unsigned int timeout, const FSH * waitfor, bool echo, bool escapeEcho) {
|
||||
unsigned long startTime = millis();
|
||||
char const *locator = waitfor;
|
||||
char *locator = (char *)waitfor;
|
||||
DIAG(F("\nWifi Check: [%E]"), waitfor);
|
||||
while ( millis() - startTime < timeout) {
|
||||
while (wifiStream->available()) {
|
||||
|
@ -317,7 +346,7 @@ bool WifiInterface::checkForOK( const unsigned int timeout, const char * waitfor
|
|||
if (escapeEcho) StringFormatter::printEscape( ch); /// THIS IS A DIAG IN DISGUISE
|
||||
else DIAG(F("%c"), ch);
|
||||
}
|
||||
if (ch != GETFLASH(locator)) locator = waitfor;
|
||||
if (ch != GETFLASH(locator)) locator = (char *)waitfor;
|
||||
if (ch == GETFLASH(locator)) {
|
||||
locator++;
|
||||
if (!GETFLASH(locator)) {
|
||||
|
@ -337,3 +366,5 @@ void WifiInterface::loop() {
|
|||
WifiInboundHandler::loop();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#ifndef WifiInterface_h
|
||||
#define WifiInterface_h
|
||||
#include "FSH.h"
|
||||
#include "config.h"
|
||||
#include "DCCEXParser.h"
|
||||
#include <Arduino.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
@ -35,23 +34,20 @@ public:
|
|||
const FSH *wifiESSID,
|
||||
const FSH *wifiPassword,
|
||||
const FSH *hostname,
|
||||
const int port = 2560);
|
||||
const int port,
|
||||
const byte channel);
|
||||
static void loop();
|
||||
static void ATCommand(const byte *command);
|
||||
|
||||
private:
|
||||
static wifiSerialState setup(Stream &setupStream, const FSH *SSSid, const FSH *password,
|
||||
const FSH *hostname, int port);
|
||||
const FSH *hostname, int port, byte channel);
|
||||
static Stream *wifiStream;
|
||||
static DCCEXParser parser;
|
||||
static wifiSerialState setup2(const FSH *SSSid, const FSH *password,
|
||||
const FSH *hostname, int port);
|
||||
static bool checkForOK(const unsigned int timeout, const char *waitfor, bool echo, bool escapeEcho = true);
|
||||
const FSH *hostname, int port, byte channel);
|
||||
static bool checkForOK(const unsigned int timeout, bool echo, bool escapeEcho = true);
|
||||
static bool checkForOK(const unsigned int timeout, const FSH *waitfor, bool echo, bool escapeEcho = true);
|
||||
static bool connected;
|
||||
static bool closeAfter;
|
||||
static byte loopstate;
|
||||
static int datalength;
|
||||
static int connectionId;
|
||||
static unsigned long loopTimeoutStart;
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
/**********************************************************************
|
||||
|
||||
Config.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
config.h
|
||||
COPYRIGHT (c) 2020 Fred Decker
|
||||
|
||||
The configuration file for DCC++ EX Command Station
|
||||
The configuration file for DCC-EX Command Station
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
|
@ -52,10 +51,14 @@ The configuration file for DCC++ EX Command Station
|
|||
// WIFI_SSID is the network name IF you want to use your existing home network.
|
||||
// Do NOT change this if you want to use the WiFi in Access Point (AP) mode.
|
||||
//
|
||||
// If you do NOT set the WIFI_SSID, the WiFi chip will first try
|
||||
// to connect to the previously configured network and if that fails
|
||||
// fall back to Access Point mode. The SSID of the AP will be
|
||||
// automatically set to DCCEX_*.
|
||||
// If you do NOT set the WIFI_SSID and do NOT set the WIFI_PASSWORD,
|
||||
// then the WiFi chip will first try to connect to the previously
|
||||
// configured network and if that fails fall back to Access Point mode.
|
||||
// The SSID of the AP will be automatically set to DCCEX_*.
|
||||
// If you DO set the WIFI_SSID then the WiFi chip will try to connect
|
||||
// to that (home) network in station (client) mode. If a WIFI_PASSWORD
|
||||
// is set (recommended), that password will be used for AP mode.
|
||||
// The AP mode password must be at least 8 characters long.
|
||||
//
|
||||
// Your SSID may not conain ``"'' (double quote, ASCII 0x22).
|
||||
#define WIFI_SSID "Your network name"
|
||||
|
@ -69,6 +72,12 @@ The configuration file for DCC++ EX Command Station
|
|||
// WIFI_HOSTNAME: You probably don't need to change this
|
||||
#define WIFI_HOSTNAME "dccex"
|
||||
//
|
||||
// WIFI_CHANNEL: If the line "#define ENABLE_WIFI true" is uncommented,
|
||||
// WiFi will be enabled (Mega only). The default channel is set to "1" whether
|
||||
// this line exists or not. If you need to use an alternate channel (we recommend
|
||||
// using only 1,6, or 11) you may change it here.
|
||||
#define WIFI_CHANNEL 1
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Wifi connect timeout in milliseconds. Default is 14000 (14 seconds). You only need
|
||||
|
@ -90,26 +99,6 @@ The configuration file for DCC++ EX Command Station
|
|||
//
|
||||
//#define IP_ADDRESS { 192, 168, 1, 200 }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DEFINE MAC ADDRESS ARRAY FOR ETHERNET COMMUNICATIONS INTERFACE
|
||||
//
|
||||
// Uncomment to use with Ethernet Shields
|
||||
//
|
||||
// Ethernet Shields do not have have a MAC address in hardware. There may be one on
|
||||
// a sticker on the Shield that you should use. Otherwise choose one of the ones below
|
||||
// Be certain that no other device on your network has this same MAC address!
|
||||
//
|
||||
// 52:b8:8a:8e:ce:21
|
||||
// e3:e9:73:e1:db:0d
|
||||
// 54:2b:13:52:ac:0c
|
||||
|
||||
// NOTE: This is not used with ESP8266 WiFi modules.
|
||||
|
||||
//#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 MAC_ADDRESS { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
|
|
@ -21,10 +21,13 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WIFI_ON: All prereqs for running with WIFI are met
|
||||
//
|
||||
// Note: WIFI_CHANNEL may not exist in early config.h files so is added here if needed.
|
||||
|
||||
#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_AVR_UNO_WIFI_REV2))
|
||||
#define WIFI_ON true
|
||||
#ifndef WIFI_CHANNEL
|
||||
#define WIFI_CHANNEL 1
|
||||
#endif
|
||||
#else
|
||||
#define WIFI_ON false
|
||||
#endif
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
* © 2020, Chris Harlow. All rights reserved.
|
||||
*
|
||||
* This file is a demonstattion of calling the DCC-EX API
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#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
|
||||
|
||||
// this code is here to demonstrate use of the DCC API and other techniques
|
||||
|
||||
// myFilter is an example of an OPTIONAL command filter used to intercept < > commands from
|
||||
// the usb or wifi streamm. It demonstrates how a command may be intercepted
|
||||
// or even a new command created without having to break open the API library code.
|
||||
// The filter is permitted to use or modify the parameter list before passing it on to
|
||||
// the standard parser. By setting the opcode to 0, the standard parser will
|
||||
// just ignore the command on the assumption that you have already handled it.
|
||||
//
|
||||
// The filter must be enabled by calling the DCC EXParser::setFilter method, see use in setup().
|
||||
|
||||
void myComandFilter(Print * stream, byte & opcode, byte & paramCount, int p[]) {
|
||||
(void)stream; // avoid compiler warning if we don't access this parameter
|
||||
switch (opcode) {
|
||||
case '!': // Create a bespoke new command to clear all loco reminders <!> or specific locos e.g <! 3 4 99>
|
||||
if (paramCount==0) DCC::forgetAllLocos();
|
||||
else for (int i=0;i<paramCount;i++) DCC::forgetLoco(p[i]);
|
||||
opcode=0; // tell parser to ignore this command as we have done it already
|
||||
break;
|
||||
default: // drop through and parser will use the command unaltered.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This is an OPTIONAL example of a HTTP filter...
|
||||
// If you have configured wifi and an HTTP request is received on the Wifi connection
|
||||
// it will normally be rejected 404 Not Found.
|
||||
|
||||
// If you wish to handle HTTP requests, you can create a filter and ask the WifiInterface to
|
||||
// call your code for each detected http request.
|
||||
|
||||
void myHttpFilter(Print * stream, byte * cmd) {
|
||||
(void)cmd; // Avoid compiler warning because this example doesnt use this parameter
|
||||
|
||||
// BEWARE - As soon as you start responding, the cmd buffer is trashed!
|
||||
// You must get everything you need from it before using StringFormatter::send!
|
||||
|
||||
StringFormatter::send(stream,F("HTTP/1.1 200 OK\nContent-Type: text/html\nConnnection: close\n\n"));
|
||||
StringFormatter::send(stream,F("<html><body>This is my HTTP filter responding.<br/></body></html>"));
|
||||
}
|
||||
|
||||
// Callback functions are necessary if you call any API that must wait for a response from the
|
||||
// programming track. The API must return immediately otherwise other loop() functions would be blocked.
|
||||
// Your callback function will be invoked when the data arrives from the prog track.
|
||||
// See the DCC:getLocoId example in the setup function.
|
||||
|
||||
|
||||
void myCallback(int result) {
|
||||
DIAG(F("\n getting Loco Id callback result=%d"),result);
|
||||
}
|
||||
|
||||
|
||||
// Create a serial command parser... This is OPTIONAL if you don't need to handle JMRI type commands
|
||||
// from the Serial port.
|
||||
DCCEXParser serialParser;
|
||||
|
||||
|
||||
// Try monitoring the memory
|
||||
#include "freeMemory.h"
|
||||
int ramLowWatermark=32767; // This figure gets overwritten dynamically in loop()
|
||||
|
||||
void setup() {
|
||||
|
||||
// The main sketch has responsibilities during setup()
|
||||
|
||||
// Responsibility 1: Start the usb connection for diagnostics and possible JMRI input
|
||||
// DIAGSERAL is normally Serial but uses SerialUSB on a SAMD processor
|
||||
DIAGSERIAL.begin(115200);
|
||||
while(!DIAGSERIAL);
|
||||
|
||||
// Responsibility 2: Start the DCC engine.
|
||||
// Note: this provides DCC with two motor drivers, main and prog, which handle the motor shield(s)
|
||||
// Standard supported devices have pre-configured macros but custome hardware installations require
|
||||
// detailed pin mappings and may also require modified subclasses of the MotorDriver to implement specialist logic.
|
||||
|
||||
// STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h
|
||||
|
||||
// Optionally a Timer number (1..4) may be passed to DCC::begin to override the default Timer1 used for the
|
||||
// waveform generation. e.g. DCC::begin(STANDARD_MOTOR_SHIELD,2); to use timer 2
|
||||
|
||||
DCC::begin(STANDARD_MOTOR_SHIELD);
|
||||
|
||||
// Responsibility 3: **Optionally** Start the WiFi interface if required.
|
||||
// NOTE: On a Uno you will have to provide a SoftwareSerial
|
||||
// configured for the pins connected to the Wifi card
|
||||
// and a 9600 baud rate.
|
||||
// setup(serial, F(router name) or NULL, F(router password), F(hostname), F(AcessPoint name) or NULL , port)
|
||||
// (port 3532 is 0xDCC decimal.)
|
||||
|
||||
|
||||
Serial1.begin(WIFI_BAUD);
|
||||
WifiInterface::setup(Serial1, F("BTHub5-M6PT"), F("49de8d4862"),F("DCCEX"),3532);
|
||||
|
||||
// Optionally tell the Wifi parser to use my http filter.
|
||||
// This will intercept http commands from Wifi.
|
||||
WifiInterface::setHTTPCallback(myHttpFilter);
|
||||
|
||||
// This is just for demonstration purposes
|
||||
DIAG(F("\n===== DCCEX demonstrating DCC::getLocoId() call ==========\n"));
|
||||
DCC::getLocoId(myCallback); // myCallback will be called with the result
|
||||
DIAG(F("\n===== DCC::getLocoId has returned, but the callback wont be executed until we are in loop() ======\n"));
|
||||
|
||||
// Optionally tell the command parser to use my example filter.
|
||||
// This will intercept JMRI commands from both USB and Wifi
|
||||
DCCEXParser::setFilter(myComandFilter);
|
||||
|
||||
|
||||
DIAG(F("\nReady for JMRI commands\n"));
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// The main sketch has responsibilities during loop()
|
||||
|
||||
// Responsibility 1: Handle DCC background processes
|
||||
// (loco reminders and power checks)
|
||||
DCC::loop();
|
||||
|
||||
// Responsibility 2: handle any incoming commands on USB connection
|
||||
serialParser.loop(DIAGSERIAL);
|
||||
|
||||
// Responsibility 3: Optionally handle any incoming WiFi traffic
|
||||
WifiInterface::loop();
|
||||
|
||||
// Your additional loop code
|
||||
|
||||
// Optionally report any decrease in memory (will automatically trigger on first call)
|
||||
int freeNow=freeMemory();
|
||||
if (freeNow<ramLowWatermark) {
|
||||
ramLowWatermark=freeNow;
|
||||
DIAG(F("\nFree RAM=%d\n"),ramLowWatermark);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* © 2020, Chris Harlow. All rights reserved.
|
||||
*
|
||||
* This is a basic, no frills DCC-EX example of a DCC++ compatible setup.
|
||||
* There are more advanced examples in the examples folder i
|
||||
*/
|
||||
|
||||
#include "DCCEX.h"
|
||||
|
||||
// Create parser for <> commands coming from keyboard or JMRI on thr USB connection.
|
||||
DCCEXParser serialParser;
|
||||
|
||||
void setup() {
|
||||
|
||||
// Responsibility 1: Start the usb connection for diagnostics and possible JMRI input
|
||||
Serial.begin(115200);
|
||||
|
||||
// Responsibility 2: Start the DCC engine with information about your Motor Shield.
|
||||
// STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorDriverss.h
|
||||
DCC::begin(STANDARD_MOTOR_SHIELD);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Responsibility 1: Handle DCC background processes (loco reminders and power checks)
|
||||
DCC::loop();
|
||||
|
||||
// Responsibility 2: handle any incoming commands on USB connection
|
||||
serialParser.loop(Serial);
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* © 2020, Chris Harlow. All rights reserved.
|
||||
*
|
||||
* This file is a demonstattion of setting up a DCC-EX
|
||||
* Command station to support direct connection of WiThrottle devices
|
||||
* such as "Engine Driver". If you contriol your layout through JMRI
|
||||
* then DON'T connect throttles to this wifi, connect them to JMRI.
|
||||
*
|
||||
* This is just 3 statements longer than the basic setup.
|
||||
*
|
||||
* THIS SETUP DOES NOT APPLY TO ARDUINO UNO WITH ONLY A SINGLE SERIAL PORT.
|
||||
* REFER TO SEPARATE EXAMPLE.
|
||||
*/
|
||||
|
||||
#include "DCCEX.h"
|
||||
|
||||
|
||||
|
||||
// Create a serial command parser... Enables certain diagnostics and commands
|
||||
// to be issued from the USB serial console
|
||||
// This is NOT intended for JMRI....
|
||||
|
||||
DCCEXParser serialParser;
|
||||
|
||||
void setup() {
|
||||
|
||||
// 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);
|
||||
|
||||
// Responsibility 3: Start the DCC engine.
|
||||
// Note: this provides DCC with two motor drivers, main and prog, which handle the motor shield(s)
|
||||
// Standard supported devices have pre-configured macros but custome hardware installations require
|
||||
// detailed pin mappings and may also require modified subclasses of the MotorDriver to implement specialist logic.
|
||||
|
||||
// STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h
|
||||
|
||||
// Optionally a Timer number (1..4) may be passed to DCC::begin to override the default Timer1 used for the
|
||||
// waveform generation. e.g. DCC::begin(STANDARD_MOTOR_SHIELD,2); to use timer 2
|
||||
|
||||
DCC::begin(STANDARD_MOTOR_SHIELD);
|
||||
|
||||
// Start the WiFi interface.
|
||||
// NOTE: References to Serial1 are for the serial port used to connect
|
||||
// your wifi chip/shield.
|
||||
|
||||
|
||||
Serial1.begin(115200); // BAUD rate of your Wifi chip/shield
|
||||
WifiInterface::setup(Serial1,
|
||||
F("BTHub5-M6PT"), // Router name
|
||||
F("49de8d4862"), // Router password
|
||||
F("DCCEX"), // Hostname (ignored by some wifi chip firmware)
|
||||
3532); // port (3532 is 0xDCC)
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// The main sketch has responsibilities during loop()
|
||||
|
||||
// Responsibility 1: Handle DCC background processes
|
||||
// (loco reminders and power checks)
|
||||
DCC::loop();
|
||||
|
||||
// Responsibility 2: handle any incoming commands on USB connection
|
||||
serialParser.loop(Serial);
|
||||
|
||||
// Responsibility 3: Optionally handle any incoming WiFi traffic
|
||||
WifiInterface::loop();
|
||||
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
#ifndef version_h
|
||||
#define version_h
|
||||
|
||||
#define VERSION "3.0.3"
|
||||
#include "StringFormatter.h"
|
||||
|
||||
#define VERSION "3.0.4"
|
||||
// 3.0.4 Includes:
|
||||
// Wifi startup bugfixes
|
||||
// 3.0.3 Includes:
|
||||
// <W addr> command to write loco address and clear consist
|
||||
// <R> command will allow for consist address
|
||||
|
|
Loading…
Reference in New Issue
Block a user