Initialisation depot
This commit is contained in:
BIN
DCC-Centrale/Firmware/BaseStation/DCC++ Arduino Sketch.pdf
Normal file
BIN
DCC-Centrale/Firmware/BaseStation/DCC++ Arduino Sketch.pdf
Normal file
Binary file not shown.
239
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Accessories.cpp
Normal file
239
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Accessories.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
/**********************************************************************
|
||||
|
||||
Accessories.cpp
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
/**********************************************************************
|
||||
|
||||
DCC++ BASE STATION can keep track of the direction of any turnout that is controlled
|
||||
by a DCC stationary accessory decoder. All turnouts, as well as any other DCC accessories
|
||||
connected in this fashion, can always be operated using the DCC BASE STATION Accessory command:
|
||||
|
||||
<a ADDRESS SUBADDRESS ACTIVATE>
|
||||
|
||||
However, this general command simply sends the appropriate DCC instruction packet to the main tracks
|
||||
to operate connected accessories. It does not store or retain any information regarding the current
|
||||
status of that accessory.
|
||||
|
||||
To have this sketch store and retain the direction of DCC-connected turnouts, as well as automatically
|
||||
invoke the required <a> command as needed, first define/edit/delete such turnouts using the following
|
||||
variations of the "T" command:
|
||||
|
||||
<T ID ADDRESS SUBADDRESS>: creates a new turnout ID, with specified ADDRESS and SUBADDRESS
|
||||
if turnout ID already exists, it is updated with specificed ADDRESS and SUBADDRESS
|
||||
returns: <O> if successful and <X> if unsuccessful (e.g. out of memory)
|
||||
|
||||
<T ID>: deletes definition of turnout ID
|
||||
returns: <O> if successful and <X> if unsuccessful (e.g. ID does not exist)
|
||||
|
||||
<T>: lists all defined turnouts
|
||||
returns: <H ID ADDRESS SUBADDRESS THROW> for each defined turnout or <X> if no turnouts defined
|
||||
|
||||
where
|
||||
|
||||
ID: the numeric ID (0-32767) of the turnout to control
|
||||
ADDRESS: the primary address of the decoder controlling this turnout (0-511)
|
||||
SUBADDRESS: the subaddress of the decoder controlling this turnout (0-3)
|
||||
|
||||
Once all turnouts have been properly defined, use the <E> command to store their definitions to EEPROM.
|
||||
If you later make edits/additions/deletions to the turnout definitions, you must invoke the <E> command if you want those
|
||||
new definitions updated in the EEPROM. You can also clear everything stored in the EEPROM by invoking the <e> command.
|
||||
|
||||
To "throw" turnouts that have been defined use:
|
||||
|
||||
<T ID THROW>: sets turnout ID to either the "thrown" or "unthrown" position
|
||||
returns: <H ID THROW>, or <X> if turnout ID does not exist
|
||||
|
||||
where
|
||||
|
||||
ID: the numeric ID (0-32767) of the turnout to control
|
||||
THROW: 0 (unthrown) or 1 (thrown)
|
||||
|
||||
When controlled as such, the Arduino updates and stores the direction of each Turnout in EEPROM so
|
||||
that it is retained even without power. A list of the current directions of each Turnout in the form <H ID THROW> is generated
|
||||
by this sketch whenever the <s> status command is invoked. This provides an efficient way of initializing
|
||||
the directions of any Turnouts being monitored or controlled by a separate interface or GUI program.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "Accessories.h"
|
||||
#include "SerialCommand.h"
|
||||
#include "DCCpp_Uno.h"
|
||||
#include "EEStore.h"
|
||||
#include <EEPROM.h>
|
||||
#include "Comm.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Turnout::activate(int s){
|
||||
char c[20];
|
||||
data.tStatus=(s>0); // if s>0 set turnout=ON, else if zero or negative set turnout=OFF
|
||||
sprintf(c,"a %d %d %d",data.address,data.subAddress,data.tStatus);
|
||||
SerialCommand::parse(c);
|
||||
if(num>0)
|
||||
EEPROM.put(num,data.tStatus);
|
||||
INTERFACE.print("<H");
|
||||
INTERFACE.print(data.id);
|
||||
if(data.tStatus==0)
|
||||
INTERFACE.print(" 0>");
|
||||
else
|
||||
INTERFACE.print(" 1>");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Turnout* Turnout::get(int n){
|
||||
Turnout *tt;
|
||||
for(tt=firstTurnout;tt!=NULL && tt->data.id!=n;tt=tt->nextTurnout);
|
||||
return(tt);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Turnout::remove(int n){
|
||||
Turnout *tt,*pp;
|
||||
|
||||
for(tt=firstTurnout;tt!=NULL && tt->data.id!=n;pp=tt,tt=tt->nextTurnout);
|
||||
|
||||
if(tt==NULL){
|
||||
INTERFACE.print("<X>");
|
||||
return;
|
||||
}
|
||||
|
||||
if(tt==firstTurnout)
|
||||
firstTurnout=tt->nextTurnout;
|
||||
else
|
||||
pp->nextTurnout=tt->nextTurnout;
|
||||
|
||||
free(tt);
|
||||
|
||||
INTERFACE.print("<O>");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Turnout::show(int n){
|
||||
Turnout *tt;
|
||||
|
||||
if(firstTurnout==NULL){
|
||||
INTERFACE.print("<X>");
|
||||
return;
|
||||
}
|
||||
|
||||
for(tt=firstTurnout;tt!=NULL;tt=tt->nextTurnout){
|
||||
INTERFACE.print("<H");
|
||||
INTERFACE.print(tt->data.id);
|
||||
if(n==1){
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(tt->data.address);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(tt->data.subAddress);
|
||||
}
|
||||
if(tt->data.tStatus==0)
|
||||
INTERFACE.print(" 0>");
|
||||
else
|
||||
INTERFACE.print(" 1>");
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Turnout::parse(char *c){
|
||||
int n,s,m;
|
||||
Turnout *t;
|
||||
|
||||
switch(sscanf(c,"%d %d %d",&n,&s,&m)){
|
||||
|
||||
case 2: // argument is string with id number of turnout followed by zero (not thrown) or one (thrown)
|
||||
t=get(n);
|
||||
if(t!=NULL)
|
||||
t->activate(s);
|
||||
else
|
||||
INTERFACE.print("<X>");
|
||||
break;
|
||||
|
||||
case 3: // argument is string with id number of turnout followed by an address and subAddress
|
||||
create(n,s,m,1);
|
||||
break;
|
||||
|
||||
case 1: // argument is a string with id number only
|
||||
remove(n);
|
||||
break;
|
||||
|
||||
case -1: // no arguments
|
||||
show(1); // verbose show
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Turnout::load(){
|
||||
struct TurnoutData data;
|
||||
Turnout *tt;
|
||||
|
||||
for(int i=0;i<EEStore::eeStore->data.nTurnouts;i++){
|
||||
EEPROM.get(EEStore::pointer(),data);
|
||||
tt=create(data.id,data.address,data.subAddress);
|
||||
tt->data.tStatus=data.tStatus;
|
||||
tt->num=EEStore::pointer();
|
||||
EEStore::advance(sizeof(tt->data));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Turnout::store(){
|
||||
Turnout *tt;
|
||||
|
||||
tt=firstTurnout;
|
||||
EEStore::eeStore->data.nTurnouts=0;
|
||||
|
||||
while(tt!=NULL){
|
||||
tt->num=EEStore::pointer();
|
||||
EEPROM.put(EEStore::pointer(),tt->data);
|
||||
EEStore::advance(sizeof(tt->data));
|
||||
tt=tt->nextTurnout;
|
||||
EEStore::eeStore->data.nTurnouts++;
|
||||
}
|
||||
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Turnout *Turnout::create(int id, int add, int subAdd, int v){
|
||||
Turnout *tt;
|
||||
|
||||
if(firstTurnout==NULL){
|
||||
firstTurnout=(Turnout *)calloc(1,sizeof(Turnout));
|
||||
tt=firstTurnout;
|
||||
} else if((tt=get(id))==NULL){
|
||||
tt=firstTurnout;
|
||||
while(tt->nextTurnout!=NULL)
|
||||
tt=tt->nextTurnout;
|
||||
tt->nextTurnout=(Turnout *)calloc(1,sizeof(Turnout));
|
||||
tt=tt->nextTurnout;
|
||||
}
|
||||
|
||||
if(tt==NULL){ // problem allocating memory
|
||||
if(v==1)
|
||||
INTERFACE.print("<X>");
|
||||
return(tt);
|
||||
}
|
||||
|
||||
tt->data.id=id;
|
||||
tt->data.address=add;
|
||||
tt->data.subAddress=subAdd;
|
||||
tt->data.tStatus=0;
|
||||
if(v==1)
|
||||
INTERFACE.print("<O>");
|
||||
return(tt);
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Turnout *Turnout::firstTurnout=NULL;
|
||||
|
||||
|
||||
39
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Accessories.h
Normal file
39
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Accessories.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**********************************************************************
|
||||
|
||||
Accessories.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#ifndef Accessories_h
|
||||
#define Accessories_h
|
||||
|
||||
struct TurnoutData {
|
||||
byte tStatus;
|
||||
byte subAddress;
|
||||
int id;
|
||||
int address;
|
||||
};
|
||||
|
||||
struct Turnout{
|
||||
static Turnout *firstTurnout;
|
||||
int num;
|
||||
struct TurnoutData data;
|
||||
Turnout *nextTurnout;
|
||||
void activate(int s);
|
||||
static void parse(char *c);
|
||||
static Turnout* get(int);
|
||||
static void remove(int);
|
||||
static void load();
|
||||
static void store();
|
||||
static Turnout *create(int, int, int, int=0);
|
||||
static void show(int=0);
|
||||
}; // Turnout
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
35
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Comm.h
Normal file
35
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Comm.h
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
|
||||
Comm.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#if COMM_TYPE == 1 // Ethernet Shield Card Selected
|
||||
|
||||
#if COMM_INTERFACE == 1
|
||||
#define COMM_SHIELD_NAME "ARDUINO-CC ETHERNET SHIELD (WIZNET 5100)"
|
||||
#include <Ethernet.h> // built-in Arduino.cc library
|
||||
|
||||
#elif COMM_INTERFACE == 2
|
||||
#define COMM_SHIELD_NAME "ARDUINO-ORG ETHERNET-2 SHIELD (WIZNET 5500)"
|
||||
#include <Ethernet2.h> // https://github.com/arduino-org/Arduino
|
||||
|
||||
#elif COMM_INTERFACE == 3
|
||||
#define COMM_SHIELD_NAME "SEEED STUDIO ETHERNET SHIELD (WIZNET 5200)"
|
||||
#include <EthernetV2_0.h> // https://github.com/Seeed-Studio/Ethernet_Shield_W5200
|
||||
|
||||
#endif
|
||||
|
||||
extern EthernetServer INTERFACE;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
58
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Config.h
Normal file
58
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Config.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/**********************************************************************
|
||||
|
||||
Config.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DEFINE MOTOR_SHIELD_TYPE ACCORDING TO THE FOLLOWING TABLE:
|
||||
//
|
||||
// 0 = ARDUINO MOTOR SHIELD (MAX 18V/2A PER CHANNEL)
|
||||
// 1 = POLOLU MC33926 MOTOR SHIELD (MAX 28V/3A PER CHANNEL)
|
||||
|
||||
#define MOTOR_SHIELD_TYPE 0
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DEFINE NUMBER OF MAIN TRACK REGISTER
|
||||
|
||||
#define MAX_MAIN_REGISTERS 12
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DEFINE COMMUNICATIONS INTERFACE
|
||||
//
|
||||
// 0 = Built-in Serial Port
|
||||
// 1 = Arduino.cc Ethernet/SD-Card Shield
|
||||
// 2 = Arduino.org Ethernet/SD-Card Shield
|
||||
// 3 = Seeed Studio Ethernet/SD-Card Shield W5200
|
||||
|
||||
#define COMM_INTERFACE 0
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DEFINE STATIC IP ADDRESS *OR* COMMENT OUT TO USE DHCP
|
||||
//
|
||||
|
||||
//#define IP_ADDRESS { 192, 168, 1, 200 }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DEFINE PORT TO USE FOR ETHERNET COMMUNICATIONS INTERFACE
|
||||
//
|
||||
|
||||
#define ETHERNET_PORT 2560
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DEFINE MAC ADDRESS ARRAY FOR ETHERNET COMMUNICATIONS INTERFACE
|
||||
//
|
||||
|
||||
#define MAC_ADDRESS { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**********************************************************************
|
||||
|
||||
CurrentMonitor.cpp
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "DCCpp_Uno.h"
|
||||
#include "CurrentMonitor.h"
|
||||
#include "Comm.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CurrentMonitor::CurrentMonitor(int pin, char *msg){
|
||||
this->pin=pin;
|
||||
this->msg=msg;
|
||||
current=0;
|
||||
} // CurrentMonitor::CurrentMonitor
|
||||
|
||||
boolean CurrentMonitor::checkTime(){
|
||||
if(millis()-sampleTime<CURRENT_SAMPLE_TIME) // no need to check current yet
|
||||
return(false);
|
||||
sampleTime=millis(); // note millis() uses TIMER-0. For UNO, we change the scale on Timer-0. For MEGA we do not. This means CURENT_SAMPLE_TIME is different for UNO then MEGA
|
||||
return(true);
|
||||
} // CurrentMonitor::checkTime
|
||||
|
||||
void CurrentMonitor::check(){
|
||||
current=analogRead(pin)*CURRENT_SAMPLE_SMOOTHING+current*(1.0-CURRENT_SAMPLE_SMOOTHING); // compute new exponentially-smoothed current
|
||||
if(current>CURRENT_SAMPLE_MAX && digitalRead(SIGNAL_ENABLE_PIN_PROG)==HIGH){ // current overload and Prog Signal is on (or could have checked Main Signal, since both are always on or off together)
|
||||
digitalWrite(SIGNAL_ENABLE_PIN_PROG,LOW); // disable both Motor Shield Channels
|
||||
digitalWrite(SIGNAL_ENABLE_PIN_MAIN,LOW); // regardless of which caused current overload
|
||||
INTERFACE.print(msg); // print corresponding error message
|
||||
}
|
||||
} // CurrentMonitor::check
|
||||
|
||||
long int CurrentMonitor::sampleTime=0;
|
||||
|
||||
35
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/CurrentMonitor.h
Normal file
35
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/CurrentMonitor.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**********************************************************************
|
||||
|
||||
CurrentMonitor.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef CurrentMonitor_h
|
||||
#define CurrentMonitor_h
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define CURRENT_SAMPLE_SMOOTHING 0.01
|
||||
#define CURRENT_SAMPLE_MAX 300
|
||||
|
||||
#ifdef ARDUINO_AVR_UNO // Configuration for UNO
|
||||
#define CURRENT_SAMPLE_TIME 10
|
||||
#else // Configuration for MEGA
|
||||
#define CURRENT_SAMPLE_TIME 1
|
||||
#endif
|
||||
|
||||
struct CurrentMonitor{
|
||||
static long int sampleTime;
|
||||
int pin;
|
||||
float current;
|
||||
char *msg;
|
||||
CurrentMonitor(int, char *);
|
||||
static boolean checkTime();
|
||||
void check();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
135
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/DCCpp_Uno.h
Normal file
135
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/DCCpp_Uno.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/**********************************************************************
|
||||
|
||||
DCCpp_Uno.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#ifndef DCCpp_Uno_h
|
||||
#define DCCpp_Uno_h
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// RELEASE VERSION
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define VERSION "1.2.1+"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// AUTO-SELECT ARDUINO BOARD
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef ARDUINO_AVR_MEGA // is using Mega 1280, define as Mega 2560 (pinouts and functionality are identical)
|
||||
#define ARDUINO_AVR_MEGA2560
|
||||
#endif
|
||||
|
||||
#if defined ARDUINO_AVR_UNO
|
||||
|
||||
#define ARDUINO_TYPE "UNO"
|
||||
|
||||
#define DCC_SIGNAL_PIN_MAIN 10 // Ardunio Uno - uses OC1B
|
||||
#define DCC_SIGNAL_PIN_PROG 5 // Arduino Uno - uses OC0B
|
||||
|
||||
#if COMM_INTERFACE != 0 // Serial was not selected
|
||||
|
||||
#error CANNOT COMPILE - DCC++ FOR THE UNO CAN ONLY USE SERIAL COMMUNICATION - PLEASE SELECT THIS IN THE CONFIG FILE
|
||||
|
||||
#endif
|
||||
|
||||
#elif defined ARDUINO_AVR_MEGA2560
|
||||
|
||||
#define ARDUINO_TYPE "MEGA"
|
||||
|
||||
#define DCC_SIGNAL_PIN_MAIN 12 // Arduino Mega - uses OC1B
|
||||
#define DCC_SIGNAL_PIN_PROG 2 // Arduino Mega - uses OC3B
|
||||
|
||||
#else
|
||||
|
||||
#error CANNOT COMPILE - DCC++ ONLY WORKS WITH AN ARDUINO UNO OR AN ARDUINO MEGA 1280/2560
|
||||
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// SELECT MOTOR SHIELD
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if MOTOR_SHIELD_TYPE == 0
|
||||
|
||||
#define MOTOR_SHIELD_NAME "ARDUINO MOTOR SHIELD"
|
||||
|
||||
#define SIGNAL_ENABLE_PIN_MAIN 3
|
||||
#define SIGNAL_ENABLE_PIN_PROG 11
|
||||
|
||||
#define CURRENT_MONITOR_PIN_MAIN A0
|
||||
#define CURRENT_MONITOR_PIN_PROG A1
|
||||
|
||||
#define DIRECTION_MOTOR_CHANNEL_PIN_A 12
|
||||
#define DIRECTION_MOTOR_CHANNEL_PIN_B 13
|
||||
|
||||
#elif MOTOR_SHIELD_TYPE == 1
|
||||
|
||||
#define MOTOR_SHIELD_NAME "POLOLU MC33926 MOTOR SHIELD"
|
||||
|
||||
#define SIGNAL_ENABLE_PIN_MAIN 9
|
||||
#define SIGNAL_ENABLE_PIN_PROG 11
|
||||
|
||||
#define CURRENT_MONITOR_PIN_MAIN A0
|
||||
#define CURRENT_MONITOR_PIN_PROG A1
|
||||
|
||||
#define DIRECTION_MOTOR_CHANNEL_PIN_A 7
|
||||
#define DIRECTION_MOTOR_CHANNEL_PIN_B 8
|
||||
|
||||
#else
|
||||
|
||||
#error CANNOT COMPILE - PLEASE SELECT A PROPER MOTOR SHIELD TYPE
|
||||
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// SELECT COMMUNICATION INTERACE
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if COMM_INTERFACE == 0
|
||||
|
||||
#define COMM_TYPE 0
|
||||
#define INTERFACE Serial
|
||||
|
||||
#elif (COMM_INTERFACE==1) || (COMM_INTERFACE==2) || (COMM_INTERFACE==3)
|
||||
|
||||
#define COMM_TYPE 1
|
||||
#define INTERFACE eServer
|
||||
#define SDCARD_CS 4
|
||||
|
||||
#else
|
||||
|
||||
#error CANNOT COMPILE - Please select a proper value for COMM_INTERFACE in CONFIG.H file
|
||||
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// SET WHETHER TO SHOW PACKETS - DIAGNOSTIC MODE ONLY
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// If SHOW_PACKETS is set to 1, then for select main operations track commands that modify an internal DCC packet register,
|
||||
// if printFlag for that command is also set to 1, DCC++ BASE STATION will additionally return the
|
||||
// DCC packet contents of the modified register in the following format:
|
||||
|
||||
// <* REG: B1 B2 ... Bn CSUM / REPEAT>
|
||||
//
|
||||
// REG: the number of the main operations track packet register that was modified
|
||||
// B1: the first hexidecimal byte of the DCC packet
|
||||
// B2: the second hexidecimal byte of the DCC packet
|
||||
// Bn: the nth hexidecimal byte of the DCC packet
|
||||
// CSUM: a checksum byte that is required to be the final byte in any DCC packet
|
||||
// REPEAT: the number of times the DCC packet was re-transmitted to the tracks after its iniital transmission
|
||||
|
||||
#define SHOW_PACKETS 0 // set to zero to disable printing of every packet for select main operations track commands
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
563
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/DCCpp_Uno.ino
Normal file
563
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/DCCpp_Uno.ino
Normal file
@@ -0,0 +1,563 @@
|
||||
/**********************************************************************
|
||||
|
||||
DCC++ BASE STATION
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see http://www.gnu.org/licenses
|
||||
|
||||
**********************************************************************/
|
||||
/**********************************************************************
|
||||
|
||||
DCC++ BASE STATION is a C++ program written for the Arduino Uno and Arduino Mega
|
||||
using the Arduino IDE 1.6.6.
|
||||
|
||||
It allows a standard Arduino Uno or Mega with an Arduino Motor Shield (as well as others)
|
||||
to be used as a fully-functioning digital command and control (DCC) base station
|
||||
for controlling model train layouts that conform to current National Model
|
||||
Railroad Association (NMRA) DCC standards.
|
||||
|
||||
This version of DCC++ BASE STATION supports:
|
||||
|
||||
* 2-byte and 4-byte locomotive addressing
|
||||
* Simultaneous control of multiple locomotives
|
||||
* 128-step speed throttling
|
||||
* Cab functions F0-F28
|
||||
* Activate/de-activate accessory functions using 512 addresses, each with 4 sub-addresses
|
||||
- includes optional functionailty to monitor and store of the direction of any connected turnouts
|
||||
* Programming on the Main Operations Track
|
||||
- write configuration variable bytes
|
||||
- set/clear specific configuration variable bits
|
||||
* Programming on the Programming Track
|
||||
- write configuration variable bytes
|
||||
- set/clear specific configuration variable bits
|
||||
- read configuration variable bytes
|
||||
|
||||
DCC++ BASE STATION is controlled with simple text commands received via
|
||||
the Arduino's serial interface. Users can type these commands directly
|
||||
into the Arduino IDE Serial Monitor, or can send such commands from another
|
||||
device or computer program.
|
||||
|
||||
When compiled for the Arduino Mega, an Ethernet Shield can be used for network
|
||||
communications instead of using serial communications.
|
||||
|
||||
DCC++ CONTROLLER, available separately under a similar open-source
|
||||
license, is a Java program written using the Processing library and Processing IDE
|
||||
that provides a complete and configurable graphic interface to control model train layouts
|
||||
via the DCC++ BASE STATION.
|
||||
|
||||
With the exception of a standard 15V power supply that can be purchased in
|
||||
any electronics store, no additional hardware is required.
|
||||
|
||||
Neither DCC++ BASE STATION nor DCC++ CONTROLLER use any known proprietary or
|
||||
commercial hardware, software, interfaces, specifications, or methods related
|
||||
to the control of model trains using NMRA DCC standards. Both programs are wholly
|
||||
original, developed by the author, and are not derived from any known commercial,
|
||||
free, or open-source model railroad control packages by any other parties.
|
||||
|
||||
However, DCC++ BASE STATION and DCC++ CONTROLLER do heavily rely on the IDEs and
|
||||
embedded libraries associated with Arduino and Processing. Tremendous thanks to those
|
||||
responsible for these terrific open-source initiatives that enable programs like
|
||||
DCC++ to be developed and distributed in the same fashion.
|
||||
|
||||
REFERENCES:
|
||||
|
||||
NMRA DCC Standards: http://www.nmra.org/index-nmra-standards-and-recommended-practices
|
||||
Arduino: http://www.arduino.cc/
|
||||
Processing: http://processing.org/
|
||||
GNU General Public License: http://opensource.org/licenses/GPL-3.0
|
||||
|
||||
BRIEF NOTES ON THE THEORY AND OPERATION OF DCC++ BASE STATION:
|
||||
|
||||
DCC++ BASE STATION for the Uno configures the OC0B interrupt pin associated with Timer 0,
|
||||
and the OC1B interupt pin associated with Timer 1, to generate separate 0-5V
|
||||
unipolar signals that each properly encode zero and one bits conforming with
|
||||
DCC timing standards. When compiled for the Mega, DCC++ BASE STATION uses OC3B instead of OC0B.
|
||||
|
||||
Series of DCC bit streams are bundled into Packets that each form the basis of
|
||||
a standard DCC instruction. Packets are stored in Packet Registers that contain
|
||||
methods for updating and queuing according to text commands sent by the user
|
||||
(or another program) over the serial interface. There is one set of registers that controls
|
||||
the main operations track and one that controls the programming track.
|
||||
|
||||
For the main operations track, packets to store cab throttle settings are stored in
|
||||
registers numbered 1 through MAX_MAIN_REGISTERS (as defined in DCCpp_Uno.h).
|
||||
It is generally considered good practice to continuously send throttle control packets
|
||||
to every cab so that if an engine should momentarily lose electrical connectivity with the tracks,
|
||||
it will very quickly receive another throttle control signal as soon as connectivity is
|
||||
restored (such as when a trin passes over rough portion of track or the frog of a turnout).
|
||||
|
||||
DCC++ Base Station therefore sequentially loops through each main operations track packet regsiter
|
||||
that has been loaded with a throttle control setting for a given cab. For each register, it
|
||||
transmits the appropriate DCC packet bits to the track, then moves onto the next register
|
||||
without any pausing to ensure continuous bi-polar power is being provided to the tracks.
|
||||
Updates to the throttle setting stored in any given packet register are done in a double-buffered
|
||||
fashion and the sequencer is pointed to that register immediately after being changes so that updated DCC bits
|
||||
can be transmitted to the appropriate cab without delay or any interruption in the bi-polar power signal.
|
||||
The cabs identified in each stored throttle setting should be unique across registers. If two registers
|
||||
contain throttle setting for the same cab, the throttle in the engine will oscillate between the two,
|
||||
which is probably not a desireable outcome.
|
||||
|
||||
For both the main operations track and the programming track there is also a special packet register with id=0
|
||||
that is used to store all other DCC packets that do not require continious transmittal to the tracks.
|
||||
This includes DCC packets to control decoder functions, set accessory decoders, and read and write Configuration Variables.
|
||||
It is common practice that transmittal of these one-time packets is usually repeated a few times to ensure
|
||||
proper receipt by the receiving decoder. DCC decoders are designed to listen for repeats of the same packet
|
||||
and provided there are no other packets received in between the repeats, the DCC decoder will not repeat the action itself.
|
||||
Some DCC decoders actually require receipt of sequential multiple identical one-time packets as a way of
|
||||
verifying proper transmittal before acting on the instructions contained in those packets
|
||||
|
||||
An Arduino Motor Shield (or similar), powered by a standard 15V DC power supply and attached
|
||||
on top of the Arduino Uno or Mega, is used to transform the 0-5V DCC logic signals
|
||||
produced by the Uno's Timer interrupts into proper 0-15V bi-polar DCC signals.
|
||||
|
||||
This is accomplished on the Uno by using one small jumper wire to connect the Uno's OC1B output (pin 10)
|
||||
to the Motor Shield's DIRECTION A input (pin 12), and another small jumper wire to connect
|
||||
the Uno's OC0B output (pin 5) to the Motor Shield's DIRECTION B input (pin 13).
|
||||
|
||||
For the Mega, the OC1B output is produced directly on pin 12, so no jumper is needed to connect to the
|
||||
Motor Shield's DIRECTION A input. However, one small jumper wire is needed to connect the Mega's OC3B output (pin 2)
|
||||
to the Motor Shield's DIRECTION B input (pin 13).
|
||||
|
||||
Other Motor Shields may require different sets of jumper or configurations (see Config.h and DCCpp_Uno.h for details).
|
||||
|
||||
When configured as such, the CHANNEL A and CHANNEL B outputs of the Motor Shield may be
|
||||
connected directly to the tracks. This software assumes CHANNEL A is connected
|
||||
to the Main Operations Track, and CHANNEL B is connected to the Programming Track.
|
||||
|
||||
DCC++ BASE STATION in split into multiple modules, each with its own header file:
|
||||
|
||||
DCCpp_Uno: declares required global objects and contains initial Arduino setup()
|
||||
and Arduino loop() functions, as well as interrput code for OC0B and OC1B.
|
||||
Also includes declarations of optional array of Turn-Outs and optional array of Sensors
|
||||
|
||||
SerialCommand: contains methods to read and interpret text commands from the serial line,
|
||||
process those instructions, and, if necessary call appropriate Packet RegisterList methods
|
||||
to update either the Main Track or Programming Track Packet Registers
|
||||
|
||||
PacketRegister: contains methods to load, store, and update Packet Registers with DCC instructions
|
||||
|
||||
CurrentMonitor: contains methods to separately monitor and report the current drawn from CHANNEL A and
|
||||
CHANNEL B of the Arduino Motor Shield's, and shut down power if a short-circuit overload
|
||||
is detected
|
||||
|
||||
Accessories: contains methods to operate and store the status of any optionally-defined turnouts controlled
|
||||
by a DCC stationary accessory decoder.
|
||||
|
||||
Sensor: contains methods to monitor and report on the status of optionally-defined infrared
|
||||
sensors embedded in the Main Track and connected to various pins on the Arudino Uno
|
||||
|
||||
Outputs: contains methods to configure one or more Arduino pins as an output for your own custom use
|
||||
|
||||
EEStore: contains methods to store, update, and load various DCC settings and status
|
||||
(e.g. the states of all defined turnouts) in the EEPROM for recall after power-up
|
||||
|
||||
DCC++ BASE STATION is configured through the Config.h file that contains all user-definable parameters
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
// BEGIN BY INCLUDING THE HEADER FILES FOR EACH MODULE
|
||||
|
||||
#include "DCCpp_Uno.h"
|
||||
#include "PacketRegister.h"
|
||||
#include "CurrentMonitor.h"
|
||||
#include "Sensor.h"
|
||||
#include "SerialCommand.h"
|
||||
#include "Accessories.h"
|
||||
#include "EEStore.h"
|
||||
#include "Config.h"
|
||||
#include "Comm.h"
|
||||
|
||||
void showConfiguration();
|
||||
|
||||
// SET UP COMMUNICATIONS INTERFACE - FOR STANDARD SERIAL, NOTHING NEEDS TO BE DONE
|
||||
|
||||
#if COMM_TYPE == 1
|
||||
byte mac[] = MAC_ADDRESS; // Create MAC address (to be used for DHCP when initializing server)
|
||||
EthernetServer INTERFACE(ETHERNET_PORT); // Create and instance of an EnternetServer
|
||||
#endif
|
||||
|
||||
// NEXT DECLARE GLOBAL OBJECTS TO PROCESS AND STORE DCC PACKETS AND MONITOR TRACK CURRENTS.
|
||||
// NOTE REGISTER LISTS MUST BE DECLARED WITH "VOLATILE" QUALIFIER TO ENSURE THEY ARE PROPERLY UPDATED BY INTERRUPT ROUTINES
|
||||
|
||||
volatile RegisterList mainRegs(MAX_MAIN_REGISTERS); // create list of registers for MAX_MAIN_REGISTER Main Track Packets
|
||||
volatile RegisterList progRegs(2); // create a shorter list of only two registers for Program Track Packets
|
||||
|
||||
CurrentMonitor mainMonitor(CURRENT_MONITOR_PIN_MAIN,"<p2>"); // create monitor for current on Main Track
|
||||
CurrentMonitor progMonitor(CURRENT_MONITOR_PIN_PROG,"<p3>"); // create monitor for current on Program Track
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MAIN ARDUINO LOOP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
SerialCommand::process(); // check for, and process, and new serial commands
|
||||
|
||||
if(CurrentMonitor::checkTime()){ // if sufficient time has elapsed since last update, check current draw on Main and Program Tracks
|
||||
mainMonitor.check();
|
||||
progMonitor.check();
|
||||
}
|
||||
|
||||
Sensor::check(); // check sensors for activate/de-activate
|
||||
|
||||
} // loop
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// INITIAL SETUP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setup(){
|
||||
|
||||
Serial.begin(115200); // configure serial interface
|
||||
Serial.flush();
|
||||
|
||||
#ifdef SDCARD_CS
|
||||
pinMode(SDCARD_CS,OUTPUT);
|
||||
digitalWrite(SDCARD_CS,HIGH); // Deselect the SD card
|
||||
#endif
|
||||
|
||||
EEStore::init(); // initialize and load Turnout and Sensor definitions stored in EEPROM
|
||||
|
||||
pinMode(A5,INPUT); // if pin A5 is grounded upon start-up, print system configuration and halt
|
||||
digitalWrite(A5,HIGH);
|
||||
if(!digitalRead(A5))
|
||||
showConfiguration();
|
||||
|
||||
Serial.print("<iDCC++ BASE STATION FOR ARDUINO "); // Print Status to Serial Line regardless of COMM_TYPE setting so use can open Serial Monitor and check configurtion
|
||||
Serial.print(ARDUINO_TYPE);
|
||||
Serial.print(" / ");
|
||||
Serial.print(MOTOR_SHIELD_NAME);
|
||||
Serial.print(": V-");
|
||||
Serial.print(VERSION);
|
||||
Serial.print(" / ");
|
||||
Serial.print(__DATE__);
|
||||
Serial.print(" ");
|
||||
Serial.print(__TIME__);
|
||||
Serial.print(">");
|
||||
|
||||
#if COMM_TYPE == 1
|
||||
#ifdef IP_ADDRESS
|
||||
Ethernet.begin(mac,IP_ADDRESS); // Start networking using STATIC IP Address
|
||||
#else
|
||||
Ethernet.begin(mac); // Start networking using DHCP to get an IP Address
|
||||
#endif
|
||||
INTERFACE.begin();
|
||||
#endif
|
||||
|
||||
SerialCommand::init(&mainRegs, &progRegs, &mainMonitor); // create structure to read and parse commands from serial line
|
||||
|
||||
Serial.print("<N");
|
||||
Serial.print(COMM_TYPE);
|
||||
Serial.print(": ");
|
||||
|
||||
#if COMM_TYPE == 0
|
||||
Serial.print("SERIAL>");
|
||||
#elif COMM_TYPE == 1
|
||||
Serial.print(Ethernet.localIP());
|
||||
Serial.print(">");
|
||||
#endif
|
||||
|
||||
// CONFIGURE TIMER_1 TO OUTPUT 50% DUTY CYCLE DCC SIGNALS ON OC1B INTERRUPT PINS
|
||||
|
||||
// Direction Pin for Motor Shield Channel A - MAIN OPERATIONS TRACK
|
||||
// Controlled by Arduino 16-bit TIMER 1 / OC1B Interrupt Pin
|
||||
// Values for 16-bit OCR1A and OCR1B registers calibrated for 1:1 prescale at 16 MHz clock frequency
|
||||
// Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with exactly 50% duty cycle
|
||||
|
||||
#define DCC_ZERO_BIT_TOTAL_DURATION_TIMER1 3199
|
||||
#define DCC_ZERO_BIT_PULSE_DURATION_TIMER1 1599
|
||||
|
||||
#define DCC_ONE_BIT_TOTAL_DURATION_TIMER1 1855
|
||||
#define DCC_ONE_BIT_PULSE_DURATION_TIMER1 927
|
||||
|
||||
pinMode(DIRECTION_MOTOR_CHANNEL_PIN_A,INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
|
||||
digitalWrite(DIRECTION_MOTOR_CHANNEL_PIN_A,LOW);
|
||||
|
||||
pinMode(DCC_SIGNAL_PIN_MAIN, OUTPUT); // THIS ARDUINO OUPUT PIN MUST BE PHYSICALLY CONNECTED TO THE PIN FOR DIRECTION-A OF MOTOR CHANNEL-A
|
||||
|
||||
bitSet(TCCR1A,WGM10); // set Timer 1 to FAST PWM, with TOP=OCR1A
|
||||
bitSet(TCCR1A,WGM11);
|
||||
bitSet(TCCR1B,WGM12);
|
||||
bitSet(TCCR1B,WGM13);
|
||||
|
||||
bitSet(TCCR1A,COM1B1); // set Timer 1, OC1B (pin 10/UNO, pin 12/MEGA) to inverting toggle (actual direction is arbitrary)
|
||||
bitSet(TCCR1A,COM1B0);
|
||||
|
||||
bitClear(TCCR1B,CS12); // set Timer 1 prescale=1
|
||||
bitClear(TCCR1B,CS11);
|
||||
bitSet(TCCR1B,CS10);
|
||||
|
||||
OCR1A=DCC_ONE_BIT_TOTAL_DURATION_TIMER1;
|
||||
OCR1B=DCC_ONE_BIT_PULSE_DURATION_TIMER1;
|
||||
|
||||
pinMode(SIGNAL_ENABLE_PIN_MAIN,OUTPUT); // master enable for motor channel A
|
||||
|
||||
mainRegs.loadPacket(1,RegisterList::idlePacket,2,0); // load idle packet into register 1
|
||||
|
||||
bitSet(TIMSK1,OCIE1B); // enable interrupt vector for Timer 1 Output Compare B Match (OCR1B)
|
||||
|
||||
// CONFIGURE EITHER TIMER_0 (UNO) OR TIMER_3 (MEGA) TO OUTPUT 50% DUTY CYCLE DCC SIGNALS ON OC0B (UNO) OR OC3B (MEGA) INTERRUPT PINS
|
||||
|
||||
#ifdef ARDUINO_AVR_UNO // Configuration for UNO
|
||||
|
||||
// Directon Pin for Motor Shield Channel B - PROGRAMMING TRACK
|
||||
// Controlled by Arduino 8-bit TIMER 0 / OC0B Interrupt Pin
|
||||
// Values for 8-bit OCR0A and OCR0B registers calibrated for 1:64 prescale at 16 MHz clock frequency
|
||||
// Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with as-close-as-possible to 50% duty cycle
|
||||
|
||||
#define DCC_ZERO_BIT_TOTAL_DURATION_TIMER0 49
|
||||
#define DCC_ZERO_BIT_PULSE_DURATION_TIMER0 24
|
||||
|
||||
#define DCC_ONE_BIT_TOTAL_DURATION_TIMER0 28
|
||||
#define DCC_ONE_BIT_PULSE_DURATION_TIMER0 14
|
||||
|
||||
pinMode(DIRECTION_MOTOR_CHANNEL_PIN_B,INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
|
||||
digitalWrite(DIRECTION_MOTOR_CHANNEL_PIN_B,LOW);
|
||||
|
||||
pinMode(DCC_SIGNAL_PIN_PROG,OUTPUT); // THIS ARDUINO OUTPUT PIN MUST BE PHYSICALLY CONNECTED TO THE PIN FOR DIRECTION-B OF MOTOR CHANNEL-B
|
||||
|
||||
bitSet(TCCR0A,WGM00); // set Timer 0 to FAST PWM, with TOP=OCR0A
|
||||
bitSet(TCCR0A,WGM01);
|
||||
bitSet(TCCR0B,WGM02);
|
||||
|
||||
bitSet(TCCR0A,COM0B1); // set Timer 0, OC0B (pin 5) to inverting toggle (actual direction is arbitrary)
|
||||
bitSet(TCCR0A,COM0B0);
|
||||
|
||||
bitClear(TCCR0B,CS02); // set Timer 0 prescale=64
|
||||
bitSet(TCCR0B,CS01);
|
||||
bitSet(TCCR0B,CS00);
|
||||
|
||||
OCR0A=DCC_ONE_BIT_TOTAL_DURATION_TIMER0;
|
||||
OCR0B=DCC_ONE_BIT_PULSE_DURATION_TIMER0;
|
||||
|
||||
pinMode(SIGNAL_ENABLE_PIN_PROG,OUTPUT); // master enable for motor channel B
|
||||
|
||||
progRegs.loadPacket(1,RegisterList::idlePacket,2,0); // load idle packet into register 1
|
||||
|
||||
bitSet(TIMSK0,OCIE0B); // enable interrupt vector for Timer 0 Output Compare B Match (OCR0B)
|
||||
|
||||
#else // Configuration for MEGA
|
||||
|
||||
// Directon Pin for Motor Shield Channel B - PROGRAMMING TRACK
|
||||
// Controlled by Arduino 16-bit TIMER 3 / OC3B Interrupt Pin
|
||||
// Values for 16-bit OCR3A and OCR3B registers calibrated for 1:1 prescale at 16 MHz clock frequency
|
||||
// Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with exactly 50% duty cycle
|
||||
|
||||
#define DCC_ZERO_BIT_TOTAL_DURATION_TIMER3 3199
|
||||
#define DCC_ZERO_BIT_PULSE_DURATION_TIMER3 1599
|
||||
|
||||
#define DCC_ONE_BIT_TOTAL_DURATION_TIMER3 1855
|
||||
#define DCC_ONE_BIT_PULSE_DURATION_TIMER3 927
|
||||
|
||||
pinMode(DIRECTION_MOTOR_CHANNEL_PIN_B,INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
|
||||
digitalWrite(DIRECTION_MOTOR_CHANNEL_PIN_B,LOW);
|
||||
|
||||
pinMode(DCC_SIGNAL_PIN_PROG,OUTPUT); // THIS ARDUINO OUTPUT PIN MUST BE PHYSICALLY CONNECTED TO THE PIN FOR DIRECTION-B OF MOTOR CHANNEL-B
|
||||
|
||||
bitSet(TCCR3A,WGM30); // set Timer 3 to FAST PWM, with TOP=OCR3A
|
||||
bitSet(TCCR3A,WGM31);
|
||||
bitSet(TCCR3B,WGM32);
|
||||
bitSet(TCCR3B,WGM33);
|
||||
|
||||
bitSet(TCCR3A,COM3B1); // set Timer 3, OC3B (pin 2) to inverting toggle (actual direction is arbitrary)
|
||||
bitSet(TCCR3A,COM3B0);
|
||||
|
||||
bitClear(TCCR3B,CS32); // set Timer 3 prescale=1
|
||||
bitClear(TCCR3B,CS31);
|
||||
bitSet(TCCR3B,CS30);
|
||||
|
||||
OCR3A=DCC_ONE_BIT_TOTAL_DURATION_TIMER3;
|
||||
OCR3B=DCC_ONE_BIT_PULSE_DURATION_TIMER3;
|
||||
|
||||
pinMode(SIGNAL_ENABLE_PIN_PROG,OUTPUT); // master enable for motor channel B
|
||||
|
||||
progRegs.loadPacket(1,RegisterList::idlePacket,2,0); // load idle packet into register 1
|
||||
|
||||
bitSet(TIMSK3,OCIE3B); // enable interrupt vector for Timer 3 Output Compare B Match (OCR3B)
|
||||
|
||||
#endif
|
||||
|
||||
} // setup
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// DEFINE THE INTERRUPT LOGIC THAT GENERATES THE DCC SIGNAL
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The code below will be called every time an interrupt is triggered on OCNB, where N can be 0 or 1.
|
||||
// It is designed to read the current bit of the current register packet and
|
||||
// updates the OCNA and OCNB counters of Timer-N to values that will either produce
|
||||
// a long (200 microsecond) pulse, or a short (116 microsecond) pulse, which respectively represent
|
||||
// DCC ZERO and DCC ONE bits.
|
||||
|
||||
// These are hardware-driven interrupts that will be called automatically when triggered regardless of what
|
||||
// DCC++ BASE STATION was otherwise processing. But once inside the interrupt, all other interrupt routines are temporarily diabled.
|
||||
// Since a short pulse only lasts for 116 microseconds, and there are TWO separate interrupts
|
||||
// (one for Main Track Registers and one for the Program Track Registers), the interrupt code must complete
|
||||
// in much less than 58 microsends, otherwise there would be no time for the rest of the program to run. Worse, if the logic
|
||||
// of the interrupt code ever caused it to run longer than 58 microsends, an interrupt trigger would be missed, the OCNA and OCNB
|
||||
// registers would not be updated, and the net effect would be a DCC signal that keeps sending the same DCC bit repeatedly until the
|
||||
// interrupt code completes and can be called again.
|
||||
|
||||
// A significant portion of this entire program is designed to do as much of the heavy processing of creating a properly-formed
|
||||
// DCC bit stream upfront, so that the interrupt code below can be as simple and efficient as possible.
|
||||
|
||||
// Note that we need to create two very similar copies of the code --- one for the Main Track OC1B interrupt and one for the
|
||||
// Programming Track OCOB interrupt. But rather than create a generic function that incurrs additional overhead, we create a macro
|
||||
// that can be invoked with proper paramters for each interrupt. This slightly increases the size of the code base by duplicating
|
||||
// some of the logic for each interrupt, but saves additional time.
|
||||
|
||||
// As structured, the interrupt code below completes at an average of just under 6 microseconds with a worse-case of just under 11 microseconds
|
||||
// when a new register is loaded and the logic needs to switch active register packet pointers.
|
||||
|
||||
// THE INTERRUPT CODE MACRO: R=REGISTER LIST (mainRegs or progRegs), and N=TIMER (0 or 1)
|
||||
|
||||
#define DCC_SIGNAL(R,N) \
|
||||
if(R.currentBit==R.currentReg->activePacket->nBits){ /* IF no more bits in this DCC Packet */ \
|
||||
R.currentBit=0; /* reset current bit pointer and determine which Register and Packet to process next--- */ \
|
||||
if(R.nRepeat>0 && R.currentReg==R.reg){ /* IF current Register is first Register AND should be repeated */ \
|
||||
R.nRepeat--; /* decrement repeat count; result is this same Packet will be repeated */ \
|
||||
} else if(R.nextReg!=NULL){ /* ELSE IF another Register has been updated */ \
|
||||
R.currentReg=R.nextReg; /* update currentReg to nextReg */ \
|
||||
R.nextReg=NULL; /* reset nextReg to NULL */ \
|
||||
R.tempPacket=R.currentReg->activePacket; /* flip active and update Packets */ \
|
||||
R.currentReg->activePacket=R.currentReg->updatePacket; \
|
||||
R.currentReg->updatePacket=R.tempPacket; \
|
||||
} else{ /* ELSE simply move to next Register */ \
|
||||
if(R.currentReg==R.maxLoadedReg) /* BUT IF this is last Register loaded */ \
|
||||
R.currentReg=R.reg; /* first reset currentReg to base Register, THEN */ \
|
||||
R.currentReg++; /* increment current Register (note this logic causes Register[0] to be skipped when simply cycling through all Registers) */ \
|
||||
} /* END-ELSE */ \
|
||||
} /* END-IF: currentReg, activePacket, and currentBit should now be properly set to point to next DCC bit */ \
|
||||
\
|
||||
if(R.currentReg->activePacket->buf[R.currentBit/8] & R.bitMask[R.currentBit%8]){ /* IF bit is a ONE */ \
|
||||
OCR ## N ## A=DCC_ONE_BIT_TOTAL_DURATION_TIMER ## N; /* set OCRA for timer N to full cycle duration of DCC ONE bit */ \
|
||||
OCR ## N ## B=DCC_ONE_BIT_PULSE_DURATION_TIMER ## N; /* set OCRB for timer N to half cycle duration of DCC ONE but */ \
|
||||
} else{ /* ELSE it is a ZERO */ \
|
||||
OCR ## N ## A=DCC_ZERO_BIT_TOTAL_DURATION_TIMER ## N; /* set OCRA for timer N to full cycle duration of DCC ZERO bit */ \
|
||||
OCR ## N ## B=DCC_ZERO_BIT_PULSE_DURATION_TIMER ## N; /* set OCRB for timer N to half cycle duration of DCC ZERO bit */ \
|
||||
} /* END-ELSE */ \
|
||||
\
|
||||
R.currentBit++; /* point to next bit in current Packet */
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOW USE THE ABOVE MACRO TO CREATE THE CODE FOR EACH INTERRUPT
|
||||
|
||||
ISR(TIMER1_COMPB_vect){ // set interrupt service for OCR1B of TIMER-1 which flips direction bit of Motor Shield Channel A controlling Main Track
|
||||
DCC_SIGNAL(mainRegs,1)
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_AVR_UNO // Configuration for UNO
|
||||
|
||||
ISR(TIMER0_COMPB_vect){ // set interrupt service for OCR1B of TIMER-0 which flips direction bit of Motor Shield Channel B controlling Prog Track
|
||||
DCC_SIGNAL(progRegs,0)
|
||||
}
|
||||
|
||||
#else // Configuration for MEGA
|
||||
|
||||
ISR(TIMER3_COMPB_vect){ // set interrupt service for OCR3B of TIMER-3 which flips direction bit of Motor Shield Channel B controlling Prog Track
|
||||
DCC_SIGNAL(progRegs,3)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRINT CONFIGURATION INFO TO SERIAL PORT REGARDLESS OF INTERFACE TYPE
|
||||
// - ACTIVATED ON STARTUP IF SHOW_CONFIG_PIN IS TIED HIGH
|
||||
|
||||
void showConfiguration(){
|
||||
|
||||
int mac_address[]=MAC_ADDRESS;
|
||||
|
||||
Serial.print("\n*** DCC++ CONFIGURATION ***\n");
|
||||
|
||||
Serial.print("\nVERSION: ");
|
||||
Serial.print(VERSION);
|
||||
Serial.print("\nCOMPILED: ");
|
||||
Serial.print(__DATE__);
|
||||
Serial.print(" ");
|
||||
Serial.print(__TIME__);
|
||||
|
||||
Serial.print("\nARDUINO: ");
|
||||
Serial.print(ARDUINO_TYPE);
|
||||
|
||||
Serial.print("\n\nMOTOR SHIELD: ");
|
||||
Serial.print(MOTOR_SHIELD_NAME);
|
||||
|
||||
Serial.print("\n\nDCC SIG MAIN: ");
|
||||
Serial.print(DCC_SIGNAL_PIN_MAIN);
|
||||
Serial.print("\n DIRECTION: ");
|
||||
Serial.print(DIRECTION_MOTOR_CHANNEL_PIN_A);
|
||||
Serial.print("\n ENABLE: ");
|
||||
Serial.print(SIGNAL_ENABLE_PIN_MAIN);
|
||||
Serial.print("\n CURRENT: ");
|
||||
Serial.print(CURRENT_MONITOR_PIN_MAIN);
|
||||
|
||||
Serial.print("\n\nDCC SIG PROG: ");
|
||||
Serial.print(DCC_SIGNAL_PIN_PROG);
|
||||
Serial.print("\n DIRECTION: ");
|
||||
Serial.print(DIRECTION_MOTOR_CHANNEL_PIN_B);
|
||||
Serial.print("\n ENABLE: ");
|
||||
Serial.print(SIGNAL_ENABLE_PIN_PROG);
|
||||
Serial.print("\n CURRENT: ");
|
||||
Serial.print(CURRENT_MONITOR_PIN_PROG);
|
||||
|
||||
Serial.print("\n\nNUM TURNOUTS: ");
|
||||
Serial.print(EEStore::eeStore->data.nTurnouts);
|
||||
Serial.print("\n SENSORS: ");
|
||||
Serial.print(EEStore::eeStore->data.nSensors);
|
||||
Serial.print("\n OUTPUTS: ");
|
||||
Serial.print(EEStore::eeStore->data.nOutputs);
|
||||
|
||||
Serial.print("\n\nINTERFACE: ");
|
||||
#if COMM_TYPE == 0
|
||||
Serial.print("SERIAL");
|
||||
#elif COMM_TYPE == 1
|
||||
Serial.print(COMM_SHIELD_NAME);
|
||||
Serial.print("\nMAC ADDRESS: ");
|
||||
for(int i=0;i<5;i++){
|
||||
Serial.print(mac_address[i],HEX);
|
||||
Serial.print(":");
|
||||
}
|
||||
Serial.print(mac_address[5],HEX);
|
||||
Serial.print("\nPORT: ");
|
||||
Serial.print(ETHERNET_PORT);
|
||||
Serial.print("\nIP ADDRESS: ");
|
||||
|
||||
#ifdef IP_ADDRESS
|
||||
Ethernet.begin(mac,IP_ADDRESS); // Start networking using STATIC IP Address
|
||||
#else
|
||||
Ethernet.begin(mac); // Start networking using DHCP to get an IP Address
|
||||
#endif
|
||||
|
||||
Serial.print(Ethernet.localIP());
|
||||
|
||||
#ifdef IP_ADDRESS
|
||||
Serial.print(" (STATIC)");
|
||||
#else
|
||||
Serial.print(" (DHCP)");
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Serial.print("\n\nPROGRAM HALTED - PLEASE RESTART ARDUINO");
|
||||
|
||||
while(true);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
83
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/EEStore.cpp
Normal file
83
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/EEStore.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/**********************************************************************
|
||||
|
||||
EEStore.cpp
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "DCCpp_Uno.h"
|
||||
#include "EEStore.h"
|
||||
#include "Accessories.h"
|
||||
#include "Sensor.h"
|
||||
#include "Outputs.h"
|
||||
#include <EEPROM.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EEStore::init(){
|
||||
|
||||
|
||||
eeStore=(EEStore *)calloc(1,sizeof(EEStore));
|
||||
|
||||
EEPROM.get(0,eeStore->data); // get eeStore data
|
||||
|
||||
if(strncmp(eeStore->data.id,EESTORE_ID,sizeof(EESTORE_ID))!=0){ // check to see that eeStore contains valid DCC++ ID
|
||||
sprintf(eeStore->data.id,EESTORE_ID); // if not, create blank eeStore structure (no turnouts, no sensors) and save it back to EEPROM
|
||||
eeStore->data.nTurnouts=0;
|
||||
eeStore->data.nSensors=0;
|
||||
eeStore->data.nOutputs=0;
|
||||
EEPROM.put(0,eeStore->data);
|
||||
}
|
||||
|
||||
reset(); // set memory pointer to first free EEPROM space
|
||||
Turnout::load(); // load turnout definitions
|
||||
Sensor::load(); // load sensor definitions
|
||||
Output::load(); // load output definitions
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EEStore::clear(){
|
||||
|
||||
sprintf(eeStore->data.id,EESTORE_ID); // create blank eeStore structure (no turnouts, no sensors) and save it back to EEPROM
|
||||
eeStore->data.nTurnouts=0;
|
||||
eeStore->data.nSensors=0;
|
||||
eeStore->data.nOutputs=0;
|
||||
EEPROM.put(0,eeStore->data);
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EEStore::store(){
|
||||
reset();
|
||||
Turnout::store();
|
||||
Sensor::store();
|
||||
Output::store();
|
||||
EEPROM.put(0,eeStore->data);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EEStore::advance(int n){
|
||||
eeAddress+=n;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EEStore::reset(){
|
||||
eeAddress=sizeof(EEStore);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int EEStore::pointer(){
|
||||
return(eeAddress);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EEStore *EEStore::eeStore=NULL;
|
||||
int EEStore::eeAddress=0;
|
||||
|
||||
35
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/EEStore.h
Normal file
35
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/EEStore.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**********************************************************************
|
||||
|
||||
EEStore.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef EEStore_h
|
||||
#define EEStore_h
|
||||
|
||||
#define EESTORE_ID "DCC++"
|
||||
|
||||
struct EEStoreData{
|
||||
char id[sizeof(EESTORE_ID)];
|
||||
int nTurnouts;
|
||||
int nSensors;
|
||||
int nOutputs;
|
||||
};
|
||||
|
||||
struct EEStore{
|
||||
static EEStore *eeStore;
|
||||
EEStoreData data;
|
||||
static int eeAddress;
|
||||
static void init();
|
||||
static void reset();
|
||||
static int pointer();
|
||||
static void advance(int);
|
||||
static void store();
|
||||
static void clear();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
256
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Outputs.cpp
Normal file
256
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Outputs.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
/**********************************************************************
|
||||
|
||||
Outputs.cpp
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
/**********************************************************************
|
||||
|
||||
DCC++ BASE STATION supports optional OUTPUT control of any unused Arduino Pins for custom purposes.
|
||||
Pins can be activited or de-activated. The default is to set ACTIVE pins HIGH and INACTIVE pins LOW.
|
||||
However, this default behavior can be inverted for any pin in which case ACTIVE=LOW and INACTIVE=HIGH.
|
||||
|
||||
Definitions and state (ACTIVE/INACTIVE) for pins are retained in EEPROM and restored on power-up.
|
||||
The default is to set each defined pin to active or inactive according to its restored state.
|
||||
However, the default behavior can be modified so that any pin can be forced to be either active or inactive
|
||||
upon power-up regardless of its previous state before power-down.
|
||||
|
||||
To have this sketch utilize one or more Arduino pins as custom outputs, first define/edit/delete
|
||||
output definitions using the following variation of the "Z" command:
|
||||
|
||||
<Z ID PIN IFLAG>: creates a new output ID, with specified PIN and IFLAG values.
|
||||
if output ID already exists, it is updated with specificed PIN and IFLAG.
|
||||
note: output state will be immediately set to ACTIVE/INACTIVE and pin will be set to HIGH/LOW
|
||||
according to IFLAG value specifcied (see below).
|
||||
returns: <O> if successful and <X> if unsuccessful (e.g. out of memory)
|
||||
|
||||
<Z ID>: deletes definition of output ID
|
||||
returns: <O> if successful and <X> if unsuccessful (e.g. ID does not exist)
|
||||
|
||||
<Z>: lists all defined output pins
|
||||
returns: <Y ID PIN IFLAG STATE> for each defined output pin or <X> if no output pins defined
|
||||
|
||||
where
|
||||
|
||||
ID: the numeric ID (0-32767) of the output
|
||||
PIN: the arduino pin number to use for the output
|
||||
STATE: the state of the output (0=INACTIVE / 1=ACTIVE)
|
||||
IFLAG: defines the operational behavior of the output based on bits 0, 1, and 2 as follows:
|
||||
|
||||
IFLAG, bit 0: 0 = forward operation (ACTIVE=HIGH / INACTIVE=LOW)
|
||||
1 = inverted operation (ACTIVE=LOW / INACTIVE=HIGH)
|
||||
|
||||
IFLAG, bit 1: 0 = state of pin restored on power-up to either ACTIVE or INACTIVE depending
|
||||
on state before power-down; state of pin set to INACTIVE when first created
|
||||
1 = state of pin set on power-up, or when first created, to either ACTIVE of INACTIVE
|
||||
depending on IFLAG, bit 2
|
||||
|
||||
IFLAG, bit 2: 0 = state of pin set to INACTIVE uponm power-up or when first created
|
||||
1 = state of pin set to ACTIVE uponm power-up or when first created
|
||||
|
||||
Once all outputs have been properly defined, use the <E> command to store their definitions to EEPROM.
|
||||
If you later make edits/additions/deletions to the output definitions, you must invoke the <E> command if you want those
|
||||
new definitions updated in the EEPROM. You can also clear everything stored in the EEPROM by invoking the <e> command.
|
||||
|
||||
To change the state of outputs that have been defined use:
|
||||
|
||||
<Z ID STATE>: sets output ID to either ACTIVE or INACTIVE state
|
||||
returns: <Y ID STATE>, or <X> if turnout ID does not exist
|
||||
|
||||
where
|
||||
|
||||
ID: the numeric ID (0-32767) of the turnout to control
|
||||
STATE: the state of the output (0=INACTIVE / 1=ACTIVE)
|
||||
|
||||
When controlled as such, the Arduino updates and stores the direction of each output in EEPROM so
|
||||
that it is retained even without power. A list of the current states of each output in the form <Y ID STATE> is generated
|
||||
by this sketch whenever the <s> status command is invoked. This provides an efficient way of initializing
|
||||
the state of any outputs being monitored or controlled by a separate interface or GUI program.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "Outputs.h"
|
||||
#include "SerialCommand.h"
|
||||
#include "DCCpp_Uno.h"
|
||||
#include "EEStore.h"
|
||||
#include <EEPROM.h>
|
||||
#include "Comm.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Output::activate(int s){
|
||||
data.oStatus=(s>0); // if s>0, set status to active, else inactive
|
||||
digitalWrite(data.pin,data.oStatus ^ bitRead(data.iFlag,0)); // set state of output pin to HIGH or LOW depending on whether bit zero of iFlag is set to 0 (ACTIVE=HIGH) or 1 (ACTIVE=LOW)
|
||||
if(num>0)
|
||||
EEPROM.put(num,data.oStatus);
|
||||
INTERFACE.print("<Y");
|
||||
INTERFACE.print(data.id);
|
||||
if(data.oStatus==0)
|
||||
INTERFACE.print(" 0>");
|
||||
else
|
||||
INTERFACE.print(" 1>");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Output* Output::get(int n){
|
||||
Output *tt;
|
||||
for(tt=firstOutput;tt!=NULL && tt->data.id!=n;tt=tt->nextOutput);
|
||||
return(tt);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Output::remove(int n){
|
||||
Output *tt,*pp;
|
||||
|
||||
for(tt=firstOutput;tt!=NULL && tt->data.id!=n;pp=tt,tt=tt->nextOutput);
|
||||
|
||||
if(tt==NULL){
|
||||
INTERFACE.print("<X>");
|
||||
return;
|
||||
}
|
||||
|
||||
if(tt==firstOutput)
|
||||
firstOutput=tt->nextOutput;
|
||||
else
|
||||
pp->nextOutput=tt->nextOutput;
|
||||
|
||||
free(tt);
|
||||
|
||||
INTERFACE.print("<O>");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Output::show(int n){
|
||||
Output *tt;
|
||||
|
||||
if(firstOutput==NULL){
|
||||
INTERFACE.print("<X>");
|
||||
return;
|
||||
}
|
||||
|
||||
for(tt=firstOutput;tt!=NULL;tt=tt->nextOutput){
|
||||
INTERFACE.print("<Y");
|
||||
INTERFACE.print(tt->data.id);
|
||||
if(n==1){
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(tt->data.pin);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(tt->data.iFlag);
|
||||
}
|
||||
if(tt->data.oStatus==0)
|
||||
INTERFACE.print(" 0>");
|
||||
else
|
||||
INTERFACE.print(" 1>");
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Output::parse(char *c){
|
||||
int n,s,m;
|
||||
Output *t;
|
||||
|
||||
switch(sscanf(c,"%d %d %d",&n,&s,&m)){
|
||||
|
||||
case 2: // argument is string with id number of output followed by zero (LOW) or one (HIGH)
|
||||
t=get(n);
|
||||
if(t!=NULL)
|
||||
t->activate(s);
|
||||
else
|
||||
INTERFACE.print("<X>");
|
||||
break;
|
||||
|
||||
case 3: // argument is string with id number of output followed by a pin number and invert flag
|
||||
create(n,s,m,1);
|
||||
break;
|
||||
|
||||
case 1: // argument is a string with id number only
|
||||
remove(n);
|
||||
break;
|
||||
|
||||
case -1: // no arguments
|
||||
show(1); // verbose show
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Output::load(){
|
||||
struct OutputData data;
|
||||
Output *tt;
|
||||
|
||||
for(int i=0;i<EEStore::eeStore->data.nOutputs;i++){
|
||||
EEPROM.get(EEStore::pointer(),data);
|
||||
tt=create(data.id,data.pin,data.iFlag);
|
||||
tt->data.oStatus=bitRead(tt->data.iFlag,1)?bitRead(tt->data.iFlag,2):data.oStatus; // restore status to EEPROM value is bit 1 of iFlag=0, otherwise set to value of bit 2 of iFlag
|
||||
digitalWrite(tt->data.pin,tt->data.oStatus ^ bitRead(tt->data.iFlag,0));
|
||||
pinMode(tt->data.pin,OUTPUT);
|
||||
tt->num=EEStore::pointer();
|
||||
EEStore::advance(sizeof(tt->data));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Output::store(){
|
||||
Output *tt;
|
||||
|
||||
tt=firstOutput;
|
||||
EEStore::eeStore->data.nOutputs=0;
|
||||
|
||||
while(tt!=NULL){
|
||||
tt->num=EEStore::pointer();
|
||||
EEPROM.put(EEStore::pointer(),tt->data);
|
||||
EEStore::advance(sizeof(tt->data));
|
||||
tt=tt->nextOutput;
|
||||
EEStore::eeStore->data.nOutputs++;
|
||||
}
|
||||
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Output *Output::create(int id, int pin, int iFlag, int v){
|
||||
Output *tt;
|
||||
|
||||
if(firstOutput==NULL){
|
||||
firstOutput=(Output *)calloc(1,sizeof(Output));
|
||||
tt=firstOutput;
|
||||
} else if((tt=get(id))==NULL){
|
||||
tt=firstOutput;
|
||||
while(tt->nextOutput!=NULL)
|
||||
tt=tt->nextOutput;
|
||||
tt->nextOutput=(Output *)calloc(1,sizeof(Output));
|
||||
tt=tt->nextOutput;
|
||||
}
|
||||
|
||||
if(tt==NULL){ // problem allocating memory
|
||||
if(v==1)
|
||||
INTERFACE.print("<X>");
|
||||
return(tt);
|
||||
}
|
||||
|
||||
tt->data.id=id;
|
||||
tt->data.pin=pin;
|
||||
tt->data.iFlag=iFlag;
|
||||
tt->data.oStatus=0;
|
||||
|
||||
if(v==1){
|
||||
tt->data.oStatus=bitRead(tt->data.iFlag,1)?bitRead(tt->data.iFlag,2):0; // sets status to 0 (INACTIVE) is bit 1 of iFlag=0, otherwise set to value of bit 2 of iFlag
|
||||
digitalWrite(tt->data.pin,tt->data.oStatus ^ bitRead(tt->data.iFlag,0));
|
||||
pinMode(tt->data.pin,OUTPUT);
|
||||
INTERFACE.print("<O>");
|
||||
}
|
||||
|
||||
return(tt);
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Output *Output::firstOutput=NULL;
|
||||
|
||||
39
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Outputs.h
Normal file
39
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Outputs.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**********************************************************************
|
||||
|
||||
Outputs.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#ifndef Outputs_h
|
||||
#define Outputs_h
|
||||
|
||||
struct OutputData {
|
||||
byte oStatus;
|
||||
int id;
|
||||
byte pin;
|
||||
byte iFlag;
|
||||
};
|
||||
|
||||
struct Output{
|
||||
static Output *firstOutput;
|
||||
int num;
|
||||
struct OutputData data;
|
||||
Output *nextOutput;
|
||||
void activate(int s);
|
||||
static void parse(char *c);
|
||||
static Output* get(int);
|
||||
static void remove(int);
|
||||
static void load();
|
||||
static void store();
|
||||
static Output *create(int, int, int, int=0);
|
||||
static void show(int=0);
|
||||
}; // Output
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
476
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/PacketRegister.cpp
Normal file
476
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/PacketRegister.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
/**********************************************************************
|
||||
|
||||
PacketRegister.cpp
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "DCCpp_Uno.h"
|
||||
#include "PacketRegister.h"
|
||||
#include "Comm.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Register::initPackets(){
|
||||
activePacket=packet;
|
||||
updatePacket=packet+1;
|
||||
} // Register::initPackets
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RegisterList::RegisterList(int maxNumRegs){
|
||||
this->maxNumRegs=maxNumRegs;
|
||||
reg=(Register *)calloc((maxNumRegs+1),sizeof(Register));
|
||||
for(int i=0;i<=maxNumRegs;i++)
|
||||
reg[i].initPackets();
|
||||
regMap=(Register **)calloc((maxNumRegs+1),sizeof(Register *));
|
||||
speedTable=(int *)calloc((maxNumRegs+1),sizeof(int *));
|
||||
currentReg=reg;
|
||||
regMap[0]=reg;
|
||||
maxLoadedReg=reg;
|
||||
nextReg=NULL;
|
||||
currentBit=0;
|
||||
nRepeat=0;
|
||||
} // RegisterList::RegisterList
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// LOAD DCC PACKET INTO TEMPORARY REGISTER 0, OR PERMANENT REGISTERS 1 THROUGH DCC_PACKET_QUEUE_MAX (INCLUSIVE)
|
||||
// CONVERTS 2, 3, 4, OR 5 BYTES INTO A DCC BIT STREAM WITH PREAMBLE, CHECKSUM, AND PROPER BYTE SEPARATORS
|
||||
// BITSTREAM IS STORED IN UP TO A 10-BYTE ARRAY (USING AT MOST 76 OF 80 BITS)
|
||||
|
||||
void RegisterList::loadPacket(int nReg, byte *b, int nBytes, int nRepeat, int printFlag) volatile {
|
||||
|
||||
nReg=nReg%((maxNumRegs+1)); // force nReg to be between 0 and maxNumRegs, inclusive
|
||||
|
||||
while(nextReg!=NULL); // pause while there is a Register already waiting to be updated -- nextReg will be reset to NULL by interrupt when prior Register updated fully processed
|
||||
|
||||
if(regMap[nReg]==NULL) // first time this Register Number has been called
|
||||
regMap[nReg]=maxLoadedReg+1; // set Register Pointer for this Register Number to next available Register
|
||||
|
||||
Register *r=regMap[nReg]; // set Register to be updated
|
||||
Packet *p=r->updatePacket; // set Packet in the Register to be updated
|
||||
byte *buf=p->buf; // set byte buffer in the Packet to be updated
|
||||
|
||||
b[nBytes]=b[0]; // copy first byte into what will become the checksum byte
|
||||
for(int i=1;i<nBytes;i++) // XOR remaining bytes into checksum byte
|
||||
b[nBytes]^=b[i];
|
||||
nBytes++; // increment number of bytes in packet to include checksum byte
|
||||
|
||||
buf[0]=0xFF; // first 8 bytes of 22-byte preamble
|
||||
buf[1]=0xFF; // second 8 bytes of 22-byte preamble
|
||||
buf[2]=0xFC + bitRead(b[0],7); // last 6 bytes of 22-byte preamble + data start bit + b[0], bit 7
|
||||
buf[3]=b[0]<<1; // b[0], bits 6-0 + data start bit
|
||||
buf[4]=b[1]; // b[1], all bits
|
||||
buf[5]=b[2]>>1; // b[2], bits 7-1
|
||||
buf[6]=b[2]<<7; // b[2], bit 0
|
||||
|
||||
if(nBytes==3){
|
||||
p->nBits=49;
|
||||
} else{
|
||||
buf[6]+=b[3]>>2; // b[3], bits 7-2
|
||||
buf[7]=b[3]<<6; // b[3], bit 1-0
|
||||
if(nBytes==4){
|
||||
p->nBits=58;
|
||||
} else{
|
||||
buf[7]+=b[4]>>3; // b[4], bits 7-3
|
||||
buf[8]=b[4]<<5; // b[4], bits 2-0
|
||||
if(nBytes==5){
|
||||
p->nBits=67;
|
||||
} else{
|
||||
buf[8]+=b[5]>>4; // b[5], bits 7-4
|
||||
buf[9]=b[5]<<4; // b[5], bits 3-0
|
||||
p->nBits=76;
|
||||
} // >5 bytes
|
||||
} // >4 bytes
|
||||
} // >3 bytes
|
||||
|
||||
nextReg=r;
|
||||
this->nRepeat=nRepeat;
|
||||
maxLoadedReg=max(maxLoadedReg,nextReg);
|
||||
|
||||
if(printFlag && SHOW_PACKETS) // for debugging purposes
|
||||
printPacket(nReg,b,nBytes,nRepeat);
|
||||
|
||||
} // RegisterList::loadPacket
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterList::setThrottle(char *s) volatile{
|
||||
byte b[5]; // save space for checksum byte
|
||||
int nReg;
|
||||
int cab;
|
||||
int tSpeed;
|
||||
int tDirection;
|
||||
byte nB=0;
|
||||
|
||||
if(sscanf(s,"%d %d %d %d",&nReg,&cab,&tSpeed,&tDirection)!=4)
|
||||
return;
|
||||
|
||||
if(nReg<1 || nReg>maxNumRegs)
|
||||
return;
|
||||
|
||||
if(cab>127)
|
||||
b[nB++]=highByte(cab) | 0xC0; // convert train number into a two-byte address
|
||||
|
||||
b[nB++]=lowByte(cab);
|
||||
b[nB++]=0x3F; // 128-step speed control byte
|
||||
if(tSpeed>=0)
|
||||
b[nB++]=tSpeed+(tSpeed>0)+tDirection*128; // max speed is 126, but speed codes range from 2-127 (0=stop, 1=emergency stop)
|
||||
else{
|
||||
b[nB++]=1;
|
||||
tSpeed=0;
|
||||
}
|
||||
|
||||
loadPacket(nReg,b,nB,0,1);
|
||||
|
||||
INTERFACE.print("<T");
|
||||
INTERFACE.print(nReg); INTERFACE.print(" ");
|
||||
INTERFACE.print(tSpeed); INTERFACE.print(" ");
|
||||
INTERFACE.print(tDirection);
|
||||
INTERFACE.print(">");
|
||||
|
||||
speedTable[nReg]=tDirection==1?tSpeed:-tSpeed;
|
||||
|
||||
} // RegisterList::setThrottle()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterList::setFunction(char *s) volatile{
|
||||
byte b[5]; // save space for checksum byte
|
||||
int cab;
|
||||
int fByte, eByte;
|
||||
int nParams;
|
||||
byte nB=0;
|
||||
|
||||
nParams=sscanf(s,"%d %d %d",&cab,&fByte,&eByte);
|
||||
|
||||
if(nParams<2)
|
||||
return;
|
||||
|
||||
if(cab>127)
|
||||
b[nB++]=highByte(cab) | 0xC0; // convert train number into a two-byte address
|
||||
|
||||
b[nB++]=lowByte(cab);
|
||||
|
||||
if(nParams==2){ // this is a request for functions FL,F1-F12
|
||||
b[nB++]=(fByte | 0x80) & 0xBF; // for safety this guarantees that first nibble of function byte will always be of binary form 10XX which should always be the case for FL,F1-F12
|
||||
} else { // this is a request for functions F13-F28
|
||||
b[nB++]=(fByte | 0xDE) & 0xDF; // for safety this guarantees that first byte will either be 0xDE (for F13-F20) or 0xDF (for F21-F28)
|
||||
b[nB++]=eByte;
|
||||
}
|
||||
|
||||
loadPacket(0,b,nB,4,1);
|
||||
|
||||
} // RegisterList::setFunction()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterList::setAccessory(char *s) volatile{
|
||||
byte b[3]; // save space for checksum byte
|
||||
int aAdd; // the accessory address (0-511 = 9 bits)
|
||||
int aNum; // the accessory number within that address (0-3)
|
||||
int activate; // flag indicated whether accessory should be activated (1) or deactivated (0) following NMRA recommended convention
|
||||
|
||||
if(sscanf(s,"%d %d %d",&aAdd,&aNum,&activate)!=3)
|
||||
return;
|
||||
|
||||
b[0]=aAdd%64+128; // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least signifcant bits of accessory address
|
||||
b[1]=((((aAdd/64)%8)<<4) + (aNum%4<<1) + activate%2) ^ 0xF8; // second byte is of the form 1AAACDDD, where C should be 1, and the least significant D represent activate/deactivate
|
||||
|
||||
loadPacket(0,b,2,4,1);
|
||||
|
||||
} // RegisterList::setAccessory()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterList::writeTextPacket(char *s) volatile{
|
||||
|
||||
int nReg;
|
||||
byte b[6];
|
||||
int nBytes;
|
||||
volatile RegisterList *regs;
|
||||
|
||||
nBytes=sscanf(s,"%d %x %x %x %x %x",&nReg,b,b+1,b+2,b+3,b+4)-1;
|
||||
|
||||
if(nBytes<2 || nBytes>5){ // invalid valid packet
|
||||
INTERFACE.print("<mInvalid Packet>");
|
||||
return;
|
||||
}
|
||||
|
||||
loadPacket(nReg,b,nBytes,0,1);
|
||||
|
||||
} // RegisterList::writeTextPacket()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterList::readCV(char *s) volatile{
|
||||
byte bRead[4];
|
||||
int bValue;
|
||||
int c,d,base;
|
||||
int cv, callBack, callBackSub;
|
||||
|
||||
if(sscanf(s,"%d %d %d",&cv,&callBack,&callBackSub)!=3) // cv = 1-1024
|
||||
return;
|
||||
cv--; // actual CV addresses are cv-1 (0-1023)
|
||||
|
||||
bRead[0]=0x78+(highByte(cv)&0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
|
||||
bRead[1]=lowByte(cv);
|
||||
|
||||
bValue=0;
|
||||
|
||||
for(int i=0;i<8;i++){
|
||||
|
||||
c=0;
|
||||
d=0;
|
||||
base=0;
|
||||
|
||||
for(int j=0;j<ACK_BASE_COUNT;j++)
|
||||
base+=analogRead(CURRENT_MONITOR_PIN_PROG);
|
||||
base/=ACK_BASE_COUNT;
|
||||
|
||||
bRead[2]=0xE8+i;
|
||||
|
||||
loadPacket(0,resetPacket,2,3); // NMRA recommends starting with 3 reset packets
|
||||
loadPacket(0,bRead,3,5); // NMRA recommends 5 verfy packets
|
||||
loadPacket(0,resetPacket,2,1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
|
||||
|
||||
for(int j=0;j<ACK_SAMPLE_COUNT;j++){
|
||||
c=(analogRead(CURRENT_MONITOR_PIN_PROG)-base)*ACK_SAMPLE_SMOOTHING+c*(1.0-ACK_SAMPLE_SMOOTHING);
|
||||
if(c>ACK_SAMPLE_THRESHOLD)
|
||||
d=1;
|
||||
}
|
||||
|
||||
bitWrite(bValue,i,d);
|
||||
}
|
||||
|
||||
c=0;
|
||||
d=0;
|
||||
base=0;
|
||||
|
||||
for(int j=0;j<ACK_BASE_COUNT;j++)
|
||||
base+=analogRead(CURRENT_MONITOR_PIN_PROG);
|
||||
base/=ACK_BASE_COUNT;
|
||||
|
||||
bRead[0]=0x74+(highByte(cv)&0x03); // set-up to re-verify entire byte
|
||||
bRead[2]=bValue;
|
||||
|
||||
loadPacket(0,resetPacket,2,3); // NMRA recommends starting with 3 reset packets
|
||||
loadPacket(0,bRead,3,5); // NMRA recommends 5 verfy packets
|
||||
loadPacket(0,resetPacket,2,1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
|
||||
|
||||
for(int j=0;j<ACK_SAMPLE_COUNT;j++){
|
||||
c=(analogRead(CURRENT_MONITOR_PIN_PROG)-base)*ACK_SAMPLE_SMOOTHING+c*(1.0-ACK_SAMPLE_SMOOTHING);
|
||||
if(c>ACK_SAMPLE_THRESHOLD)
|
||||
d=1;
|
||||
}
|
||||
|
||||
if(d==0) // verify unsuccessful
|
||||
bValue=-1;
|
||||
|
||||
INTERFACE.print("<r");
|
||||
INTERFACE.print(callBack);
|
||||
INTERFACE.print("|");
|
||||
INTERFACE.print(callBackSub);
|
||||
INTERFACE.print("|");
|
||||
INTERFACE.print(cv+1);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(bValue);
|
||||
INTERFACE.print(">");
|
||||
|
||||
} // RegisterList::readCV()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterList::writeCVByte(char *s) volatile{
|
||||
byte bWrite[4];
|
||||
int bValue;
|
||||
int c,d,base;
|
||||
int cv, callBack, callBackSub;
|
||||
|
||||
if(sscanf(s,"%d %d %d %d",&cv,&bValue,&callBack,&callBackSub)!=4) // cv = 1-1024
|
||||
return;
|
||||
cv--; // actual CV addresses are cv-1 (0-1023)
|
||||
|
||||
bWrite[0]=0x7C+(highByte(cv)&0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
|
||||
bWrite[1]=lowByte(cv);
|
||||
bWrite[2]=bValue;
|
||||
|
||||
loadPacket(0,resetPacket,2,1);
|
||||
loadPacket(0,bWrite,3,4);
|
||||
loadPacket(0,resetPacket,2,1);
|
||||
loadPacket(0,idlePacket,2,10);
|
||||
|
||||
c=0;
|
||||
d=0;
|
||||
base=0;
|
||||
|
||||
for(int j=0;j<ACK_BASE_COUNT;j++)
|
||||
base+=analogRead(CURRENT_MONITOR_PIN_PROG);
|
||||
base/=ACK_BASE_COUNT;
|
||||
|
||||
bWrite[0]=0x74+(highByte(cv)&0x03); // set-up to re-verify entire byte
|
||||
|
||||
loadPacket(0,resetPacket,2,3); // NMRA recommends starting with 3 reset packets
|
||||
loadPacket(0,bWrite,3,5); // NMRA recommends 5 verfy packets
|
||||
loadPacket(0,resetPacket,2,1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
|
||||
|
||||
for(int j=0;j<ACK_SAMPLE_COUNT;j++){
|
||||
c=(analogRead(CURRENT_MONITOR_PIN_PROG)-base)*ACK_SAMPLE_SMOOTHING+c*(1.0-ACK_SAMPLE_SMOOTHING);
|
||||
if(c>ACK_SAMPLE_THRESHOLD)
|
||||
d=1;
|
||||
}
|
||||
|
||||
if(d==0) // verify unsuccessful
|
||||
bValue=-1;
|
||||
|
||||
INTERFACE.print("<r");
|
||||
INTERFACE.print(callBack);
|
||||
INTERFACE.print("|");
|
||||
INTERFACE.print(callBackSub);
|
||||
INTERFACE.print("|");
|
||||
INTERFACE.print(cv+1);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(bValue);
|
||||
INTERFACE.print(">");
|
||||
|
||||
} // RegisterList::writeCVByte()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterList::writeCVBit(char *s) volatile{
|
||||
byte bWrite[4];
|
||||
int bNum,bValue;
|
||||
int c,d,base;
|
||||
int cv, callBack, callBackSub;
|
||||
|
||||
if(sscanf(s,"%d %d %d %d %d",&cv,&bNum,&bValue,&callBack,&callBackSub)!=5) // cv = 1-1024
|
||||
return;
|
||||
cv--; // actual CV addresses are cv-1 (0-1023)
|
||||
bValue=bValue%2;
|
||||
bNum=bNum%8;
|
||||
|
||||
bWrite[0]=0x78+(highByte(cv)&0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
|
||||
bWrite[1]=lowByte(cv);
|
||||
bWrite[2]=0xF0+bValue*8+bNum;
|
||||
|
||||
loadPacket(0,resetPacket,2,1);
|
||||
loadPacket(0,bWrite,3,4);
|
||||
loadPacket(0,resetPacket,2,1);
|
||||
loadPacket(0,idlePacket,2,10);
|
||||
|
||||
c=0;
|
||||
d=0;
|
||||
base=0;
|
||||
|
||||
for(int j=0;j<ACK_BASE_COUNT;j++)
|
||||
base+=analogRead(CURRENT_MONITOR_PIN_PROG);
|
||||
base/=ACK_BASE_COUNT;
|
||||
|
||||
bitClear(bWrite[2],4); // change instruction code from Write Bit to Verify Bit
|
||||
|
||||
loadPacket(0,resetPacket,2,3); // NMRA recommends starting with 3 reset packets
|
||||
loadPacket(0,bWrite,3,5); // NMRA recommends 5 verfy packets
|
||||
loadPacket(0,resetPacket,2,1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
|
||||
|
||||
for(int j=0;j<ACK_SAMPLE_COUNT;j++){
|
||||
c=(analogRead(CURRENT_MONITOR_PIN_PROG)-base)*ACK_SAMPLE_SMOOTHING+c*(1.0-ACK_SAMPLE_SMOOTHING);
|
||||
if(c>ACK_SAMPLE_THRESHOLD)
|
||||
d=1;
|
||||
}
|
||||
|
||||
if(d==0) // verify unsuccessful
|
||||
bValue=-1;
|
||||
|
||||
INTERFACE.print("<r");
|
||||
INTERFACE.print(callBack);
|
||||
INTERFACE.print("|");
|
||||
INTERFACE.print(callBackSub);
|
||||
INTERFACE.print("|");
|
||||
INTERFACE.print(cv+1);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(bNum);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(bValue);
|
||||
INTERFACE.print(">");
|
||||
|
||||
} // RegisterList::writeCVBit()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterList::writeCVByteMain(char *s) volatile{
|
||||
byte b[6]; // save space for checksum byte
|
||||
int cab;
|
||||
int cv;
|
||||
int bValue;
|
||||
byte nB=0;
|
||||
|
||||
if(sscanf(s,"%d %d %d",&cab,&cv,&bValue)!=3)
|
||||
return;
|
||||
cv--;
|
||||
|
||||
if(cab>127)
|
||||
b[nB++]=highByte(cab) | 0xC0; // convert train number into a two-byte address
|
||||
|
||||
b[nB++]=lowByte(cab);
|
||||
b[nB++]=0xEC+(highByte(cv)&0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
|
||||
b[nB++]=lowByte(cv);
|
||||
b[nB++]=bValue;
|
||||
|
||||
loadPacket(0,b,nB,4);
|
||||
|
||||
} // RegisterList::writeCVByteMain()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterList::writeCVBitMain(char *s) volatile{
|
||||
byte b[6]; // save space for checksum byte
|
||||
int cab;
|
||||
int cv;
|
||||
int bNum;
|
||||
int bValue;
|
||||
byte nB=0;
|
||||
|
||||
if(sscanf(s,"%d %d %d %d",&cab,&cv,&bNum,&bValue)!=4)
|
||||
return;
|
||||
cv--;
|
||||
|
||||
bValue=bValue%2;
|
||||
bNum=bNum%8;
|
||||
|
||||
if(cab>127)
|
||||
b[nB++]=highByte(cab) | 0xC0; // convert train number into a two-byte address
|
||||
|
||||
b[nB++]=lowByte(cab);
|
||||
b[nB++]=0xE8+(highByte(cv)&0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
|
||||
b[nB++]=lowByte(cv);
|
||||
b[nB++]=0xF0+bValue*8+bNum;
|
||||
|
||||
loadPacket(0,b,nB,4);
|
||||
|
||||
} // RegisterList::writeCVBitMain()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RegisterList::printPacket(int nReg, byte *b, int nBytes, int nRepeat) volatile {
|
||||
|
||||
INTERFACE.print("<*");
|
||||
INTERFACE.print(nReg);
|
||||
INTERFACE.print(":");
|
||||
for(int i=0;i<nBytes;i++){
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(b[i],HEX);
|
||||
}
|
||||
INTERFACE.print(" / ");
|
||||
INTERFACE.print(nRepeat);
|
||||
INTERFACE.print(">");
|
||||
} // RegisterList::printPacket()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
byte RegisterList::idlePacket[3]={0xFF,0x00,0}; // always leave extra byte for checksum computation
|
||||
byte RegisterList::resetPacket[3]={0x00,0x00,0};
|
||||
|
||||
byte RegisterList::bitMask[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; // masks used in interrupt routine to speed the query of a single bit in a Packet
|
||||
64
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/PacketRegister.h
Normal file
64
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/PacketRegister.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/**********************************************************************
|
||||
|
||||
PacketRegister.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef PacketRegister_h
|
||||
#define PacketRegister_h
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
// Define constants used for reading CVs from the Programming Track
|
||||
|
||||
#define ACK_BASE_COUNT 100 // number of analogRead samples to take before each CV verify to establish a baseline current
|
||||
#define ACK_SAMPLE_COUNT 500 // number of analogRead samples to take when monitoring current after a CV verify (bit or byte) has been sent
|
||||
#define ACK_SAMPLE_SMOOTHING 0.2 // exponential smoothing to use in processing the analogRead samples after a CV verify (bit or byte) has been sent
|
||||
#define ACK_SAMPLE_THRESHOLD 30 // the threshold that the exponentially-smoothed analogRead samples (after subtracting the baseline current) must cross to establish ACKNOWLEDGEMENT
|
||||
|
||||
// Define a series of registers that can be sequentially accessed over a loop to generate a repeating series of DCC Packets
|
||||
|
||||
struct Packet{
|
||||
byte buf[10];
|
||||
byte nBits;
|
||||
}; // Packet
|
||||
|
||||
struct Register{
|
||||
Packet packet[2];
|
||||
Packet *activePacket;
|
||||
Packet *updatePacket;
|
||||
void initPackets();
|
||||
}; // Register
|
||||
|
||||
struct RegisterList{
|
||||
int maxNumRegs;
|
||||
Register *reg;
|
||||
Register **regMap;
|
||||
Register *currentReg;
|
||||
Register *maxLoadedReg;
|
||||
Register *nextReg;
|
||||
Packet *tempPacket;
|
||||
byte currentBit;
|
||||
byte nRepeat;
|
||||
int *speedTable;
|
||||
static byte idlePacket[];
|
||||
static byte resetPacket[];
|
||||
static byte bitMask[];
|
||||
RegisterList(int);
|
||||
void loadPacket(int, byte *, int, int, int=0) volatile;
|
||||
void setThrottle(char *) volatile;
|
||||
void setFunction(char *) volatile;
|
||||
void setAccessory(char *) volatile;
|
||||
void writeTextPacket(char *) volatile;
|
||||
void readCV(char *) volatile;
|
||||
void writeCVByte(char *) volatile;
|
||||
void writeCVBit(char *) volatile;
|
||||
void writeCVByteMain(char *) volatile;
|
||||
void writeCVBitMain(char *s) volatile;
|
||||
void printPacket(int, byte *, int, int) volatile;
|
||||
};
|
||||
|
||||
#endif
|
||||
248
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Sensor.cpp
Normal file
248
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Sensor.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/**********************************************************************
|
||||
|
||||
Sensor.cpp
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
/**********************************************************************
|
||||
|
||||
DCC++ BASE STATION supports Sensor inputs that can be connected to any Aruidno Pin
|
||||
not in use by this program. Sensors can be of any type (infrared, magentic, mechanical...).
|
||||
The only requirement is that when "activated" the Sensor must force the specified Arduino
|
||||
Pin LOW (i.e. to ground), and when not activated, this Pin should remain HIGH (e.g. 5V),
|
||||
or be allowed to float HIGH if use of the Arduino Pin's internal pull-up resistor is specified.
|
||||
|
||||
To ensure proper voltage levels, some part of the Sensor circuitry
|
||||
MUST be tied back to the same ground as used by the Arduino.
|
||||
|
||||
The Sensor code below utilizes exponential smoothing to "de-bounce" spikes generated by
|
||||
mechanical switches and transistors. This avoids the need to create smoothing circuitry
|
||||
for each sensor. You may need to change these parameters through trial and error for your specific sensors.
|
||||
|
||||
To have this sketch monitor one or more Arduino pins for sensor triggers, first define/edit/delete
|
||||
sensor definitions using the following variation of the "S" command:
|
||||
|
||||
<S ID PIN PULLUP>: creates a new sensor ID, with specified PIN and PULLUP
|
||||
if sensor ID already exists, it is updated with specificed PIN and PULLUP
|
||||
returns: <O> if successful and <X> if unsuccessful (e.g. out of memory)
|
||||
|
||||
<S ID>: deletes definition of sensor ID
|
||||
returns: <O> if successful and <X> if unsuccessful (e.g. ID does not exist)
|
||||
|
||||
<S>: lists all defined sensors
|
||||
returns: <Q ID PIN PULLUP> for each defined sensor or <X> if no sensors defined
|
||||
|
||||
where
|
||||
|
||||
ID: the numeric ID (0-32767) of the sensor
|
||||
PIN: the arduino pin number the sensor is connected to
|
||||
PULLUP: 1=use internal pull-up resistor for PIN, 0=don't use internal pull-up resistor for PIN
|
||||
|
||||
Once all sensors have been properly defined, use the <E> command to store their definitions to EEPROM.
|
||||
If you later make edits/additions/deletions to the sensor definitions, you must invoke the <E> command if you want those
|
||||
new definitions updated in the EEPROM. You can also clear everything stored in the EEPROM by invoking the <e> command.
|
||||
|
||||
All sensors defined as per above are repeatedly and sequentially checked within the main loop of this sketch.
|
||||
If a Sensor Pin is found to have transitioned from one state to another, one of the following serial messages are generated:
|
||||
|
||||
<Q ID> - for transition of Sensor ID from HIGH state to LOW state (i.e. the sensor is triggered)
|
||||
<q ID> - for transition of Sensor ID from LOW state to HIGH state (i.e. the sensor is no longer triggered)
|
||||
|
||||
Depending on whether the physical sensor is acting as an "event-trigger" or a "detection-sensor," you may
|
||||
decide to ignore the <q ID> return and only react to <Q ID> triggers.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "DCCpp_Uno.h"
|
||||
#include "Sensor.h"
|
||||
#include "EEStore.h"
|
||||
#include <EEPROM.h>
|
||||
#include "Comm.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Sensor::check(){
|
||||
Sensor *tt;
|
||||
|
||||
for(tt=firstSensor;tt!=NULL;tt=tt->nextSensor){
|
||||
tt->signal=tt->signal*(1.0-SENSOR_DECAY)+digitalRead(tt->data.pin)*SENSOR_DECAY;
|
||||
|
||||
if(!tt->active && tt->signal<0.5){
|
||||
tt->active=true;
|
||||
INTERFACE.print("<Q");
|
||||
INTERFACE.print(tt->data.snum);
|
||||
INTERFACE.print(">");
|
||||
} else if(tt->active && tt->signal>0.9){
|
||||
tt->active=false;
|
||||
INTERFACE.print("<q");
|
||||
INTERFACE.print(tt->data.snum);
|
||||
INTERFACE.print(">");
|
||||
}
|
||||
} // loop over all sensors
|
||||
|
||||
} // Sensor::check
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Sensor *Sensor::create(int snum, int pin, int pullUp, int v){
|
||||
Sensor *tt;
|
||||
|
||||
if(firstSensor==NULL){
|
||||
firstSensor=(Sensor *)calloc(1,sizeof(Sensor));
|
||||
tt=firstSensor;
|
||||
} else if((tt=get(snum))==NULL){
|
||||
tt=firstSensor;
|
||||
while(tt->nextSensor!=NULL)
|
||||
tt=tt->nextSensor;
|
||||
tt->nextSensor=(Sensor *)calloc(1,sizeof(Sensor));
|
||||
tt=tt->nextSensor;
|
||||
}
|
||||
|
||||
if(tt==NULL){ // problem allocating memory
|
||||
if(v==1)
|
||||
INTERFACE.print("<X>");
|
||||
return(tt);
|
||||
}
|
||||
|
||||
tt->data.snum=snum;
|
||||
tt->data.pin=pin;
|
||||
tt->data.pullUp=(pullUp==0?LOW:HIGH);
|
||||
tt->active=false;
|
||||
tt->signal=1;
|
||||
pinMode(pin,INPUT); // set mode to input
|
||||
digitalWrite(pin,pullUp); // don't use Arduino's internal pull-up resistors for external infrared sensors --- each sensor must have its own 1K external pull-up resistor
|
||||
|
||||
if(v==1)
|
||||
INTERFACE.print("<O>");
|
||||
return(tt);
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Sensor* Sensor::get(int n){
|
||||
Sensor *tt;
|
||||
for(tt=firstSensor;tt!=NULL && tt->data.snum!=n;tt=tt->nextSensor);
|
||||
return(tt);
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Sensor::remove(int n){
|
||||
Sensor *tt,*pp;
|
||||
|
||||
for(tt=firstSensor;tt!=NULL && tt->data.snum!=n;pp=tt,tt=tt->nextSensor);
|
||||
|
||||
if(tt==NULL){
|
||||
INTERFACE.print("<X>");
|
||||
return;
|
||||
}
|
||||
|
||||
if(tt==firstSensor)
|
||||
firstSensor=tt->nextSensor;
|
||||
else
|
||||
pp->nextSensor=tt->nextSensor;
|
||||
|
||||
free(tt);
|
||||
|
||||
INTERFACE.print("<O>");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Sensor::show(){
|
||||
Sensor *tt;
|
||||
|
||||
if(firstSensor==NULL){
|
||||
INTERFACE.print("<X>");
|
||||
return;
|
||||
}
|
||||
|
||||
for(tt=firstSensor;tt!=NULL;tt=tt->nextSensor){
|
||||
INTERFACE.print("<Q");
|
||||
INTERFACE.print(tt->data.snum);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(tt->data.pin);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(tt->data.pullUp);
|
||||
INTERFACE.print(">");
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Sensor::status(){
|
||||
Sensor *tt;
|
||||
|
||||
if(firstSensor==NULL){
|
||||
INTERFACE.print("<X>");
|
||||
return;
|
||||
}
|
||||
|
||||
for(tt=firstSensor;tt!=NULL;tt=tt->nextSensor){
|
||||
INTERFACE.print(tt->active?"<Q":"<q");
|
||||
INTERFACE.print(tt->data.snum);
|
||||
INTERFACE.print(">");
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Sensor::parse(char *c){
|
||||
int n,s,m;
|
||||
Sensor *t;
|
||||
|
||||
switch(sscanf(c,"%d %d %d",&n,&s,&m)){
|
||||
|
||||
case 3: // argument is string with id number of sensor followed by a pin number and pullUp indicator (0=LOW/1=HIGH)
|
||||
create(n,s,m,1);
|
||||
break;
|
||||
|
||||
case 1: // argument is a string with id number only
|
||||
remove(n);
|
||||
break;
|
||||
|
||||
case -1: // no arguments
|
||||
show();
|
||||
break;
|
||||
|
||||
case 2: // invalid number of arguments
|
||||
INTERFACE.print("<X>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Sensor::load(){
|
||||
struct SensorData data;
|
||||
Sensor *tt;
|
||||
|
||||
for(int i=0;i<EEStore::eeStore->data.nSensors;i++){
|
||||
EEPROM.get(EEStore::pointer(),data);
|
||||
tt=create(data.snum,data.pin,data.pullUp);
|
||||
EEStore::advance(sizeof(tt->data));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Sensor::store(){
|
||||
Sensor *tt;
|
||||
|
||||
tt=firstSensor;
|
||||
EEStore::eeStore->data.nSensors=0;
|
||||
|
||||
while(tt!=NULL){
|
||||
EEPROM.put(EEStore::pointer(),tt->data);
|
||||
EEStore::advance(sizeof(tt->data));
|
||||
tt=tt->nextSensor;
|
||||
EEStore::eeStore->data.nSensors++;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Sensor *Sensor::firstSensor=NULL;
|
||||
|
||||
41
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Sensor.h
Normal file
41
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/Sensor.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/**********************************************************************
|
||||
|
||||
Sensor.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef Sensor_h
|
||||
#define Sensor_h
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define SENSOR_DECAY 0.03
|
||||
|
||||
struct SensorData {
|
||||
int snum;
|
||||
byte pin;
|
||||
byte pullUp;
|
||||
};
|
||||
|
||||
struct Sensor{
|
||||
static Sensor *firstSensor;
|
||||
SensorData data;
|
||||
boolean active;
|
||||
float signal;
|
||||
Sensor *nextSensor;
|
||||
static void load();
|
||||
static void store();
|
||||
static Sensor *create(int, int, int, int=0);
|
||||
static Sensor* get(int);
|
||||
static void remove(int);
|
||||
static void show();
|
||||
static void status();
|
||||
static void parse(char *c);
|
||||
static void check();
|
||||
}; // Sensor
|
||||
|
||||
#endif
|
||||
|
||||
571
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/SerialCommand.cpp
Normal file
571
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/SerialCommand.cpp
Normal file
@@ -0,0 +1,571 @@
|
||||
/**********************************************************************
|
||||
|
||||
SerialCommand.cpp
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
// DCC++ BASE STATION COMMUNICATES VIA THE SERIAL PORT USING SINGLE-CHARACTER TEXT COMMANDS
|
||||
// WITH OPTIONAL PARAMTERS, AND BRACKETED BY < AND > SYMBOLS. SPACES BETWEEN PARAMETERS
|
||||
// ARE REQUIRED. SPACES ANYWHERE ELSE ARE IGNORED. A SPACE BETWEEN THE SINGLE-CHARACTER
|
||||
// COMMAND AND THE FIRST PARAMETER IS ALSO NOT REQUIRED.
|
||||
|
||||
// See SerialCommand::parse() below for defined text commands.
|
||||
|
||||
#include "SerialCommand.h"
|
||||
#include "DCCpp_Uno.h"
|
||||
#include "Accessories.h"
|
||||
#include "Sensor.h"
|
||||
#include "Outputs.h"
|
||||
#include "EEStore.h"
|
||||
#include "Comm.h"
|
||||
|
||||
extern int __heap_start, *__brkval;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
char SerialCommand::commandString[MAX_COMMAND_LENGTH+1];
|
||||
volatile RegisterList *SerialCommand::mRegs;
|
||||
volatile RegisterList *SerialCommand::pRegs;
|
||||
CurrentMonitor *SerialCommand::mMonitor;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SerialCommand::init(volatile RegisterList *_mRegs, volatile RegisterList *_pRegs, CurrentMonitor *_mMonitor){
|
||||
mRegs=_mRegs;
|
||||
pRegs=_pRegs;
|
||||
mMonitor=_mMonitor;
|
||||
sprintf(commandString,"");
|
||||
} // SerialCommand:SerialCommand
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SerialCommand::process(){
|
||||
char c;
|
||||
|
||||
#if COMM_TYPE == 0
|
||||
|
||||
while(INTERFACE.available()>0){ // while there is data on the serial line
|
||||
c=INTERFACE.read();
|
||||
if(c=='<') // start of new command
|
||||
sprintf(commandString,"");
|
||||
else if(c=='>') // end of new command
|
||||
parse(commandString);
|
||||
else if(strlen(commandString)<MAX_COMMAND_LENGTH) // if comandString still has space, append character just read from serial line
|
||||
sprintf(commandString,"%s%c",commandString,c); // otherwise, character is ignored (but continue to look for '<' or '>')
|
||||
} // while
|
||||
|
||||
#elif COMM_TYPE == 1
|
||||
|
||||
EthernetClient client=INTERFACE.available();
|
||||
|
||||
if(client){
|
||||
while(client.connected() && client.available()){ // while there is data on the network
|
||||
c=client.read();
|
||||
if(c=='<') // start of new command
|
||||
sprintf(commandString,"");
|
||||
else if(c=='>') // end of new command
|
||||
parse(commandString);
|
||||
else if(strlen(commandString)<MAX_COMMAND_LENGTH) // if comandString still has space, append character just read from network
|
||||
sprintf(commandString,"%s%c",commandString,c); // otherwise, character is ignored (but continue to look for '<' or '>')
|
||||
} // while
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // SerialCommand:process
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SerialCommand::parse(char *com){
|
||||
|
||||
switch(com[0]){
|
||||
|
||||
/***** SET ENGINE THROTTLES USING 128-STEP SPEED CONTROL ****/
|
||||
|
||||
case 't': // <t REGISTER CAB SPEED DIRECTION>
|
||||
/*
|
||||
* sets the throttle for a given register/cab combination
|
||||
*
|
||||
* REGISTER: an internal register number, from 1 through MAX_MAIN_REGISTERS (inclusive), to store the DCC packet used to control this throttle setting
|
||||
* CAB: the short (1-127) or long (128-10293) address of the engine decoder
|
||||
* SPEED: throttle speed from 0-126, or -1 for emergency stop (resets SPEED to 0)
|
||||
* DIRECTION: 1=forward, 0=reverse. Setting direction when speed=0 or speed=-1 only effects directionality of cab lighting for a stopped train
|
||||
*
|
||||
* returns: <T REGISTER SPEED DIRECTION>
|
||||
*
|
||||
*/
|
||||
mRegs->setThrottle(com+1);
|
||||
break;
|
||||
|
||||
/***** OPERATE ENGINE DECODER FUNCTIONS F0-F28 ****/
|
||||
|
||||
case 'f': // <f CAB BYTE1 [BYTE2]>
|
||||
/*
|
||||
* turns on and off engine decoder functions F0-F28 (F0 is sometimes called FL)
|
||||
* NOTE: setting requests transmitted directly to mobile engine decoder --- current state of engine functions is not stored by this program
|
||||
*
|
||||
* CAB: the short (1-127) or long (128-10293) address of the engine decoder
|
||||
*
|
||||
* To set functions F0-F4 on (=1) or off (=0):
|
||||
*
|
||||
* BYTE1: 128 + F1*1 + F2*2 + F3*4 + F4*8 + F0*16
|
||||
* BYTE2: omitted
|
||||
*
|
||||
* To set functions F5-F8 on (=1) or off (=0):
|
||||
*
|
||||
* BYTE1: 176 + F5*1 + F6*2 + F7*4 + F8*8
|
||||
* BYTE2: omitted
|
||||
*
|
||||
* To set functions F9-F12 on (=1) or off (=0):
|
||||
*
|
||||
* BYTE1: 160 + F9*1 +F10*2 + F11*4 + F12*8
|
||||
* BYTE2: omitted
|
||||
*
|
||||
* To set functions F13-F20 on (=1) or off (=0):
|
||||
*
|
||||
* BYTE1: 222
|
||||
* BYTE2: F13*1 + F14*2 + F15*4 + F16*8 + F17*16 + F18*32 + F19*64 + F20*128
|
||||
*
|
||||
* To set functions F21-F28 on (=1) of off (=0):
|
||||
*
|
||||
* BYTE1: 223
|
||||
* BYTE2: F21*1 + F22*2 + F23*4 + F24*8 + F25*16 + F26*32 + F27*64 + F28*128
|
||||
*
|
||||
* returns: NONE
|
||||
*
|
||||
*/
|
||||
mRegs->setFunction(com+1);
|
||||
break;
|
||||
|
||||
/***** OPERATE STATIONARY ACCESSORY DECODERS ****/
|
||||
|
||||
case 'a': // <a ADDRESS SUBADDRESS ACTIVATE>
|
||||
/*
|
||||
* turns an accessory (stationary) decoder on or off
|
||||
*
|
||||
* ADDRESS: the primary address of the decoder (0-511)
|
||||
* SUBADDRESS: the subaddress of the decoder (0-3)
|
||||
* ACTIVATE: 1=on (set), 0=off (clear)
|
||||
*
|
||||
* Note that many decoders and controllers combine the ADDRESS and SUBADDRESS into a single number, N,
|
||||
* from 1 through a max of 2044, where
|
||||
*
|
||||
* N = (ADDRESS - 1) * 4 + SUBADDRESS + 1, for all ADDRESS>0
|
||||
*
|
||||
* OR
|
||||
*
|
||||
* ADDRESS = INT((N - 1) / 4) + 1
|
||||
* SUBADDRESS = (N - 1) % 4
|
||||
*
|
||||
* returns: NONE
|
||||
*/
|
||||
mRegs->setAccessory(com+1);
|
||||
break;
|
||||
|
||||
/***** CREATE/EDIT/REMOVE/SHOW & OPERATE A TURN-OUT ****/
|
||||
|
||||
case 'T': // <T ID THROW>
|
||||
/*
|
||||
* <T ID THROW>: sets turnout ID to either the "thrown" or "unthrown" position
|
||||
*
|
||||
* ID: the numeric ID (0-32767) of the turnout to control
|
||||
* THROW: 0 (unthrown) or 1 (thrown)
|
||||
*
|
||||
* returns: <H ID THROW> or <X> if turnout ID does not exist
|
||||
*
|
||||
* *** SEE ACCESSORIES.CPP FOR COMPLETE INFO ON THE DIFFERENT VARIATIONS OF THE "T" COMMAND
|
||||
* USED TO CREATE/EDIT/REMOVE/SHOW TURNOUT DEFINITIONS
|
||||
*/
|
||||
Turnout::parse(com+1);
|
||||
break;
|
||||
|
||||
/***** CREATE/EDIT/REMOVE/SHOW & OPERATE AN OUTPUT PIN ****/
|
||||
|
||||
case 'Z': // <Z ID ACTIVATE>
|
||||
/*
|
||||
* <Z ID ACTIVATE>: sets output ID to either the "active" or "inactive" state
|
||||
*
|
||||
* ID: the numeric ID (0-32767) of the output to control
|
||||
* ACTIVATE: 0 (active) or 1 (inactive)
|
||||
*
|
||||
* returns: <Y ID ACTIVATE> or <X> if output ID does not exist
|
||||
*
|
||||
* *** SEE OUTPUTS.CPP FOR COMPLETE INFO ON THE DIFFERENT VARIATIONS OF THE "O" COMMAND
|
||||
* USED TO CREATE/EDIT/REMOVE/SHOW TURNOUT DEFINITIONS
|
||||
*/
|
||||
Output::parse(com+1);
|
||||
break;
|
||||
|
||||
/***** CREATE/EDIT/REMOVE/SHOW A SENSOR ****/
|
||||
|
||||
case 'S':
|
||||
/*
|
||||
* *** SEE SENSOR.CPP FOR COMPLETE INFO ON THE DIFFERENT VARIATIONS OF THE "S" COMMAND
|
||||
* USED TO CREATE/EDIT/REMOVE/SHOW SENSOR DEFINITIONS
|
||||
*/
|
||||
Sensor::parse(com+1);
|
||||
break;
|
||||
|
||||
/***** SHOW STATUS OF ALL SENSORS ****/
|
||||
|
||||
case 'Q': // <Q>
|
||||
/*
|
||||
* returns: the status of each sensor ID in the form <Q ID> (active) or <q ID> (not active)
|
||||
*/
|
||||
Sensor::status();
|
||||
break;
|
||||
|
||||
/***** WRITE CONFIGURATION VARIABLE BYTE TO ENGINE DECODER ON MAIN OPERATIONS TRACK ****/
|
||||
|
||||
case 'w': // <w CAB CV VALUE>
|
||||
/*
|
||||
* writes, without any verification, a Configuration Variable to the decoder of an engine on the main operations track
|
||||
*
|
||||
* CAB: the short (1-127) or long (128-10293) address of the engine decoder
|
||||
* CV: the number of the Configuration Variable memory location in the decoder to write to (1-1024)
|
||||
* VALUE: the value to be written to the Configuration Variable memory location (0-255)
|
||||
*
|
||||
* returns: NONE
|
||||
*/
|
||||
mRegs->writeCVByteMain(com+1);
|
||||
break;
|
||||
|
||||
/***** WRITE CONFIGURATION VARIABLE BIT TO ENGINE DECODER ON MAIN OPERATIONS TRACK ****/
|
||||
|
||||
case 'b': // <b CAB CV BIT VALUE>
|
||||
/*
|
||||
* writes, without any verification, a single bit within a Configuration Variable to the decoder of an engine on the main operations track
|
||||
*
|
||||
* CAB: the short (1-127) or long (128-10293) address of the engine decoder
|
||||
* CV: the number of the Configuration Variable memory location in the decoder to write to (1-1024)
|
||||
* BIT: the bit number of the Configurarion Variable regsiter to write (0-7)
|
||||
* VALUE: the value of the bit to be written (0-1)
|
||||
*
|
||||
* returns: NONE
|
||||
*/
|
||||
mRegs->writeCVBitMain(com+1);
|
||||
break;
|
||||
|
||||
/***** WRITE CONFIGURATION VARIABLE BYTE TO ENGINE DECODER ON PROGRAMMING TRACK ****/
|
||||
|
||||
case 'W': // <W CV VALUE CALLBACKNUM CALLBACKSUB>
|
||||
/*
|
||||
* writes, and then verifies, a Configuration Variable to the decoder of an engine on the programming track
|
||||
*
|
||||
* CV: the number of the Configuration Variable memory location in the decoder to write to (1-1024)
|
||||
* VALUE: the value to be written to the Configuration Variable memory location (0-255)
|
||||
* CALLBACKNUM: an arbitrary integer (0-32767) that is ignored by the Base Station and is simply echoed back in the output - useful for external programs that call this function
|
||||
* CALLBACKSUB: a second arbitrary integer (0-32767) that is ignored by the Base Station and is simply echoed back in the output - useful for external programs (e.g. DCC++ Interface) that call this function
|
||||
*
|
||||
* returns: <r CALLBACKNUM|CALLBACKSUB|CV Value)
|
||||
* where VALUE is a number from 0-255 as read from the requested CV, or -1 if verificaiton read fails
|
||||
*/
|
||||
pRegs->writeCVByte(com+1);
|
||||
break;
|
||||
|
||||
/***** WRITE CONFIGURATION VARIABLE BIT TO ENGINE DECODER ON PROGRAMMING TRACK ****/
|
||||
|
||||
case 'B': // <B CV BIT VALUE CALLBACKNUM CALLBACKSUB>
|
||||
/*
|
||||
* writes, and then verifies, a single bit within a Configuration Variable to the decoder of an engine on the programming track
|
||||
*
|
||||
* CV: the number of the Configuration Variable memory location in the decoder to write to (1-1024)
|
||||
* BIT: the bit number of the Configurarion Variable memory location to write (0-7)
|
||||
* VALUE: the value of the bit to be written (0-1)
|
||||
* CALLBACKNUM: an arbitrary integer (0-32767) that is ignored by the Base Station and is simply echoed back in the output - useful for external programs that call this function
|
||||
* CALLBACKSUB: a second arbitrary integer (0-32767) that is ignored by the Base Station and is simply echoed back in the output - useful for external programs (e.g. DCC++ Interface) that call this function
|
||||
*
|
||||
* returns: <r CALLBACKNUM|CALLBACKSUB|CV BIT VALUE)
|
||||
* where VALUE is a number from 0-1 as read from the requested CV bit, or -1 if verificaiton read fails
|
||||
*/
|
||||
pRegs->writeCVBit(com+1);
|
||||
break;
|
||||
|
||||
/***** READ CONFIGURATION VARIABLE BYTE FROM ENGINE DECODER ON PROGRAMMING TRACK ****/
|
||||
|
||||
case 'R': // <R CV CALLBACKNUM CALLBACKSUB>
|
||||
/*
|
||||
* reads a Configuration Variable from the decoder of an engine on the programming track
|
||||
*
|
||||
* CV: the number of the Configuration Variable memory location in the decoder to read from (1-1024)
|
||||
* CALLBACKNUM: an arbitrary integer (0-32767) that is ignored by the Base Station and is simply echoed back in the output - useful for external programs that call this function
|
||||
* CALLBACKSUB: a second arbitrary integer (0-32767) that is ignored by the Base Station and is simply echoed back in the output - useful for external programs (e.g. DCC++ Interface) that call this function
|
||||
*
|
||||
* returns: <r CALLBACKNUM|CALLBACKSUB|CV VALUE)
|
||||
* where VALUE is a number from 0-255 as read from the requested CV, or -1 if read could not be verified
|
||||
*/
|
||||
pRegs->readCV(com+1);
|
||||
break;
|
||||
|
||||
/***** TURN ON POWER FROM MOTOR SHIELD TO TRACKS ****/
|
||||
|
||||
case '1': // <1>
|
||||
/*
|
||||
* enables power from the motor shield to the main operations and programming tracks
|
||||
*
|
||||
* returns: <p1>
|
||||
*/
|
||||
digitalWrite(SIGNAL_ENABLE_PIN_PROG,HIGH);
|
||||
digitalWrite(SIGNAL_ENABLE_PIN_MAIN,HIGH);
|
||||
INTERFACE.print("<p1>");
|
||||
break;
|
||||
|
||||
/***** TURN OFF POWER FROM MOTOR SHIELD TO TRACKS ****/
|
||||
|
||||
case '0': // <0>
|
||||
/*
|
||||
* disables power from the motor shield to the main operations and programming tracks
|
||||
*
|
||||
* returns: <p0>
|
||||
*/
|
||||
digitalWrite(SIGNAL_ENABLE_PIN_PROG,LOW);
|
||||
digitalWrite(SIGNAL_ENABLE_PIN_MAIN,LOW);
|
||||
INTERFACE.print("<p0>");
|
||||
break;
|
||||
|
||||
/***** READ MAIN OPERATIONS TRACK CURRENT ****/
|
||||
|
||||
case 'c': // <c>
|
||||
/*
|
||||
* reads current being drawn on main operations track
|
||||
*
|
||||
* returns: <a CURRENT>
|
||||
* where CURRENT = 0-1024, based on exponentially-smoothed weighting scheme
|
||||
*/
|
||||
INTERFACE.print("<a");
|
||||
INTERFACE.print(int(mMonitor->current));
|
||||
INTERFACE.print(">");
|
||||
break;
|
||||
|
||||
/***** READ STATUS OF DCC++ BASE STATION ****/
|
||||
|
||||
case 's': // <s>
|
||||
/*
|
||||
* returns status messages containing track power status, throttle status, turn-out status, and a version number
|
||||
* NOTE: this is very useful as a first command for an interface to send to this sketch in order to verify connectivity and update any GUI to reflect actual throttle and turn-out settings
|
||||
*
|
||||
* returns: series of status messages that can be read by an interface to determine status of DCC++ Base Station and important settings
|
||||
*/
|
||||
if(digitalRead(SIGNAL_ENABLE_PIN_PROG)==LOW) // could check either PROG or MAIN
|
||||
INTERFACE.print("<p0>");
|
||||
else
|
||||
INTERFACE.print("<p1>");
|
||||
|
||||
for(int i=1;i<=MAX_MAIN_REGISTERS;i++){
|
||||
if(mRegs->speedTable[i]==0)
|
||||
continue;
|
||||
INTERFACE.print("<T");
|
||||
INTERFACE.print(i); INTERFACE.print(" ");
|
||||
if(mRegs->speedTable[i]>0){
|
||||
INTERFACE.print(mRegs->speedTable[i]);
|
||||
INTERFACE.print(" 1>");
|
||||
} else{
|
||||
INTERFACE.print(-mRegs->speedTable[i]);
|
||||
INTERFACE.print(" 0>");
|
||||
}
|
||||
}
|
||||
INTERFACE.print("<iDCC++ BASE STATION FOR ARDUINO ");
|
||||
INTERFACE.print(ARDUINO_TYPE);
|
||||
INTERFACE.print(" / ");
|
||||
INTERFACE.print(MOTOR_SHIELD_NAME);
|
||||
INTERFACE.print(": V-");
|
||||
INTERFACE.print(VERSION);
|
||||
INTERFACE.print(" / ");
|
||||
INTERFACE.print(__DATE__);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(__TIME__);
|
||||
INTERFACE.print(">");
|
||||
|
||||
INTERFACE.print("<N");
|
||||
INTERFACE.print(COMM_TYPE);
|
||||
INTERFACE.print(": ");
|
||||
|
||||
#if COMM_TYPE == 0
|
||||
INTERFACE.print("SERIAL>");
|
||||
#elif COMM_TYPE == 1
|
||||
INTERFACE.print(Ethernet.localIP());
|
||||
INTERFACE.print(">");
|
||||
#endif
|
||||
|
||||
Turnout::show();
|
||||
Output::show();
|
||||
|
||||
break;
|
||||
|
||||
/***** STORE SETTINGS IN EEPROM ****/
|
||||
|
||||
case 'E': // <E>
|
||||
/*
|
||||
* stores settings for turnouts and sensors EEPROM
|
||||
*
|
||||
* returns: <e nTurnouts nSensors>
|
||||
*/
|
||||
|
||||
EEStore::store();
|
||||
INTERFACE.print("<e ");
|
||||
INTERFACE.print(EEStore::eeStore->data.nTurnouts);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(EEStore::eeStore->data.nSensors);
|
||||
INTERFACE.print(" ");
|
||||
INTERFACE.print(EEStore::eeStore->data.nOutputs);
|
||||
INTERFACE.print(">");
|
||||
break;
|
||||
|
||||
/***** CLEAR SETTINGS IN EEPROM ****/
|
||||
|
||||
case 'e': // <e>
|
||||
/*
|
||||
* clears settings for Turnouts in EEPROM
|
||||
*
|
||||
* returns: <O>
|
||||
*/
|
||||
|
||||
EEStore::clear();
|
||||
INTERFACE.print("<O>");
|
||||
break;
|
||||
|
||||
/***** PRINT CARRIAGE RETURN IN SERIAL MONITOR WINDOW ****/
|
||||
|
||||
case ' ': // < >
|
||||
/*
|
||||
* simply prints a carriage return - useful when interacting with Ardiuno through serial monitor window
|
||||
*
|
||||
* returns: a carriage return
|
||||
*/
|
||||
INTERFACE.println("");
|
||||
break;
|
||||
|
||||
///
|
||||
/// THE FOLLOWING COMMANDS ARE NOT NEEDED FOR NORMAL OPERATIONS AND ARE ONLY USED FOR TESTING AND DEBUGGING PURPOSES
|
||||
/// PLEASE SEE SPECIFIC WARNINGS IN EACH COMMAND BELOW
|
||||
///
|
||||
|
||||
/***** ENTER DIAGNOSTIC MODE ****/
|
||||
|
||||
case 'D': // <D>
|
||||
/*
|
||||
* changes the clock speed of the chip and the pre-scaler for the timers so that you can visually see the DCC signals flickering with an LED
|
||||
* SERIAL COMMUNICAITON WILL BE INTERUPTED ONCE THIS COMMAND IS ISSUED - MUST RESET BOARD OR RE-OPEN SERIAL WINDOW TO RE-ESTABLISH COMMS
|
||||
*/
|
||||
|
||||
Serial.println("\nEntering Diagnostic Mode...");
|
||||
delay(1000);
|
||||
|
||||
bitClear(TCCR1B,CS12); // set Timer 1 prescale=8 - SLOWS NORMAL SPEED BY FACTOR OF 8
|
||||
bitSet(TCCR1B,CS11);
|
||||
bitClear(TCCR1B,CS10);
|
||||
|
||||
#ifdef ARDUINO_AVR_UNO // Configuration for UNO
|
||||
|
||||
bitSet(TCCR0B,CS02); // set Timer 0 prescale=256 - SLOWS NORMAL SPEED BY A FACTOR OF 4
|
||||
bitClear(TCCR0B,CS01);
|
||||
bitClear(TCCR0B,CS00);
|
||||
|
||||
#else // Configuration for MEGA
|
||||
|
||||
bitClear(TCCR3B,CS32); // set Timer 3 prescale=8 - SLOWS NORMAL SPEED BY A FACTOR OF 8
|
||||
bitSet(TCCR3B,CS31);
|
||||
bitClear(TCCR3B,CS30);
|
||||
|
||||
#endif
|
||||
|
||||
CLKPR=0x80; // THIS SLOWS DOWN SYSYEM CLOCK BY FACTOR OF 256
|
||||
CLKPR=0x08; // BOARD MUST BE RESET TO RESUME NORMAL OPERATIONS
|
||||
|
||||
break;
|
||||
|
||||
/***** WRITE A DCC PACKET TO ONE OF THE REGSITERS DRIVING THE MAIN OPERATIONS TRACK ****/
|
||||
|
||||
case 'M': // <M REGISTER BYTE1 BYTE2 [BYTE3] [BYTE4] [BYTE5]>
|
||||
/*
|
||||
* writes a DCC packet of two, three, four, or five hexidecimal bytes to a register driving the main operations track
|
||||
* FOR DEBUGGING AND TESTING PURPOSES ONLY. DO NOT USE UNLESS YOU KNOW HOW TO CONSTRUCT NMRA DCC PACKETS - YOU CAN INADVERTENTLY RE-PROGRAM YOUR ENGINE DECODER
|
||||
*
|
||||
* REGISTER: an internal register number, from 0 through MAX_MAIN_REGISTERS (inclusive), to write (if REGISTER=0) or write and store (if REGISTER>0) the packet
|
||||
* BYTE1: first hexidecimal byte in the packet
|
||||
* BYTE2: second hexidecimal byte in the packet
|
||||
* BYTE3: optional third hexidecimal byte in the packet
|
||||
* BYTE4: optional fourth hexidecimal byte in the packet
|
||||
* BYTE5: optional fifth hexidecimal byte in the packet
|
||||
*
|
||||
* returns: NONE
|
||||
*/
|
||||
mRegs->writeTextPacket(com+1);
|
||||
break;
|
||||
|
||||
/***** WRITE A DCC PACKET TO ONE OF THE REGSITERS DRIVING THE MAIN OPERATIONS TRACK ****/
|
||||
|
||||
case 'P': // <P REGISTER BYTE1 BYTE2 [BYTE3] [BYTE4] [BYTE5]>
|
||||
/*
|
||||
* writes a DCC packet of two, three, four, or five hexidecimal bytes to a register driving the programming track
|
||||
* FOR DEBUGGING AND TESTING PURPOSES ONLY. DO NOT USE UNLESS YOU KNOW HOW TO CONSTRUCT NMRA DCC PACKETS - YOU CAN INADVERTENTLY RE-PROGRAM YOUR ENGINE DECODER
|
||||
*
|
||||
* REGISTER: an internal register number, from 0 through MAX_MAIN_REGISTERS (inclusive), to write (if REGISTER=0) or write and store (if REGISTER>0) the packet
|
||||
* BYTE1: first hexidecimal byte in the packet
|
||||
* BYTE2: second hexidecimal byte in the packet
|
||||
* BYTE3: optional third hexidecimal byte in the packet
|
||||
* BYTE4: optional fourth hexidecimal byte in the packet
|
||||
* BYTE5: optional fifth hexidecimal byte in the packet
|
||||
*
|
||||
* returns: NONE
|
||||
*/
|
||||
pRegs->writeTextPacket(com+1);
|
||||
break;
|
||||
|
||||
/***** ATTEMPTS TO DETERMINE HOW MUCH FREE SRAM IS AVAILABLE IN ARDUINO ****/
|
||||
|
||||
case 'F': // <F>
|
||||
/*
|
||||
* measure amount of free SRAM memory left on the Arduino based on trick found on the internet.
|
||||
* Useful when setting dynamic array sizes, considering the Uno only has 2048 bytes of dynamic SRAM.
|
||||
* Unfortunately not very reliable --- would be great to find a better method
|
||||
*
|
||||
* returns: <f MEM>
|
||||
* where MEM is the number of free bytes remaining in the Arduino's SRAM
|
||||
*/
|
||||
int v;
|
||||
INTERFACE.print("<f");
|
||||
INTERFACE.print((int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval));
|
||||
INTERFACE.print(">");
|
||||
break;
|
||||
|
||||
/***** LISTS BIT CONTENTS OF ALL INTERNAL DCC PACKET REGISTERS ****/
|
||||
|
||||
case 'L': // <L>
|
||||
/*
|
||||
* lists the packet contents of the main operations track registers and the programming track registers
|
||||
* FOR DIAGNOSTIC AND TESTING USE ONLY
|
||||
*/
|
||||
INTERFACE.println("");
|
||||
for(Register *p=mRegs->reg;p<=mRegs->maxLoadedReg;p++){
|
||||
INTERFACE.print("M"); INTERFACE.print((int)(p-mRegs->reg)); INTERFACE.print(":\t");
|
||||
INTERFACE.print((int)p); INTERFACE.print("\t");
|
||||
INTERFACE.print((int)p->activePacket); INTERFACE.print("\t");
|
||||
INTERFACE.print(p->activePacket->nBits); INTERFACE.print("\t");
|
||||
for(int i=0;i<10;i++){
|
||||
INTERFACE.print(p->activePacket->buf[i],HEX); INTERFACE.print("\t");
|
||||
}
|
||||
INTERFACE.println("");
|
||||
}
|
||||
for(Register *p=pRegs->reg;p<=pRegs->maxLoadedReg;p++){
|
||||
INTERFACE.print("P"); INTERFACE.print((int)(p-pRegs->reg)); INTERFACE.print(":\t");
|
||||
INTERFACE.print((int)p); INTERFACE.print("\t");
|
||||
INTERFACE.print((int)p->activePacket); INTERFACE.print("\t");
|
||||
INTERFACE.print(p->activePacket->nBits); INTERFACE.print("\t");
|
||||
for(int i=0;i<10;i++){
|
||||
INTERFACE.print(p->activePacket->buf[i],HEX); INTERFACE.print("\t");
|
||||
}
|
||||
INTERFACE.println("");
|
||||
}
|
||||
INTERFACE.println("");
|
||||
break;
|
||||
|
||||
} // switch
|
||||
}; // SerialCommand::parse
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
31
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/SerialCommand.h
Normal file
31
DCC-Centrale/Firmware/BaseStation/DCCpp_Uno/SerialCommand.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/**********************************************************************
|
||||
|
||||
SerialCommand.h
|
||||
COPYRIGHT (c) 2013-2016 Gregg E. Berman
|
||||
|
||||
Part of DCC++ BASE STATION for the Arduino
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SerialCommand_h
|
||||
#define SerialCommand_h
|
||||
|
||||
#include "PacketRegister.h"
|
||||
#include "CurrentMonitor.h"
|
||||
|
||||
#define MAX_COMMAND_LENGTH 30
|
||||
|
||||
struct SerialCommand{
|
||||
static char commandString[MAX_COMMAND_LENGTH+1];
|
||||
static volatile RegisterList *mRegs, *pRegs;
|
||||
static CurrentMonitor *mMonitor;
|
||||
static void init(volatile RegisterList *, volatile RegisterList *, CurrentMonitor *);
|
||||
static void parse(char *);
|
||||
static void process();
|
||||
}; // SerialCommand
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
37
DCC-Centrale/Firmware/BaseStation/README.md
Normal file
37
DCC-Centrale/Firmware/BaseStation/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
What’s DCC++
|
||||
------------
|
||||
|
||||
DCC++ is an open-source hardware and software system for the operation of DCC-equipped model railroads.
|
||||
|
||||
The system consists of two parts, the DCC++ Base Station and the DCC++ Controller.
|
||||
|
||||
The DCC++ Base Station consists of an Arduino micro controller fitted with an Arduino Motor Shield that can be connected directly to the tracks of a model railroad.
|
||||
|
||||
The DCC++ Controller provides operators with a customizable GUI to control their model railroad. It is written in Java using the Processing graphics library and IDE and communicates with the DCC++ Base Station via a standard serial connection over a USB cable or wireless over BlueTooth.
|
||||
|
||||
What’s in this Repository
|
||||
-------------------------
|
||||
|
||||
This repository, BaseStation-Uno, contains a complete DCC++ Base Station sketch designed for compiling and uploading into an Arduino Uno. All sketch files are in the folder named DCCpp_Uno. More information about the sketch can be found in the included PDF file.
|
||||
|
||||
To utilize this sketch, simply download a zip file of this repository and open the file DCCpp_Uno.ino within the DCCpp_Uno folder using your Arduino IDE. Please do not rename the folder containing the sketch code, nor add any files to that folder. The Arduino IDE relies on the structure and name of the folder to properly display and compile the code.
|
||||
|
||||
The latest production release of the Master branch is 1.2.1:
|
||||
|
||||
* Supports both the Arduino Uno and Arduino Mega
|
||||
* Built-in configuration for both the original Arduino Motor Shield as well as a Pololu MC33926 Motor Shield
|
||||
* Built-in configuration and support of Ethernet Shields (for use with Mega only)
|
||||
|
||||
For more information on the overall DCC++ system, please follow the links in the PDF file.
|
||||
|
||||
Detailed diagrams showing pin mappings and required jumpers for the Motor Shields can be found in the Documentation Repository
|
||||
|
||||
The Master branch contains all of the Base Station functionality showed in the DCC++ YouTube channel with the exception of 2 layout-specific modules:
|
||||
|
||||
* Control for an RGB LED Light Strip using pins 44, 45, and 46 on the Mega
|
||||
* An embedded AutoPilot routine that randomly selects a train to run through the entire layout, after which it is brought back into its original siding and the the patterns repeats with another randomly-selected train. This is the AutoPilot routine showed on the DCC++ YouTube channel. It does not require any computer, not DCC++ Controller to be running (DCC++ Controller contains a much more complicated 3-train Auto Pilot mode, also as shown on the DCC++ YouTube channel).
|
||||
|
||||
Since these modules are very layout-specififc, they are not included in the Master branch. However, they are included in the Development branch. Please feel free to download and copy any relevant code to customize your own version of DCC++ Base Station.
|
||||
|
||||
-December 27, 2015
|
||||
|
||||
Reference in New Issue
Block a user