-
Notifications
You must be signed in to change notification settings - Fork 1
/
XpressNetMaster.cpp
1513 lines (1385 loc) · 60.9 KB
/
XpressNetMaster.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
*****************************************************************************
* XpressNetMaster.h - library for XpressNet protocoll
* Copyright (c) 08/2016 - 2023 Philipp Gahtow All right reserved.
*
*****************************************************************************
* FUNKTIONS:
*
* Build a XpressNet Master for driving and switch
* Support for Roco LokMaus2 and Multimaus
* Basic support for direct CV programming (read and write)
* Check XOR before packet dekoding
*
* -> Please contact Lenz Inc. for more details about XpressNet.
*
* not yet Supported:
* - Emergency stop a locomotive: 0x92 AddrH AddrL [XOR]
* - Emergency stop a locomotive: 0x91 loco_addr [XOR] (v2)
* - Service Mode: only direct Mode
* - DCC extended accessory command: 0x13 0x01 B+AddrH AddrL
* - DCC FAST CLOCK: 0x01 0xF2 0xF3
* - Command Tunnel: 0x3* ** ** [XOR]
* - BiDi messages: 0x7* ** ** [XOR]
* - Library Entry: 0xE9 0xF1 AddrH AddrL IDX SIZE [NAME][XOR]
* - Set function state: 0xE4 0x2* AddrH AddrL Group [XOR]
*****************************************************************************
*/
// include this library's description file
#include "XpressNetMaster.h"
#if defined(__AVR__)
#include <avr/interrupt.h>
XpressNetMasterClass *XpressNetMasterClass::active_object = 0; //Static
#elif defined(ESP8266) || defined(ESP32)
#include <SoftwareSerial.h>
SoftwareSerial XNetSwSerial;
#endif
// Constructor /////////////////////////////////////////////////////////////////
// Function that handles the creation and setup of instances
XpressNetMasterClass::XpressNetMasterClass()
{
// initialize this instance's variables
XNetAdr = 0; //Startaddresse des ersten XNet Device
XNetSlaveMode = 0; //Start in MASTER MODE
XNetSlaveInit = 0; //for init state in Slave Mode
XModeAuto = true; //Automatische Umschaltung Master/Slave Mode aktiv
Railpower = csNormal;
Fahrstufe = Loco128;
for (byte s = 0; s < 32; s++) { //clear busy slots
SlotLokUse[s] = 0xFFFF; //slot is inactiv
}
XNetTXBuffer.get = 0; //start position to read data from the buffer
XNetTXBuffer.pos = 0; //position of byte that we are sending
XNetTXBuffer.put = 0; //start position to store data in buffer
XNetRXBuffer.get = 0; //start position to read data from the buffer
XNetRXBuffer.pos = 0; //position of byte that we are sending
XNetRXBuffer.put = 0; //start position to store data in buffer
for (byte b = 0; b < XNetBufferSize; b++) { //clear send buffer
XNetTXBuffer.msg[b].length = 0x00;
XNetRXBuffer.msg[b].length = 0x00;
for (byte d = 0; d < XNetBufferMaxData; d++) {
XNetTXBuffer.msg[b].data[d] = 0x00;
XNetRXBuffer.msg[b].data[d] = 0x00;
}
}
XNetCVAdr = 0; //no CV read
XNetCVvalue = 0; //no CV value
}
//******************************************Serial*******************************************
#if defined(ESP8266) || defined(ESP32)
void XpressNetMasterClass::setup(uint8_t FStufen, uint8_t XNetPort, uint8_t XControl, bool XnModeAuto) //Initialisierung Serial
#else
void XpressNetMasterClass::setup(uint8_t FStufen, uint8_t XControl, bool XnModeAuto) //Initialisierung Serial
#endif
{
Fahrstufe = FStufen;
MAX485_CONTROL = XControl;
XModeAuto = XnModeAuto;
if (XnModeAuto == false) //change to SLAVE MODE ONLY?
XNetSlaveMode = XNetSlaveCycle; //reactivate SLAVE MODE
// LISTEN_MODE
pinMode(MAX485_CONTROL, OUTPUT);
digitalWrite (MAX485_CONTROL, LOW); //RECEIVE_MODE - SLAVE
#if defined(__AVR__) //Configuration for 8-Bit MCU
//Set up on 62500 Baud
cli(); //disable interrupts while initializing the USART
#if defined(__AVR_ATmega328p__)
UBRRH = 0;
UBRRL = 0x0F;
UCSRA = 0;
UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE) | (1<<TXCIE) | (1<<UCSZ2);
UCSRC = (1<<UCSZ1) | (1<<UCSZ0);
#elif defined(SERIAL_PORT_0)
UBRR0H = 0;
UBRR0L = 0x0F;
UCSR0A = 0;
UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0) | (1<<TXCIE0)| (1<<UCSZ02);
UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
#else
UBRR1H = 0;
UBRR1L = 0x0F;
UCSR1A = 0;
UCSR1B = (1<<RXEN1) | (1<<TXEN1) | (1<<RXCIE1) | (1<<TXCIE1) | (1<<UCSZ12);
UCSR1C = (1<<UCSZ11) | (1<<UCSZ10);
#endif
sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed
/*
* Enable reception (RXEN = 1).
* Enable transmission (TXEN0 = 1).
* Enable Receive Interrupt (RXCIE = 1).
* Enable Transmit Interrupt (TXCIE = 1).
* Set 9-bit character mode (UCSZ00, UCSZ01, and UCSZ02 together control this,
* But UCSZ00, UCSZ01 are in Register UCSR0C).
*/
active_object = this; //hold Object to call it back in ISR
#elif defined(ESP8266) || defined(ESP32)
XNetSwSerial.begin(62500, SWSERIAL_8S1, XNetPort, XNetPort, false, 95); //One Wire Half Duplex Serial, parity mode SPACE
if (!XNetSwSerial) { // If the object did not initialize, then its configuration is invalid
Serial.println("Invalid SoftwareSerial pin configuration, check config");
while (1) { // Don't continue with invalid configuration
delay (1000);
}
}
/* At high bitrates (115200bps) send bit timing can be improved at the expense of blocking concurrent full duplex receives,
with the EspSoftwareSerial::UART::enableIntTx(false) function call.*/
XNetSwSerial.enableIntTx(true); // high speed half duplex, don't turn off interrupts during tx!
/* Remove to make the start up faster because of multiMaus V2-00!!!
//wait to make the library work before first messages will be send!
unsigned long time = micros();
while (micros() < (time + 4000)) {
update();
}
*/
#endif
}
// Public Methods //////////////////////////////////////////////////////////////
// Functions available in Wiring sketches, this library, and other libraries
//*******************************************************************************************
//Daten Auswerten
bool XpressNetMasterClass::update(void)
{
bool status = false; //nothing to do!
#if defined(ESP8266) || defined(ESP32)
//Check if we have data available for receive:
XNetReceive();
#endif
//check if have some receive data in our buffer to decode:
if (XNetRXBuffer.get != XNetRXBuffer.put) {
status = true; //work on a packet!
#if defined (XNetDEBUGTime)
XNetSerial.print(XNetRXBuffer.put);
XNetSerial.print("r");
XNetSerial.print(XNetRXBuffer.get);
XNetSerial.print(" Paket time: ");
XNetSerial.print(micros() - XSendCount);
#endif
//XNetDataReady = false;
if (XNetCheckXOR()) { //Checks the XOR
#if defined (XNetDEBUGTime)
XNetSerial.println(" OK");
#endif
XNetAnalyseReceived(); //Auswerten der empfangenen Daten
}
XNetRXclear(XNetRXBuffer.get); //alte Nachricht löschen
XNetRXBuffer.get++;
if (XNetRXBuffer.get >= XNetBufferSize) //overflow?
XNetRXBuffer.get = 0; //start from the first value
if (XNetSlaveMode == 0x00) { //MASTER MODE
XNetSendData(); //start sending an answer
//Start next Transmission Window direct after receive!
getNextXNetAdr(); //Send next CallByte
XNetSendData(); //start sending out by interrupt
XSendCount = micros(); //save time last Data on Bus!
XNetSlaveInit = 0; //reset the init prozess for slave Mode
}
}
if (XNetSlaveMode == 0x00) { //MASTER MODE
if ( (micros() - XSendCount) > XNetTransmissionWindow) {
XNetRXclear(XNetRXBuffer.put); //alte Nachricht löschen
getNextXNetAdr(); //Send next CallByte
XNetSendData(); //start sending out by interrupt
XSendCount = micros(); //save time last Data on Bus!
XNetSlaveInit = 0; //reset the init prozess for slave Mode
}
}
else {
if (XNetSlaveInit == 0) {
XNetSlaveInit = 1;
uint8_t Init[] = { 0x00, 0x21, 0x21, 0x00 };
XNetsend(Init, 4); //send initsequence
#if defined (XNetDEBUG)
XNetSerial.print("Slave INIT: ");
XNetSerial.println(XNetSlaveMode);
#endif
}
if ((micros() - XSendCount) > 2000) {
//we need to initialize the SLAVE MODE?
XSendCount = micros(); //save time last Data on Bus!
if (XModeAuto && (XNetSlaveMode > 0))
XNetSlaveMode--; //stay only in SLAVE MODE if we receive CallBytes
}
if (XNetTXBuffer.put != XNetTXBuffer.get)
status = true;
}
return status;
}
//--------------------------------------------------------------------------------------------
//Checks the XOR
bool XpressNetMasterClass::XNetCheckXOR(void) {
byte rxXOR = 0x00; //store the read in XOR
for (byte i = 0; i < ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader] & 0x0F) + 2); i++) {
rxXOR = rxXOR ^ XNetRXBuffer.msg[XNetRXBuffer.get].data[i+1]; //jump over CallByte
}
if (rxXOR == 0x00) { //XOR is 0x00!
return true;
}
#if defined (XNetDEBUG)
XNetSerial.print(" XOR Fails: ");
XNetSerial.print((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader] & 0x0F) + 2);
for (byte i = 0; i < XNetRXBuffer.msg[XNetRXBuffer.get].length+1; i++) {
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[i] < 0x10)
XNetSerial.print(" 0x0");
else XNetSerial.print(" 0x");
XNetSerial.print(XNetRXBuffer.msg[XNetRXBuffer.get].data[i], HEX);
}
XNetSerial.println();
#endif
//Übertragungsfehler:
if (XNetSlaveMode == 0x00) { //MASTER MODE
uint8_t ERR[] = {DirectedOps, 0x61, 0x80, 0xE1 };
XNetsend(ERR, 4);
}
return false;
}
//--------------------------------------------------------------------------------------------
//Daten Auswerten
void XpressNetMasterClass::XNetAnalyseReceived(void) { //work on received data
#if defined (XNetDEBUG)
if (XNetSlaveMode == 0x00)
XNetSerial.print("MRX: 0x1");
else XNetSerial.print("SRX: 0x1");
XNetSerial.print(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetCallByte], HEX);
for (byte i = 1; i < ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader] & 0x0F) + 3); i++) {
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[i] < 0x10)
XNetSerial.print(" 0x0");
else XNetSerial.print(" 0x");
XNetSerial.print(XNetRXBuffer.msg[XNetRXBuffer.get].data[i], HEX);
}
XNetSerial.print(" msg:");
XNetSerial.println(XNetRXBuffer.get);
#endif
switch (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader]) {
case 0x21:
if (XNetSlaveMode == 0x00) {
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x24 && XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] == 0x05) { //Command station status indication response
/*
Bit 0: =1 - Command station is in emergency off (Nothalt)
Bit 1: =1 - Command station is in emergency stop (Notaus)
Bit 2: Command station-Start mode (0 = manual mode, 1 = automatic mode)
Automatic Mode: All locomotives start at their last known speed each
time the command station is powered up
Manual Mode: All locomotives have a speed of 0 and functions out on
command station power up
Bit 3: = 1 - The command station is in service mode
Bit 4: reserved
Bit 5: reserved
Bit 6: = 1 - The command station is performing a power up.
Bit 7: = 1 - There was a RAM check error in the command station
*/
byte status = 0x01; //csTrackVoltageOff = B1;
switch (Railpower) {
case csNormal: status = 0; break;
case csEmergencyStop: status = 0x01; break;
case csServiceMode: status = 0x08; break;
case csShortCircuit: status = 0x02; break;
}
uint8_t sendStatus[] = { DirectedOps, 0x62, 0x22, status, 0x00 };
getXOR(sendStatus, 5);
XNetsend(sendStatus, 5);
}
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x21 && XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] == 0x00) { //Command station softwareversion response
uint8_t sendVersion[] = { DirectedOps, 0x63, 0x21, XNetVersion, XNetID, 0x00 }; //63-21 36 0 74
getXOR(sendVersion, 6);
XNetsend(sendVersion, 6);
}
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x80 && XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] == 0xA1) { //Alles Aus (Notaus)
// Track power off
Railpower = csTrackVoltageOff;
if (notifyXNetPower)
notifyXNetPower(Railpower);
}
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x81 && XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] == 0xA0) { //Alles An
// Normal Operation Resumed
Railpower = csNormal;
if (notifyXNetPower)
notifyXNetPower(Railpower);
}
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x10 && XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] == 0x31) { //Request for Service Mode results
if (XNetCVAdr != 0) {
uint8_t sendStatus[] = { DirectedOps, 0x63, 0x14, lowByte(XNetCVAdr), XNetCVvalue, 0x00}; //Service Mode response for Direct CV mode
getXOR(sendStatus, 6);
XNetsend(sendStatus, 6);
XNetCVAdr = 0; //reset CV read Adr
XNetCVvalue = 0;//reset CV value
}
else {
// Programming info. "Command station busy"
uint8_t sendStatus[] = { DirectedOps, 0x61, 0x1F, 0x00 };
if (XNetCVvalue == 0xFF) { //"no ACK"
sendStatus[2] = 0x13; //Programming info. "Data byte not found"
XNetCVvalue = 0; //reset error!
}
// Programming info. "Command station ready "
//uint8_t sendStatus[] = { DirectedOps, 0x61, 0x11, 0x00 };
getXOR(sendStatus, 4);
XNetsend(sendStatus, 4);
}
}
if (SlotLokUse[DirectedOps & 0x1F] == 0xFFFF)
SlotLokUse[DirectedOps & 0x1F] = 0; //mark Slot as activ
//XNetclear(); //alte Nachricht löschen
}
break;
case 0x22: //Start Programming
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x11) { //Register Mode read request (Register Mode)
}
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x14) { //Paged Mode read request (Paged Mode)
}
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x15) { //Direct Mode CV read request (CV mode)
XNetCVAdr = 0; //no CV read
XNetCVvalue = 0; //no CV value
if (notifyXNetDirectReadCV)
notifyXNetDirectReadCV(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]-1); //try to read the CV 1..255
break;
}
unknown(); //unbekannte Anfrage
//XNetclear(); //alte Nachricht löschen
break;
case 0x23:
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x12) { //Register Mode write request (Register Mode)
}
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x16) { //Direct Mode CV write request (CV mode)
XNetCVAdr = 0; //no CV read
XNetCVvalue = 0; //no CV value
if (notifyXNetDirectCV)
notifyXNetDirectCV(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]-1, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]);
break;
}
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x17) { //0x23 0x17 CV DAT[XOR] - Paged Mode write request(Paged mode)
}
unknown(); //unbekannte Anfrage
//XNetclear();
break;
case 0xE6: { //POM CV write MultiMaus
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x30) {
uint16_t Adr = ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F) << 8) + XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3];
uint16_t CVAdr = ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4] & B11) << 8) + XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata5];
if ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4] & 0xFC) == 0xEC) { //set byte
if (notifyXNetPOMwriteByte)
notifyXNetPOMwriteByte (Adr, CVAdr, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata6]);
}
if ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4] & 0xFC) == 0xE8) { //set bit
if (notifyXNetPOMwriteBit)
notifyXNetPOMwriteBit (Adr, CVAdr, (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata6] & 0x0F));
}
}
break; }
case 0x80:
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x80) { //EmStop
//Emergency Stop
Railpower = csEmergencyStop;
if (notifyXNetPower)
notifyXNetPower(Railpower);
break;
}
unknown(); //unbekannte Anfrage
//XNetclear(); //alte Nachricht löschen
break;
case 0xE3: {
if (XNetSlaveMode == 0x00) {
switch (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1]) {
case 0x00: //Lokdaten anfordern & F0 bis F12 anfordern
if (notifyXNetgiveLocoInfo)
notifyXNetgiveLocoInfo(DirectedOps, word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]));
break;
case 0x07: { //Funktionsstatus F0 bis F12 anfordern (Funktion ist tastend oder nicht tastend)
//0x07, sonst ist LokMaus2 langsam!
uint8_t LocoFkt[] = { DirectedOps, 0xE3, 0x50, 0x00, 0x00, 0x00 };
getXOR(LocoFkt, 6);
XNetsend(LocoFkt, 6);
break;
}
case 0x08: { //Funktionsstatus F13 bis F28 anfordern (Funktion ist tastend oder nicht tastend)
uint8_t LocoFkt[] = { DirectedOps, 0xE3, 0x51, 0x00, 0x00, 0x00 };
getXOR(LocoFkt, 6);
XNetsend(LocoFkt, 6);
break;
}
case 0x09: //Funktionszustand F13 bis F28 anfordern
if (notifyXNetgiveLocoFunc)
notifyXNetgiveLocoFunc(DirectedOps, word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]));
break;
case 0xF0: //Lok und Funktionszustand MultiMaus anfordern
if (notifyXNetgiveLocoMM)
notifyXNetgiveLocoMM(DirectedOps, word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]));
break;
default: unknown(); //unbekannte Anfrage
}
}
//XNetclear(); //alte Nachricht löschen
break;
}
case 0xE4: { //Fahrbefehle
AddBusySlot(DirectedOps, word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3])); //set Busy
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x10) { //14 Fahrstufen
if (notifyXNetLocoDrive14)
notifyXNetLocoDrive14(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x11) { //27 Fahrstufen
if (notifyXNetLocoDrive27)
notifyXNetLocoDrive27(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x12) { //28 Fahrstufen
if (notifyXNetLocoDrive28)
notifyXNetLocoDrive28(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x13) { //128 Fahrstufen
if (notifyXNetLocoDrive128)
notifyXNetLocoDrive128(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x20) { //Funktionsbefehl Gruppe1 0 0 0 F0 F4 F3 F2 F1
if (notifyXNetLocoFunc1)
notifyXNetLocoFunc1(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x21) { //Funktionsbefehl Gruppe2 0000 F8 F7 F6 F5
if (notifyXNetLocoFunc2)
notifyXNetLocoFunc2(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x22) { //Funktionsbefehl Gruppe3 0000 F12 F11 F10 F9
if (notifyXNetLocoFunc3)
notifyXNetLocoFunc3(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x23 || XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0xF3) { //Funktionsbefehl Gruppe4 F20-F13
//0xF3 = undocumented command is used when a mulitMAUS is controlling functions f20..f13.
if (notifyXNetLocoFuncX)
notifyXNetLocoFuncX(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), 0x04, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x28) { //Funktionsbefehl Gruppe5 F28-F21
if (notifyXNetLocoFuncX)
notifyXNetLocoFuncX(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), 0x05, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x29) { //Funktionsbefehl Gruppe6 F36-F29
if (notifyXNetLocoFuncX)
notifyXNetLocoFuncX(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), 0x06, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x2A) { //Funktionsbefehl Gruppe7 F37-F44
if (notifyXNetLocoFuncX)
notifyXNetLocoFuncX(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), 0x07, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x2B) { //Funktionsbefehl Gruppe8 F45-F52
if (notifyXNetLocoFuncX)
notifyXNetLocoFuncX(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), 0x08, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x50) { //Funktionsbefehl Gruppe9 F53-F60
if (notifyXNetLocoFuncX)
notifyXNetLocoFuncX(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), 0x09, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x51) { //Funktionsbefehl Gruppe10 F61-F68
if (notifyXNetLocoFuncX)
notifyXNetLocoFuncX(word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]), 0x0A, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4]);
}
else unknown(); //unbekannte Anfrage
/*
if (XNetSlaveMode == 0x00) { //we are the MASTER
//für MultiMaus als Rückmeldung:
if (notifyXNetgiveLocoMM)
notifyXNetgiveLocoMM(DirectedOps, word(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & 0x3F, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]));
}
*/
break;
}
case 0x42: //Accessory Decoder information request
if (notifyXNetTrntInfo && XNetSlaveMode == 0x00)
notifyXNetTrntInfo(DirectedOps, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1], XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]);
//XNetclear(); //alte Nachricht löschen
break;
case 0x43: //Accessory Decoder >1024 information request
if (notifyXNetTrntInfo && XNetSlaveMode == 0x00)
notifyXNetTrntInfo(DirectedOps, (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] << 8) | XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2], XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]);
break;
case 0x52: //Accessory Decoder operation request
if (notifyXNetTrnt)
notifyXNetTrnt((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] << 2) | ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & B110) >> 1), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]);
//XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] = 0000 ABBP
//A = Weichenausgang(Spulenspannung EIN/AUS)
//BB = Adresse des Dekoderport 1..4
//P = Ausgang (Gerade = 0 / Abzweigen = 1)
//XNetclear(); //alte Nachricht löschen
break;
case 0x53: //Accessory Decoder >1024 operation request ab Version 3.8
if (notifyXNetTrnt)
notifyXNetTrnt(( ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] << 8) | XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]) << 2) | ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3] & B110) >> 1), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]);
break;
default: //Befehl in Zentrale nicht vorhanden
unknown(); //unbekannte Anfrage
}
if (XNetSlaveMode != 0x00) { //SLAVE-MODE
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetCallByte] == GENERAL_BROADCAST) { //Central Station broadcast data
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader] == 0x61) {
if ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x01) && (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] == 0x60)) {
// Normal Operation Resumed
Railpower = csNormal;
if (notifyXNetPower)
notifyXNetPower(Railpower);
}
else if ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x00) && (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] == 0x61)) {
// Track power off
Railpower = csTrackVoltageOff;
if (notifyXNetPower)
notifyXNetPower(Railpower);
}
else if ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x08)) {
// Track Short
Railpower = csShortCircuit;
if (notifyXNetPower)
notifyXNetPower(Railpower);
}
else if ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x02) && (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] == 0x63)) {
// Service Mode Entry
Railpower = csServiceMode;
if (notifyXNetPower)
notifyXNetPower(Railpower);
}
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader] == 0x81) {
if ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x00) && (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] == 0x81)) {
//Emergency Stop
Railpower = csEmergencyStop;
if (notifyXNetPower)
notifyXNetPower(Railpower);
}
}
else if ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader] & 0xF0) == 0x40) {
//Rückmeldung Schaltinformation
byte len = (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader] & 0x0F) / 2; //each Adr and Data
if(notifyXNetFeedback) {
for (byte i = 1; i <= len; i++) {
notifyXNetFeedback((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader+(i*2)-1] << 2) | ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader+(i*2)] & B110) >> 1), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader+(i*2)]);
//XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] = 0000 ABBP
//A = Weichenausgang(Spulenspannung EIN/AUS)
//BB = Adresse des Dekoderport 1..4
//P = Ausgang (Gerade = 0 / Abzweigen = 1)
}
}
}
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader] == 0x05 && XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0xF1) {
//DCC FAST CLOCK set request
/* 0x05 0xF1 TCODE1 TCODE2 TCODE3 TCODE4 [XOR]
00mmmmmm TCODE1, mmmmmm = denotes minutes, range 0...59.
100HHHHH TCODE2, HHHHH = denotes hours, range 0...23.
01000www TCODE3, www = denotes day of week, 0=monday, 1=tuesday, a.s.o.
110fffff TCODE4, fffff = denotes speed ratio, range 0..31. (0=stopped)
*/
}
} //Broadcast END
else if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetCallByte] == ACK_REQ) { //Central Station ask client for ACK?
uint8_t AckSeq[] = {0x00, 0x20, 0x20};
XNetsend (AckSeq, 3);
} //ACK END
else { //Central Station send data ...
switch (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetheader]) {
case 0x52: // Some other device asked for an accessory change
break;
case 0x61: //Zustand
break;
case 0x62: //Version
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x22) {
switch (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]) {
case 0x00: //csNormal
Railpower = csNormal;
if (notifyXNetPower)
notifyXNetPower(Railpower);
break;
case 0x02: //csTrackVoltageOff
Railpower = csTrackVoltageOff;
if (notifyXNetPower)
notifyXNetPower(Railpower);
break;
case 0x01: //csEmergencyStop
Railpower = csEmergencyStop;
if (notifyXNetPower)
notifyXNetPower(Railpower);
break;
case 0x08: //csServiceMode
Railpower = csServiceMode;
if (notifyXNetPower)
notifyXNetPower(Railpower);
break;
}
if (XNetSlaveInit == 2) {
XNetSlaveInit = 0xFF; //init Fertig!
}
}
break;
case 0x63:
if (XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x21) {
//x1FF 0x63 0x21 0x36 0x13 0x67 Version der Zentrale
if (XNetSlaveInit == 1) {
XNetSlaveInit = 2; //init Fertig!
//starte 2. Stufe
//Slave Init Softwareversion anfragen:
getStatus(); //0x21, 0x24, 0x05
}
}
break;
case 0xE3: //Antwort abgefrage Funktionen F13-F28
if (SlaveRequestLocoFkt != 0 && XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x52) { //save Loco and KENNUNG
if (notifyXNetLocoFuncX) {
notifyXNetLocoFuncX(SlaveRequestLocoFkt, 0x04, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]);
notifyXNetLocoFuncX(SlaveRequestLocoFkt, 0x05, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]);
}
SlaveRequestLocoFkt = 0; //reset
}
break;
case 0xE4: //Antwort der abgefragen Lok
if (SlaveRequestLocoInfo != 0) { //save Loco
switch(XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1]) { //KENNUNG
case 0x00: if (notifyXNetLocoDrive14)
notifyXNetLocoDrive14(SlaveRequestLocoInfo, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]);
break; //14 steps
case 0x01: if (notifyXNetLocoDrive27)
notifyXNetLocoDrive27(SlaveRequestLocoInfo, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]);
break; //17 steps
case 0x02: if (notifyXNetLocoDrive28)
notifyXNetLocoDrive28(SlaveRequestLocoInfo, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]);
break; //28 steps
case 0x04: if (notifyXNetLocoDrive128)
notifyXNetLocoDrive128(SlaveRequestLocoInfo, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]);
break; //128 steps
}
if (notifyXNetLocoFunc1)
notifyXNetLocoFunc1(SlaveRequestLocoInfo, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]);
if (notifyXNetLocoFunc2)
notifyXNetLocoFunc2(SlaveRequestLocoInfo, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4] & 0x0F);
if (notifyXNetLocoFunc3)
notifyXNetLocoFunc3(SlaveRequestLocoInfo, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata4] >> 4);
SlaveRequestLocoInfo = 0; //reset
}
if (SlaveRequestLocoFkt != 0 && XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] == 0x51) { //LENZ only F13-F28
if (notifyXNetLocoFuncX) {
notifyXNetLocoFuncX(SlaveRequestLocoFkt, 0x04, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]);
notifyXNetLocoFuncX(SlaveRequestLocoFkt, 0x05, XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata3]);
}
SlaveRequestLocoFkt = 0; //reset
}
break;
case 0x42: //Antwort Schaltinformation
if (notifyXNetTrnt)
notifyXNetTrnt((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata1] << 2) | ((XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2] & B110) >> 1), XNetRXBuffer.msg[XNetRXBuffer.get].data[XNetdata2]);
break;
case 0xE1: //Err Lok control
break;
} //switch HEADER END
} //MY_ADDRESS END
} //ENDE SLAVE MODE
}
//--------------------------------------------------------------------------------------------
//Rückmeldung über Zustand Master-Mode:
bool XpressNetMasterClass::getOperationModeMaster(void)
{
if (XNetSlaveMode == 0x00) {
return true; //Master Mode
}
return false; //Slave Mode
}
//--------------------------------------------------------------------------------------------
//Befehl in Zentrale nicht vorhanden
void XpressNetMasterClass::unknown(void) //unbekannte Anfrage
{
if (XNetSlaveMode == 0x00) { //MASTER MODE
uint8_t NotIn[] = {DirectedOps, 0x61, 0x82, 0xE3 };
XNetsend(NotIn, 4);
}
}
//--------------------------------------------------------------------------------------------
void XpressNetMasterClass::getNextXNetAdr(void)
{
XNetAdr++; //nächste Adresse im XNet
if (XNetAdr == 0)
XNetAdr++; //nächste Adresse im XNet 1..31 only!
uint8_t TempAdr = XNetAdr % 32;
if (XNetAdr > 31) { //wenn letzte erreicht von Beginn!
//search for used slots to call them more then unused:
while((SlotLokUse[TempAdr % 32] == 0xFFFF) && (TempAdr <= 31)) { //slot used an loco address?
TempAdr++;
XNetAdr++; //nächste Adresse im XNet
}
if ((TempAdr % 32) == 0)
TempAdr = 1; //start at the beginning
}
/*
AAAAA = XNetAdr % 32 (Slot 1 to 31)
Call Byte Definitions:
Call_Response P11A AAAA
Call_Inquiry P10A AAAA
Call_Request_Ack_From_Device P00A AAAA
Call_Broadcast P110 0000
Call_Transfer_Err P11A AAAA
*/
//Generate byte for the new window:
CallByteInquiry = callByteParity((TempAdr % 32) | 0x40); // | 0x100; // the address for Call Byte Window
RequestAck = callByteParity((TempAdr % 32)| 0x00); // | 0x100; // the address for a request acknowlegement sent
DirectedOps = callByteParity((TempAdr % 32)| 0x60); // | 0x100; // the address when we are sending ops
//Send CallByteInquiry for next Addr:
uint8_t NormalInquiry[] = { CallByteInquiry };
XNetsend(NormalInquiry, 1);
XNetRXBuffer.msg[XNetRXBuffer.put].length = 0; //clear - only for sync!
XNetRXBuffer.msg[XNetRXBuffer.put].data[XNetCallByte] = CallByteInquiry; //add CallByte to RX because we are Master
}
//--------------------------------------------------------------------------------------------
//Zustand der Gleisversorgung setzten
void XpressNetMasterClass::setPower(byte Power)
{ switch (Power) {
case csNormal: {
if (XNetSlaveMode == 0x00) { //MASTER MODE
uint8_t NormalPower[] = { GENERAL_BROADCAST, 0x61, 0x01, 0x60 };
XNetsend(NormalPower, 4);
}
else {
uint8_t PowerAn[] = {0x00, 0x21, 0x81, 0xA0 };
XNetsend(PowerAn, 4);
}
break;
}
case csEmergencyStop: {
if (XNetSlaveMode == 0x00) { //MASTER MODE
uint8_t EStopPower[] = { GENERAL_BROADCAST, 0x81, 0x00, 0x81 };
XNetsend(EStopPower, 4);
}
else {
uint8_t EmStop[] = {0x00, 0x80, 0x80 };
XNetsend(EmStop, 3);
}
break;
}
case csTrackVoltageOff: {
if (XNetSlaveMode == 0x00) { //MASTER MODE
uint8_t OffPower[] = { GENERAL_BROADCAST, 0x61, 0x00, 0x61 };
XNetsend(OffPower, 4);
}
else {
uint8_t PowerAus[] = {0x00, 0x21, 0x80, 0xA1 };
XNetsend(PowerAus, 4);
}
break;
}
case csShortCircuit: {
if (XNetSlaveMode == 0x00) { //MASTER MODE
uint8_t ShortPower[] = { GENERAL_BROADCAST, 0x61, 0x08, 0x69 };
XNetsend(ShortPower, 4);
}
break;
}
case csServiceMode: {
if (XNetSlaveMode == 0x00) { //MASTER MODE
uint8_t ServicePower[] = { GENERAL_BROADCAST, 0x61, 0x02, 0x63 };
XNetsend(ServicePower, 4);
}
break;
}
}
Railpower = Power; //Save new Power State
}
//--------------------------------------------------------------------------------------------
//Rückmeldung verteilen
void XpressNetMasterClass::setBCFeedback(byte data1, byte data2)
{
uint8_t Feedback[] = { GENERAL_BROADCAST, 0x42, data1, data2, 0x00};
XNetsend(Feedback, 5);
}
//--------------------------------------------------------------------------------------------
//Lok in use (Request/Vormerken)
void XpressNetMasterClass::ReqLocoBusy(uint16_t Adr) {
//SetLocoBusyAdr = Adr;
//BusyAdrCount = 0;
//AddBusySlot(0, Adr);
for (byte s = 1; s < 32; s++) {
if (SlotLokUse[s] == Adr) { //if in use from X-Net device -> set busy
SetLocoBusy(callByteParity(s | 0x60), SlotLokUse[s]);
SlotLokUse[s] = 0; //clean slot
}
}
SlotLokUse[0] = Adr;
}
//--------------------------------------------------------------------------------------------
//Lok in use (Busy)
void XpressNetMasterClass::SetLocoBusy(uint8_t UserOps, uint16_t Adr) {
uint8_t LocoInfo[] = { UserOps, 0xE3, 0x40, 0x00, 0x00, 0x00 };
if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707)
LocoInfo[3] = (Adr >> 8) | 0xC0;
else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063)
LocoInfo[4] = Adr & 0xFF;
getXOR(LocoInfo, 6);
XNetsend(LocoInfo, 6);
}
//--------------------------------------------------------------------------------------------
//Zentralen Status an XNet abfragen
void XpressNetMasterClass::getStatus() {
uint8_t commandVersionSequence[] = { 0x00, 0x21, 0x24, 0x05};
XNetsend (commandVersionSequence, 4);
}
//--------------------------------------------------------------------------------------------
//Lokinfo an XNet abfragen
void XpressNetMasterClass::getLocoInfo(uint16_t Adr) {
uint8_t LocoInfo[] = { 0x00, 0xE3, 0x00, 0x00, 0x00, 0x00 };
if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707)
LocoInfo[3] = (Adr >> 8) | 0xC0;
else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063)
LocoInfo[4] = Adr & 0xFF;
getXOR(LocoInfo, 6);
XNetsend(LocoInfo, 6);
SlaveRequestLocoInfo = Adr; //save Loco
}
//--------------------------------------------------------------------------------------------
//Lokfkt an XNet abfragen
void XpressNetMasterClass::getLocoFkt(uint16_t Adr) {
uint8_t LocoInfo[] = { 0x00, 0xE3, 0x08, 0x00, 0x00, 0x00 };
if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707)
LocoInfo[3] = (Adr >> 8) | 0xC0;
else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063)
LocoInfo[4] = Adr & 0xFF;
getXOR(LocoInfo, 6);
XNetsend(LocoInfo, 6);
SlaveRequestLocoFkt = Adr; //save Loco
}
//--------------------------------------------------------------------------------------------
//Lokinfo an XNet Melden
void XpressNetMasterClass::SetLocoInfo(uint8_t UserOps, uint8_t Speed, uint8_t F0, uint8_t F1) {
SetLocoInfo(UserOps, Fahrstufe, Speed, F0, F1);
}
void XpressNetMasterClass::SetLocoInfo(uint8_t UserOps, uint8_t Steps, uint8_t Speed, uint8_t F0, uint8_t F1) {
//0xE4 | 0000 BFFF | RVVV VVVV | 000F FFFF | FFFF FFFF | XOr
// B = 0 free; B = 1 controlled by another Device
// FFF -> 000 = 14; 001 = 27; 010 = 28; 100 = 128
//LokInfo Identification Speed FA FB
byte v = Speed;
if (Steps == Loco28 || Steps == Loco27) {
v = (Speed & 0x0F) << 1; //Speed Bit
v |= (Speed >> 4) & 0x01; //Addition Speed Bit
v |= 0x80 & Speed; //Dir
}
uint8_t LocoInfo[] = {UserOps, 0xE4, Steps, v, F0, F1, 0x00 };
if (SlotLokUse[UserOps & 0x1F] == 0x00) //has Slot a Address?
LocoInfo[2] |= 0x08; //set BUSY
getXOR(LocoInfo, 7);
XNetsend(LocoInfo, 7);
}
//--------------------------------------------------------------------------------------------
//LokFkt an XNet Melden
void XpressNetMasterClass::SetFktStatus(uint8_t UserOps, uint8_t F4, uint8_t F5) {
//F4 = F20-F13
//F5 = F28-F21
uint8_t LocoFkt[] = {UserOps, 0xE3, 0x52, F4, F5, 0x00 };
getXOR(LocoFkt, 6);
XNetsend(LocoFkt, 6);
}
//--------------------------------------------------------------------------------------------
//Lokinfo for MultiMaus
void XpressNetMasterClass::SetLocoInfoMM(uint8_t UserOps, uint8_t Steps, uint8_t Speed, uint8_t F0, uint8_t F1, uint8_t F2, uint8_t F3) {
//E7 0x04=128 Steps||0x0C=128+Busy (Dir,Speed) (F0,F4,F3,F2,F1) F5-F12 F13-F20 0x00 0x00 CRC
byte v = Speed;
if (Steps == Loco28 || Steps == Loco27) {
v = (Speed & 0x0F) << 1; //Speed Bit
v |= (Speed >> 4) & 0x01; //Addition Speed Bit
v |= 0x80 & Speed; //Dir
}
uint8_t LocoInfo[] = {UserOps, 0xE7, /*0x04*/ Steps, v, 0x20, F1, F2, F3, 0x00, 0x00 };
/*ERROR: Steps change form > 99 to 28steps only!!! why?? (This info comes from DCC:"notifyLokAll(..)"*/
LocoInfo[4] |= F0;
getXOR(LocoInfo, 10);
XNetsend(LocoInfo, 10);
}
//--------------------------------------------------------------------------------------------
void XpressNetMasterClass::setSpeed(uint16_t Adr, uint8_t Steps, uint8_t Speed) {
//Locomotive speed and direction operation
// 0xE4 | Ident | AH | AL | RV | XOr
// Ident: 0x10 = F14; 0x11 = F27; 0x12 = F28; 0x13 = F128
// RV = RVVV VVVV Dirction and Speed
byte v = Speed;
if (Steps == Loco28 || Steps == Loco27) {
v = (Speed & 0x0F) << 1; //Speed Bit
v |= (Speed >> 4) & 0x01; //Addition Speed Bit
v |= 0x80 & Speed; //Dir
}
uint8_t LocoInfo[] = {0x00, 0xE4, 0x13, 0x00, 0x00, v, 0x00 }; //default to 128 Steps!
switch (Steps) {
case 14: LocoInfo[2] = 0x10; break;
case 27: LocoInfo[2] = 0x11; break;
case 28: LocoInfo[2] = 0x12; break;
}
if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707)
LocoInfo[3] = (Adr >> 8) | 0xC0;
else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063)
LocoInfo[4] = Adr & 0xFF;
getXOR(LocoInfo, 7);
XNetsend(LocoInfo, 7);
}
//--------------------------------------------------------------------------------------------
//Gruppe 1: 0 0 0 F0 F4 F3 F2 F1
void XpressNetMasterClass::setFunc0to4(uint16_t Adr, uint8_t G1) {
uint8_t LocoInfo[] = {0x00, 0xE4, 0x20, 0x00, 0x00, G1 & 0x1F, 0x00 };
if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707)
LocoInfo[3] = (Adr >> 8) | 0xC0;
else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063)
LocoInfo[4] = Adr & 0xFF;
getXOR(LocoInfo, 7);
XNetsend(LocoInfo, 7);
}
//--------------------------------------------------------------------------------------------
//Gruppe 2: 0 0 0 0 F8 F7 F6 F5
void XpressNetMasterClass::setFunc5to8(uint16_t Adr, uint8_t G2) {
uint8_t LocoInfo[] = {0x00, 0xE4, 0x21, 0x00, 0x00, G2 & 0x0F, 0x00 };
if (Adr > 99) //Xpressnet long addresses (100 to 9999: AH/AL = 0xC064 to 0xE707)
LocoInfo[3] = (Adr >> 8) | 0xC0;
else LocoInfo[3] = Adr >> 8; //short addresses (0 to 99: AH = 0x0000 and AL = 0x0000 to 0x0063)
LocoInfo[4] = Adr & 0xFF;
getXOR(LocoInfo, 7);
XNetsend(LocoInfo, 7);
}
//--------------------------------------------------------------------------------------------
//Gruppe 3: 0 0 0 0 F12 F11 F10 F9