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  loadPacket(nReg, b, nB, 4, 1);
210 } // RegisterList::setFunction(ints)
211 
212 #ifdef USE_TEXTCOMMAND
213 void RegisterList::setFunction(char *s) volatile
214 {
215  int cab;
216  int fByte, eByte;
217  int nParams;
218 
219  nParams = sscanf(s, "%d %d %d", &cab, &fByte, &eByte);
220  if (nParams<2)
221  {
222 #ifdef DCCPP_DEBUG_MODE
223  Serial.println(F("f Syntax error"));
224 #endif
225  return;
226  }
227 
228  if (nParams == 2) // this is a request for functions FL,F1-F12
229  eByte = -1;
230 
231  this->setFunction(0, cab, fByte, eByte); // TODO : nReg 0 is not valid !
232 
233 } // RegisterList::setFunction(string)
234 #endif
235 
237 
238 void RegisterList::setAccessory(int aAdd, int aNum, int activate) volatile
239 {
240  byte b[3]; // save space for checksum byte
241 
242  b[0] = aAdd % 64 + 128; // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least signifcant bits of accessory address
243  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
244 
245  loadPacket(0, b, 2, 4, 1);
246 
247 } // RegisterList::setAccessory(ints)
248 
249 #ifdef USE_TEXTCOMMAND
250 void RegisterList::setAccessory(char *s) volatile
251 {
252  int aAdd; // the accessory address (0-511 = 9 bits)
253  int aNum; // the accessory number within that address (0-3)
254  int activate; // flag indicated whether accessory should be activated (1) or deactivated (0) following NMRA recommended convention
255 
256  if (sscanf(s, "%d %d %d", &aAdd, &aNum, &activate) != 3)
257  {
258 #ifdef DCCPP_DEBUG_MODE
259  Serial.println(F("a Syntax error"));
260 #endif
261  return;
262  }
263 
264  this->setAccessory(aAdd, aNum, activate);
265 
266 } // RegisterList::setAccessory(string)
267 #endif
268 
270 
271 void RegisterList::writeTextPacket(int nReg, byte *b, int nBytes) volatile
272 {
273 
274  if (nBytes<2 || nBytes>5) { // invalid valid packet
275  INTERFACE.print("<mInvalid Packet>");
276 #if !defined(USE_ETHERNET)
277  INTERFACE.println("");
278 #endif
279  return;
280  }
281 
282  loadPacket(nReg, b, nBytes, 0, 1);
283 
284 } // RegisterList::writeTextPacket(bytes)
285 
286 #ifdef USE_TEXTCOMMAND
287 void RegisterList::writeTextPacket(char *s) volatile
288 {
289  int nReg;
290  byte b[6];
291  int nBytes;
292 
293  nBytes = sscanf(s, "%d %hhx %hhx %hhx %hhx %hhx", &nReg, b, b + 1, b + 2, b + 3, b + 4) - 1;
294 
295  this->writeTextPacket(nReg, b, nBytes);
296 
297 } // RegisterList::writeTextPacket(string)
298 #endif
299 
301 
302 int RegisterList::readCVraw(int cv, int callBack, int callBackSub, bool FromProg) volatile
303 {
304  byte bRead[4];
305  int bValue;
306  int c, d, base;
307  cv--; // actual CV addresses are cv-1 (0-1023)
308 
309  byte MonitorPin = DCCppConfig::CurrentMonitorProg;
310  if (!FromProg)
311  MonitorPin = DCCppConfig::CurrentMonitorMain;
312 
313  if (MonitorPin == UNDEFINED_PIN)
314  return -1;
315 
316  bRead[0] = 0x78 + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
317  bRead[1] = lowByte(cv);
318 
319  bValue = 0;
320 
321  for (int i = 0; i<8; i++) {
322 
323  c = 0;
324  d = 0;
325  base = 0;
326 
327  for (int j = 0; j < ACK_BASE_COUNT; j++)
328  {
329  int val = (int)analogRead(MonitorPin);
330  base += val;
331  }
332  base /= ACK_BASE_COUNT;
333 
334  bRead[2] = 0xE8 + i;
335 
336  loadPacket(0, resetPacket, 2, 3); // NMRA recommends starting with 3 reset packets
337  loadPacket(0, bRead, 3, 5); // NMRA recommends 5 verfy packets
338  loadPacket(0, resetPacket, 2, 1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
339 
340  for (int j = 0; j<ACK_SAMPLE_COUNT; j++)
341  {
342  int val = (int)analogRead(MonitorPin);
343  c = (int)((val - base)*ACK_SAMPLE_SMOOTHING + c*(1.0 - ACK_SAMPLE_SMOOTHING));
344  if (c>ACK_SAMPLE_THRESHOLD)
345  d = 1;
346  }
347 
348  bitWrite(bValue, i, d);
349  }
350 
351  c = 0;
352  d = 0;
353  base = 0;
354 
355  for (int j = 0; j<ACK_BASE_COUNT; j++)
356  base += analogRead(MonitorPin);
357  base /= ACK_BASE_COUNT;
358 
359  bRead[0] = 0x74 + (highByte(cv) & 0x03); // set-up to re-verify entire byte
360  bRead[2] = bValue;
361 
362  loadPacket(0, resetPacket, 2, 3); // NMRA recommends starting with 3 reset packets
363  loadPacket(0, bRead, 3, 5); // NMRA recommends 5 verfy packets
364  loadPacket(0, resetPacket, 2, 1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
365 
366  for (int j = 0; j<ACK_SAMPLE_COUNT; j++) {
367  c = (int)((analogRead(MonitorPin) - base)*ACK_SAMPLE_SMOOTHING + c*(1.0 - ACK_SAMPLE_SMOOTHING));
368  if (c>ACK_SAMPLE_THRESHOLD)
369  d = 1;
370  }
371 
372  if (d == 0) // verify unsuccessful
373  bValue = -1;
374 
375 #if defined(USE_TEXTCOMMAND)
376  INTERFACE.print("<r");
377  INTERFACE.print(callBack);
378  INTERFACE.print("|");
379  INTERFACE.print(callBackSub);
380  INTERFACE.print("|");
381  INTERFACE.print(cv + 1);
382  INTERFACE.print(" ");
383  INTERFACE.print(bValue);
384  INTERFACE.print(">");
385 #if !defined(USE_ETHERNET)
386  INTERFACE.println("");
387 #endif
388 #endif
389 
390  return bValue;
391 }
392 
393 void RegisterList::readCV(int cv, int callBack, int callBackSub) volatile
394 {
395  /*int bValue = */RegisterList::readCVraw(cv, callBack, callBackSub, true);
396 
397 } // RegisterList::readCV(ints)
398 
399 #ifdef USE_TEXTCOMMAND
400 void RegisterList::readCV(char *s) volatile
401 {
402  int cv, callBack, callBackSub;
403 
404  if (sscanf(s, "%d %d %d", &cv, &callBack, &callBackSub) != 3) // cv = 1-1024
405  {
406 #ifdef DCCPP_DEBUG_MODE
407  Serial.println(F("R Syntax error"));
408 #endif
409  return;
410  }
411 
412  this->readCV(cv, callBack, callBackSub);
413 } // RegisterList::readCV(string)
414 #endif
415 
416 int RegisterList::readCVmain(int cv, int callBack, int callBackSub) volatile
417 {
418  return RegisterList::readCVraw(cv, callBack, callBackSub, false);
419 
420 } // RegisterList::readCV_Main()
421 
422 #ifdef USE_TEXTCOMMAND
423 int RegisterList::readCVmain(char *s) volatile
424 {
425  int cv, callBack, callBackSub;
426 
427  if (sscanf(s, "%d %d %d", &cv, &callBack, &callBackSub) != 3) // cv = 1-1024
428  {
429 #ifdef DCCPP_DEBUG_MODE
430  Serial.println(F("r Syntax error"));
431 #endif
432  return -1;
433  }
434 
435  return this->readCVmain(cv, callBack, callBackSub);
436 } // RegisterList::readCVmain(string)
437 #endif
438 
440 
441 void RegisterList::writeCVByte(int cv, int bValue, int callBack, int callBackSub) volatile
442 {
443  byte bWrite[4];
444  int c, d, base;
445 
446  cv--; // actual CV addresses are cv-1 (0-1023)
447 
448  bWrite[0] = 0x7C + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
449  bWrite[1] = lowByte(cv);
450  bWrite[2] = bValue;
451 
452  loadPacket(0, resetPacket, 2, 1);
453  loadPacket(0, bWrite, 3, 4);
454  loadPacket(0, resetPacket, 2, 1);
455  loadPacket(0, idlePacket, 2, 10);
456 
457  // If monitor pin undefined, write cv without any confirmation...
458  if (DCCppConfig::CurrentMonitorProg != UNDEFINED_PIN)
459  {
460  c = 0;
461  d = 0;
462  base = 0;
463 
464  for (int j = 0; j < ACK_BASE_COUNT; j++)
465  base += analogRead(DCCppConfig::CurrentMonitorProg);
466  base /= ACK_BASE_COUNT;
467 
468  bWrite[0] = 0x74 + (highByte(cv) & 0x03); // set-up to re-verify entire byte
469 
470  loadPacket(0, resetPacket, 2, 3); // NMRA recommends starting with 3 reset packets
471  loadPacket(0, bWrite, 3, 5); // NMRA recommends 5 verfy packets
472  loadPacket(0, resetPacket, 2, 1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
473 
474  for (int j = 0; j < ACK_SAMPLE_COUNT; j++) {
475  c = (int)((analogRead(DCCppConfig::CurrentMonitorProg) - base)*ACK_SAMPLE_SMOOTHING + c*(1.0 - ACK_SAMPLE_SMOOTHING));
476  if (c > ACK_SAMPLE_THRESHOLD)
477  d = 1;
478  }
479 
480  if (d == 0) // verify unsuccessful
481  bValue = -1;
482  }
483 
484 #if defined(USE_TEXTCOMMAND)
485  INTERFACE.print("<r");
486  INTERFACE.print(callBack);
487  INTERFACE.print("|");
488  INTERFACE.print(callBackSub);
489  INTERFACE.print("|");
490  INTERFACE.print(cv + 1);
491  INTERFACE.print(" ");
492  INTERFACE.print(bValue);
493  INTERFACE.print(">");
494 #if !defined(USE_ETHERNET)
495  INTERFACE.println("");
496 #endif
497 #endif
498 } // RegisterList::writeCVByte(ints)
499 
500 #ifdef USE_TEXTCOMMAND
501 void RegisterList::writeCVByte(char *s) volatile
502 {
503  int bValue, cv, callBack, callBackSub;
504 
505  if (sscanf(s, "%d %d %d %d", &cv, &bValue, &callBack, &callBackSub) != 4) // cv = 1-1024
506  {
507 #ifdef DCCPP_DEBUG_MODE
508  Serial.println(F("W Syntax error"));
509 #endif
510  return;
511  }
512 
513  this->writeCVByte(cv, bValue, callBack, callBackSub);
514 } // RegisterList::writeCVByte(string)
515 #endif
516 
518 
519 void RegisterList::writeCVBit(int cv, int bNum, int bValue, int callBack, int callBackSub) volatile
520 {
521  byte bWrite[4];
522  int c, d, base;
523 
524  cv--; // actual CV addresses are cv-1 (0-1023)
525  bValue = bValue % 2;
526  bNum = bNum % 8;
527 
528  bWrite[0] = 0x78 + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
529  bWrite[1] = lowByte(cv);
530  bWrite[2] = 0xF0 + bValue * 8 + bNum;
531 
532  loadPacket(0, resetPacket, 2, 1);
533  loadPacket(0, bWrite, 3, 4);
534  loadPacket(0, resetPacket, 2, 1);
535  loadPacket(0, idlePacket, 2, 10);
536 
537  // If monitor pin undefined, write cv without any confirmation...
538  if (DCCppConfig::CurrentMonitorProg != UNDEFINED_PIN)
539  {
540  c = 0;
541  d = 0;
542  base = 0;
543 
544  for (int j = 0; j < ACK_BASE_COUNT; j++)
545  base += analogRead(DCCppConfig::CurrentMonitorProg);
546  base /= ACK_BASE_COUNT;
547 
548  bitClear(bWrite[2], 4); // change instruction code from Write Bit to Verify Bit
549 
550  loadPacket(0, resetPacket, 2, 3); // NMRA recommends starting with 3 reset packets
551  loadPacket(0, bWrite, 3, 5); // NMRA recommends 5 verfy packets
552  loadPacket(0, resetPacket, 2, 1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)
553 
554  for (int j = 0; j < ACK_SAMPLE_COUNT; j++) {
555  c = (int)((analogRead(DCCppConfig::CurrentMonitorProg) - base)*ACK_SAMPLE_SMOOTHING + c*(1.0 - ACK_SAMPLE_SMOOTHING));
556  if (c > ACK_SAMPLE_THRESHOLD)
557  d = 1;
558  }
559 
560  if (d == 0) // verify unsuccessful
561  bValue = -1;
562  }
563 
564 #if defined(USE_TEXTCOMMAND)
565  INTERFACE.print("<r");
566  INTERFACE.print(callBack);
567  INTERFACE.print("|");
568  INTERFACE.print(callBackSub);
569  INTERFACE.print("|");
570  INTERFACE.print(cv + 1);
571  INTERFACE.print(" ");
572  INTERFACE.print(bNum);
573  INTERFACE.print(" ");
574  INTERFACE.print(bValue);
575  INTERFACE.print(">");
576 #if !defined(USE_ETHERNET)
577  INTERFACE.println("");
578 #endif
579 #endif
580 } // RegisterList::writeCVBit(ints)
581 
582 #ifdef USE_TEXTCOMMAND
583 void RegisterList::writeCVBit(char *s) volatile
584 {
585  int bNum, bValue, cv, callBack, callBackSub;
586 
587  if(sscanf(s,"%d %d %d %d %d",&cv,&bNum,&bValue,&callBack,&callBackSub) != 5) // cv = 1-1024
588  {
589 #ifdef DCCPP_DEBUG_MODE
590  Serial.println(F("W Syntax error"));
591 #endif
592  return;
593  }
594 
595  this->writeCVBit(cv, bNum, bValue, callBack, callBackSub);
596 } // RegisterList::writeCVBit(string)
597 #endif
598 
600 
601 void RegisterList::writeCVByteMain(int cab, int cv, int bValue) volatile
602 {
603  byte b[6]; // save space for checksum byte
604  byte nB = 0;
605 
606  cv--;
607 
608  if (cab>127)
609  b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
610 
611  b[nB++] = lowByte(cab);
612  b[nB++] = 0xEC + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
613  b[nB++] = lowByte(cv);
614  b[nB++] = bValue;
615 
616  loadPacket(0, b, nB, 4);
617 
618 } // RegisterList::writeCVByteMain(ints)
619 
620 #ifdef USE_TEXTCOMMAND
621 void RegisterList::writeCVByteMain(char *s) volatile
622 {
623  int cab;
624  int cv;
625  int bValue;
626 
627  if (sscanf(s, "%d %d %d", &cab, &cv, &bValue) != 3)
628  {
629 #ifdef DCCPP_DEBUG_MODE
630  Serial.println(F("w Syntax error"));
631 #endif
632  return;
633  }
634 
635  this->writeCVByteMain(cab, cv, bValue);
636 } // RegisterList::writeCVByteMain(string)
637 #endif
638 
640 
641 void RegisterList::writeCVBitMain(int cab, int cv, int bNum, int bValue) volatile
642 {
643  byte b[6]; // save space for checksum byte
644  byte nB = 0;
645 
646  cv--;
647 
648  bValue = bValue % 2;
649  bNum = bNum % 8;
650 
651  if (cab>127)
652  b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address
653 
654  b[nB++] = lowByte(cab);
655  b[nB++] = 0xE8 + (highByte(cv) & 0x03); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
656  b[nB++] = lowByte(cv);
657  b[nB++] = 0xF0 + bValue * 8 + bNum;
658 
659  loadPacket(0, b, nB, 4);
660 
661 } // RegisterList::writeCVBitMain(ints)
662 
663 #ifdef USE_TEXTCOMMAND
664 void RegisterList::writeCVBitMain(char *s) volatile
665 {
666  int cab;
667  int cv;
668  int bNum;
669  int bValue;
670 
671  if (sscanf(s, "%d %d %d %d", &cab, &cv, &bNum, &bValue) != 4)
672  {
673 #ifdef DCCPP_DEBUG_MODE
674  Serial.println(F("w Syntax error"));
675 #endif
676  return;
677  }
678 
679  this->writeCVBitMain(cab, cv, bNum, bValue);
680 } // RegisterList::writeCVBitMain(string)
681 #endif
682 
684 
685 #ifdef DCCPP_DEBUG_MODE
686 void RegisterList::printPacket(int nReg, byte *b, int nBytes, int nRepeat) volatile
687 {
688  INTERFACE.print("<*");
689  INTERFACE.print(nReg);
690  INTERFACE.print(":");
691  for(int i=0;i<nBytes;i++){
692  INTERFACE.print(" ");
693  INTERFACE.print(b[i],HEX);
694  }
695  INTERFACE.print(" / ");
696  INTERFACE.print(nRepeat);
697  INTERFACE.print(">");
698 #if !defined(USE_ETHERNET)
699  INTERFACE.println("");
700 #endif
701 } // RegisterList::printPacket()
702 #endif
703 
705 
706 byte RegisterList::idlePacket[3]={0xFF,0x00,0}; // always leave extra byte for checksum computation
707 byte RegisterList::resetPacket[3]={0x00,0x00,0};
708 
709 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