Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Packet.cxx
Go to the documentation of this file.
1
35//#define LOGLEVEL WARNING
36
37#include "dcc/Packet.hxx"
38
39#include "dcc/Defs.hxx"
40#include "utils/Crc.hxx"
41#include "utils/logging.h"
42#include "utils/macros.h"
43
44
45namespace dcc
46{
47
48// Imports the bit declarations from the enums in Defs. This import may only be
49// performed in a .cxx file.
50using namespace Defs;
51
52static_assert(sizeof(Packet) == sizeof(DCCPacket), "DCCPacket size missmatch");
53
55{
57 // Protects against double call of add checksum.
58 HASSERT(!packet_header.skip_ec);
59
60 // Performs CRC computation if needed.
61 if ((payload[0] == ADDRESS_LOGON || payload[0] == ADDRESS_EXT) &&
62 (dlc >= 6))
63 {
65 for (int i = 0; i < dlc; ++i)
66 {
67 m.update16(payload[i]);
68 }
69 payload[dlc++] = m.get();
70 }
71
72 uint8_t cs = 0;
73 for (int i = 0; i < dlc; ++i)
74 {
75 cs ^= payload[i];
76 }
77 payload[dlc++] = cs;
78 packet_header.skip_ec = 1;
79}
80
82{
84 dlc = 3;
85 payload[0] = payload[2] = 0xFF;
86 payload[1] = 0;
87 packet_header.skip_ec = 1;
88}
89
91{
93 dlc = 3;
94 payload[0] = payload[1] = payload[2] = 0;
95 packet_header.skip_ec = 1;
96}
97
99{
101 payload[dlc++] = address.value & 0x7F;
102 feedback_key = address.value;
103}
104
106{
108 payload[dlc++] = DCC_LONG_ADDRESS_FIRST | (address.value >> 8);
109 payload[dlc++] = address.value & 0xff;
110 feedback_key = address.value;
111}
112
113void Packet::add_dcc_accy_address(bool is_basic, unsigned address)
114{
116
117 payload[dlc++] = DCC_BASIC_ACCESSORY_B1 | ((address >> 2) & 0b111111);
118 uint8_t b2 = is_basic ? DCC_BASIC_ACCESSORY_B2 : DCC_EXT_ACCESSORY_B2;
119 b2 |= ((~(address >> 8)) & 0b111) << 4;
120 b2 |= (address & 0b11) << 1;
121 payload[dlc++] = b2;
122 feedback_key = 0x10000 | address;
123}
124
125void Packet::add_dcc_speed14(bool is_fwd, bool light, unsigned speed)
126{
128 uint8_t b1 = DCC_BASELINE_SPEED;
129 if (is_fwd)
130 b1 |= DCC_BASELINE_SPEED_FORWARD;
131 if (light)
132 b1 |= DCC_BASELINE_SPEED_LIGHT;
133 if (speed == EMERGENCY_STOP)
134 {
135 b1 |= 1;
136 }
137 else if (speed == 0)
138 {
139 }
140 else
141 {
142 HASSERT(speed <= 14);
143 speed += 1; // avoids 01 (e-stop)
144 b1 |= speed & 0xf;
145 }
146 payload[dlc++] = b1;
148}
149
150void Packet::add_dcc_speed28(bool is_fwd, unsigned speed)
151{
152 uint8_t b1 = DCC_BASELINE_SPEED;
153 if (is_fwd)
154 b1 |= DCC_BASELINE_SPEED_FORWARD;
155 if (speed == EMERGENCY_STOP)
156 {
157 b1 |= 1;
158 }
159 else if (speed == 0)
160 {
161 }
162 else
163 {
164 HASSERT(speed <= 28);
165 speed += 3; // avoids 00, 01 (stop) and 10 11 (e-stop)
166 if (speed & 1)
167 b1 |= DCC_BASELINE_SPEED_LIGHT;
168 b1 |= (speed >> 1) & 0xf;
169 }
170 payload[dlc++] = b1;
172}
173
174void Packet::add_dcc_speed128(bool is_fwd, unsigned speed)
175{
176 payload[dlc++] = DCC_EXT_SPEED;
177 uint8_t b = 0;
178 if (is_fwd)
179 b |= DCC_EXT_SPEED_FORWARD;
180 if (speed == EMERGENCY_STOP)
181 {
182 b |= 1;
183 }
184 else if (speed > 0)
185 {
186 HASSERT(speed <= 126);
187 speed += 1; // avoids 00, 01 ([e]stop)
188 b |= speed & 0x7f;
189 }
190 payload[dlc++] = b;
192}
193
194void Packet::add_dcc_function0_4(unsigned values)
195{
196 uint8_t b1 = DCC_FUNCTION1;
197 if (values & 1)
198 b1 |= DCC_FUNCTION1_F0;
199 b1 |= (values >> 1) & 0xf;
200 payload[dlc++] = b1;
202}
203
204void Packet::add_dcc_function5_8(unsigned values)
205{
206 uint8_t b1 = DCC_FUNCTION2_F5;
207 b1 |= values & 0xf;
208 payload[dlc++] = b1;
210}
211
212void Packet::add_dcc_function9_12(unsigned values)
213{
214 uint8_t b1 = DCC_FUNCTION2_F9;
215 b1 |= values & 0xf;
216 payload[dlc++] = b1;
218}
219
220void Packet::add_dcc_function_hi(uint8_t base, uint8_t values)
221{
222 base -= 13;
223 HASSERT((base & 0b111) == 0);
224 HASSERT(base <= (61 - 13));
225 base >>= 3;
226 base -= 2;
227 payload[dlc++] = DCC_FEATURE_EXP_FNHI | (base & 0b111);
228 payload[dlc++] = values;
230}
231
232void Packet::add_dcc_binary_state(uint16_t fn, bool value)
233{
234 if (fn <= 127)
235 {
236 payload[dlc++] = DCC_BINARY_SHORT;
237 payload[dlc++] = fn | (value ? 0x80 : 0);
238 }
239 else
240 {
241 payload[dlc++] = DCC_BINARY_LONG;
242 payload[dlc++] = (fn & 0x7F) | (value ? 0x80 : 0);
243 payload[dlc++] = (fn >> 8) & 0xFF;
244 }
246}
247
248void Packet::add_dcc_analog_function(uint8_t fn, uint8_t value)
249{
250 payload[dlc++] = DCC_ANALOG_FN;
251 payload[dlc++] = fn;
252 payload[dlc++] = value;
254}
255
257 uint8_t cmd_hi, unsigned cv_number, uint8_t value)
258{
259 payload[dlc++] = (cmd_hi & (~3)) | ((cv_number >> 8) & 3);
260 payload[dlc++] = cv_number & 0xff;
261 payload[dlc++] = value;
263}
264
265void Packet::add_dcc_pom_read1(unsigned cv_number)
266{
267 add_dcc_prog_command(DCC_PROG_READ1, cv_number, 0);
268}
269
270void Packet::add_dcc_pom_write1(unsigned cv_number, uint8_t value) {
271 add_dcc_prog_command(DCC_PROG_WRITE1, cv_number, value);
272}
273
274void Packet::set_dcc_svc_verify_byte(unsigned cv_number, uint8_t value)
275{
277 add_dcc_prog_command(DCC_SVC_VERIFY, cv_number, value);
278}
279
280void Packet::set_dcc_svc_write_byte(unsigned cv_number, uint8_t value)
281{
283 add_dcc_prog_command(DCC_SVC_WRITE, cv_number, value);
284}
285
287 unsigned cv_number, unsigned bit, bool expected)
288{
290 uint8_t vvv = DCC_SVC_BITVAL_VERIFY |
291 (expected ? DCC_SVC_BITVAL_VALUE : 0) | (bit & 7);
292 add_dcc_prog_command(DCC_SVC_BIT_MANIPULATE, cv_number, vvv);
293}
294
296 unsigned cv_number, unsigned bit, bool desired)
297{
299 uint8_t vvv =
300 DCC_SVC_BITVAL_WRITE | (desired ? DCC_SVC_BITVAL_VALUE : 0) | (bit & 7);
301 add_dcc_prog_command(DCC_SVC_BIT_MANIPULATE, cv_number, vvv);
302}
303
304void Packet::set_dcc_svc_paged_write_reg(uint8_t reg, uint8_t value)
305{
306 HASSERT(reg < 8);
308 payload[dlc++] = DCC_SVC_PAGED_WRITE | reg;
309 payload[dlc++] = value;
311}
312
313void Packet::set_dcc_svc_paged_verify_reg(uint8_t reg, uint8_t value)
314{
315 HASSERT(reg < 8);
317 payload[dlc++] = DCC_SVC_PAGED_VERIFY | reg;
318 payload[dlc++] = value;
320}
321
322void Packet::set_dcc_basic_accy_params(bool is_normal, bool is_activate)
323{
324 if (is_normal)
325 {
326 payload[1] |= DCC_BASIC_ACCESSORY_B2_CLOSED;
327 }
328 else
329 {
330 payload[1] |= DCC_BASIC_ACCESSORY_B2_THROWN;
331 }
332 if (is_activate)
333 {
334 payload[1] |= DCC_BASIC_ACCESSORY_B2_ACTIVATE;
335 }
336 else
337 {
338 payload[1] |= DCC_BASIC_ACCESSORY_B2_DEACTIVATE;
339 }
340}
341
342void Packet::add_dcc_basic_accessory(unsigned address, bool is_activate)
343{
344 add_dcc_accy_address(true, address >> 1);
345 set_dcc_basic_accy_params(address & 1, is_activate);
347}
348
349void Packet::add_dcc_ext_accessory(unsigned address, uint8_t aspect)
350{
351 add_dcc_accy_address(false, address);
352 payload[dlc++] = aspect;
354}
355
356
358 Defs::LogonEnableParam param, uint16_t cid, uint8_t session_id)
359{
362 payload[dlc++] = DCC_LOGON_ENABLE | ((uint8_t)param & 0x3);
363 payload[dlc++] = cid >> 8;
364 payload[dlc++] = cid & 0xff;
365 payload[dlc++] = session_id;
367}
368
369void Packet::set_dcc_select_shortinfo(uint64_t decoder_id)
370{
373
374 payload[dlc++] = DCC_SELECT | ((decoder_id >> 40) & 0xf);
375 payload[dlc++] = (decoder_id >> 32) & 0xff;
376 payload[dlc++] = (decoder_id >> 24) & 0xff;
377 payload[dlc++] = (decoder_id >> 16) & 0xff;
378 payload[dlc++] = (decoder_id >> 8) & 0xff;
379 payload[dlc++] = (decoder_id) & 0xff;
380 payload[dlc++] = CMD_READ_SHORT_INFO;
382}
383
384void Packet::set_dcc_logon_assign(uint64_t decoder_id, uint16_t address)
385{
388
389 payload[dlc++] = DCC_LOGON_ASSIGN | ((decoder_id >> 40) & 0xf);
390 payload[dlc++] = (decoder_id >> 32) & 0xff;
391 payload[dlc++] = (decoder_id >> 24) & 0xff;
392 payload[dlc++] = (decoder_id >> 16) & 0xff;
393 payload[dlc++] = (decoder_id >> 8) & 0xff;
394 payload[dlc++] = (decoder_id) & 0xff;
395 payload[dlc++] = ((address >> 8) & 0xff) ^ 0b11000000;
396 payload[dlc++] = (address) & 0xff;
398}
399
401{
402 dlc = 3;
403 payload[0] = 0;
404 payload[1] = 0;
405 payload[2] = 0;
406 header_raw_data = MARKLIN_DEFAULT_CMD;
407}
408
410static const uint8_t marklin_address[3] = {0b00, 0b11, 0b10};
412static const uint8_t marklin_fn_bits[5] = {0, 0b01010000, 0b00000100,
413 0b00010100, 0b01010100};
414
418{
419 uint8_t address = a.value;
420 payload[0] |= marklin_address[address % 3];
421 address /= 3;
422 payload[1] |= marklin_address[address % 3] << 6;
423 address /= 3;
424 payload[1] |= marklin_address[address % 3] << 4;
425 address /= 3;
426 payload[1] |= marklin_address[address % 3] << 2;
427 if (light)
428 {
429 payload[1] |= 0b11;
430 }
431}
432
433unsigned Packet::set_mm_speed_bits(unsigned speed)
434{
435 payload[2] = 0;
436 // avoids speed step 1.
437 if (speed > 0)
438 ++speed;
439 // clips speed
440 if (speed > 15)
441 speed = 15;
442 if (speed & 1)
443 payload[2] |= 0x80;
444 if (speed & 2)
445 payload[2] |= 0x20;
446 if (speed & 4)
447 payload[2] |= 0x08;
448 if (speed & 8)
449 payload[2] |= 0x02;
450 return speed;
451}
452
456void Packet::add_mm_speed(unsigned speed)
457{
458 if (speed == CHANGE_DIR || speed == EMERGENCY_STOP)
459 {
460 payload[2] = MARKLIN_CHANGE_DIR_B2;
461 // Up the repeat count so that it will be surely heard.
462 header_raw_data |= 0b01100000;
463 }
464 else
465 {
466 set_mm_speed_bits(speed);
467 // The old marklin protocol needs all bits doubled.
468 payload[2] |= (payload[2] >> 1);
469 }
470}
471
474void Packet::add_mm_new_speed(bool is_fwd, unsigned speed)
475{
476 if (speed == CHANGE_DIR || speed == EMERGENCY_STOP)
477 {
478 payload[2] = MARKLIN_CHANGE_DIR_B2;
479 // Up the repeat count so that it will be surely heard.
480 header_raw_data |= 0b01100000;
481 }
482 else
483 {
484 speed = set_mm_speed_bits(speed);
485 if (is_fwd)
486 {
487 payload[2] |= 0x10;
488 }
489 else
490 {
491 payload[2] |= 0x44;
492 }
493 if (speed <= 7)
494 {
495 payload[2] |= 1;
496 }
497 }
498}
499
500void Packet::add_mm_new_fn(unsigned fn_num, bool value, unsigned speed)
501{
502 if (speed == CHANGE_DIR || speed == EMERGENCY_STOP || fn_num < 1 ||
503 fn_num > 4)
504 {
505 return add_mm_new_speed(false, speed);
506 }
507 // Sets the base speed bits.
508 speed = set_mm_speed_bits(speed);
509 // Sets the function number bits.
510 payload[2] |= marklin_fn_bits[fn_num];
511 // And the function bit.
512 if (value)
513 {
514 payload[2] |= 1;
515 }
516 // Checks for exceptions, when we generated a packet valid for the old
517 // protocol. (aka all bits are doubled)
518 if (((payload[2] & 0xaa) >> 1) == (payload[2] & 0x55))
519 {
520 payload[2] &= 0xaa;
521 if (speed >= 8)
522 {
523 // High speeds. new bits are 0101.
524 payload[2] |= 0b00010001;
525 }
526 else
527 {
528 // Low speeds. New bits are 1010.
529 payload[2] |= 0b01000100;
530 }
531 }
532}
533
535 HASSERT(dlc == 3);
536 dlc = 6;
537 payload[3] = payload[0];
538 payload[4] = payload[1];
539 payload[5] = payload[2];
540}
541
542} // namespace dcc
static const uint8_t marklin_address[3]
Helper array to translate a marklin address to a bit sequence.
Definition Packet.cxx:410
static const uint8_t marklin_fn_bits[5]
Helper array to translate a marklin function set command to a packet.
Definition Packet.cxx:412
Helper class for computing CRC-8 according to Dallas/Maxim specification for 1-wire protocol.
Definition Crc.hxx:83
void update16(uint8_t message_byte)
Processes one byte of the incoming message.
Definition Crc.hxx:164
uint8_t get()
Definition Crc.hxx:97
LogonEnableParam
Parameters for the Logon Enable command.
Definition dcc/Defs.hxx:265
@ DCC_LOGON_ENABLE
Logon enable in the 254 address partition.
Definition dcc/Defs.hxx:154
@ ADDRESS_LOGON
Address partition used for logon features per S-9.2.1.1 and RCN-218.
Definition dcc/Defs.hxx:148
@ DCC_LONG_ADDRESS_FIRST
Prefix for DCC long addresses (first byte).
Definition dcc/Defs.hxx:82
@ DCC_SELECT
Select command in the 254 address partition.
Definition dcc/Defs.hxx:157
@ DCC_LOGON_ASSIGN
Logon Assign command the 254 address partition.
Definition dcc/Defs.hxx:164
@ ADDRESS_EXT
Address partition used for advanced extended packets per S-9.2.1.1.
Definition dcc/Defs.hxx:150
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
uint8_t skip_ec
typically for DCC packets: 1: do NOT append an EC byte to the end of the packet.
Definition packet.h:66
Stores a DCC packet in memory.
Definition packet.h:55
uintptr_t feedback_key
An opaque key used by the hardware driver to attribute feedback information to the source of the pack...
Definition packet.h:104
uint8_t payload[DCC_PACKET_MAX_PAYLOAD]
Packet payload bytes.
Definition packet.h:97
uint8_t dlc
Specifies the number of used payload bytes.
Definition packet.h:95
Strongly typed wrapper representing a long DCC address.
Definition Address.hxx:66
uint16_t value
Address value.
Definition Address.hxx:70
Strongly typed wrapper representing a short DCC address.
Definition Address.hxx:49
uint8_t value
Address value.
Definition Address.hxx:53
Strongly typed wrapper representing a marklin-motorola protocol address.
Definition Address.hxx:82
uint8_t value
Address value.
Definition Address.hxx:86
void start_mm_packet()
Sets the packet type to marklin-motorola.
Definition Packet.cxx:400
void set_dcc_reset_all_decoders()
Creates a DCC reset-all-decoders packet.
Definition Packet.cxx:90
void add_mm_new_speed(bool is_fwd, unsigned speed)
Sets the packet to a direction-aware 14-step MM speed-and-light packet.
Definition Packet.cxx:474
void add_dcc_speed28(bool is_fwd, unsigned speed)
Adds a speed-and-direction command (dcc baseline command) to the packet.
Definition Packet.cxx:150
void start_dcc_svc_packet()
Initializes the packet structure for a regular DCC packet.
Definition Packet.hxx:106
void add_dcc_pom_write1(unsigned cv_number, uint8_t value)
Adds a DCC POM write single CV command and the xor byte.
Definition Packet.cxx:270
static const unsigned EMERGENCY_STOP
Send this speed step to emergency-stop the locomotive.
Definition Packet.hxx:56
void add_dcc_basic_accessory(unsigned address, bool is_activate)
Adds a DCC basic accessory decoder command packet and the checksum byte.
Definition Packet.cxx:342
void add_mm_speed(unsigned speed)
Sets the packet to a 14-step MM speed-and-light packet.
Definition Packet.cxx:456
void set_dcc_svc_write_byte(unsigned cv_number, uint8_t value)
Sets the packet to a DCC service mode packet writing the contents of an entire CV.
Definition Packet.cxx:280
void set_dcc_basic_accy_params(bool is_normal, bool is_activate)
Call this function after setting a basic accy address to set the accessory packet options.
Definition Packet.cxx:322
void set_dcc_svc_paged_verify_reg(uint8_t reg, uint8_t value)
Sets the packet to a DCC service mode packet in Paged Mode, setting the page register.
Definition Packet.cxx:313
void set_dcc_logon_assign(uint64_t decoder_id, uint16_t address)
Sets the packet to a logon assign packet.
Definition Packet.cxx:384
void add_dcc_function_hi(uint8_t base, uint8_t values)
Adds a DCC function group command to the packet.
Definition Packet.cxx:220
void set_dcc_svc_write_bit(unsigned cv_number, unsigned bit, bool desired)
Sets the packet to a DCC service mode packet verifying the contents of a single bit in a CV.
Definition Packet.cxx:295
unsigned set_mm_speed_bits(unsigned speed)
Sets the speed bits of an MM packet.
Definition Packet.cxx:433
void add_dcc_pom_read1(unsigned cv_number)
Adds a DCC POM read single CV command and the xor byte.
Definition Packet.cxx:265
void add_dcc_analog_function(uint8_t fn, uint8_t value)
Adds a DCC analog function control command to the packet.
Definition Packet.cxx:248
void set_dcc_select_shortinfo(uint64_t decoder_id)
Sets the packet to a Select+GetShortInfo packet.
Definition Packet.cxx:369
void add_dcc_speed128(bool is_fwd, unsigned speed)
Adds a speed-and-direction command (dcc extended command) for 128 speed steps to the packet.
Definition Packet.cxx:174
void add_dcc_prog_command(uint8_t cmd_hi, unsigned cv_number, uint8_t value)
Helper function for adding programming mode packets.
Definition Packet.cxx:256
void set_dcc_svc_verify_byte(unsigned cv_number, uint8_t value)
Sets the packet to a DCC service mode packet verifying the contents of an entire CV.
Definition Packet.cxx:274
void set_dcc_svc_verify_bit(unsigned cv_number, unsigned bit, bool expected)
Sets the packet to a DCC service mode packet verifying the contents of a single bit in a CV.
Definition Packet.cxx:286
void add_dcc_function0_4(unsigned values)
Adds a DCC function group command to the packet.
Definition Packet.cxx:194
void add_dcc_function5_8(unsigned values)
Adds a DCC function group command to the packet.
Definition Packet.cxx:204
void add_dcc_speed14(bool is_fwd, bool light, unsigned speed)
Adds a speed-and-direction command (dcc baseline command) ot the packet.
Definition Packet.cxx:125
void add_mm_new_fn(unsigned fn_num, bool value, unsigned speed)
Creates a speed-and-fn packet for the new MM format.
Definition Packet.cxx:500
void start_dcc_packet()
Initializes the packet structure for a regular DCC packet.
Definition Packet.hxx:99
void set_dcc_logon_enable(Defs::LogonEnableParam param, uint16_t cid, uint8_t session_id)
Sets the packet to a logon enable packet.
Definition Packet.cxx:357
static const unsigned MAX_PAYLOAD
Maximum number of payload bytes.
Definition Packet.hxx:54
static const unsigned CHANGE_DIR
Send this speed step to switch direction of the locomotive.
Definition Packet.hxx:59
void add_dcc_address(DccShortAddress address)
Adds the header to the packet needed for addressing a DCC locomotive.
Definition Packet.cxx:98
void add_dcc_ext_accessory(unsigned address, uint8_t aspect)
Adds a DCC extended accessory decoder command packet and the checksum byte.
Definition Packet.cxx:349
void add_dcc_checksum()
Appends one byte to the packet payload that represents the XOR checksum for DCC.
Definition Packet.cxx:54
void add_mm_address(MMAddress address, bool light)
Sets the address and F0 bits of an MM packet to a specific loco address.
Definition Packet.cxx:417
void add_dcc_accy_address(bool is_basic, unsigned address)
Adds the header to the packet needed for addressing a DCC accessory (e.g.
Definition Packet.cxx:113
void add_dcc_binary_state(uint16_t fn, bool value)
Adds a DCC binary state control command to the packet.
Definition Packet.cxx:232
void mm_shift()
Shifts a MM packet to the second half of the packet buffer.
Definition Packet.cxx:534
void set_dcc_svc_paged_write_reg(uint8_t reg, uint8_t value)
Sets the packet to a DCC service mode packet in Paged Mode, setting any register.
Definition Packet.cxx:304
void add_dcc_function9_12(unsigned values)
Adds a DCC function group command to the packet.
Definition Packet.cxx:212
void set_dcc_idle()
Creates a DCC idle packet.
Definition Packet.cxx:81