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
|
// This file is the main sketch for the Command Station.
|
||||||
// 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.
|
|
||||||
//
|
//
|
||||||
// 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"
|
#include "DCCEX.h"
|
||||||
|
|
||||||
// Create a serial command parser for the USB connection,
|
// 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
|
// Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi
|
||||||
|
|
||||||
#if WIFI_ON
|
#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
|
#endif // WIFI_ON
|
||||||
|
|
||||||
#if ETHERNET_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) {
|
void DCC::setLocoId(int id,ACK_CALLBACK callback, bool blocking) {
|
||||||
if (id<=0 || id>9999) callback(-1);
|
if (id<1 || id>10239) { //0x27FF according to standard
|
||||||
if (id<=127) ackManagerSetup(id,SHORT_LOCO_ID_PROG, callback, blocking);
|
callback(-1);
|
||||||
else ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback, blocking);
|
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
|
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_MAX = 16244;
|
||||||
const int HASH_KEYWORD_MIN = 15978;
|
const int HASH_KEYWORD_MIN = 15978;
|
||||||
|
|
||||||
int DCCEXParser::stashP[MAX_PARAMS];
|
int DCCEXParser::stashP[MAX_COMMAND_PARAMS];
|
||||||
bool DCCEXParser::stashBusy;
|
bool DCCEXParser::stashBusy;
|
||||||
|
|
||||||
Print *DCCEXParser::stashStream = NULL;
|
Print *DCCEXParser::stashStream = NULL;
|
||||||
|
@ -102,7 +102,7 @@ void DCCEXParser::loop(Stream &stream)
|
||||||
Sensor::checkAll(&stream); // Update and print changes
|
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 state = 1;
|
||||||
byte parameterCount = 0;
|
byte parameterCount = 0;
|
||||||
|
@ -111,10 +111,10 @@ int DCCEXParser::splitValues(int result[MAX_PARAMS], const byte *cmd)
|
||||||
bool signNegative = false;
|
bool signNegative = false;
|
||||||
|
|
||||||
// clear all parameters in case not enough found
|
// 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;
|
result[i] = 0;
|
||||||
|
|
||||||
while (parameterCount < MAX_PARAMS)
|
while (parameterCount < MAX_COMMAND_PARAMS)
|
||||||
{
|
{
|
||||||
byte hot = *remainingCmd;
|
byte hot = *remainingCmd;
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ int DCCEXParser::splitValues(int result[MAX_PARAMS], const byte *cmd)
|
||||||
return parameterCount;
|
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 state = 1;
|
||||||
byte parameterCount = 0;
|
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
|
const byte *remainingCmd = cmd + 1; // skips the opcode
|
||||||
|
|
||||||
// clear all parameters in case not enough found
|
// 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;
|
result[i] = 0;
|
||||||
|
|
||||||
while (parameterCount < MAX_PARAMS)
|
while (parameterCount < MAX_COMMAND_PARAMS)
|
||||||
{
|
{
|
||||||
byte hot = *remainingCmd;
|
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
|
(void)EEPROM; // tell compiler not to warn this is unused
|
||||||
if (Diag::CMD)
|
if (Diag::CMD)
|
||||||
DIAG(F("\nPARSING:%s\n"), com);
|
DIAG(F("\nPARSING:%s\n"), com);
|
||||||
int p[MAX_PARAMS];
|
int p[MAX_COMMAND_PARAMS];
|
||||||
while (com[0] == '<' || com[0] == ' ')
|
while (com[0] == '<' || com[0] == ' ')
|
||||||
com++; // strip off any number of < or spaces
|
com++; // strip off any number of < or spaces
|
||||||
byte params = splitValues(p, com);
|
byte params = splitValues(p, com);
|
||||||
|
@ -770,13 +770,13 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[])
|
||||||
}
|
}
|
||||||
|
|
||||||
// CALLBACKS must be static
|
// 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 )
|
if (stashBusy )
|
||||||
return false;
|
return false;
|
||||||
stashBusy = true;
|
stashBusy = true;
|
||||||
stashStream = stream;
|
stashStream = stream;
|
||||||
memcpy(stashP, p, MAX_PARAMS * sizeof(p[0]));
|
memcpy(stashP, p, MAX_COMMAND_PARAMS * sizeof(p[0]));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void DCCEXParser::callback_W(int result)
|
void DCCEXParser::callback_W(int result)
|
||||||
|
|
|
@ -34,7 +34,7 @@ struct DCCEXParser
|
||||||
static void setFilter(FILTER_CALLBACK filter);
|
static void setFilter(FILTER_CALLBACK filter);
|
||||||
static void setRMFTFilter(FILTER_CALLBACK filter);
|
static void setRMFTFilter(FILTER_CALLBACK filter);
|
||||||
static void setAtCommandCallback(AT_COMMAND_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:
|
private:
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ struct DCCEXParser
|
||||||
byte bufferLength=0;
|
byte bufferLength=0;
|
||||||
bool inCommandPayload=false;
|
bool inCommandPayload=false;
|
||||||
byte buffer[MAX_BUFFER+2];
|
byte buffer[MAX_BUFFER+2];
|
||||||
int splitValues( int result[MAX_PARAMS], const byte * command);
|
int splitValues( int result[MAX_COMMAND_PARAMS], const byte * command);
|
||||||
int splitHexValues( int result[MAX_PARAMS], const byte * command);
|
int splitHexValues( int result[MAX_COMMAND_PARAMS], const byte * command);
|
||||||
|
|
||||||
bool parseT(Print * stream, int params, int p[]);
|
bool parseT(Print * stream, int params, int p[]);
|
||||||
bool parseZ(Print * stream, int params, int p[]);
|
bool parseZ(Print * stream, int params, int p[]);
|
||||||
|
@ -55,8 +55,8 @@ struct DCCEXParser
|
||||||
static bool stashBusy;
|
static bool stashBusy;
|
||||||
|
|
||||||
static Print * stashStream;
|
static Print * stashStream;
|
||||||
static int stashP[MAX_PARAMS];
|
static int stashP[MAX_COMMAND_PARAMS];
|
||||||
bool stashCallback(Print * stream, int p[MAX_PARAMS]);
|
bool stashCallback(Print * stream, int p[MAX_COMMAND_PARAMS]);
|
||||||
static void callback_W(int result);
|
static void callback_W(int result);
|
||||||
static void callback_B(int result);
|
static void callback_B(int result);
|
||||||
static void callback_R(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.
|
* DCCEX works on a single timer interrupt at a regular 58uS interval.
|
||||||
* The DCCWaveform class generates the signals to the motor shield
|
* The DCCWaveform class generates the signals to the motor shield
|
||||||
* based on this timer.
|
* 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"
|
#include "DCCTimer.h"
|
||||||
|
@ -35,9 +50,11 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_MEGAAVR
|
#ifdef ARDUINO_ARCH_MEGAAVR
|
||||||
// Arduino unoWifi Rev2 and nanoEvery architectire
|
// Arduino unoWifi Rev2 and nanoEvery architectire
|
||||||
|
|
||||||
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||||
interruptHandler=callback;
|
interruptHandler=callback;
|
||||||
noInterrupts();
|
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.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.CTRLA = TCB_CLKSEL_CLKDIV2_gc; // 8 MHz ~ 0.125 us
|
||||||
TCB0.CCMP = CLOCK_CYCLES -1; // 1 tick less for timer reset
|
TCB0.CCMP = CLOCK_CYCLES -1; // 1 tick less for timer reset
|
||||||
|
@ -54,11 +71,33 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
||||||
interruptHandler();
|
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
|
#else
|
||||||
// Arduino nano, uno, mega etc
|
// 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) {
|
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
|
||||||
interruptHandler=callback;
|
interruptHandler=callback;
|
||||||
noInterrupts();
|
noInterrupts();
|
||||||
|
ADCSRA = (ADCSRA & 0b11111000) | 0b00000100; // speed up analogRead sample time
|
||||||
TCCR1A = 0;
|
TCCR1A = 0;
|
||||||
ICR1 = CLOCK_CYCLES;
|
ICR1 = CLOCK_CYCLES;
|
||||||
TCNT1 = 0;
|
TCNT1 = 0;
|
||||||
|
@ -69,4 +108,38 @@ INTERRUPT_CALLBACK interruptHandler=0;
|
||||||
|
|
||||||
// ISR called by timer interrupt every 58uS
|
// ISR called by timer interrupt every 58uS
|
||||||
ISR(TIMER1_OVF_vect){ interruptHandler(); }
|
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
|
#endif
|
||||||
|
|
|
@ -7,6 +7,9 @@ typedef void (*INTERRUPT_CALLBACK)();
|
||||||
class DCCTimer {
|
class DCCTimer {
|
||||||
public:
|
public:
|
||||||
static void begin(INTERRUPT_CALLBACK interrupt);
|
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:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#pragma GCC optimize ("-O3")
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#include "DCCWaveform.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
|
progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static
|
||||||
mainTrack.setPowerMode(POWERMODE::OFF);
|
mainTrack.setPowerMode(POWERMODE::OFF);
|
||||||
progTrack.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);
|
DCCTimer::begin(DCCWaveform::interruptHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +123,11 @@ void DCCWaveform::checkPowerOverload() {
|
||||||
case POWERMODE::ON:
|
case POWERMODE::ON:
|
||||||
// Check current
|
// Check current
|
||||||
lastCurrent=motorDriver->getCurrentRaw();
|
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) {
|
if (lastCurrent <= tripValue) {
|
||||||
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
||||||
if(power_good_counter<100)
|
if(power_good_counter<100)
|
||||||
|
@ -129,9 +138,9 @@ void DCCWaveform::checkPowerOverload() {
|
||||||
setPowerMode(POWERMODE::OVERLOAD);
|
setPowerMode(POWERMODE::OVERLOAD);
|
||||||
unsigned int mA=motorDriver->raw2mA(lastCurrent);
|
unsigned int mA=motorDriver->raw2mA(lastCurrent);
|
||||||
unsigned int maxmA=motorDriver->raw2mA(tripValue);
|
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;
|
power_good_counter=0;
|
||||||
sampleDelay = power_sample_overload_wait;
|
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)
|
if (power_sample_overload_wait >= 10000)
|
||||||
power_sample_overload_wait = 10000;
|
power_sample_overload_wait = 10000;
|
||||||
else
|
else
|
||||||
|
@ -142,6 +151,8 @@ void DCCWaveform::checkPowerOverload() {
|
||||||
// Try setting it back on after the OVERLOAD_WAIT
|
// Try setting it back on after the OVERLOAD_WAIT
|
||||||
setPowerMode(POWERMODE::ON);
|
setPowerMode(POWERMODE::ON);
|
||||||
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
||||||
|
// Debug code....
|
||||||
|
DIAG(F("\n*** %S TRACK POWER RESET delay=%d ***\n"), isMainTrack ? F("MAIN") : F("PROG"), sampleDelay);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning.
|
sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning.
|
||||||
|
@ -198,7 +209,10 @@ void DCCWaveform::interrupt2() {
|
||||||
}
|
}
|
||||||
else if (packetPending) {
|
else if (packetPending) {
|
||||||
// Copy pending packet to transmit packet
|
// 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;
|
transmitLength = pendingLength;
|
||||||
transmitRepeats = pendingRepeats;
|
transmitRepeats = pendingRepeats;
|
||||||
packetPending = false;
|
packetPending = false;
|
||||||
|
@ -223,7 +237,7 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea
|
||||||
while (packetPending);
|
while (packetPending);
|
||||||
|
|
||||||
byte checksum = 0;
|
byte checksum = 0;
|
||||||
for (int b = 0; b < byteCount; b++) {
|
for (byte b = 0; b < byteCount; b++) {
|
||||||
checksum ^= buffer[b];
|
checksum ^= buffer[b];
|
||||||
pendingPacket[b] = buffer[b];
|
pendingPacket[b] = buffer[b];
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ const int POWER_SAMPLE_OVERLOAD_WAIT = 20;
|
||||||
// Number of preamble bits.
|
// Number of preamble bits.
|
||||||
const int PREAMBLE_BITS_MAIN = 16;
|
const int PREAMBLE_BITS_MAIN = 16;
|
||||||
const int PREAMBLE_BITS_PROG = 22;
|
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
|
// The WAVE_STATE enum is deliberately numbered because a change of order would be catastrophic
|
||||||
// to the transform array.
|
// to the transform array.
|
||||||
|
|
|
@ -17,13 +17,18 @@
|
||||||
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#if __has_include ( "config.h")
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "defines.h" // This should be changed to DCCEX.h when possible
|
#else
|
||||||
|
#warning config.h not found. Using defaults from config.example.h
|
||||||
|
#include "config.example.h"
|
||||||
|
#endif
|
||||||
|
#include "defines.h"
|
||||||
#if ETHERNET_ON == true
|
#if ETHERNET_ON == true
|
||||||
#include "EthernetInterface.h"
|
#include "EthernetInterface.h"
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
#include "CommandDistributor.h"
|
#include "CommandDistributor.h"
|
||||||
|
#include "DCCTimer.h"
|
||||||
|
|
||||||
EthernetInterface * EthernetInterface::singleton=NULL;
|
EthernetInterface * EthernetInterface::singleton=NULL;
|
||||||
/**
|
/**
|
||||||
|
@ -45,9 +50,14 @@ void EthernetInterface::setup()
|
||||||
*/
|
*/
|
||||||
EthernetInterface::EthernetInterface()
|
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
|
#ifdef IP_ADDRESS
|
||||||
|
@ -73,11 +83,11 @@ EthernetInterface::EthernetInterface()
|
||||||
|
|
||||||
IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address
|
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();
|
server->begin();
|
||||||
|
|
||||||
LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
|
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);
|
outboundRing=new RingStream(OUTBOUND_RING_SIZE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,12 @@
|
||||||
|
|
||||||
#ifndef EthernetInterface_h
|
#ifndef EthernetInterface_h
|
||||||
#define 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 "DCCEXParser.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <avr/pgmspace.h>
|
#include <avr/pgmspace.h>
|
||||||
|
@ -33,11 +38,7 @@
|
||||||
* @brief Network Configuration
|
* @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 MAX_ETH_BUFFER 512
|
||||||
#define OUTBOUND_RING_SIZE 2048
|
#define OUTBOUND_RING_SIZE 2048
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
#define GITHUB_SHA "9db6d36"
|
#define GITHUB_SHA "0228345"
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
|
|
||||||
#ifndef LCD_Implementation_h
|
#ifndef LCD_Implementation_h
|
||||||
#define LCD_Implementation_h
|
#define LCD_Implementation_h
|
||||||
#include "config.h"
|
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
#include "LCDDisplay.h"
|
#include "LCDDisplay.h"
|
||||||
|
|
||||||
|
|
116
MotorDriver.cpp
116
MotorDriver.cpp
|
@ -18,51 +18,72 @@
|
||||||
*/
|
*/
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "MotorDriver.h"
|
#include "MotorDriver.h"
|
||||||
#include "AnalogReadFast.h"
|
#include "DCCTimer.h"
|
||||||
#include "DIAG.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))
|
||||||
|
|
||||||
|
bool MotorDriver::usePWM=false;
|
||||||
#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
|
|
||||||
|
|
||||||
MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin,
|
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) {
|
byte current_pin, float sense_factor, unsigned int trip_milliamps, byte fault_pin) {
|
||||||
powerPin=power_pin;
|
powerPin=power_pin;
|
||||||
|
getFastPin(F("POWER"),powerPin,fastPowerPin);
|
||||||
|
pinMode(powerPin, OUTPUT);
|
||||||
|
|
||||||
signalPin=signal_pin;
|
signalPin=signal_pin;
|
||||||
|
getFastPin(F("SIG"),signalPin,fastSignalPin);
|
||||||
|
pinMode(signalPin, OUTPUT);
|
||||||
|
|
||||||
signalPin2=signal_pin2;
|
signalPin2=signal_pin2;
|
||||||
|
if (signalPin2!=UNUSED_PIN) {
|
||||||
|
dualSignal=true;
|
||||||
|
getFastPin(F("SIG2"),signalPin2,fastSignalPin2);
|
||||||
|
pinMode(signalPin2, OUTPUT);
|
||||||
|
}
|
||||||
|
else dualSignal=false;
|
||||||
|
|
||||||
brakePin=brake_pin;
|
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;
|
currentPin=current_pin;
|
||||||
senseFactor=sense_factor;
|
pinMode(currentPin, INPUT);
|
||||||
|
|
||||||
faultPin=fault_pin;
|
faultPin=fault_pin;
|
||||||
|
if (faultPin != UNUSED_PIN) {
|
||||||
|
getFastPin(F("FAULT"),faultPin, 1 /*input*/, fastFaultPin);
|
||||||
|
pinMode(faultPin, INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
senseFactor=sense_factor;
|
||||||
tripMilliamps=trip_milliamps;
|
tripMilliamps=trip_milliamps;
|
||||||
rawCurrentTripValue=(int)(trip_milliamps / sense_factor);
|
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) {
|
void MotorDriver::setPower(bool on) {
|
||||||
if (brakePin == -4 && on) {
|
if (on) {
|
||||||
// toggle brake before turning power on - resets overcurrent error
|
// toggle brake before turning power on - resets overcurrent error
|
||||||
// on the Pololu board if brake is wired to ^D2.
|
// on the Pololu board if brake is wired to ^D2.
|
||||||
setBrake(true);
|
setBrake(true);
|
||||||
setBrake(false);
|
setBrake(false);
|
||||||
|
setHIGH(fastPowerPin);
|
||||||
}
|
}
|
||||||
WritePin(powerPin, on ? HIGH : LOW);
|
else setLOW(fastPowerPin);
|
||||||
}
|
}
|
||||||
|
|
||||||
// setBrake applies brake if on == true. So to get
|
// setBrake applies brake if on == true. So to get
|
||||||
|
@ -74,30 +95,35 @@ void MotorDriver::setPower(bool on) {
|
||||||
// compensate for that.
|
// compensate for that.
|
||||||
//
|
//
|
||||||
void MotorDriver::setBrake(bool on) {
|
void MotorDriver::setBrake(bool on) {
|
||||||
bool state = on;
|
if (brakePin == UNUSED_PIN) return;
|
||||||
byte pin = brakePin;
|
if (on ^ invertBrake) setHIGH(fastBrakePin);
|
||||||
if (brakePin < 0) {
|
else setLOW(fastBrakePin);
|
||||||
pin=-pin;
|
|
||||||
state=!state;
|
|
||||||
}
|
|
||||||
WritePin(pin, state ? HIGH : LOW);
|
|
||||||
//DIAG(F("BrakePin: %d is %d\n"), pin, ReadPin(pin));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotorDriver::setSignal( bool high) {
|
void MotorDriver::setSignal( bool high) {
|
||||||
WritePin(signalPin, high ? HIGH : LOW);
|
if (usePWM) {
|
||||||
if (signalPin2 != UNUSED_PIN) WritePin(signalPin2, high ? LOW : HIGH);
|
DCCTimer::setPWM(signalPin,high);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (high) {
|
||||||
|
setHIGH(fastSignalPin);
|
||||||
|
if (dualSignal) setLOW(fastSignalPin2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setLOW(fastSignalPin);
|
||||||
|
if (dualSignal) setHIGH(fastSignalPin2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int MotorDriver::getCurrentRaw() {
|
int MotorDriver::getCurrentRaw() {
|
||||||
if (faultPin != UNUSED_PIN && ReadPin(faultPin) == LOW && ReadPin(powerPin) == HIGH)
|
int current = analogRead(currentPin);
|
||||||
return simulatedOverload;
|
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
|
// IMPORTANT: This function can be called in Interrupt() time within the 56uS timer
|
||||||
// The default analogRead takes ~100uS which is catastrphic
|
// The default analogRead takes ~100uS which is catastrphic
|
||||||
// so analogReadFast is used here. (-2uS)
|
// so DCCTimer has set the sample time to be much faster.
|
||||||
return analogReadFast(currentPin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int MotorDriver::raw2mA( int raw) {
|
unsigned int MotorDriver::raw2mA( int raw) {
|
||||||
|
@ -106,3 +132,15 @@ unsigned int MotorDriver::raw2mA( int raw) {
|
||||||
int MotorDriver::mA2raw( unsigned int mA) {
|
int MotorDriver::mA2raw( unsigned int mA) {
|
||||||
return (int)(mA / senseFactor);
|
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
|
#ifndef MotorDriver_h
|
||||||
#define MotorDriver_h
|
#define MotorDriver_h
|
||||||
|
#include "FSH.h"
|
||||||
|
|
||||||
// Virtualised Motor shield 1-track hardware Interface
|
// Virtualised Motor shield 1-track hardware Interface
|
||||||
|
|
||||||
#ifndef UNUSED_PIN // sync define with the one in MotorDrivers.h
|
#ifndef UNUSED_PIN // sync define with the one in MotorDrivers.h
|
||||||
#define UNUSED_PIN 127 // inside int8_t
|
#define UNUSED_PIN 127 // inside int8_t
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct FASTPIN {
|
||||||
|
volatile uint8_t *inout;
|
||||||
|
uint8_t maskHIGH;
|
||||||
|
uint8_t maskLOW;
|
||||||
|
};
|
||||||
|
|
||||||
class MotorDriver {
|
class MotorDriver {
|
||||||
public:
|
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);
|
MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin);
|
||||||
|
@ -36,13 +44,20 @@ class MotorDriver {
|
||||||
inline int getRawCurrentTripValue() {
|
inline int getRawCurrentTripValue() {
|
||||||
return rawCurrentTripValue;
|
return rawCurrentTripValue;
|
||||||
}
|
}
|
||||||
|
bool isPWMCapable();
|
||||||
|
static bool usePWM;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
byte powerPin, signalPin, signalPin2, currentPin, faultPin;
|
void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result);
|
||||||
int8_t brakePin; // negative means pin is inverted
|
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;
|
float senseFactor;
|
||||||
unsigned int tripMilliamps;
|
unsigned int tripMilliamps;
|
||||||
int rawCurrentTripValue;
|
int rawCurrentTripValue;
|
||||||
int simulatedOverload;
|
|
||||||
};
|
};
|
||||||
#endif
|
#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 <Arduino.h>
|
||||||
#include "WifiInboundHandler.h"
|
#include "WifiInboundHandler.h"
|
||||||
#include "RingStream.h"
|
#include "RingStream.h"
|
||||||
|
@ -228,3 +248,5 @@ void WifiInboundHandler::purgeCurrentCIPSEND() {
|
||||||
pendingCipsend=false;
|
pendingCipsend=false;
|
||||||
clientPendingCIPSEND=-1;
|
clientPendingCIPSEND=-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#ifndef 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 "WifiInterface.h" /* config.h included there */
|
||||||
#include <avr/pgmspace.h>
|
#include <avr/pgmspace.h>
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
|
@ -25,11 +26,9 @@
|
||||||
|
|
||||||
#include "WifiInboundHandler.h"
|
#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;
|
const unsigned long LOOP_TIMEOUT = 2000;
|
||||||
bool WifiInterface::connected = false;
|
bool WifiInterface::connected = false;
|
||||||
Stream * WifiInterface::wifiStream;
|
Stream * WifiInterface::wifiStream;
|
||||||
|
@ -43,7 +42,7 @@ Stream * WifiInterface::wifiStream;
|
||||||
//
|
//
|
||||||
// Figure out number of serial ports depending on hardware
|
// 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
|
#define NUM_SERIAL 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -59,7 +58,8 @@ bool WifiInterface::setup(long serial_link_speed,
|
||||||
const FSH *wifiESSID,
|
const FSH *wifiESSID,
|
||||||
const FSH *wifiPassword,
|
const FSH *wifiPassword,
|
||||||
const FSH *hostname,
|
const FSH *hostname,
|
||||||
const int port) {
|
const int port,
|
||||||
|
const byte channel) {
|
||||||
|
|
||||||
wifiSerialState wifiUp = WIFI_NOAT;
|
wifiSerialState wifiUp = WIFI_NOAT;
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
||||||
|
|
||||||
#if NUM_SERIAL > 0
|
#if NUM_SERIAL > 0
|
||||||
Serial1.begin(serial_link_speed);
|
Serial1.begin(serial_link_speed);
|
||||||
wifiUp = setup(Serial1, wifiESSID, wifiPassword, hostname, port);
|
wifiUp = setup(Serial1, wifiESSID, wifiPassword, hostname, port, channel);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Other serials are tried, depending on hardware.
|
// Other serials are tried, depending on hardware.
|
||||||
|
@ -82,7 +82,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
||||||
if (wifiUp == WIFI_NOAT)
|
if (wifiUp == WIFI_NOAT)
|
||||||
{
|
{
|
||||||
Serial2.begin(serial_link_speed);
|
Serial2.begin(serial_link_speed);
|
||||||
wifiUp = setup(Serial2, wifiESSID, wifiPassword, hostname, port);
|
wifiUp = setup(Serial2, wifiESSID, wifiPassword, hostname, port, channel);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
||||||
if (wifiUp == WIFI_NOAT)
|
if (wifiUp == WIFI_NOAT)
|
||||||
{
|
{
|
||||||
Serial3.begin(serial_link_speed);
|
Serial3.begin(serial_link_speed);
|
||||||
wifiUp = setup(Serial3, wifiESSID, wifiPassword, hostname, port);
|
wifiUp = setup(Serial3, wifiESSID, wifiPassword, hostname, port, channel);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
||||||
}
|
}
|
||||||
|
|
||||||
wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, const FSH* password,
|
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;
|
wifiSerialState wifiState;
|
||||||
static uint8_t ntry = 0;
|
static uint8_t ntry = 0;
|
||||||
ntry++;
|
ntry++;
|
||||||
|
@ -117,7 +117,7 @@ wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, con
|
||||||
|
|
||||||
DIAG(F("\n++ Wifi Setup Try %d ++\n"), ntry);
|
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) {
|
if (wifiState == WIFI_NOAT) {
|
||||||
DIAG(F("\n++ Wifi Setup NO AT ++\n"));
|
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) {
|
if (wifiState == WIFI_CONNECTED) {
|
||||||
StringFormatter::send(wifiStream, F("ATE0\r\n")); // turn off the echo
|
StringFormatter::send(wifiStream, F("ATE0\r\n")); // turn off the echo
|
||||||
checkForOK(200, OK_SEARCH, true);
|
checkForOK(200, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, con
|
||||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
#endif
|
#endif
|
||||||
wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
|
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 ipOK = false;
|
||||||
bool oldCmd = 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.
|
// First check... Restarting the Arduino does not restart the ES.
|
||||||
// There may alrerady be a connection with data in the pipeline.
|
// 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 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"));
|
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;
|
return WIFI_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringFormatter::send(wifiStream, F("AT\r\n")); // Is something here that understands AT?
|
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
|
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
|
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
|
// Display the AT version information
|
||||||
StringFormatter::send(wifiStream, F("AT+GMR\r\n"));
|
StringFormatter::send(wifiStream, F("AT+GMR\r\n"));
|
||||||
checkForOK(2000, OK_SEARCH, true, false); // Makes this visible on the console
|
checkForOK(2000, true, false); // Makes this visible on the console
|
||||||
|
|
||||||
#ifdef DONT_TOUCH_WIFI_CONF
|
#ifdef DONT_TOUCH_WIFI_CONF
|
||||||
DIAG(F("\nDONT_TOUCH_WIFI_CONF was set: Using existing config\n"));
|
DIAG(F("\nDONT_TOUCH_WIFI_CONF was set: Using existing config\n"));
|
||||||
#else
|
#else
|
||||||
StringFormatter::send(wifiStream, F("AT+CWMODE=1\r\n")); // configure as "station" = WiFi client
|
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"
|
||||||
|
|
||||||
// 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
|
// Older ES versions have AT+CWJAP, newer ones have AT+CWJAP_CUR and AT+CWHOSTNAME
|
||||||
StringFormatter::send(wifiStream, F("AT+CWJAP?\r\n"));
|
StringFormatter::send(wifiStream, F("AT+CWJAP?\r\n"));
|
||||||
if (checkForOK(2000, OK_SEARCH, true)) {
|
if (checkForOK(2000, true)) {
|
||||||
oldCmd=true;
|
oldCmd=true;
|
||||||
while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE
|
while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE
|
||||||
|
|
||||||
// AT command early version supports CWJAP/CWSAP
|
|
||||||
if (SSid) {
|
|
||||||
StringFormatter::send(wifiStream, F("AT+CWJAP=\"%S\",\"%S\"\r\n"), SSid, password);
|
|
||||||
ipOK = checkForOK(WIFI_CONNECT_TIMEOUT, OK_SEARCH, true);
|
|
||||||
}
|
}
|
||||||
DIAG(F("\n**\n"));
|
|
||||||
|
|
||||||
|
const char *yourNetwork = "Your network ";
|
||||||
|
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
|
||||||
|
StringFormatter::send(wifiStream, F("AT+CWJAP=\"%S\",\"%S\"\r\n"), SSid, password);
|
||||||
|
ipOK = checkForOK(WIFI_CONNECT_TIMEOUT, true);
|
||||||
} else {
|
} else {
|
||||||
// later version supports CWJAP_CUR
|
// later version supports CWJAP_CUR
|
||||||
|
|
||||||
StringFormatter::send(wifiStream, F("AT+CWHOSTNAME=\"%S\"\r\n"), hostname); // Set Host name for Wifi Client
|
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);
|
StringFormatter::send(wifiStream, F("AT+CWJAP_CUR=\"%S\",\"%S\"\r\n"), SSid, password);
|
||||||
ipOK = checkForOK(WIFI_CONNECT_TIMEOUT, OK_SEARCH, true);
|
ipOK = checkForOK(WIFI_CONNECT_TIMEOUT, true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ipOK) {
|
if (ipOK) {
|
||||||
// But we really only have the ESSID and password correct
|
// But we really only have the ESSID and password correct
|
||||||
// Let's check for IP
|
// Let's check for IP (via DHCP)
|
||||||
ipOK = false;
|
ipOK = false;
|
||||||
StringFormatter::send(wifiStream, F("AT+CIFSR\r\n"));
|
StringFormatter::send(wifiStream, F("AT+CIFSR\r\n"));
|
||||||
if (checkForOK(5000, (const char*) F("+CIFSR:STAIP"), true,false))
|
if (checkForOK(5000, F("+CIFSR:STAIP"), true,false))
|
||||||
if (!checkForOK(1000, (const char*) F("0.0.0.0"), true,false))
|
if (!checkForOK(1000, F("0.0.0.0"), true,false))
|
||||||
ipOK = true;
|
ipOK = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!ipOK) {
|
if (!ipOK) {
|
||||||
// If we have not managed to get this going in station mode, go for AP mode
|
// 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.
|
// StringFormatter::send(wifiStream, F("AT+RST\r\n"));
|
||||||
checkForOK(1000, OK_SEARCH, true); // Not always OK, sometimes "no change"
|
// 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
|
// 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"
|
// 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
|
// Copy 17 byte mac address
|
||||||
for (int i=0; i<17;i++) {
|
for (int i=0; i<17;i++) {
|
||||||
while(!wifiStream->available());
|
while(!wifiStream->available());
|
||||||
macAddress[i]=wifiStream->read();
|
macAddress[i]=wifiStream->read();
|
||||||
StringFormatter::printEscape(macAddress[i]);
|
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'};
|
char macTail[]={macAddress[9],macAddress[10],macAddress[12],macAddress[13],macAddress[15],macAddress[16],'\0'};
|
||||||
|
|
||||||
if (oldCmd) {
|
checkForOK(1000, true, false); // suck up remainder of AT+CIFSR
|
||||||
while (wifiStream->available()) StringFormatter::printEscape( wifiStream->read()); /// THIS IS A DIAG IN DISGUISE
|
|
||||||
|
|
||||||
int i=0;
|
i=0;
|
||||||
do {
|
do {
|
||||||
if (strncmp_P(yourNetwork, (const char*)password, 13) == 0) {
|
if (strncmp_P(yourNetwork, (const char*)password, 13) == 0) {
|
||||||
// unconfigured
|
// unconfigured
|
||||||
StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"PASS_%s\",1,4\r\n"), macTail, macTail);
|
StringFormatter::send(wifiStream, F("AT+CWSAP%s=\"DCCEX_%s\",\"PASS_%s\",%d,4\r\n"),
|
||||||
|
oldCmd ? "" : "_CUR", macTail, macTail, channel);
|
||||||
} else {
|
} else {
|
||||||
// password configured by user
|
// password configured by user
|
||||||
StringFormatter::send(wifiStream, F("AT+CWSAP=\"DCCEX_%s\",\"%s\",1,4\r\n"), macTail, password);
|
StringFormatter::send(wifiStream, F("AT+CWSAP%s=\"DCCEX_%s\",\"%S\",%d,4\r\n"), oldCmd ? "" : "_CUR",
|
||||||
|
macTail, password, channel);
|
||||||
}
|
}
|
||||||
} while (i++<2 && !checkForOK(WIFI_CONNECT_TIMEOUT, OK_SEARCH, true)); // do twice if necessary but ignore failure as AP mode may still be ok
|
} while (!checkForOK(WIFI_CONNECT_TIMEOUT, true) && i++<2); // do twice if necessary but ignore failure as AP mode may still be ok
|
||||||
} else {
|
if (i >= 2)
|
||||||
|
DIAG(F("\nWarning: Setting AP SSID and password failed\n")); // but issue warning
|
||||||
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
|
|
||||||
|
|
||||||
|
if (!oldCmd) {
|
||||||
StringFormatter::send(wifiStream, F("AT+CIPRECVMODE=0\r\n"), port); // make sure transfer mode is correct
|
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)
|
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
|
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
|
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
|
#endif //DONT_TOUCH_WIFI_CONF
|
||||||
|
|
||||||
StringFormatter::send(wifiStream, F("AT+CIFSR\r\n")); // Display ip addresses to the DIAG
|
StringFormatter::send(wifiStream, F("AT+CIFSR\r\n")); // Display ip addresses to the DIAG
|
||||||
if (!checkForOK(1000, OK_SEARCH, true, false)) return WIFI_DISCONNECTED;
|
if (!checkForOK(1000, F("IP,\"") , true, false)) return WIFI_DISCONNECTED;
|
||||||
DIAG(F("\nPORT=%d\n"),port);
|
// 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;
|
return WIFI_CONNECTED;
|
||||||
}
|
}
|
||||||
|
@ -300,15 +325,19 @@ void WifiInterface::ATCommand(const byte * command) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
StringFormatter:: send(wifiStream, F("AT+%s\r\n"), command);
|
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();
|
unsigned long startTime = millis();
|
||||||
char const *locator = waitfor;
|
char *locator = (char *)waitfor;
|
||||||
DIAG(F("\nWifi Check: [%E]"), waitfor);
|
DIAG(F("\nWifi Check: [%E]"), waitfor);
|
||||||
while ( millis() - startTime < timeout) {
|
while ( millis() - startTime < timeout) {
|
||||||
while (wifiStream->available()) {
|
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
|
if (escapeEcho) StringFormatter::printEscape( ch); /// THIS IS A DIAG IN DISGUISE
|
||||||
else DIAG(F("%c"), ch);
|
else DIAG(F("%c"), ch);
|
||||||
}
|
}
|
||||||
if (ch != GETFLASH(locator)) locator = waitfor;
|
if (ch != GETFLASH(locator)) locator = (char *)waitfor;
|
||||||
if (ch == GETFLASH(locator)) {
|
if (ch == GETFLASH(locator)) {
|
||||||
locator++;
|
locator++;
|
||||||
if (!GETFLASH(locator)) {
|
if (!GETFLASH(locator)) {
|
||||||
|
@ -337,3 +366,5 @@ void WifiInterface::loop() {
|
||||||
WifiInboundHandler::loop();
|
WifiInboundHandler::loop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#ifndef WifiInterface_h
|
#ifndef WifiInterface_h
|
||||||
#define WifiInterface_h
|
#define WifiInterface_h
|
||||||
#include "FSH.h"
|
#include "FSH.h"
|
||||||
#include "config.h"
|
|
||||||
#include "DCCEXParser.h"
|
#include "DCCEXParser.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <avr/pgmspace.h>
|
#include <avr/pgmspace.h>
|
||||||
|
@ -35,23 +34,20 @@ public:
|
||||||
const FSH *wifiESSID,
|
const FSH *wifiESSID,
|
||||||
const FSH *wifiPassword,
|
const FSH *wifiPassword,
|
||||||
const FSH *hostname,
|
const FSH *hostname,
|
||||||
const int port = 2560);
|
const int port,
|
||||||
|
const byte channel);
|
||||||
static void loop();
|
static void loop();
|
||||||
static void ATCommand(const byte *command);
|
static void ATCommand(const byte *command);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static wifiSerialState setup(Stream &setupStream, const FSH *SSSid, const FSH *password,
|
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 Stream *wifiStream;
|
||||||
static DCCEXParser parser;
|
static DCCEXParser parser;
|
||||||
static wifiSerialState setup2(const FSH *SSSid, const FSH *password,
|
static wifiSerialState setup2(const FSH *SSSid, const FSH *password,
|
||||||
const FSH *hostname, int port);
|
const FSH *hostname, int port, byte channel);
|
||||||
static bool checkForOK(const unsigned int timeout, const char *waitfor, bool echo, bool escapeEcho = true);
|
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 connected;
|
||||||
static bool closeAfter;
|
|
||||||
static byte loopstate;
|
|
||||||
static int datalength;
|
|
||||||
static int connectionId;
|
|
||||||
static unsigned long loopTimeoutStart;
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|
||||||
Config.h
|
config.h
|
||||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
|
||||||
COPYRIGHT (c) 2020 Fred Decker
|
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.
|
// 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.
|
// 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
|
// If you do NOT set the WIFI_SSID and do NOT set the WIFI_PASSWORD,
|
||||||
// to connect to the previously configured network and if that fails
|
// then the WiFi chip will first try to connect to the previously
|
||||||
// fall back to Access Point mode. The SSID of the AP will be
|
// configured network and if that fails fall back to Access Point mode.
|
||||||
// automatically set to DCCEX_*.
|
// 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).
|
// Your SSID may not conain ``"'' (double quote, ASCII 0x22).
|
||||||
#define WIFI_SSID "Your network name"
|
#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
|
// WIFI_HOSTNAME: You probably don't need to change this
|
||||||
#define WIFI_HOSTNAME "dccex"
|
#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
|
// 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 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
|
// 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))
|
#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
|
#define WIFI_ON true
|
||||||
|
#ifndef WIFI_CHANNEL
|
||||||
|
#define WIFI_CHANNEL 1
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
#define WIFI_ON false
|
#define WIFI_ON false
|
||||||
#endif
|
#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
|
#ifndef version_h
|
||||||
#define 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:
|
// 3.0.3 Includes:
|
||||||
// <W addr> command to write loco address and clear consist
|
// <W addr> command to write loco address and clear consist
|
||||||
// <R> command will allow for consist address
|
// <R> command will allow for consist address
|
||||||
|
|
Loading…
Reference in New Issue
Block a user