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 int RegisterList::readCV(int cv, int callBack, int callBackSub) volatile
400 {
401  int bValue = RegisterList::readCVraw(cv, callBack, callBackSub, true);
402 
403  return bValue;
404 } // RegisterList::readCV(ints)
405 
406 #ifdef USE_TEXTCOMMAND
407 int RegisterList::readCV(char *s) volatile
408 {
409  int cv, callBack, callBackSub;
410 
411  if (sscanf(s, "%d %d %d", &cv, &callBack, &callBackSub) != 3) // cv = 1-1024
412  {
413 #ifdef DCCPP_DEBUG_MODE
414  Serial.println(F("R Syntax error"));
415 #endif
416  return -1;
417  }
418 
419  return this->readCV(cv, callBack, callBackSub);
420 } // RegisterList::readCV(string)
421 #endif
422 
423 int RegisterList::readCVmain(int cv, int callBack, int callBackSub) volatile
424 {
425  return RegisterList::readCVraw(cv, callBack, callBackSub, false);
426 
427 } // RegisterList::readCV_Main()
428 
429 #ifdef USE_TEXTCOMMAND
430 int RegisterList::readCVmain(char *s) volatile
431 {
432  int cv, callBack, callBackSub;
433 
434  if (sscanf(s, "%d %d %d", &cv, &callBack, &callBackSub) != 3) // cv = 1-1024
435  {
436 #ifdef DCCPP_DEBUG_MODE
437  Serial.println(F("r Syntax error"));
438 #endif
439  return -1;
440  }
441 
442  return this->readCVmain(cv, callBack, callBackSub);
443 } // RegisterList::readCVmain(string)
444 #endif
445 
447 
448 void RegisterList::writeCVByte(int cv, int bValue, int callBack, int callBackSub) volatile
449 {
450  byte bWrite[4];
451  int c, d, base;
452 
453  cv--; // actual CV addresses are cv-1 (0-1023)
454 
455  bWrite[0] = 0x7C + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
456  bWrite[1] = lowByte(cv);
457  bWrite[2] = bValue;
458 
459  loadPacket(0, resetPacket, 2, 1);
460  loadPacket(0, bWrite, 3, 4);
461  loadPacket(0, resetPacket, 2, 1);
462  loadPacket(0, idlePacket, 2, 10);
463 
464  // If monitor pin undefined, write cv without any confirmation...
465  if (DCCppConfig::CurrentMonitorProg != UNDEFINED_PIN)
466  {
467  c = 0;
468  d = 0;
469  base = 0;
470 
471  for (int j = 0; j < ACK_BASE_COUNT; j++)
472  base += analogRead(DCCppConfig::CurrentMonitorProg);
473  base /= ACK_BASE_COUNT;
474 
475  bWrite[0] = 0x74 + (highByte(cv) & 0x03); // set-up to re-verify entire byte
476 
477  loadPacket(0, resetPacket, 2, 3); // NMRA recommends starting with 3 reset packets
478  loadPacket(0, bWrite, 3, 5); // NMRA recommends 5 verfy packets
479  loadPacket(0, resetPacket, 2, 1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
480 
481  for (int j = 0; j < ACK_SAMPLE_COUNT; j++) {
482  c = (int)((analogRead(DCCppConfig::CurrentMonitorProg) - base)*ACK_SAMPLE_SMOOTHING + c*(1.0 - ACK_SAMPLE_SMOOTHING));
483  if (c > ACK_SAMPLE_THRESHOLD)
484  d = 1;
485  }
486 
487  if (d == 0) // verify unsuccessful
488  bValue = -1;
489  }
490 
491 #if defined(USE_TEXTCOMMAND)
492  INTERFACE.print("<r");
493  INTERFACE.print(callBack);
494  INTERFACE.print("|");
495  INTERFACE.print(callBackSub);
496  INTERFACE.print("|");
497  INTERFACE.print(cv + 1);
498  INTERFACE.print(" ");
499  INTERFACE.print(bValue);
500  INTERFACE.print(">");
501 #if !defined(USE_ETHERNET)
502  INTERFACE.println("");
503 #endif
504 #endif
505 } // RegisterList::writeCVByte(ints)
506 
507 #ifdef USE_TEXTCOMMAND
508 void RegisterList::writeCVByte(char *s) volatile
509 {
510  int bValue, cv, callBack, callBackSub;
511 
512  if (sscanf(s, "%d %d %d %d", &cv, &bValue, &callBack, &callBackSub) != 4) // cv = 1-1024
513  {
514 #ifdef DCCPP_DEBUG_MODE
515  Serial.println(F("W Syntax error"));
516 #endif
517  return;
518  }
519 
520  this->writeCVByte(cv, bValue, callBack, callBackSub);
521 } // RegisterList::writeCVByte(string)
522 #endif
523 
525 
526 void RegisterList::writeCVBit(int cv, int bNum, int bValue, int callBack, int callBackSub) volatile
527 {
528  byte bWrite[4];
529  int c, d, base;
530 
531  cv--; // actual CV addresses are cv-1 (0-1023)
532  bValue = bValue % 2;
533  bNum = bNum % 8;
534 
535  bWrite[0] = 0x78 + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
536  bWrite[1] = lowByte(cv);
537  bWrite[2] = 0xF0 + bValue * 8 + bNum;
538 
539  loadPacket(0, resetPacket, 2, 1);
540  loadPacket(0, bWrite, 3, 4);
541  loadPacket(0, resetPacket, 2, 1);
542  loadPacket(0, idlePacket, 2, 10);
543 
544  // If monitor pin undefined, write cv without any confirmation...
545  if (DCCppConfig::CurrentMonitorProg != UNDEFINED_PIN)
546  {
547  c = 0;
548  d = 0;
549  base = 0;
550 
551  for (int j = 0; j < ACK_BASE_COUNT; j++)
552  base += analogRead(DCCppConfig::CurrentMonitorProg);
553  base /= ACK_BASE_COUNT;
554 
555  bitClear(bWrite[2], 4); // change instruction code from Write Bit to Verify Bit
556 
557  loadPacket(0, resetPacket, 2, 3); // NMRA recommends starting with 3 reset packets
558  loadPacket(0, bWrite, 3, 5); // NMRA recommends 5 verfy packets
559  loadPacket(0, resetPacket, 2, 1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
560 
561  for (int j = 0; j < ACK_SAMPLE_COUNT; j++) {
562  c = (int)((analogRead(DCCppConfig::CurrentMonitorProg) - base)*ACK_SAMPLE_SMOOTHING + c*(1.0 - ACK_SAMPLE_SMOOTHING));
563  if (c > ACK_SAMPLE_THRESHOLD)
564  d = 1;
565  }
566 
567  if (d == 0) // verify unsuccessful
568  bValue = -1;
569  }
570 
571 #if defined(USE_TEXTCOMMAND)
572  INTERFACE.print("<r");
573  INTERFACE.print(callBack);
574  INTERFACE.print("|");
575  INTERFACE.print(callBackSub);
576  INTERFACE.print("|");
577  INTERFACE.print(cv + 1);
578  INTERFACE.print(" ");
579  INTERFACE.print(bNum);
580  INTERFACE.print(" ");
581  INTERFACE.print(bValue);
582  INTERFACE.print(">");
583 #if !defined(USE_ETHERNET)
584  INTERFACE.println("");
585 #endif
586 #endif
587 } // RegisterList::writeCVBit(ints)
588 
589 #ifdef USE_TEXTCOMMAND
590 void RegisterList::writeCVBit(char *s) volatile
591 {
592  int bNum, bValue, cv, callBack, callBackSub;
593 
594  if(sscanf(s,"%d %d %d %d %d",&cv,&bNum,&bValue,&callBack,&callBackSub) != 5) // cv = 1-1024
595  {
596 #ifdef DCCPP_DEBUG_MODE
597  Serial.println(F("W Syntax error"));
598 #endif
599  return;
600  }
601 
602  this->writeCVBit(cv, bNum, bValue, callBack, callBackSub);
603 } // RegisterList::writeCVBit(string)
604 #endif
605 
607 
608 void RegisterList::writeCVByteMain(int cab, int cv, int bValue) volatile
609 {
610  byte b[6]; // save space for checksum byte
611  byte nB = 0;
612 
613  cv--;
614 
615  if (cab>127)
616  b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
617 
618  b[nB++] = lowByte(cab);
619  b[nB++] = 0xEC + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
620  b[nB++] = lowByte(cv);
621  b[nB++] = bValue;
622 
623  loadPacket(0, b, nB, 4);
624 
625 } // RegisterList::writeCVByteMain(ints)
626 
627 #ifdef USE_TEXTCOMMAND
628 void RegisterList::writeCVByteMain(char *s) volatile
629 {
630  int cab;
631  int cv;
632  int bValue;
633 
634  if (sscanf(s, "%d %d %d", &cab, &cv, &bValue) != 3)
635  {
636 #ifdef DCCPP_DEBUG_MODE
637  Serial.println(F("w Syntax error"));
638 #endif
639  return;
640  }
641 
642  this->writeCVByteMain(cab, cv, bValue);
643 } // RegisterList::writeCVByteMain(string)
644 #endif
645 
647 
648 void RegisterList::writeCVBitMain(int cab, int cv, int bNum, int bValue) volatile
649 {
650  byte b[6]; // save space for checksum byte
651  byte nB = 0;
652 
653  cv--;
654 
655  bValue = bValue % 2;
656  bNum = bNum % 8;
657 
658  if (cab>127)
659  b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
660 
661  b[nB++] = lowByte(cab);
662  b[nB++] = 0xE8 + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
663  b[nB++] = lowByte(cv);
664  b[nB++] = 0xF0 + bValue * 8 + bNum;
665 
666  loadPacket(0, b, nB, 4);
667 
668 } // RegisterList::writeCVBitMain(ints)
669 
670 #ifdef USE_TEXTCOMMAND
671 void RegisterList::writeCVBitMain(char *s) volatile
672 {
673  int cab;
674  int cv;
675  int bNum;
676  int bValue;
677 
678  if (sscanf(s, "%d %d %d %d", &cab, &cv, &bNum, &bValue) != 4)
679  {
680 #ifdef DCCPP_DEBUG_MODE
681  Serial.println(F("w Syntax error"));
682 #endif
683  return;
684  }
685 
686  this->writeCVBitMain(cab, cv, bNum, bValue);
687 } // RegisterList::writeCVBitMain(string)
688 #endif
689 
691 
692 #ifdef DCCPP_DEBUG_MODE
693 void RegisterList::printPacket(int nReg, byte *b, int nBytes, int nRepeat) volatile
694 {
695  INTERFACE.print("<*");
696  INTERFACE.print(nReg);
697  INTERFACE.print(":");
698  for(int i=0;i<nBytes;i++){
699  INTERFACE.print(" ");
700  INTERFACE.print(b[i],HEX);
701  }
702  INTERFACE.print(" / ");
703  INTERFACE.print(nRepeat);
704  INTERFACE.print(">");
705 #if !defined(USE_ETHERNET)
706  INTERFACE.println("");
707 #endif
708 } // RegisterList::printPacket()
709 #endif
710 
712 
713 byte RegisterList::idlePacket[3]={0xFF,0x00,0}; // always leave extra byte for checksum computation
714 byte RegisterList::resetPacket[3]={0x00,0x00,0};
715 
716 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