DCCpp
This is the library version of a program for Arduino to control railroading DCC devices.
DCCpp.cpp
1 /*************************************************************
2 project: <DCCpp library>
3 author: <Thierry PARIS>
4 description: <DCCpp class>
5 *************************************************************/
6 
7 #include "DCCpp.h"
8 #include "arduino.h"
9 
10 // NEXT DECLARE GLOBAL OBJECTS TO PROCESS AND STORE DCC PACKETS AND MONITOR TRACK CURRENTS.
11 // NOTE REGISTER LISTS MUST BE DECLARED WITH "VOLATILE" QUALIFIER TO ENSURE THEY ARE PROPERLY UPDATED BY INTERRUPT ROUTINES
12 
13 volatile RegisterList DCCpp::mainRegs(MAX_MAIN_REGISTERS); // create list of registers for MAX_MAIN_REGISTER Main Track Packets
14 volatile RegisterList DCCpp::progRegs(3); // create a shorter list of only two registers for Program Track Packets
15 
16 CurrentMonitor DCCpp::mainMonitor; // create monitor for current on Main Track
17 CurrentMonitor DCCpp::progMonitor; // create monitor for current on Program Track
18 
19 bool DCCpp::programMode;
20 bool DCCpp::panicStopped;
21 
22 // *********************************************************** FunctionsState
23 
25 {
26  this->clear();
27 }
28 
30 {
31  // Clear all functions
32  this->activeFlags[0] = 0;
33  this->activeFlags[1] = 0;
34  this->activeFlags[2] = 0;
35  this->activeFlags[3] = 0;
36 }
37 
38 void FunctionsState::activate(byte inFunctionNumber)
39 {
40  bitSet(this->activeFlags[inFunctionNumber / 8], inFunctionNumber % 8);
41 }
42 
43 void FunctionsState::inactivate(byte inFunctionNumber)
44 {
45  bitClear(this->activeFlags[inFunctionNumber / 8], inFunctionNumber % 8);
46 }
47 
48 bool FunctionsState::isActivated(byte inFunctionNumber)
49 {
50  return bitRead(this->activeFlags[inFunctionNumber / 8], inFunctionNumber % 8);
51 }
52 
53 #ifdef DCCPP_DEBUG_MODE
54 void FunctionsState::printActivated()
55 {
56  for (int i = 0; i < 32; i++)
57  {
58  if (this->isActivated(i))
59  {
60  Serial.print(i);
61  Serial.print(" ");
62  }
63  }
64 
65  Serial.println("");
66 }
67 #endif
68 
69 // *********************************************************** end of FunctionsState
70 
71 // *********************************************************** DCCpp class
72 
73 static bool first = true;
74 
76 // MAIN ARDUINO LOOP
78 
80 {
81 #ifdef USE_TEXTCOMMAND
82  TextCommand::process(); // check for, and process, and new serial commands
83 #endif
84 
85  if (first)
86  {
87  first = false;
88 #if defined(DCCPP_DEBUG_MODE) && defined(DCCPP_PRINT_DCCPP)
89  showConfiguration();
90 #endif
91  }
92 
94  { // if sufficient time has elapsed since last update, check current draw on Main and Program Tracks
95  mainMonitor.check();
96  progMonitor.check();
97  }
98 
99 #ifdef USE_SENSOR
100  Sensor::check(); // check sensors for activated or not
101 #endif
102 }
103 
104 void DCCpp::beginMain(uint8_t inOptionalDirectionMotor, uint8_t inSignalPin, uint8_t inSignalEnable, uint8_t inCurrentMonitor)
105 {
106  DCCppConfig::DirectionMotorA = inOptionalDirectionMotor;
107  DCCppConfig::SignalEnablePinMain = inSignalEnable; // PWM
108  DCCppConfig::CurrentMonitorMain = inCurrentMonitor;
109 
110  // If no main line, exit.
111  if (DCCppConfig::SignalEnablePinMain == UNDEFINED_PIN)
112  {
113 #ifdef DCCPP_DEBUG_MODE
114  Serial.println("No main line");
115 #endif
116  return;
117  }
118 
119  mainMonitor.begin(DCCppConfig::CurrentMonitorMain, (char *) "<p2>");
120 
121  // CONFIGURE TIMER_1 TO OUTPUT 50% DUTY CYCLE DCC SIGNALS ON OC1B INTERRUPT PINS
122 
123  // Direction Pin for Motor Shield Channel A - MAIN OPERATIONS TRACK
124  // Controlled by Arduino 16-bit TIMER 1 / OC1B Interrupt Pin
125  // Values for 16-bit OCR1A and OCR1B registers calibrated for 1:1 prescale at 16 MHz clock frequency
126  // Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with exactly 50% duty cycle
127 
128 #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER1 3199
129 #define DCC_ZERO_BIT_PULSE_DURATION_TIMER1 1599
130 
131 #define DCC_ONE_BIT_TOTAL_DURATION_TIMER1 1855
132 #define DCC_ONE_BIT_PULSE_DURATION_TIMER1 927
133  if (DCCppConfig::DirectionMotorA != UNDEFINED_PIN)
134  {
135  pinMode(DCCppConfig::DirectionMotorA, INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
136  digitalWrite(DCCppConfig::DirectionMotorA, LOW);
137  }
138 
139  pinMode(inSignalPin, OUTPUT); // FOR SHIELDS, THIS ARDUINO OUPUT PIN MUST BE PHYSICALLY CONNECTED TO THE PIN FOR DIRECTION-A OF MOTOR CHANNEL-A
140 
141  bitSet(TCCR1A, WGM10); // set Timer 1 to FAST PWM, with TOP=OCR1A
142  bitSet(TCCR1A, WGM11);
143  bitSet(TCCR1B, WGM12);
144  bitSet(TCCR1B, WGM13);
145 
146  bitSet(TCCR1A, COM1B1); // set Timer 1, OC1B (pin 10/UNO, pin 12/MEGA) to inverting toggle (actual direction is arbitrary)
147  bitSet(TCCR1A, COM1B0);
148 
149  bitClear(TCCR1B, CS12); // set Timer 1 prescale=1
150  bitClear(TCCR1B, CS11);
151  bitSet(TCCR1B, CS10);
152 
153  OCR1A = DCC_ONE_BIT_TOTAL_DURATION_TIMER1;
154  OCR1B = DCC_ONE_BIT_PULSE_DURATION_TIMER1;
155 
156  pinMode(DCCppConfig::SignalEnablePinMain, OUTPUT); // master enable for motor channel A
157 
158  mainRegs.loadPacket(1, RegisterList::idlePacket, 2, 0); // load idle packet into register 1
159 
160  bitSet(TIMSK1, OCIE1B); // enable interrupt vector for Timer 1 Output Compare B Match (OCR1B)
161  digitalWrite(DCCppConfig::SignalEnablePinMain, LOW);
162 
163 #ifdef DCCPP_DEBUG_MODE
164  Serial.println(F("beginMain achivied"));
165 #endif
166 }
167 
168 void DCCpp::beginProg(uint8_t inOptionalDirectionMotor, uint8_t inSignalPin, uint8_t inSignalEnable, uint8_t inCurrentMonitor)
169 {
170  DCCppConfig::DirectionMotorB = inOptionalDirectionMotor;
171  DCCppConfig::SignalEnablePinProg = inSignalEnable;
172  DCCppConfig::CurrentMonitorProg = inCurrentMonitor;
173 
174  // If no programming line, exit.
175  if (DCCppConfig::SignalEnablePinProg == UNDEFINED_PIN)
176  {
177 #ifdef DCCPP_DEBUG_MODE
178  Serial.println("No prog line");
179 #endif
180  return;
181  }
182 
183  progMonitor.begin(DCCppConfig::CurrentMonitorProg, (char *) "<p3>");
184 
185  // CONFIGURE EITHER TIMER_0 (UNO) OR TIMER_3 (MEGA) TO OUTPUT 50% DUTY CYCLE DCC SIGNALS ON OC0B (UNO) OR OC3B (MEGA) INTERRUPT PINS
186 
187 #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) // Configuration for UNO
188 
189  // Direction Pin for Motor Shield Channel B - PROGRAMMING TRACK
190  // Controlled by Arduino 8-bit TIMER 0 / OC0B Interrupt Pin
191  // Values for 8-bit OCR0A and OCR0B registers calibrated for 1:64 prescale at 16 MHz clock frequency
192  // 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
193 
194 #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER0 49
195 #define DCC_ZERO_BIT_PULSE_DURATION_TIMER0 24
196 
197 #define DCC_ONE_BIT_TOTAL_DURATION_TIMER0 28
198 #define DCC_ONE_BIT_PULSE_DURATION_TIMER0 14
199 
200  if (DCCppConfig::DirectionMotorB != UNDEFINED_PIN)
201  {
202  pinMode(DCCppConfig::DirectionMotorB, INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
203  digitalWrite(DCCppConfig::DirectionMotorB, LOW);
204  }
205 
206  pinMode(inSignalPin, OUTPUT); // THIS ARDUINO OUTPUT PIN MUST BE PHYSICALLY CONNECTED TO THE PIN FOR DIRECTION-B OF MOTOR CHANNEL-B
207 
208  bitSet(TCCR0A, WGM00); // set Timer 0 to FAST PWM, with TOP=OCR0A
209  bitSet(TCCR0A, WGM01);
210  bitSet(TCCR0B, WGM02);
211 
212  bitSet(TCCR0A, COM0B1); // set Timer 0, OC0B (pin 5) to inverting toggle (actual direction is arbitrary)
213  bitSet(TCCR0A, COM0B0);
214 
215  bitClear(TCCR0B, CS02); // set Timer 0 prescale=64
216  bitSet(TCCR0B, CS01);
217  bitSet(TCCR0B, CS00);
218 
219  OCR0A = DCC_ONE_BIT_TOTAL_DURATION_TIMER0;
220  OCR0B = DCC_ONE_BIT_PULSE_DURATION_TIMER0;
221 
222  pinMode(DCCppConfig::SignalEnablePinProg, OUTPUT); // master enable for motor channel B
223 
224  progRegs.loadPacket(1, RegisterList::idlePacket, 2, 0); // load idle packet into register 1
225 
226  bitSet(TIMSK0, OCIE0B); // enable interrupt vector for Timer 0 Output Compare B Match (OCR0B)
227 
228 #else // Configuration for MEGA
229 
230  // Direction Pin for Motor Shield Channel B - PROGRAMMING TRACK
231  // Controlled by Arduino 16-bit TIMER 3 / OC3B Interrupt Pin
232  // Values for 16-bit OCR3A and OCR3B registers calibrated for 1:1 prescale at 16 MHz clock frequency
233  // Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with exactly 50% duty cycle
234 
235 #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER3 3199
236 #define DCC_ZERO_BIT_PULSE_DURATION_TIMER3 1599
237 
238 #define DCC_ONE_BIT_TOTAL_DURATION_TIMER3 1855
239 #define DCC_ONE_BIT_PULSE_DURATION_TIMER3 927
240 
241  if (DCCppConfig::DirectionMotorB != UNDEFINED_PIN)
242  {
243  pinMode(DCCppConfig::DirectionMotorB, INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
244  digitalWrite(DCCppConfig::DirectionMotorB, LOW);
245  }
246 
247  pinMode(DCC_SIGNAL_PIN_PROG, OUTPUT); // THIS ARDUINO OUTPUT PIN MUST BE PHYSICALLY CONNECTED TO THE PIN FOR DIRECTION-B OF MOTOR CHANNEL-B
248 
249  bitSet(TCCR3A, WGM30); // set Timer 3 to FAST PWM, with TOP=OCR3A
250  bitSet(TCCR3A, WGM31);
251  bitSet(TCCR3B, WGM32);
252  bitSet(TCCR3B, WGM33);
253 
254  bitSet(TCCR3A, COM3B1); // set Timer 3, OC3B (pin 2) to inverting toggle (actual direction is arbitrary)
255  bitSet(TCCR3A, COM3B0);
256 
257  bitClear(TCCR3B, CS32); // set Timer 3 prescale=1
258  bitClear(TCCR3B, CS31);
259  bitSet(TCCR3B, CS30);
260 
261  OCR3A = DCC_ONE_BIT_TOTAL_DURATION_TIMER3;
262  OCR3B = DCC_ONE_BIT_PULSE_DURATION_TIMER3;
263 
264  pinMode(DCCppConfig::SignalEnablePinProg, OUTPUT); // master enable for motor channel B
265 
266  progRegs.loadPacket(1, RegisterList::idlePacket, 2, 0); // load idle packet into register 1
267 
268  bitSet(TIMSK3, OCIE3B); // enable interrupt vector for Timer 3 Output Compare B Match (OCR3B)
269 
270 #endif
271  digitalWrite(DCCppConfig::SignalEnablePinProg, LOW);
272 
273 #ifdef DCCPP_DEBUG_MODE
274  Serial.println(F("beginProg achivied"));
275 #endif
276 }
277 
279 {
280  programMode = false;
281  panicStopped = false;
282 
283  DCCppConfig::SignalEnablePinMain = UNDEFINED_PIN;
284  DCCppConfig::CurrentMonitorMain = UNDEFINED_PIN;
285 
286  DCCppConfig::SignalEnablePinProg = UNDEFINED_PIN;
287  DCCppConfig::CurrentMonitorProg = UNDEFINED_PIN;
288 
289  DCCppConfig::DirectionMotorA = UNDEFINED_PIN;
290  DCCppConfig::DirectionMotorB = UNDEFINED_PIN;
291 
292  mainMonitor.begin(UNDEFINED_PIN, "");
293  progMonitor.begin(UNDEFINED_PIN, "");
294 
295 #ifdef SDCARD_CS
296  pinMode(SDCARD_CS, OUTPUT);
297  digitalWrite(SDCARD_CS, HIGH); // De-select the SD card
298 #endif
299 
300 #ifdef USE_EEPROM
301  EEStore::init(); // initialize and load Turnout and Sensor definitions stored in EEPROM
303  EEStore::store();
304 #endif
305 
306 #ifdef DCCPP_DEBUG_MODE
307  //pinMode(LED_BUILTIN, OUTPUT);
308  Serial.println(F("begin achieved"));
309 #endif
310 
311 } // begin
312 
313 #ifdef USE_ETHERNET
314 void DCCpp::beginEthernet(uint8_t *inMac, uint8_t *inIp, EthernetProtocol inProtocol)
315 {
316  if (inIp != NULL)
317  for (int i = 0; i < 4; i++)
318  DCCppConfig::EthernetIp[i] = inIp[i];
319 
320  for (int i = 0; i < 6; i++)
321  DCCppConfig::EthernetMac[i] = inMac[i];
322 
323  DCCppConfig::Protocol = inProtocol;
324 
325  if (inIp == NULL)
326  Ethernet.begin(inMac); // Start networking using DHCP to get an IP Address
327  else
328  Ethernet.begin(inMac, inIp); // Start networking using STATIC IP Address
329 
330  INTERFACE.begin();
331 #ifdef DCCPP_DEBUG_MODE
332  //pinMode(LED_BUILTIN, OUTPUT);
333  showConfiguration();
334  Serial.println(F("beginEthernet achieved"));
335 #endif
336 } // beginEthernet
337 #endif
338 
340  // DEFINE THE INTERRUPT LOGIC THAT GENERATES THE DCC SIGNAL
342 
343  // The code below will be called every time an interrupt is triggered on OCNB, where N can be 0 or 1.
344  // It is designed to read the current bit of the current register packet and
345  // updates the OCNA and OCNB counters of Timer-N to values that will either produce
346  // a long (200 microsecond) pulse, or a short (116 microsecond) pulse, which respectively represent
347  // DCC ZERO and DCC ONE bits.
348 
349  // These are hardware-driven interrupts that will be called automatically when triggered regardless of what
350  // DCC++ BASE STATION was otherwise processing. But once inside the interrupt, all other interrupt routines are temporarily disabled.
351  // Since a short pulse only lasts for 116 microseconds, and there are TWO separate interrupts
352  // (one for Main Track Registers and one for the Program Track Registers), the interrupt code must complete
353  // in much less than 58 microseconds, otherwise there would be no time for the rest of the program to run. Worse, if the logic
354  // of the interrupt code ever caused it to run longer than 58 microseconds, an interrupt trigger would be missed, the OCNA and OCNB
355  // registers would not be updated, and the net effect would be a DCC signal that keeps sending the same DCC bit repeatedly until the
356  // interrupt code completes and can be called again.
357 
358  // A significant portion of this entire program is designed to do as much of the heavy processing of creating a properly-formed
359  // DCC bit stream upfront, so that the interrupt code below can be as simple and efficient as possible.
360 
361  // Note that we need to create two very similar copies of the code --- one for the Main Track OC1B interrupt and one for the
362  // Programming Track OCOB interrupt. But rather than create a generic function that incurs additional overhead, we create a macro
363  // that can be invoked with proper parameters for each interrupt. This slightly increases the size of the code base by duplicating
364  // some of the logic for each interrupt, but saves additional time.
365 
366  // As structured, the interrupt code below completes at an average of just under 6 microseconds with a worse-case of just under 11 microseconds
367  // when a new register is loaded and the logic needs to switch active register packet pointers.
368 
369  // THE INTERRUPT CODE MACRO: R=REGISTER LIST (mainRegs or progRegs), and N=TIMER (0 or 1)
370 
371 #define DCC_SIGNAL(R,N)
372  if(R.currentBit==R.currentReg->activePacket->nBits){ /* IF no more bits in this DCC Packet */
373  R.currentBit=0; /* reset current bit pointer and determine which Register and Packet to process next--- */
374  if (R.nRepeat>0 && R.currentReg == R.reg) { /* IF current Register is first Register AND should be repeated */
375  R.nRepeat--; /* decrement repeat count; result is this same Packet will be repeated */
376  }
377  else if (R.nextReg != NULL) { /* ELSE IF another Register has been updated */
378  R.currentReg = R.nextReg; /* update currentReg to nextReg */
379  R.nextReg = NULL; /* reset nextReg to NULL */
380  R.tempPacket = R.currentReg->activePacket; /* flip active and update Packets */
381  R.currentReg->activePacket = R.currentReg->updatePacket;
382  R.currentReg->updatePacket = R.tempPacket;
383  }
384  else { /* ELSE simply move to next Register */
385  if (R.currentReg == R.maxLoadedReg) /* BUT IF this is last Register loaded */
386  R.currentReg = R.reg; /* first reset currentReg to base Register, THEN */
387  R.currentReg++; /* increment current Register (note this logic causes Register[0] to be skipped when simply cycling through all Registers) */
388  } /* END-ELSE */
389  } /* END-IF: currentReg, activePacket, and currentBit should now be properly set to point to next DCC bit */
390 
391  if (R.currentReg->activePacket->buf[R.currentBit / 8] & R.bitMask[R.currentBit % 8]) { /* IF bit is a ONE */
392  OCR ## N ## A = DCC_ONE_BIT_TOTAL_DURATION_TIMER ## N; /* set OCRA for timer N to full cycle duration of DCC ONE bit */
393  OCR ## N ## B=DCC_ONE_BIT_PULSE_DURATION_TIMER ## N; /* set OCRB for timer N to half cycle duration of DCC ONE but */
394  } else{ /* ELSE it is a ZERO */
395  OCR ## N ## A=DCC_ZERO_BIT_TOTAL_DURATION_TIMER ## N; /* set OCRA for timer N to full cycle duration of DCC ZERO bit */
396  OCR ## N ## B=DCC_ZERO_BIT_PULSE_DURATION_TIMER ## N; /* set OCRB for timer N to half cycle duration of DCC ZERO bit */
397  } /* END-ELSE */
398 
399  R.currentBit++; /* point to next bit in current Packet */
400 
402 // NOW USE THE ABOVE MACRO TO CREATE THE CODE FOR EACH INTERRUPT
403 
404 ISR(TIMER1_COMPB_vect) { // set interrupt service for OCR1B of TIMER-1 which flips direction bit of Motor Shield Channel A controlling Main Track
405  DCC_SIGNAL(DCCpp::mainRegs, 1)
406 }
407 
408 #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) // Configuration for UNO
409 
410 ISR(TIMER0_COMPB_vect) { // set interrupt service for OCR1B of TIMER-0 which flips direction bit of Motor Shield Channel B controlling Programming Track
411  DCC_SIGNAL(DCCpp::progRegs, 0)
412 }
413 
414 #else // Configuration for MEGA
415 
416 ISR(TIMER3_COMPB_vect) { // set interrupt service for OCR3B of TIMER-3 which flips direction bit of Motor Shield Channel B controlling Programming Track
417  DCC_SIGNAL(DCCpp::progRegs, 3)
418 }
419 
420 #endif
421 
422 #ifdef DCCPP_PRINT_DCCPP
423 // PRINT CONFIGURATION INFO TO SERIAL PORT REGARDLESS OF INTERFACE TYPE
425 // - ACTIVATED ON STARTUP IF SHOW_CONFIG_PIN IS TIED HIGH
426 
427 void DCCpp::showConfiguration()
428 {
429  Serial.println(F("*** DCCpp LIBRARY ***"));
430 
431  Serial.print(F("VERSION DCC++: "));
432  Serial.println(VERSION);
433  Serial.println(F("VERSION DCCpp library: 0.8.0"));
434  Serial.print(F("COMPILED: "));
435  Serial.print(__DATE__);
436  Serial.print(F(" "));
437  Serial.println(__TIME__);
438 
439  //Serial.print(F("nARDUINO: "));
440  //Serial.print(ARDUINO_TYPE);
441 
442  //Serial.print(F("nnMOTOR SHIELD: "));
443  //Serial.print(MOTOR_SHIELD_NAME);
444 
445  if (DCCppConfig::SignalEnablePinMain!= UNDEFINED_PIN)
446  {
447  Serial.print(F("nnDCC SIG MAIN(DIR): "));
448  Serial.println(DCC_SIGNAL_PIN_MAIN);
449  Serial.print(F(" DIRECTION: "));
450  Serial.println(DCCppConfig::DirectionMotorA);
451  Serial.print(F(" ENABLE(PWM): "));
452  Serial.println(DCCppConfig::SignalEnablePinMain);
453  Serial.print(F(" CURRENT: "));
454  Serial.println(DCCppConfig::CurrentMonitorMain);
455  }
456 
457  if (DCCppConfig::SignalEnablePinProg!= UNDEFINED_PIN)
458  {
459  Serial.print(F("nnDCC SIG PROG(DIR): "));
460  Serial.println(DCC_SIGNAL_PIN_PROG);
461  Serial.print(F(" DIRECTION: "));
462  Serial.println(DCCppConfig::DirectionMotorB);
463  Serial.print(F(" ENABLE(PWM): "));
464  Serial.println(DCCppConfig::SignalEnablePinProg);
465  Serial.print(F(" CURRENT: "));
466  Serial.println(DCCppConfig::CurrentMonitorProg);
467  }
468 #if defined(USE_EEPROM)
469 #if defined(USE_TURNOUT)
470  Serial.print(F("nnNUM TURNOUTS: "));
471  Serial.println(EEStore::data.nTurnouts);
472 #endif
473 #if defined(USE_SENSOR)
474  Serial.print(F(" SENSORS: "));
475  Serial.println(EEStore::data.nSensors);
476 #endif
477 #if defined(USE_OUTPUT)
478  Serial.print(F(" OUTPUTS: "));
479  Serial.println(EEStore::data.nOutputs);
480 #endif
481 #endif
482 
483 #ifdef USE_TEXTCOMMAND
484  Serial.print(F("nnINTERFACE: "));
485 #ifdef USE_ETHERNET
486  Serial.println(F("ETHERNET "));
487  Serial.print(F("MAC ADDRESS: "));
488  for (int i = 0; i<5; i++) {
489  Serial.print(DCCppConfig::EthernetMac[i], HEX);
490  Serial.print(F(":"));
491  }
492  Serial.println(DCCppConfig::EthernetMac[5], HEX);
493 // Serial.print(F("PORT: "));
494 // Serial.println(DCCppConfig::EthernetPort);
495  Serial.print(F("IP ADDRESS: "));
496  Serial.println(Ethernet.localIP());
497 
498 /*#ifdef IP_ADDRESS
499  Serial.println(F(" (STATIC)"));
500 #else
501  Serial.println(F(" (DHCP)"));
502 #endif*/
503 
504 #else
505  Serial.println(F("SERIAL"));
506 #endif
507 
508 #endif
509 // Serial.print(F("nnPROGRAM HALTED - PLEASE RESTART ARDUINO"));
510 
511 // while (true);
512 // Serial.println("");
513 }
514 #endif
515 
516 void DCCpp::panicStop(bool inStop)
517 {
518  panicStopped = inStop;
519 
520 #ifdef DCCPP_DEBUG_MODE
521  Serial.print(F("DCCpp PanicStop "));
522  Serial.println(inStop ? F("pressed"):F("canceled"));
523 #endif
524 
525  /* activate or not the current output on rails */
526 
527  if (inStop)
528  powerOff();
529  else
530  powerOn();
531 }
532 
534 {
535  if (DCCppConfig::SignalEnablePinProg != UNDEFINED_PIN)
536  digitalWrite(DCCppConfig::SignalEnablePinProg, HIGH);
537  if (DCCppConfig::SignalEnablePinMain != UNDEFINED_PIN)
538  digitalWrite(DCCppConfig::SignalEnablePinMain, HIGH);
539  INTERFACE.print("<p1>");
540 #if !defined(USE_ETHERNET)
541  INTERFACE.println("");
542 #endif
543 }
544 
546 {
547  if (DCCppConfig::SignalEnablePinProg != UNDEFINED_PIN)
548  digitalWrite(DCCppConfig::SignalEnablePinProg, LOW);
549  if (DCCppConfig::SignalEnablePinMain != UNDEFINED_PIN)
550  digitalWrite(DCCppConfig::SignalEnablePinMain, LOW);
551  INTERFACE.print("<p0>");
552 #if !defined(USE_ETHERNET)
553  INTERFACE.println("");
554 #endif
555 }
556 
557 /***************************** Driving functions */
558 
559 bool DCCpp::setThrottle(volatile RegisterList *inpRegs, int nReg, int inLocoId, int inStepsNumber, int inNewSpeed, bool inForward)
560 {
561  int val = 0;
562 
563  if (panicStopped)
564  val = 1;
565  else
566  if (inNewSpeed > 0)
567  val = map(inNewSpeed, 0, inStepsNumber, 2, 127);
568 
569 #ifdef DCCPP_DEBUG_MODE
570  Serial.print(F("DCCpp SetSpeed "));
571  Serial.print(inForward?inNewSpeed:-inNewSpeed);
572  Serial.print(F("/"));
573  Serial.print(inStepsNumber);
574  Serial.print(F(" (in Dcc "));
575  Serial.print(val);
576  Serial.println(F(" )"));
577 #endif
578 
579  inpRegs->setThrottle(nReg, inLocoId, val, inForward);
580 
581  return true;
582 }
583 
584 void DCCpp::setFunctions(volatile RegisterList *inpRegs, int nReg, int inLocoId, FunctionsState inStates)
585 {
586 #ifdef DCCPP_DEBUG_MODE
587  if (inpRegs == &mainRegs)
588  {
589  if (nReg > MAX_MAIN_REGISTERS)
590  Serial.println(F("Invalid register number on main track."));
591  }
592  else
593  {
594  if (nReg > MAX_PROG_REGISTERS)
595  Serial.println(F("Invalid register number on programming track."));
596  }
597 #endif
598  byte flags = 0;
599 
600  byte oneByte1 = 128; // Group one functions F0-F4
601  byte twoByte1 = 176; // Group two F5-F8
602  byte threeByte1 = 160; // Group three F9-F12
603  byte fourByte2 = 0; // Group four F13-F20
604  byte fiveByte2 = 0; // Group five F21-F28
605 
606  for (byte func = 0; func <= 28; func++)
607  {
608  if (func <= 4)
609  {
610  /*
611  * To set functions F0 - F4 on(= 1) or off(= 0) :
612  *
613  * BYTE1 : 128 + F1 * 1 + F2 * 2 + F3 * 4 + F4 * 8 + F0 * 16
614  * BYTE2 : omitted
615  */
616 
617  flags |= 1;
618  if (inStates.isActivated(func))
619  {
620  if (func == 0)
621  oneByte1 += 16;
622  else
623  oneByte1 += (1 << (func - 1));
624  }
625  }
626  else if (func <= 8)
627  {
628  /*
629  * To set functions F5 - F8 on(= 1) or off(= 0) :
630  *
631  * BYTE1 : 176 + F5 * 1 + F6 * 2 + F7 * 4 + F8 * 8
632  * BYTE2 : omitted
633  */
634 
635  flags |= 2;
636  if (inStates.isActivated(func))
637  twoByte1 += (1 << (func - 5));
638  }
639  else if (func <= 12)
640  {
641  /*
642  * To set functions F9 - F12 on(= 1) or off(= 0) :
643  *
644  * BYTE1 : 160 + F9 * 1 + F10 * 2 + F11 * 4 + F12 * 8
645  * BYTE2 : omitted
646  */
647 
648  flags |= 4;
649  if (inStates.isActivated(func))
650  threeByte1 += (1 << (func - 9));
651  }
652  else if (func <= 20)
653  {
654  /*
655  * To set functions F13 - F20 on(= 1) or off(= 0) :
656  *
657  * BYTE1 : 222
658  * BYTE2 : F13 * 1 + F14 * 2 + F15 * 4 + F16 * 8 + F17 * 16 + F18 * 32 + F19 * 64 + F20 * 128
659  */
660 
661  flags |= 8;
662  if (inStates.isActivated(func))
663  fourByte2 += (1 << (func - 13));
664  }
665  else if (func <= 28)
666  {
667  /*
668  * To set functions F21 - F28 on(= 1) of off(= 0) :
669  *
670  * BYTE1 : 223
671  * BYTE2 : F21 * 1 + F22 * 2 + F23 * 4 + F24 * 8 + F25 * 16 + F26 * 32 + F27 * 64 + F28 * 128
672  */
673 
674  flags |= 16;
675  if (inStates.isActivated(func))
676  fiveByte2 += (1 << (func - 21));
677  }
678  }
679 
680  if (flags & 1)
681  inpRegs->setFunction(nReg, inLocoId, oneByte1, -1);
682  if (flags & 2)
683  inpRegs->setFunction(nReg, inLocoId, twoByte1, -1);
684  if (flags & 4)
685  inpRegs->setFunction(nReg, inLocoId, threeByte1, -1);
686  if (flags & 8)
687  inpRegs->setFunction(nReg, inLocoId, 222, fourByte2);
688  if (flags & 16)
689  inpRegs->setFunction(nReg, inLocoId, 223, fiveByte2);
690 
691 #ifdef DCCPP_DEBUG_MODE
692  Serial.print(F("DCCpp SetFunctions for loco"));
693  Serial.print(inLocoId);
694  Serial.print(" / Activated : ");
695  inStates.printActivated();
696 #endif
697 }
698 
699 void DCCpp::writeCv(volatile RegisterList *inReg, int inLocoId, int inCv, byte inValue)
700 {
701  inReg->writeCVByte(inCv, inValue, 100, 101);
702 
703 #ifdef DCCPP_DEBUG_MODE
704  Serial.print(F("DCCpp WriteCv "));
705  Serial.print(inCv);
706  Serial.print(F(" : "));
707  Serial.println(inValue);
708 #endif
709 }
710 
711 int DCCpp::readCv(volatile RegisterList *inReg, int inLocoId, byte inCv)
712 {
713  return inReg->readCVmain(1, 100+inCv, 100+inCv);
714 }
715 
716 void DCCpp::setAccessory(int inAddress, byte inSubAddress, byte inActivate)
717 {
718  mainRegs.setAccessory(inAddress, inSubAddress, inActivate);
719 
720 #ifdef DCCPP_DEBUG_MODE
721  Serial.print(F("DCCpp AccessoryOperation "));
722  Serial.print(inAddress);
723  Serial.print(F(" / "));
724  Serial.print(inSubAddress);
725  Serial.print(F(" : "));
726  Serial.println(inActivate);
727 #endif
728 }
729 
static boolean checkTime()
static void loop()
Definition: DCCpp.cpp:79
void activate(byte inFunctionNumber)
Definition: DCCpp.cpp:38
static void powerOn()
Definition: DCCpp.cpp:533
static void powerOff()
Definition: DCCpp.cpp:545
static void panicStop(bool inStop)
Definition: DCCpp.cpp:516
static void setAccessory(int inAddress, byte inSubAddress, byte inActivate)
Definition: DCCpp.cpp:716
FunctionsState()
Definition: DCCpp.cpp:24
void inactivate(byte inFunctionNumber)
Definition: DCCpp.cpp:43
static void check()
Definition: Sensor.cpp:120
static void beginMain(uint8_t inOptionalDirectionMotor, uint8_t inSignalPin, uint8_t inSignalEnablePin, uint8_t inCurrentMonitor)
Definition: DCCpp.cpp:104
static void begin()
Definition: DCCpp.cpp:278
void clear()
Definition: DCCpp.cpp:29
void begin(int pin, const char *msg, float inSampleMax = 300)
bool isActivated(byte inFunctionNumber)
Definition: DCCpp.cpp:48
static void init()
Definition: EEStore.cpp:24
static void beginEthernet(uint8_t *inMac, uint8_t *inIp, EthernetProtocol inProtocol = EthernetProtocol::TCP)
Definition: DCCpp.cpp:314
static EEStoreData data
Definition: EEStore.h:45
static void beginProg(uint8_t inOptionalDirectionMotor, uint8_t inSignalPin, uint8_t inSignalEnablePin, uint8_t inCurrentMonitor)
Definition: DCCpp.cpp:168
static bool needsRefreshing()
Definition: EEStore.cpp:110
static void store()
Definition: EEStore.cpp:90