DCCpp
This is the library version of a program for Arduino to control railroading DCC devices.
PacketRegister.cpp
1 /**********************************************************************
2 
3 PacketRegister.cpp
4 COPYRIGHT (c) 2013-2016 Gregg E. Berman
5 Adapted by Thierry PARIS
6 
7 Part of DCC++ BASE STATION for the Arduino
8 **********************************************************************/
9 
10 #include "DCCpp.h"
11 //#include "DCCpp_Uno.h"
12 //#include "PacketRegister.h"
13 //#include "Comm.h"
14 
15 #ifdef USE_ETHERNET
16 uint8_t DCCppConfig::EthernetIp[4];
17 uint8_t DCCppConfig::EthernetMac[6];
18 int DCCppConfig::EthernetPort = 0;
19 
20 EthernetProtocol DCCppConfig::Protocol = EthernetProtocol::TCP;
21 #endif
22 
23 byte DCCppConfig::SignalEnablePinMain = UNDEFINED_PIN;
24 byte DCCppConfig::CurrentMonitorMain = UNDEFINED_PIN;
25 
26 byte DCCppConfig::SignalEnablePinProg = UNDEFINED_PIN;
27 byte DCCppConfig::CurrentMonitorProg = UNDEFINED_PIN;
28 
29 byte DCCppConfig::DirectionMotorA = UNDEFINED_PIN;
30 byte DCCppConfig::DirectionMotorB = UNDEFINED_PIN;
31 
33 
34 void Register::initPackets(){
35  activePacket=packet;
36  updatePacket=packet+1;
37 } // Register::initPackets
38 
40 
41 RegisterList::RegisterList(int maxNumRegs){
42  this->maxNumRegs=maxNumRegs;
43  reg=(Register *)calloc((maxNumRegs+1),sizeof(Register));
44  for(int i=0;i<=maxNumRegs;i++)
45  reg[i].initPackets();
46  regMap=(Register **)calloc((maxNumRegs+1),sizeof(Register *));
47  speedTable=(int *)calloc((maxNumRegs+1),sizeof(int *));
48  currentReg=reg;
49  regMap[0]=reg;
50  maxLoadedReg=reg;
51  nextReg=NULL;
52  currentBit=0;
53  nRepeat=0;
54 } // RegisterList::RegisterList
55 
57 
58 // LOAD DCC PACKET INTO TEMPORARY REGISTER 0, OR PERMANENT REGISTERS 1 THROUGH DCC_PACKET_QUEUE_MAX (INCLUSIVE)
59 // CONVERTS 2, 3, 4, OR 5 BYTES INTO A DCC BIT STREAM WITH PREAMBLE, CHECKSUM, AND PROPER BYTE SEPARATORS
60 // BITSTREAM IS STORED IN UP TO A 10-BYTE ARRAY (USING AT MOST 76 OF 80 BITS)
61 
62 void RegisterList::loadPacket(int nReg, byte *b, int nBytes, int nRepeat, int printFlag) volatile
63 {
64 #ifdef VISUALSTUDIO
65  return;
66 #endif
67  nReg=nReg%((maxNumRegs+1)); // force nReg to be between 0 and maxNumRegs, inclusive
68 
69  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
70 
71  if(regMap[nReg]==NULL) // first time this Register Number has been called
72  regMap[nReg]=maxLoadedReg+1; // set Register Pointer for this Register Number to next available Register
73 
74  Register *r=regMap[nReg]; // set Register to be updated
75  Packet *p=r->updatePacket; // set Packet in the Register to be updated
76  byte *buf=p->buf; // set byte buffer in the Packet to be updated
77 
78  b[nBytes]=b[0]; // copy first byte into what will become the checksum byte
79  for(int i=1;i<nBytes;i++) // XOR remaining bytes into checksum byte
80  b[nBytes]^=b[i];
81  nBytes++; // increment number of bytes in packet to include checksum byte
82 
83  buf[0]=0xFF; // first 8 bytes of 22-byte preamble
84  buf[1]=0xFF; // second 8 bytes of 22-byte preamble
85  buf[2]=0xFC + bitRead(b[0],7); // last 6 bytes of 22-byte preamble + data start bit + b[0], bit 7
86  buf[3]=b[0]<<1; // b[0], bits 6-0 + data start bit
87  buf[4]=b[1]; // b[1], all bits
88  buf[5]=b[2]>>1; // b[2], bits 7-1
89  buf[6]=b[2]<<7; // b[2], bit 0
90 
91  if(nBytes==3){
92  p->nBits=49;
93  } else{
94  buf[6]+=b[3]>>2; // b[3], bits 7-2
95  buf[7]=b[3]<<6; // b[3], bit 1-0
96  if(nBytes==4){
97  p->nBits=58;
98  } else{
99  buf[7]+=b[4]>>3; // b[4], bits 7-3
100  buf[8]=b[4]<<5; // b[4], bits 2-0
101  if(nBytes==5){
102  p->nBits=67;
103  } else{
104  buf[8]+=b[5]>>4; // b[5], bits 7-4
105  buf[9]=b[5]<<4; // b[5], bits 3-0
106  p->nBits=76;
107  } // >5 bytes
108  } // >4 bytes
109  } // >3 bytes
110 
111  nextReg=r;
112  this->nRepeat=nRepeat;
113  maxLoadedReg=max(maxLoadedReg,nextReg);
114 
115 #ifdef DCCPP_DEBUG_MODE
116  if(printFlag) // for debugging purposes
117  printPacket(nReg,b,nBytes,nRepeat);
118 #endif
119 
120 } // RegisterList::loadPacket
121 
123 
124 void RegisterList::setThrottle(int nReg, int cab, int tSpeed, int tDirection) volatile
125 {
126  byte b[5]; // save space for checksum byte
127  byte nB = 0;
128 
129  if (cab>127)
130  b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
131 
132  b[nB++] = lowByte(cab);
133  b[nB++] = 0x3F; // 128-step speed control byte
134  if (tSpeed >= 0)
135  b[nB++] = tSpeed + (tSpeed>0) + tDirection * 128; // max speed is 126, but speed codes range from 2-127 (0=stop, 1=emergency stop)
136  else {
137  b[nB++] = 1;
138  tSpeed = 0;
139  }
140 
141  loadPacket(nReg, b, nB, 0, 1);
142 
143 #if defined(USE_TEXTCOMMAND)
144  INTERFACE.print("<T");
145  INTERFACE.print(nReg); INTERFACE.print(" ");
146  INTERFACE.print(cab); INTERFACE.print(" ");
147  INTERFACE.print(tSpeed); INTERFACE.print(" ");
148  INTERFACE.print(tDirection);
149  INTERFACE.print(">");
150 #if !defined(USE_ETHERNET)
151  INTERFACE.println("");
152 #endif
153 #endif
154  speedTable[nReg] = tDirection == 1 ? tSpeed : -tSpeed;
155 
156 } // RegisterList::setThrottle(ints)
157 
158 #ifdef USE_TEXTCOMMAND
159 void RegisterList::setThrottle(char *s) volatile
160 {
161  int nReg;
162  int cab;
163  int tSpeed;
164  int tDirection;
165 
166  if (sscanf(s, "%d %d %d %d", &nReg, &cab, &tSpeed, &tDirection) != 4)
167  {
168 #ifdef DCCPP_DEBUG_MODE
169  Serial.println(F("t Syntax error"));
170 #endif
171  return;
172  }
173 
174  this->setThrottle(nReg, cab, tSpeed, tDirection);
175 } // RegisterList::setThrottle(string)
176 #endif
177 
179 
180 void RegisterList::setFunction(int nReg, int cab, int fByte, int eByte) volatile
181 {
182  byte b[5]; // save space for checksum byte
183  byte nB = 0;
184 
185  if (cab>127)
186  b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
187 
188  b[nB++] = lowByte(cab);
189 
190  if (eByte < 0) { // this is a request for functions FL,F1-F12
191  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
192  }
193  else { // this is a request for functions F13-F28
194  b[nB++] = (fByte | 0xDE) & 0xDF; // for safety this guarantees that first byte will either be 0xDE (for F13-F20) or 0xDF (for F21-F28)
195  b[nB++] = eByte;
196  }
197 
198 #if defined(USE_TEXTCOMMAND)
199  INTERFACE.print("<F");
200  INTERFACE.print(nReg); INTERFACE.print(" ");
201  INTERFACE.print(cab); INTERFACE.print(" ");
202  INTERFACE.print(fByte); INTERFACE.print(" ");
203  INTERFACE.print(eByte);
204  INTERFACE.print(">");
205 #if !defined(USE_ETHERNET)
206  INTERFACE.println("");
207 #endif
208 #endif
209  /* NMRA DCC norm ask for two DCC packets instead of only one:
210  "Command Stations that generate these packets, and which are not periodically refreshing these functions,
211  must send at least two repetitions of these commands when any function state is changed."
212  https://www.nmra.org/sites/default/files/s-9.2.1_2012_07.pdf
213  */
214  loadPacket(nReg, b, nB, 4, 1);
215  loadPacket(nReg, b, nB, 4, 1);
216 } // RegisterList::setFunction(ints)
217 
218 #ifdef USE_TEXTCOMMAND
219 void RegisterList::setFunction(char *s) volatile
220 {
221  int cab;
222  int fByte, eByte;
223  int nParams;
224 
225  nParams = sscanf(s, "%d %d %d", &cab, &fByte, &eByte);
226  if (nParams<2)
227  {
228 #ifdef DCCPP_DEBUG_MODE
229  Serial.println(F("f Syntax error"));
230 #endif
231  return;
232  }
233 
234  if (nParams == 2) // this is a request for functions FL,F1-F12
235  eByte = -1;
236 
237  this->setFunction(0, cab, fByte, eByte); // TODO : nReg 0 is not valid !
238 
239 } // RegisterList::setFunction(string)
240 #endif
241 
243 
244 void RegisterList::setAccessory(int aAdd, int aNum, int activate) volatile
245 {
246  byte b[3]; // save space for checksum byte
247 
248  b[0] = aAdd % 64 + 128; // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least signifcant bits of accessory address
249  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
250 
251  loadPacket(0, b, 2, 4, 1);
252 
253 } // RegisterList::setAccessory(ints)
254 
255 #ifdef USE_TEXTCOMMAND
256 void RegisterList::setAccessory(char *s) volatile
257 {
258  int aAdd; // the accessory address (0-511 = 9 bits)
259  int aNum; // the accessory number within that address (0-3)
260  int activate; // flag indicated whether accessory should be activated (1) or deactivated (0) following NMRA recommended convention
261 
262  if (sscanf(s, "%d %d %d", &aAdd, &aNum, &activate) != 3)
263  {
264 #ifdef DCCPP_DEBUG_MODE
265  Serial.println(F("a Syntax error"));
266 #endif
267  return;
268  }
269 
270  this->setAccessory(aAdd, aNum, activate);
271 
272 } // RegisterList::setAccessory(string)
273 #endif
274 
276 
277 void RegisterList::writeTextPacket(int nReg, byte *b, int nBytes) volatile
278 {
279 
280  if (nBytes<2 || nBytes>5) { // invalid valid packet
281  INTERFACE.print("<mInvalid Packet>");
282 #if !defined(USE_ETHERNET)
283  INTERFACE.println("");
284 #endif
285  return;
286  }
287 
288  loadPacket(nReg, b, nBytes, 0, 1);
289 
290 } // RegisterList::writeTextPacket(bytes)
291 
292 #ifdef USE_TEXTCOMMAND
293 void RegisterList::writeTextPacket(char *s) volatile
294 {
295  int nReg;
296  byte b[6];
297  int nBytes;
298 
299  nBytes = sscanf(s, "%d %hhx %hhx %hhx %hhx %hhx", &nReg, b, b + 1, b + 2, b + 3, b + 4) - 1;
300 
301  this->writeTextPacket(nReg, b, nBytes);
302 
303 } // RegisterList::writeTextPacket(string)
304 #endif
305 
307 
308 int RegisterList::readCVraw(int cv, int callBack, int callBackSub, bool FromProg) volatile
309 {
310  byte bRead[4];
311  int bValue;
312  int c, d, base;
313  cv--; // actual CV addresses are cv-1 (0-1023)
314 
315  byte MonitorPin = DCCppConfig::CurrentMonitorProg;
316  if (!FromProg)
317  MonitorPin = DCCppConfig::CurrentMonitorMain;
318 
319  if (MonitorPin == UNDEFINED_PIN)
320  return -1;
321 
322  bRead[0] = 0x78 + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
323  bRead[1] = lowByte(cv);
324 
325  bValue = 0;
326 
327  for (int i = 0; i<8; i++) {
328 
329  c = 0;
330  d = 0;
331  base = 0;
332 
333  for (int j = 0; j < ACK_BASE_COUNT; j++)
334  {
335  int val = (int)analogRead(MonitorPin);
336  base += val;
337  }
338  base /= ACK_BASE_COUNT;
339 
340  bRead[2] = 0xE8 + i;
341 
342  loadPacket(0, resetPacket, 2, 3); // NMRA recommends starting with 3 reset packets
343  loadPacket(0, bRead, 3, 5); // NMRA recommends 5 verify packets
344  loadPacket(0, resetPacket, 2, 1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
345 
346  for (int j = 0; j<ACK_SAMPLE_COUNT; j++)
347  {
348  int val = (int)analogRead(MonitorPin);
349  c = (int)((val - base)*ACK_SAMPLE_SMOOTHING + c*(1.0 - ACK_SAMPLE_SMOOTHING));
350  if (c>ACK_SAMPLE_THRESHOLD)
351  d = 1;
352  }
353 
354  bitWrite(bValue, i, d);
355  }
356 
357  c = 0;
358  d = 0;
359  base = 0;
360 
361  for (int j = 0; j<ACK_BASE_COUNT; j++)
362  base += analogRead(MonitorPin);
363  base /= ACK_BASE_COUNT;
364 
365  bRead[0] = 0x74 + (highByte(cv) & 0x03); // set-up to re-verify entire byte
366  bRead[2] = bValue;
367 
368  loadPacket(0, resetPacket, 2, 3); // NMRA recommends starting with 3 reset packets
369  loadPacket(0, bRead, 3, 5); // NMRA recommends 5 verify packets
370  loadPacket(0, resetPacket, 2, 1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
371 
372  for (int j = 0; j<ACK_SAMPLE_COUNT; j++) {
373  c = (int)((analogRead(MonitorPin) - base)*ACK_SAMPLE_SMOOTHING + c*(1.0 - ACK_SAMPLE_SMOOTHING));
374  if (c>ACK_SAMPLE_THRESHOLD)
375  d = 1;
376  }
377 
378  if (d == 0) // verify unsuccessful
379  bValue = -1;
380 
381 #if defined(USE_TEXTCOMMAND)
382  INTERFACE.print("<r");
383  INTERFACE.print(callBack);
384  INTERFACE.print("|");
385  INTERFACE.print(callBackSub);
386  INTERFACE.print("|");
387  INTERFACE.print(cv + 1);
388  INTERFACE.print(" ");
389  INTERFACE.print(bValue);
390  INTERFACE.print(">");
391 #if !defined(USE_ETHERNET)
392  INTERFACE.println("");
393 #endif
394 #endif
395 
396  return bValue;
397 }
398 
399 void RegisterList::readCV(int cv, int callBack, int callBackSub) volatile
400 {
401  /*int bValue = */RegisterList::readCVraw(cv, callBack, callBackSub, true);
402 
403 } // RegisterList::readCV(ints)
404 
405 #ifdef USE_TEXTCOMMAND
406 void RegisterList::readCV(char *s) volatile
407 {
408  int cv, callBack, callBackSub;
409 
410  if (sscanf(s, "%d %d %d", &cv, &callBack, &callBackSub) != 3) // cv = 1-1024
411  {
412 #ifdef DCCPP_DEBUG_MODE
413  Serial.println(F("R Syntax error"));
414 #endif
415  return;
416  }
417 
418  this->readCV(cv, callBack, callBackSub);
419 } // RegisterList::readCV(string)
420 #endif
421 
422 int RegisterList::readCVmain(int cv, int callBack, int callBackSub) volatile
423 {
424  return RegisterList::readCVraw(cv, callBack, callBackSub, false);
425 
426 } // RegisterList::readCV_Main()
427 
428 #ifdef USE_TEXTCOMMAND
429 int RegisterList::readCVmain(char *s) volatile
430 {
431  int cv, callBack, callBackSub;
432 
433  if (sscanf(s, "%d %d %d", &cv, &callBack, &callBackSub) != 3) // cv = 1-1024
434  {
435 #ifdef DCCPP_DEBUG_MODE
436  Serial.println(F("r Syntax error"));
437 #endif
438  return -1;
439  }
440 
441  return this->readCVmain(cv, callBack, callBackSub);
442 } // RegisterList::readCVmain(string)
443 #endif
444 
446 
447 void RegisterList::writeCVByte(int cv, int bValue, int callBack, int callBackSub) volatile
448 {
449  byte bWrite[4];
450  int c, d, base;
451 
452  cv--; // actual CV addresses are cv-1 (0-1023)
453 
454  bWrite[0] = 0x7C + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
455  bWrite[1] = lowByte(cv);
456  bWrite[2] = bValue;
457 
458  loadPacket(0, resetPacket, 2, 1);
459  loadPacket(0, bWrite, 3, 4);
460  loadPacket(0, resetPacket, 2, 1);
461  loadPacket(0, idlePacket, 2, 10);
462 
463  // If monitor pin undefined, write cv without any confirmation...
464  if (DCCppConfig::CurrentMonitorProg != UNDEFINED_PIN)
465  {
466  c = 0;
467  d = 0;
468  base = 0;
469 
470  for (int j = 0; j < ACK_BASE_COUNT; j++)
471  base += analogRead(DCCppConfig::CurrentMonitorProg);
472  base /= ACK_BASE_COUNT;
473 
474  bWrite[0] = 0x74 + (highByte(cv) & 0x03); // set-up to re-verify entire byte
475 
476  loadPacket(0, resetPacket, 2, 3); // NMRA recommends starting with 3 reset packets
477  loadPacket(0, bWrite, 3, 5); // NMRA recommends 5 verfy packets
478  loadPacket(0, resetPacket, 2, 1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
479 
480  for (int j = 0; j < ACK_SAMPLE_COUNT; j++) {
481  c = (int)((analogRead(DCCppConfig::CurrentMonitorProg) - base)*ACK_SAMPLE_SMOOTHING + c*(1.0 - ACK_SAMPLE_SMOOTHING));
482  if (c > ACK_SAMPLE_THRESHOLD)
483  d = 1;
484  }
485 
486  if (d == 0) // verify unsuccessful
487  bValue = -1;
488  }
489 
490 #if defined(USE_TEXTCOMMAND)
491  INTERFACE.print("<r");
492  INTERFACE.print(callBack);
493  INTERFACE.print("|");
494  INTERFACE.print(callBackSub);
495  INTERFACE.print("|");
496  INTERFACE.print(cv + 1);
497  INTERFACE.print(" ");
498  INTERFACE.print(bValue);
499  INTERFACE.print(">");
500 #if !defined(USE_ETHERNET)
501  INTERFACE.println("");
502 #endif
503 #endif
504 } // RegisterList::writeCVByte(ints)
505 
506 #ifdef USE_TEXTCOMMAND
507 void RegisterList::writeCVByte(char *s) volatile
508 {
509  int bValue, cv, callBack, callBackSub;
510 
511  if (sscanf(s, "%d %d %d %d", &cv, &bValue, &callBack, &callBackSub) != 4) // cv = 1-1024
512  {
513 #ifdef DCCPP_DEBUG_MODE
514  Serial.println(F("W Syntax error"));
515 #endif
516  return;
517  }
518 
519  this->writeCVByte(cv, bValue, callBack, callBackSub);
520 } // RegisterList::writeCVByte(string)
521 #endif
522 
524 
525 void RegisterList::writeCVBit(int cv, int bNum, int bValue, int callBack, int callBackSub) volatile
526 {
527  byte bWrite[4];
528  int c, d, base;
529 
530  cv--; // actual CV addresses are cv-1 (0-1023)
531  bValue = bValue % 2;
532  bNum = bNum % 8;
533 
534  bWrite[0] = 0x78 + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
535  bWrite[1] = lowByte(cv);
536  bWrite[2] = 0xF0 + bValue * 8 + bNum;
537 
538  loadPacket(0, resetPacket, 2, 1);
539  loadPacket(0, bWrite, 3, 4);
540  loadPacket(0, resetPacket, 2, 1);
541  loadPacket(0, idlePacket, 2, 10);
542 
543  // If monitor pin undefined, write cv without any confirmation...
544  if (DCCppConfig::CurrentMonitorProg != UNDEFINED_PIN)
545  {
546  c = 0;
547  d = 0;
548  base = 0;
549 
550  for (int j = 0; j < ACK_BASE_COUNT; j++)
551  base += analogRead(DCCppConfig::CurrentMonitorProg);
552  base /= ACK_BASE_COUNT;
553 
554  bitClear(bWrite[2], 4); // change instruction code from Write Bit to Verify Bit
555 
556  loadPacket(0, resetPacket, 2, 3); // NMRA recommends starting with 3 reset packets
557  loadPacket(0, bWrite, 3, 5); // NMRA recommends 5 verfy packets
558  loadPacket(0, resetPacket, 2, 1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
559 
560  for (int j = 0; j < ACK_SAMPLE_COUNT; j++) {
561  c = (int)((analogRead(DCCppConfig::CurrentMonitorProg) - base)*ACK_SAMPLE_SMOOTHING + c*(1.0 - ACK_SAMPLE_SMOOTHING));
562  if (c > ACK_SAMPLE_THRESHOLD)
563  d = 1;
564  }
565 
566  if (d == 0) // verify unsuccessful
567  bValue = -1;
568  }
569 
570 #if defined(USE_TEXTCOMMAND)
571  INTERFACE.print("<r");
572  INTERFACE.print(callBack);
573  INTERFACE.print("|");
574  INTERFACE.print(callBackSub);
575  INTERFACE.print("|");
576  INTERFACE.print(cv + 1);
577  INTERFACE.print(" ");
578  INTERFACE.print(bNum);
579  INTERFACE.print(" ");
580  INTERFACE.print(bValue);
581  INTERFACE.print(">");
582 #if !defined(USE_ETHERNET)
583  INTERFACE.println("");
584 #endif
585 #endif
586 } // RegisterList::writeCVBit(ints)
587 
588 #ifdef USE_TEXTCOMMAND
589 void RegisterList::writeCVBit(char *s) volatile
590 {
591  int bNum, bValue, cv, callBack, callBackSub;
592 
593  if(sscanf(s,"%d %d %d %d %d",&cv,&bNum,&bValue,&callBack,&callBackSub) != 5) // cv = 1-1024
594  {
595 #ifdef DCCPP_DEBUG_MODE
596  Serial.println(F("W Syntax error"));
597 #endif
598  return;
599  }
600 
601  this->writeCVBit(cv, bNum, bValue, callBack, callBackSub);
602 } // RegisterList::writeCVBit(string)
603 #endif
604 
606 
607 void RegisterList::writeCVByteMain(int cab, int cv, int bValue) volatile
608 {
609  byte b[6]; // save space for checksum byte
610  byte nB = 0;
611 
612  cv--;
613 
614  if (cab>127)
615  b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
616 
617  b[nB++] = lowByte(cab);
618  b[nB++] = 0xEC + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
619  b[nB++] = lowByte(cv);
620  b[nB++] = bValue;
621 
622  loadPacket(0, b, nB, 4);
623 
624 } // RegisterList::writeCVByteMain(ints)
625 
626 #ifdef USE_TEXTCOMMAND
627 void RegisterList::writeCVByteMain(char *s) volatile
628 {
629  int cab;
630  int cv;
631  int bValue;
632 
633  if (sscanf(s, "%d %d %d", &cab, &cv, &bValue) != 3)
634  {
635 #ifdef DCCPP_DEBUG_MODE
636  Serial.println(F("w Syntax error"));
637 #endif
638  return;
639  }
640 
641  this->writeCVByteMain(cab, cv, bValue);
642 } // RegisterList::writeCVByteMain(string)
643 #endif
644 
646 
647 void RegisterList::writeCVBitMain(int cab, int cv, int bNum, int bValue) volatile
648 {
649  byte b[6]; // save space for checksum byte
650  byte nB = 0;
651 
652  cv--;
653 
654  bValue = bValue % 2;
655  bNum = bNum % 8;
656 
657  if (cab>127)
658  b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
659 
660  b[nB++] = lowByte(cab);
661  b[nB++] = 0xE8 + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
662  b[nB++] = lowByte(cv);
663  b[nB++] = 0xF0 + bValue * 8 + bNum;
664 
665  loadPacket(0, b, nB, 4);
666 
667 } // RegisterList::writeCVBitMain(ints)
668 
669 #ifdef USE_TEXTCOMMAND
670 void RegisterList::writeCVBitMain(char *s) volatile
671 {
672  int cab;
673  int cv;
674  int bNum;
675  int bValue;
676 
677  if (sscanf(s, "%d %d %d %d", &cab, &cv, &bNum, &bValue) != 4)
678  {
679 #ifdef DCCPP_DEBUG_MODE
680  Serial.println(F("w Syntax error"));
681 #endif
682  return;
683  }
684 
685  this->writeCVBitMain(cab, cv, bNum, bValue);
686 } // RegisterList::writeCVBitMain(string)
687 #endif
688 
690 
691 #ifdef DCCPP_DEBUG_MODE
692 void RegisterList::printPacket(int nReg, byte *b, int nBytes, int nRepeat) volatile
693 {
694  INTERFACE.print("<*");
695  INTERFACE.print(nReg);
696  INTERFACE.print(":");
697  for(int i=0;i<nBytes;i++){
698  INTERFACE.print(" ");
699  INTERFACE.print(b[i],HEX);
700  }
701  INTERFACE.print(" / ");
702  INTERFACE.print(nRepeat);
703  INTERFACE.print(">");
704 #if !defined(USE_ETHERNET)
705  INTERFACE.println("");
706 #endif
707 } // RegisterList::printPacket()
708 #endif
709 
711 
712 byte RegisterList::idlePacket[3]={0xFF,0x00,0}; // always leave extra byte for checksum computation
713 byte RegisterList::resetPacket[3]={0x00,0x00,0};
714 
715 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