Hier der Code mit Übersetzung
#include <MD_MAX72xx.h>
#include <SPI.h>
/* FastClockLN -- F. Cañada 2024 -- https://usuaris.tinet.cat/fmco/
This software and associated files are a DIY project that is not intended for commercial use.
This software uses libraries with different licenses, follow all their different terms included.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.
Sources are only provided for building and uploading to the device.
Commercial use is forbidden.
v0.1 02jan24 Start writting code
v0.2 05jan24 Slave, Poll, RTC Poll & master modes working
v0.3 06jan24 Added configuration menu
v0.4 09jan24 Added pause button on Master
*/
// FastClockLN program version
#define VER_H 0
#define VER_L 4
#include <MD_MAX72xx.h> // https://github.com/MajicDesigns/MD_MAX72XX v3.5.1
#include <Keypad.h> // https://github.com/Chris--A/Keypad v3.1.1
#include "uRTCLib.h" // https://github.com/Naguissa/uRTCLib v6.6.1 Libreria RTC DS3231
#include <LocoNet.h> // https://github.com/mrrwa/LocoNet/ v1.1.9
#include "Wire.h" // Libreria I2C
#include <EEPROM.h>
////////////////////////////////////////////////////////////
// ***** USER OPTIONS *****
////////////////////////////////////////////////////////////
/* ///< DR - Digits as rows; CR - Columns Reversed; RR - Rows Reversed
GENERIC_HW, ///< Use 'generic' style hardware modules commonly available.
FC16_HW, ///< Use FC-16 style hardware module.
PAROLA_HW, ///< Use the Parola style hardware modules.
ICSTATION_HW, ///< Use ICStation style hardware module.
DR0CR0RR0_HW, ///< Structured name
DR0CR0RR1_HW, ///< Structured name
DR0CR1RR0_HW, ///< Structured name; equivalent to GENERIC_HW
DR0CR1RR1_HW, ///< Structured name
DR1CR0RR0_HW, ///< Structured name; equivalent to FC16_HW
DR1CR0RR1_HW, ///< Structured name
DR1CR1RR0_HW, ///< Structured name; equivalent to PAROLA_HW
DR1CR1RR1_HW ///< Structured name; equivalent to ICSTATION_HW
*/
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW // Tipo modulo
//#define USE_SPI // Uncomment if standard (faster) SPI pins are used instead of the PacoMouse board
////////////////////////////////////////////////////////////
// ***** END OF USER OPTIONS *****
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// ***** MAX7219 ***** #define HARDWARE_TYPE MD_MAX72XX::FC16_HW // module type
////////////////////////////////////////////////////////////
#define MAX_DEVICES 4 // Number of modules 8x8 (4 modules all on - brightness 0: 90mA, brightness 15: 300mA)
#define SLIDE_TIME 45 // Sliding time text
#ifdef USE_SPI
#include <SPI.h>
/*
SPI hardware interface // 8MHz SPI (~1us/byte)
SCK: D13 - CLK
MISO: D12 - No usado
MOSI: D11 - DIN
SS: D10 - CS
*/
#define CS_PIN 10 // or SS
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
#else
// Arbitrary pins // 65.8kHz (~122us/byte)
// PacoMouse board
#define CLK_PIN 2 // or SCK
#define DATA_PIN 12 // or MOSI
#define CS_PIN A2 // or SS
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
#endif
byte brillo;
bool updateMX;
byte scrMX;
byte menuOpt;
byte optBrillo, optHour, optMin, optRate, optModo;
enum screen { MX_LOGO,
MX_TIME,
MX_GPOFF,
MX_MENU,
MX_OPT_MODO,
MX_OPT_TIME,
MX_OPT_RTC,
MX_OPT_RATE,
MX_OPT_BRILLO };
enum menu { OPT_MODO,
OPT_TIME,
OPT_RTC_TIME,
OPT_RATE,
OPT_BRILLO,
OPT_MAX };
char menuTxt[][8] = {
// Name of menu options
"Mode",
"Hour",
"Uhr",
"Ratio",
"Glow",
};
////////////////////////////////////////////////////////////
// ***** KEYBOARD *****
////////////////////////////////////////////////////////////
const int pinFILA1 = 3; // Keypad 3x4 or 4x4 pins
const int pinFILA2 = 4;
const int pinFILA3 = 5;
const int pinFILA4 = 6;
const int pinCOLUMNA1 = 9;
enum teclas { K_NONE,
K_MENU,
K_HOUR_PLUS,
K_MIN_MINUS,
K_ENTER };
#define FILAS 4 // Rows and Columns of the keyboard matrix
#define COLUMNAS 1
char keys[FILAS][COLUMNAS] = { // Key layout
{ K_MENU },
{ K_HOUR_PLUS },
{ K_MIN_MINUS },
{ K_ENTER }
};
byte filaPins[FILAS] = { pinFILA1, pinFILA2, pinFILA3, pinFILA4 }; // keyboard row pins
byte colPins[COLUMNAS] = { pinCOLUMNA1 }; // keyboard column pins
Keypad keypad = Keypad(makeKeymap(keys), filaPins, colPins, FILAS, COLUMNAS);
byte keyValue; // key pressed value
bool keyOn;
////////////////////////////////////////////////////////////
// ***** CLOCK *****
////////////////////////////////////////////////////////////
#define I2C_ADDRESS_CLOCK 0x68 // Direccion I2C del DS3231
enum Mode { // Clock modes:
LN_SLAVE, // Slave: Just listen to the Fast Clock (no need RTC DS3231)
LN_POLL, // Poll: Send SYNC periodically (no need RTC DS3231)
RTC_POLL, // RTC Poll: Writes real time at startup and then sends SYNC periodically
LN_MASTER, // Master: Implementa el Slot Fast Clock (no need RTC DS3231)
MODE_MAX,
}; // Clock mode settings
char output[80];
char modoTxt[][12] = {
"Slave",
"Sync",
"RTC Sync",
"Master",
};
enum opt { SEL_RATE,
SEL_LN_TIME,
SEL_RTC_TIME };
enum Settings {
EE_BRILLO,
EE_MODE,
}; // EEPROM settings
uRTCLib rtc(I2C_ADDRESS_CLOCK); // The I2C address of the DS3231 clock module
unsigned long rtcTimeout, dotTimer;
byte hour;
byte minute;
byte seconds;
byte lastMinute;
byte errorDS3231;
byte modeClock;
bool showDot;
byte pauseRate;
byte pauseHour;
byte pauseMin;
byte pauseSecond;
////////////////////////////////////////////////////////////
// ***** LOCONET *****
////////////////////////////////////////////////////////////
#define ONE_DAY 86400000UL
#define SYNC_TIME 61000UL // Time to SYNC (70..100s LPE, 50..60s LRT)
const int pinLN_RXD = 8; // Loconet pins. DO NOT CHANGE, LN_RXD pin must be Arduino ICP pin
const int pinLN_TXD = 7;
static lnMsg *LnPacket; // Package received by Loconet
lnMsg SendPacket; // Package to be sent by Loconet
unsigned long syncTimer;
bool doSendSync;
byte clockHour, clockMin, clockRate;
unsigned long clockTimer, clockInterval;
bool newMinuteLN;
byte trk;
enum cs { CS_IB_I,
CS_IB_II,
CS_DR5K,
CS_DAISY,
CS_63820,
CS_DIG,
CS_UNKNOW };
byte typeCS;
char csTxt[][15] = {
// Name of the power plants
"Intellibox I",
"Intellibox II",
"DR5000",
"Daisy",
"63820",
"Digitrax",
"desconocido",
};
////////////////////////////////////////////////////////////
// ***** PROGRAM *****
////////////////////////////////////////////////////////////
void setup() {
byte i;
Wire.begin(); // I2C port initialization. SDA = A4, SCL = A5
Wire.setClock(400000L);
initVariables(); // Initial state
mx.begin(); // Initialize MX7219 display
mx.clear();
mx.control(0, MAX_DEVICES - 1, MD_MAX72XX::INTENSITY, brillo);
mx.control(MD_MAX72XX::UPDATE, false); // Disable auto-update
LocoNet.init(pinLN_TXD); // Initializes Loconet communications
identifyCS();
}
void loop() {
loconetProcess(); // Loconet comunications
if (doSendSync)
sendSYNC();
if (updateMX)
showMX();
readKeypad(); // keyboard reading
if (keyOn) // a key has been pressed
controlKeypad();
}
void initVariables() {
keypad.setDebounceTime(20);
keyOn = false;
brillo = EEPROM.read(EE_BRILLO) & 0x0F; // Brightness: 0..15 (30mA..190mA average)
scrMX = MX_LOGO;
updateMX = true;
typeCS = CS_UNKNOW;
showDot = true;
hour = 0;
minute = 0;
lastMinute = 99;
clockRate = 0; // fast clock internal
clockHour = 0;
clockMin = 0;
pauseRate = 0;
clockInterval = ONE_DAY;
clockTimer = millis();
rtcTimeout = millis();
syncTimer = millis();
Wire.beginTransmission(I2C_ADDRESS_CLOCK); // Search DS3231
errorDS3231 = Wire.endTransmission();
modeClock = EEPROM.read(EE_MODE);
startMode();
trk = 0x07; // GPON, not IDLE, LNv1.1
}
void startMode() {
switch (modeClock) {
case LN_SLAVE:
break;
case LN_POLL:
doSendSync = true;
break;
case RTC_POLL:
if (errorDS3231) // If RTC is not installed, it is a slave
modeClock = LN_SLAVE;
break;
case LN_MASTER:
clockRate = (errorDS3231) ? 0 : 1; // If RTC is installed it starts automatically.
break;
default: // Configuration error, convert to slave
modeClock = LN_SLAVE;
break;
}
}
////////////////////////////////////////////////////////////
// ***** KEYBOARD *****
////////////////////////////////////////////////////////////
void readKeypad() {
byte inputButton;
inputButton = keypad.getKey(); // read keyboard
if (inputButton != NO_KEY) {
keyValue = inputButton;
keyOn = true;
}
}
void controlKeypad() { // Action when pressing a key
keyOn = false;
switch (scrMX) { // according to the current screen
case MX_TIME:
isKeyMenu();
if (modeClock == LN_MASTER) { // Only in Master mode
switch (keyValue) {
case K_HOUR_PLUS: // Resume
if (pauseRate > 0) {
clockRate = pauseRate;
if ((!errorDS3231) && (clockRate == 1))
rtc.set(pauseSecond, pauseMin, pauseHour, URTCLIB_WEEKDAY_MONDAY, 1, 1, 24);
pauseRate = 0;
newMinuteLN = true;
}
break;
case K_MIN_MINUS: // Pause
if (clockRate > 0) {
pauseRate = clockRate;
if (!errorDS3231) {
rtc.refresh(); // Lee RTC DS3231
pauseHour = rtc.hour();
pauseMin = rtc.minute();
pauseSecond = rtc.second();
}
clockRate = 0;
showDot = true;
newMinuteLN = true;
}
break;
}
}
break;
case MX_LOGO:
break;
case MX_GPOFF:
break;
case MX_MENU:
switch (keyValue) {
case K_MENU: // exit the menu
scrMX = (trk & 0x01) ? MX_TIME : MX_GPOFF;
break;
case K_ENTER:
switch (menuOpt) { // enter the menu option
case OPT_BRILLO:
scrMX = MX_OPT_BRILLO;
optBrillo = brillo;
break;
case OPT_TIME:
scrMX = MX_OPT_TIME;
optHour = clockHour;
optMin = clockMin;
break;
case OPT_RTC_TIME:
if (errorDS3231) {
slideText(SLIDE_TIME, "RTC not found");
} else {
scrMX = MX_OPT_RTC;
rtc.refresh(); // Lee RTC DS3231
optHour = rtc.hour();
optMin = rtc.minute();
}
break;
case OPT_RATE:
scrMX = MX_OPT_RATE;
optRate = clockRate;
break;
case OPT_MODO:
scrMX = MX_OPT_MODO;
optModo = modeClock;
break;
}
showDot = true;
break;
case K_HOUR_PLUS:
menuOpt++;
if (menuOpt == OPT_MAX)
menuOpt = OPT_MODO;
updateMX = true;
break;
case K_MIN_MINUS:
menuOpt = (menuOpt > 0) ? --menuOpt : OPT_BRILLO;
break;
}
updateMX = true;
break;
case MX_OPT_BRILLO: // change screen brightness
isKeyMenu();
switch (keyValue) {
case K_HOUR_PLUS:
if (optBrillo < 15)
optBrillo++;
break;
case K_MIN_MINUS:
if (optBrillo > 0)
optBrillo--;
break;
case K_ENTER:
scrMX = MX_MENU;
brillo = optBrillo;
EEPROM.update(EE_BRILLO, brillo);
break;
}
updateMX = true;
break;
case MX_OPT_RATE: // Change ratio 0:1 .. 127:1
isKeyMenu();
switch (keyValue) {
case K_HOUR_PLUS:
if (optRate < 127)
optRate++;
break;
case K_MIN_MINUS:
if (optRate > 0)
optRate--;
break;
case K_ENTER:
scrMX = MX_MENU;
clockRate = optRate;
pauseRate = 0; // cancela pausa
if (clockRate > 0)
clockInterval = 60000UL / (unsigned long)clockRate; // calculate interval for one minute
if (modeClock == LN_MASTER)
sendTimeLN(clockHour, clockMin, clockRate, false);
else
sendTimeLN(clockHour, clockMin, clockRate, true);
break;
}
updateMX = true;
break;
case MX_OPT_TIME: // Change the time on the Fast Clock
isKeyMenu();
switch (keyValue) {
case K_HOUR_PLUS:
if (optHour < 23)
optHour++;
else
optHour = 0;
break;
case K_MIN_MINUS:
if (optMin < 59)
optMin++;
else
optMin = 0;
break;
case K_ENTER:
scrMX = MX_MENU;
clockHour = optHour;
clockMin = optMin;
clockTimer = millis();
newMinuteLN = true;
if (modeClock != LN_MASTER)
sendTimeLN(clockHour, clockMin, clockRate, true);
break;
}
updateMX = true;
break;
case MX_OPT_RTC: // Change RTC time
isKeyMenu();
switch (keyValue) {
case K_HOUR_PLUS:
if (optHour < 23)
optHour++;
else
optHour = 0;
break;
case K_MIN_MINUS:
if (optMin < 59)
optMin++;
else
optMin = 0;
break;
case K_ENTER:
scrMX = MX_MENU;
lastMinute = 99;
rtc.set(0, optMin, optHour, URTCLIB_WEEKDAY_MONDAY, 1, 1, 24);
break;
}
updateMX = true;
break;
case MX_OPT_MODO: // Change mode
isKeyMenu();
switch (keyValue) {
case K_HOUR_PLUS:
optModo++;
if (optModo == MODE_MAX)
optModo = LN_SLAVE;
break;
case K_MIN_MINUS:
optModo = (optModo > 0) ? --optModo : LN_MASTER;
break;
case K_ENTER:
scrMX = MX_MENU;
modeClock = optModo;
EEPROM.update(EE_MODE, modeClock);
startMode(); // start new mode
break;
}
updateMX = true;
break;
default:
isKeyMenu();
break;
}
}
void isKeyMenu() { // Pressing menu returns to the main menu
if (keyValue == K_MENU) {
scrMX = MX_MENU;
menuOpt = OPT_MODO;
updateMX = true;
mx.control(0, MAX_DEVICES - 1, MD_MAX72XX::INTENSITY, brillo);
}
}
////////////////////////////////////////////////////////////
// ***** MAX7219 *****
////////////////////////////////////////////////////////////
// 3 2 1
//10987654321098765432109876543210
// xxxxx xxxxx xx xxxxx xxxxx
//-------- --------
void printTime(byte hora, byte minuto) { // Print time on MX7219 display
byte decena, unidad;
mx.clear();
decena = hora / 10;
unidad = hora % 10;
if (decena)
printDigit(30, decena);
printDigit(23, unidad);
if (showDot)
mx.setChar(16, ':');
decena = minuto / 10;
unidad = minuto % 10;
printDigit(12, decena);
printDigit(5, unidad);
mx.update();
}
void printDigit(byte pos, byte number) { // print a digit
if (number == 1)
pos--;
mx.setChar(pos, number + 0x30);
}
int printText(int pos, char *text) { // Print a text
int w, i;
for (i = 0; text[i]; i++) {
w = mx.setChar(pos, text[i]);
pos = pos - w;
mx.setColumn(pos--, 0x00);
if (pos < 0)
break;
}
mx.update();
return pos;
}
void slideText(int ms_delay, char *mensaje) { // Print a mobile message
int col = 0;
int last_pos, ms_count;
bool completo = false;
mx.clear();
while ((!completo) && (!updateMX)) { // until it completes or the screen is updated
last_pos = printText(col, mensaje);
ms_count = ms_delay;
while (ms_count--) {
delay(1);
loconetProcess(); // Loconet comunications
}
col++;
if (last_pos > (int)mx.getColumnCount())
completo = true;
}
}
void showMX() { // display screen on MX7219 display
updateMX = false;
switch (scrMX) {
case MX_LOGO:
slideText(SLIDE_TIME, "FastClockLN");
showCS();
showMode();
scrMX = (trk & 0x01) ? MX_TIME : MX_GPOFF;
updateMX = true;
break;
case MX_TIME:
printTime(clockHour, clockMin);
break;
case MX_GPOFF:
slideText(SLIDE_TIME, "Power OFF");
updateMX = true;
break;
case MX_MENU:
mx.clear();
printText(31, menuTxt[menuOpt]);
break;
case MX_OPT_BRILLO:
mx.clear();
snprintf(output, 80, "%d", optBrillo);
if (optBrillo > 9)
printText(19, output);
else
printText(15, output);
mx.control(0, MAX_DEVICES - 1, MD_MAX72XX::INTENSITY, optBrillo);
mx.update();
break;
case MX_OPT_RTC:
case MX_OPT_TIME:
printTime(optHour, optMin);
break;
case MX_OPT_RATE:
mx.clear();
if (optRate > 0) {
snprintf(output, 80, "%d:1", optRate);
if (optRate > 99) {
printText(26, output);
} else {
if (optRate > 9)
printText(24, output);
else
printText(21, output);
}
} else {
printText(28, "Pausa");
}
break;
case MX_OPT_MODO:
mx.clear();
printText(31, modoTxt[optModo]);
break;
}
}
// 3 2 1
//1098765432109876 5432109876543210
//-------- --------
void showMode() {
snprintf(output, 80, "Mode: %s", modoTxt[modeClock]);
slideText(SLIDE_TIME, output);
}
void showCS() {
snprintf(output, 80, "Connected to %s", csTxt[typeCS]);
slideText(SLIDE_TIME, output);
}
////////////////////////////////////////////////////////////
// ***** LOCONET DECODE *****
////////////////////////////////////////////////////////////
void loconetProcess() { // Decoding Loconet packets
LnPacket = LocoNet.receive(); // receives Loconet packets
if (LnPacket) {
if (!LocoNet.processSwitchSensorMessage(LnPacket)) { // Packages diverted and sensors
switch (LnPacket->sz.command) {
case OPC_SL_RD_DATA: // slot information
trk = LnPacket->sd.trk;
if (LnPacket->sd.slot == FC_SLOT) { // FAST Clock
if (modeClock != LN_MASTER) {
if (LnPacket->fc.clk_cntrl & 0x40) { // bit 6 = 1; data is valid clock info
setFastClock();
}
}
}
if (LnPacket->sd.slot == 0x00) { // Slot 0. ID from the central
getCS();
}
break;
case OPC_RQ_SL_DATA:
if (LnPacket->sr.slot == FC_SLOT) { // FAST Clock SYNC
if (modeClock == LN_MASTER) {
sendTimeLN(clockHour, clockMin, clockRate, false);
}
}
break;
case OPC_WR_SL_DATA:
trk = LnPacket->sd.trk;
if (LnPacket->sd.slot == FC_SLOT) { // FAST Clock
setFastClock(); // update. JMRI sends only EF 0E 7B ... (OPC_WR_SL_DATA) with clk_cntrl == 0
if ((modeClock == LN_MASTER) && (!errorDS3231)) {
if (clockRate == 1) // save new time in RTC if 1:1
rtc.set(0, clockMin, clockHour, URTCLIB_WEEKDAY_MONDAY, 1, 1, 24);
}
}
break;
}
}
}
if (clockRate > 0) { // Update internal fast clock
if (millis() - clockTimer > clockInterval) {
clockTimer = millis();
clockMin++;
if (clockMin > 59) {
clockMin = 0;
clockHour++;
}
if (clockHour > 23)
clockHour = 0;
newMinuteLN = true;
}
if (scrMX == MX_TIME) { // blinking clock dots every second
if (millis() - dotTimer > 1000UL) {
dotTimer = millis();
if (showDot)
mx.setChar(16, ':');
else {
mx.setColumn(16, 0x00);
mx.setColumn(15, 0x00);
}
mx.update();
showDot = !showDot;
}
}
}
switch (modeClock) {
case LN_POLL: // mode 'Sync'
if (millis() - syncTimer > SYNC_TIME) { // timer for SYNC
syncTimer = millis();
doSendSync = true;
}
case LN_SLAVE: // 'Slave and Sync' mode
if ((scrMX == MX_TIME) && newMinuteLN) { // update clock
updateMX = true;
newMinuteLN = false;
}
break;
case RTC_POLL: // Mode 'RTC Sync'
if (millis() - syncTimer > 22000UL) {
rtc.refresh(); // Lee RTC DS3231
hour = rtc.hour();
minute = rtc.minute();
seconds = rtc.second();
sendTimeLN(hour, minute, 1, true); // Send time via LN when connecting
syncTimer = millis() - (unsigned long)(seconds * 1000UL); // fix for sync time in hh:mm:00
modeClock = LN_POLL; // switch to Sync mode
}
break;
case LN_MASTER: // mode 'Master'
if ((clockRate == 1) && (!errorDS3231)) { // Read RTC every half second if installed and Ratio 1:1
if (millis() - rtcTimeout > 500) {
rtcTimeout = millis();
clockTimer = millis(); // reset internal timeout
rtc.refresh(); // Lee RTC DS3231
if (rtc.minute() != lastMinute) { // check if the minute changes
clockHour = rtc.hour();
clockMin = rtc.minute();
lastMinute = clockMin;
newMinuteLN = true;
}
}
}
if (newMinuteLN) { // Every new minute sends by LN automatically
sendTimeLN(clockHour, clockMin, clockRate, false);
if (scrMX == MX_TIME)
updateMX = true;
newMinuteLN = false;
}
break;
}
}
void setFastClock() {
if (LnPacket->fc.clk_rate != clockRate) { // 0 = Freeze clock, 1 = normal, 10 = 10:1 etc. Max is 0x7f
clockRate = LnPacket->fc.clk_rate; // calculate new internal interval
if (clockRate > 0)
clockInterval = 60000UL / (unsigned long)clockRate; // calculate interval for one minute
showDot = true;
} // [EF 0E 7B 01 7F 79 43 07 68 1B 40 00 00 15] JMRI: 00:00 DAY 27
clockMin = LnPacket->fc.mins_60 - 0x43; // 256 - minutes ???
clockHour = LnPacket->fc.hours_24 - 0x68; // 256 - hours ???
clockTimer = millis(); // reset local sub-minute phase counter
if (modeClock != LN_MASTER)
newMinuteLN = true;
if (scrMX == MX_TIME)
updateMX = true;
}
void sendTimeLN(byte hh, byte mm, byte rate, bool writeTime) {
if ((hh > 23) || (mm > 59)) { // Avoid sending wrong time
hh = 11;
mm = 33;
}
if (writeTime)
SendPacket.data[0] = OPC_WR_SL_DATA; // Set new time
else
SendPacket.data[0] = OPC_SL_RD_DATA; // write not accepted by TwinCenter (LACK: B4 6F 00 24)
SendPacket.data[1] = 0x0E;
SendPacket.data[2] = FC_SLOT;
SendPacket.data[3] = rate; // clock rate: 1 for RTC
SendPacket.data[4] = 0x7F;
SendPacket.data[5] = 0x79;
SendPacket.data[6] = mm + 0x43;
SendPacket.data[7] = trk; // trk
SendPacket.data[8] = hh + 0x68;
SendPacket.data[9] = 0x00; // day
SendPacket.data[10] = 0x40; // control
SendPacket.data[11] = 0x00; // ID
SendPacket.data[12] = 0x00;
LocoNet.send(&SendPacket);
}
void sendSYNC() { // Envia SYNC
SendPacket.data[0] = OPC_RQ_SL_DATA;
SendPacket.data[1] = FC_SLOT;
SendPacket.data[2] = 0x00;
LocoNet.send(&SendPacket);
doSendSync = false;
}
void notifyPower(uint8_t State) { // Callback Power ON/OFF
if (State == 1) { // OPC_GPON
bitSet(trk, 0);
scrMX = MX_TIME;
} else { // OPC_GPOFF
bitClear(trk, 0);
scrMX = MX_GPOFF;
}
updateMX = true;
}
void identifyCS() { // Read slot 0 to identify central
SendPacket.data[0] = OPC_RQ_SL_DATA;
SendPacket.data[1] = 0x00;
SendPacket.data[2] = 0x00;
LocoNet.send(&SendPacket);
}
/*
Reading Slot #0 can be used to identify the Intellibox. In fact, it can be
used to identify all of our 'LocoNet master' devices: one can tell that it is
an Intellibox, a DAISY or a Power 2 in analog mode by looking at the answer
of a Slot #0 read request. One would find that ID1 and ID2 have these values:
ID1 = 'I', ID2 = 'B'.
Furthermore, the value of the Speed byte tells the "level" of the LocoNet
driver of the Command Station. The current firmware of the Intellibox (IB),
of the DAISY and of the Power2 (in analog mode) report the Speed byte as
having value 2 (this basically means: the LocoNet driver supports our FRED
- used in the "intelligent, 4 locos" mode).
Now, if one needs to find out exactly to which of our Command Stations one
is connected to (Intellibox, DAISY, Power2 in analog mode - or Digitrax),
then a Slot write of Slot #0 has to be performed (e.g. using the very values
one just read from that Slot):
- a Power 2 in analog mode does not reply at all
- an IB/TC replies with LACK, error = 0Eh (OPC_WR_SL_DATA length)
- a DAISY replies with LACK, error = 1
- a Digitrax Chief replies with LACK 'Ok' (7Fh)
One can tell a Digitrax CS also by looking at ID1 and ID2 of the Slot #0 read.
Detect Hardware type (Read Slot 0)
0xE7, 0x0E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x49, 0x42, 0x18 "Intellibox / TwinCenter" ADR: 0 ID: 'IB' SPD: 2
0xE7, 0x0E, 0x00, 0x03, 0x00, 0x03, 0x00, 0x06, 0x08, 0x00, 0x00, 0x49, 0x42, 0x13 "DR5000" ADR: 0 ID: 'IB' SPD: 3
0xE7, 0x0E, 0x00, 0x03, 0x00, 0x03, 0x00, 0x07, 0x08, 0x00, 0x00, 0x49, 0x42, 0x12 "YD7001" ADR: 0 ID: 'IB' SPD: 3
0xE7, 0x0E, 0x00, 0x02, 0x42, 0x03, 0x00, 0x07, 0x00, 0x00, 0x15, 0x49, 0x42, 0x4C "Intellibox II / IB-Basic / IB-Com" ADR: 'B' ID: 'IB' SPD: 3
0xE7, 0x0E, 0x00, 0x02, 0x42, 0x03, 0x00, 0x06, 0x00, 0x00, 0x15, 0x49, 0x42, 0x4D "System Control 7" ADR: 'B' ID: 'IB' SPD: 3
0xE7, 0x0E, 0x00, 0x02, 0x42, 0x03, 0x00, 0x07, 0x00, 0x00, 0x15, 0x49, 0x42, 0x4C "Daisy II Tillig" ADR: 'B' ID: 'IB' SPD: 3
0xE7, 0x0E, 0x00, 0x00, 0x44, 0x02, 0x00, 0x07, 0x00, 0x59, 0x01, 0x49, 0x42, 0x04 "Daisy" ADR: 'DY' ID: 'IB' SPD: 2
0xE7, 0x0E, 0x00, 0x00, 0x4C, 0x01, 0x00, 0x07, 0x00, 0x49, 0x02, 0x49, 0x42, 0x1C "Adapter 63820" ADR: 'LI' ID: 'IB' SPD: 1
0xE7, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11 "Digitrax Chief" ADR: 0 ID: 0 SPD: 0
0xe7, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11 DCS100
0xe7, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12 DCS200
0xe7, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12 DCS50
0xe7, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52 DCS52
0xe7, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x25, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30 DT200
0xe7, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51 DCS210
0xe7, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x11, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 DCS240
0xe7, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x20, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71 DCS240+
0xe7, 0x0e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16 DB150 ADR: 0 ID: 0 SPD: 4
0xe7, 0x0e, 0x00, 0x33, 0x0e, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2d DCS51 ADR: 0 ID: 1 SPD: 0
SLOT STAT1 ADRL SPD DIRF TRK STAT2 ADH SND ID1 ID2 CHK
*/
void getCS() { // Identify central
typeCS = CS_UNKNOW;
trk = LnPacket->sd.trk;
if ((LnPacket->sd.id1 == 'I') && (LnPacket->sd.id2 == 'B')) {
switch (LnPacket->sd.adr) {
case 0:
switch (LnPacket->sd.spd) {
case 2: // "Intellibox / TwinCenter"
typeCS = CS_IB_I;
break;
case 3: // "DR5000"
typeCS = CS_DR5K;
break;
}
break;
case 'B': // "Intellibox II / IB-Basic / IB-Com"
typeCS = CS_IB_II;
break;
case 'D':
if (LnPacket->sd.adr2 == 'Y') { // "Daisy"
typeCS = CS_DAISY;
}
break;
case 'L':
if (LnPacket->sd.adr2 == 'I') { // "Adapter 63820"
typeCS = CS_63820;
}
break;
}
}
if ((LnPacket->sd.id1 == 0) && (LnPacket->sd.id2 == 0)) { // "Digitrax Chief"
typeCS = CS_DIG;
}
}