7 #include <libopencm3/cm3/nvic.h>
8 #include <libopencm3/stm32/gpio.h>
9 #include <libopencm3/stm32/usart.h>
10 #include <libopencm3/stm32/timer.h>
21 #define RX_BUFSIZE 256
22 #define TX_BUFSIZE 256
24 static byte rx_buf[RX_BUFSIZE];
27 static byte state; // STATE_xxx
29 static byte *rx_frame;
30 static byte *rx_frame_end;
32 static byte tx_buf[TX_BUFSIZE];
36 #define MB_OUR_ADDRESS 42
38 static void UNUSED xx_write_char(uint c) // FIXME
40 usart_set_mode(USART2, USART_MODE_TX);
41 gpio_set(GPIOA, GPIO1);
42 usart_send_blocking(USART2, c);
43 while (!usart_get_flag(USART2, USART_SR_TC))
45 gpio_clear(GPIOA, GPIO1);
46 usart_set_mode(USART2, USART_MODE_RX);
49 static void rx_init(void)
54 usart_set_mode(USART2, USART_MODE_RX);
55 usart_enable_rx_interrupt(USART2);
58 static void rx_done(void)
60 state = STATE_RX_DONE;
61 usart_disable_rx_interrupt(USART2);
64 static void tx_init(void)
68 gpio_set(GPIOA, GPIO1);
69 usart_set_mode(USART2, USART_MODE_TX);
70 usart_enable_tx_interrupt(USART2);
73 static void tx_done(void)
75 state = STATE_TX_DONE;
76 // usart_disable_tx_interrupt(USART2); // Already done by irq handler
77 gpio_clear(GPIOA, GPIO1);
80 void modbus_init(void)
82 timer_set_prescaler(TIM2, 71); // 1 tick = 1 μs
83 timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_DOWN);
84 timer_update_on_overflow(TIM2);
85 timer_disable_preload(TIM2);
86 timer_one_shot_mode(TIM2);
87 timer_enable_irq(TIM2, TIM_DIER_UIE);
88 nvic_enable_irq(NVIC_TIM2_IRQ);
90 gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_USART2_RX);
91 gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART2_TX);
93 gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO1);
94 gpio_clear(GPIOA, GPIO1);
96 usart_set_baudrate(USART2, 19200);
97 usart_set_databits(USART2, 8);
98 usart_set_stopbits(USART2, USART_STOPBITS_1);
99 usart_set_parity(USART2, USART_PARITY_NONE);
100 // usart_set_parity(USART2, USART_PARITY_EVEN); // FIXME
101 usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);
105 nvic_enable_irq(NVIC_USART2_IRQ);
106 usart_enable(USART2);
109 void usart2_isr(void)
111 u32 status = USART_SR(USART2);
115 if (status & USART_SR_RXNE) {
116 uint ch = usart_recv(USART2);
117 if (state == STATE_RX) {
119 if (status & (USART_SR_FE | USART_SR_ORE | USART_SR_NE)) {
123 if (rx_size < RX_BUFSIZE) {
124 rx_buf[rx_size++] = ch;
129 timer_set_period(TIM2, 7500); // 0.75 ms timeout for end of frame (FIXME: right value?)
130 timer_generate_event(TIM2, TIM_EGR_UG);
131 timer_enable_counter(TIM2);
135 if (state == STATE_TX) {
136 if (status & USART_SR_TXE) {
137 if (tx_pos < tx_size) {
138 usart_send(USART2, tx_buf[tx_pos++]);
140 // The transmitter is double-buffered, so at this moment, it is transmitting
141 // the last byte of the frame. Wait until transfer is completed.
142 usart_disable_tx_interrupt(USART2);
143 USART_CR1(USART2) |= USART_CR1_TCIE;
144 state = STATE_TX_LAST;
149 if (state == STATE_TX_LAST) {
150 if (status & USART_SR_TC) {
151 // Transfer of the last byte is complete. Release the bus.
152 USART_CR1(USART2) &= ~USART_CR1_TCIE;
161 if (TIM_SR(TIM2) & TIM_SR_UIF) {
162 TIM_SR(TIM2) &= ~TIM_SR_UIF;
163 if (state == STATE_RX)
170 static const byte crc_hi[] = {
171 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0,
172 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
173 0x00, 0xc1, 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0,
174 0x80, 0x41, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
175 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1,
176 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0, 0x80, 0x41,
177 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1,
178 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
179 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0,
180 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40,
181 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1,
182 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
183 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0,
184 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40,
185 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0,
186 0x80, 0x41, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40,
187 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0,
188 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
189 0x00, 0xc1, 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0,
190 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
191 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0,
192 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40,
193 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0, 0x80, 0x41, 0x00, 0xc1,
194 0x81, 0x40, 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41,
195 0x00, 0xc1, 0x81, 0x40, 0x01, 0xc0, 0x80, 0x41, 0x01, 0xc0,
196 0x80, 0x41, 0x00, 0xc1, 0x81, 0x40
199 static const byte crc_lo[] = {
200 0x00, 0xc0, 0xc1, 0x01, 0xc3, 0x03, 0x02, 0xc2, 0xc6, 0x06,
201 0x07, 0xc7, 0x05, 0xc5, 0xc4, 0x04, 0xcc, 0x0c, 0x0d, 0xcd,
202 0x0f, 0xcf, 0xce, 0x0e, 0x0a, 0xca, 0xcb, 0x0b, 0xc9, 0x09,
203 0x08, 0xc8, 0xd8, 0x18, 0x19, 0xd9, 0x1b, 0xdb, 0xda, 0x1a,
204 0x1e, 0xde, 0xdf, 0x1f, 0xdd, 0x1d, 0x1c, 0xdc, 0x14, 0xd4,
205 0xd5, 0x15, 0xd7, 0x17, 0x16, 0xd6, 0xd2, 0x12, 0x13, 0xd3,
206 0x11, 0xd1, 0xd0, 0x10, 0xf0, 0x30, 0x31, 0xf1, 0x33, 0xf3,
207 0xf2, 0x32, 0x36, 0xf6, 0xf7, 0x37, 0xf5, 0x35, 0x34, 0xf4,
208 0x3c, 0xfc, 0xfd, 0x3d, 0xff, 0x3f, 0x3e, 0xfe, 0xfa, 0x3a,
209 0x3b, 0xfb, 0x39, 0xf9, 0xf8, 0x38, 0x28, 0xe8, 0xe9, 0x29,
210 0xeb, 0x2b, 0x2a, 0xea, 0xee, 0x2e, 0x2f, 0xef, 0x2d, 0xed,
211 0xec, 0x2c, 0xe4, 0x24, 0x25, 0xe5, 0x27, 0xe7, 0xe6, 0x26,
212 0x22, 0xe2, 0xe3, 0x23, 0xe1, 0x21, 0x20, 0xe0, 0xa0, 0x60,
213 0x61, 0xa1, 0x63, 0xa3, 0xa2, 0x62, 0x66, 0xa6, 0xa7, 0x67,
214 0xa5, 0x65, 0x64, 0xa4, 0x6c, 0xac, 0xad, 0x6d, 0xaf, 0x6f,
215 0x6e, 0xae, 0xaa, 0x6a, 0x6b, 0xab, 0x69, 0xa9, 0xa8, 0x68,
216 0x78, 0xb8, 0xb9, 0x79, 0xbb, 0x7b, 0x7a, 0xba, 0xbe, 0x7e,
217 0x7f, 0xbf, 0x7d, 0xbd, 0xbc, 0x7c, 0xb4, 0x74, 0x75, 0xb5,
218 0x77, 0xb7, 0xb6, 0x76, 0x72, 0xb2, 0xb3, 0x73, 0xb1, 0x71,
219 0x70, 0xb0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
220 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9c, 0x5c,
221 0x5d, 0x9d, 0x5f, 0x9f, 0x9e, 0x5e, 0x5a, 0x9a, 0x9b, 0x5b,
222 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4b, 0x8b,
223 0x8a, 0x4a, 0x4e, 0x8e, 0x8f, 0x4f, 0x8d, 0x4d, 0x4c, 0x8c,
224 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
225 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
228 static u16 crc16(byte *buf, u16 len)
230 byte hi = 0xff, lo = 0xff;
233 byte i = hi ^ *buf++;
238 return (hi << 8 | lo);
241 static bool check_frame(void)
244 // FIXME: Error counters?
249 // FIXME: Error counters?
253 u16 crc = crc16(rx_buf, rx_size - 2);
254 u16 rx_crc = (rx_buf[rx_size-2] << 8) | rx_buf[rx_size-1];
256 // FIXME: Error counters?
260 rx_frame = rx_buf + 1;
261 rx_frame_end = rx_frame + rx_size - 2;
266 FUNC_READ_COILS = 0x01,
267 FUNC_READ_DISCRETE_INPUTS = 0x02,
268 FUNC_READ_HOLDING_REGISTERS = 0x03,
269 FUNC_READ_INPUT_REGISTERS = 0x04,
270 FUNC_WRITE_SINGLE_COIL = 0x05,
271 FUNC_WRITE_SINGLE_REGISTER = 0x06,
272 FUNC_READ_EXCEPTION_STATUS = 0x07,
273 FUNC_DIAGNOSTICS = 0x08,
274 FUNC_GET_COMM_EVENT_COUNTER = 0x0b,
275 FUNC_GET_COMM_EVENT_LOG = 0x0c,
276 FUNC_WRITE_MULTIPLE_COILS = 0x0f,
277 FUNC_WRITE_MULTIPLE_REGISTERS = 0x10,
278 FUNC_REPORT_SLAVE_ID = 0x11,
279 FUNC_READ_FILE_RECORD = 0x14,
280 FUNC_WRITE_FILE_RECORD = 0x15,
281 FUNC_MASK_WRITE_REGISTER = 0x16,
282 FUNC_READ_WRITE_MULTIPLE_REGISTERS = 0x17,
283 FUNC_READ_FIFO_QUEUE = 0x18,
284 FUNC_ENCAPSULATED_INTERFACE_TRANSPORT = 0x2b,
288 ERR_ILLEGAL_FUNCTION = 0x01,
289 ERR_ILLEGAL_DATA_ADDRESS = 0x02,
290 ERR_ILLEGAL_DATA_VALUE = 0x03,
293 static uint read_remains(void)
295 return rx_frame_end - rx_frame;
298 static byte read_byte(void)
303 static u16 read_u16(void)
305 byte hi = *rx_frame++;
306 byte lo = *rx_frame++;
307 return (hi << 8) | lo;
310 static void write_byte(byte v)
312 tx_buf[tx_size++] = v;
315 static void write_u16(u16 v)
321 static bool body_fits(uint body_len)
323 // body_len excludes slave address, function code, and CRC
324 return (2 + body_len + 2 <= TX_BUFSIZE);
327 static void report_error(byte code)
329 // Discard the partially constructed body of the reply and rewrite the header
335 static void func_read_bits(bool coils)
337 if (read_remains() < 4)
338 return report_error(ERR_ILLEGAL_DATA_VALUE);
340 u16 start = read_u16();
341 u16 count = read_u16();
343 uint bytes = (count+7) / 8;
344 if (!body_fits(1 + bytes))
345 return report_error(ERR_ILLEGAL_DATA_VALUE);
347 for (u16 i = 0; i < count; i++)
348 if (!(coils ? modbus_check_coil : modbus_check_discrete_input)(start + i))
349 return report_error(ERR_ILLEGAL_DATA_ADDRESS);
352 for (u16 i = 0; i < bytes; i++) {
354 for (byte j = 0; j < 8 && 8*i + j < count; j++) {
355 uint addr = start + 8*i + j;
356 if ((coils ? modbus_get_coil : modbus_get_discrete_input)(addr))
363 static void func_read_registers(byte holding)
365 if (read_remains() < 4)
366 return report_error(ERR_ILLEGAL_DATA_VALUE);
368 u16 start = read_u16();
369 u16 count = read_u16();
371 uint bytes = 2*count;
372 if (!body_fits(1 + bytes))
373 return report_error(ERR_ILLEGAL_DATA_VALUE);
375 for (u16 i = 0; i < count; i++)
376 if (!(holding ? modbus_check_holding_register : modbus_check_input_register)(start + i))
377 return report_error(ERR_ILLEGAL_DATA_ADDRESS);
379 // FIXME: Reporting of slave failures?
381 for (u16 i = 0; i < count; i++)
382 write_u16((holding ? modbus_get_holding_register : modbus_get_input_register)(start + i));
385 static void func_write_single_coil(void)
387 if (read_remains() < 4)
388 return report_error(ERR_ILLEGAL_DATA_VALUE);
390 u16 addr = read_u16();
391 u16 value = read_u16();
393 if (!modbus_check_coil(addr))
394 return report_error(ERR_ILLEGAL_DATA_ADDRESS);
395 if (value != 0x0000 && value != 0xff00)
396 return report_error(ERR_ILLEGAL_DATA_VALUE);
398 modbus_set_coil(addr, value);
401 static void func_write_single_register(void)
403 if (read_remains() < 4)
404 return report_error(ERR_ILLEGAL_DATA_VALUE);
406 u16 addr = read_u16();
407 u16 value = read_u16();
409 if (!modbus_check_holding_register(addr))
410 return report_error(ERR_ILLEGAL_DATA_ADDRESS);
412 modbus_set_holding_register(addr, value);
415 static void func_write_multiple_coils(void)
417 if (read_remains() < 5)
418 return report_error(ERR_ILLEGAL_DATA_VALUE);
420 u16 start = read_u16();
421 u16 count = read_u16();
422 byte bytes = read_byte();
424 if (read_remains() < bytes || bytes != (count+7) / 8)
425 return report_error(ERR_ILLEGAL_DATA_VALUE);
427 for (u16 i = 0; i < count; i++)
428 if (!modbus_check_coil(start + i))
429 return report_error(ERR_ILLEGAL_DATA_ADDRESS);
431 for (u16 i = 0; i < count; i++)
432 modbus_set_coil(start + i, rx_frame[i/8] & (1U << (i%8)));
435 static void func_write_multiple_registers(void)
437 if (read_remains() < 5)
438 return report_error(ERR_ILLEGAL_DATA_VALUE);
440 u16 start = read_u16();
441 u16 count = read_u16();
442 byte bytes = read_byte();
444 if (read_remains() < bytes || bytes != 2*count)
445 return report_error(ERR_ILLEGAL_DATA_VALUE);
447 for (u16 i = 0; i < count; i++)
448 if (!modbus_check_holding_register(start + i))
449 return report_error(ERR_ILLEGAL_DATA_ADDRESS);
451 for (u16 i = 0; i < count; i++)
452 modbus_set_holding_register(start + i, read_u16());
455 static void func_mask_write_register(void)
457 if (read_remains() < 6)
458 return report_error(ERR_ILLEGAL_DATA_VALUE);
460 u16 addr = read_u16();
461 u16 and_mask = read_u16();
462 u16 or_mask = read_u16();
464 if (!modbus_check_holding_register(addr))
465 return report_error(ERR_ILLEGAL_DATA_ADDRESS);
467 u16 reg = modbus_get_holding_register(addr);
468 reg = (reg & and_mask) | (or_mask & ~and_mask);
469 modbus_set_holding_register(addr, reg);
472 static void func_read_write_multiple_registers(void)
474 if (read_remains() < 9)
475 return report_error(ERR_ILLEGAL_DATA_VALUE);
477 u16 read_start = read_u16();
478 u16 read_count = read_u16();
479 u16 write_start = read_u16();
480 u16 write_count = read_u16();
481 byte write_bytes = read_byte();
483 if (read_remains() < write_bytes || write_bytes != 2*write_count)
484 return report_error(ERR_ILLEGAL_DATA_VALUE);
486 for (u16 i = 0; i < read_count; i++)
487 if (!modbus_check_holding_register(read_start + i))
488 return report_error(ERR_ILLEGAL_DATA_ADDRESS);
490 for (u16 i = 0; i < write_count; i++)
491 if (!modbus_check_holding_register(write_start + i))
492 return report_error(ERR_ILLEGAL_DATA_ADDRESS);
494 byte read_bytes = 2*write_count;
495 if (!body_fits(1 + read_bytes))
496 return report_error(ERR_ILLEGAL_DATA_VALUE);
498 for (u16 i = 0; i < write_count; i++)
499 modbus_set_holding_register(write_start + i, read_u16());
501 write_byte(read_bytes);
502 for (u16 i = 0; i < read_count; i++)
503 modbus_get_holding_register(read_start + i);
506 static void func_encapsulated_interface_transport(void)
508 if (read_remains() < 3 ||
510 return report_error(ERR_ILLEGAL_DATA_VALUE);
512 byte action = read_byte();
513 byte id = read_byte();
515 byte range_min, range_max;
518 // Streaming access to basic identification
519 range_min = MODBUS_ID_VENDOR_NAME;
520 range_max = MODBUS_ID_MAJOR_MINOR_REVISION;
523 // Streaming access to regular identification
524 range_min = MODBUS_ID_VENDOR_URL;
525 range_max = MODBUS_ID_USER_APP_NAME;
529 if (id >= MODBUS_ID_MAX || !modbus_id_strings[id])
530 return report_error(ERR_ILLEGAL_DATA_ADDRESS);
531 range_min = range_max = id;
534 return report_error(ERR_ILLEGAL_DATA_VALUE);
538 if (id < range_min || id > range_max)
542 write_byte(0x0e); // Repeat a part of the request
546 if (modbus_id_strings[MODBUS_ID_VENDOR_URL] ||
547 modbus_id_strings[MODBUS_ID_PRODUCT_NAME] ||
548 modbus_id_strings[MODBUS_ID_USER_APP_NAME])
549 write_byte(0x82); // Regular identification, both stream and individual access supported
551 write_byte(0x81); // Basic identification only
553 u16 more_follows_at = tx_size;
554 write_byte(0); // More follows: so far not
555 write_byte(0); // Next object ID: so far none
556 write_byte(0); // Number of objects
558 for (id = range_min; id <= range_max; id++) {
559 if (modbus_id_strings[id]) {
560 byte len = strlen(modbus_id_strings[id]);
561 byte remains = TX_BUFSIZE - 4 - tx_size; // 2 for CRC, 2 for object header
563 // If it is the only object, cut it
564 if (!tx_buf[more_follows_at + 2])
567 // More follows, report the next ID
568 tx_buf[more_follows_at] = 0xff;
569 tx_buf[more_follows_at + 1] = id;
573 tx_buf[more_follows_at + 2] ++;
576 memcpy(tx_buf + tx_size, modbus_id_strings[id], len);
582 static void process_frame(void)
584 byte func = read_byte();
586 // Prepare reply frame
587 tx_buf[0] = MB_OUR_ADDRESS;
588 tx_buf[1] = rx_buf[1];
592 case FUNC_READ_COILS:
593 func_read_bits(true);
595 case FUNC_READ_DISCRETE_INPUTS:
596 func_read_bits(false);
598 case FUNC_READ_HOLDING_REGISTERS:
599 func_read_registers(true);
601 case FUNC_READ_INPUT_REGISTERS:
602 func_read_registers(false);
604 case FUNC_WRITE_SINGLE_COIL:
605 func_write_single_coil();
607 case FUNC_WRITE_SINGLE_REGISTER:
608 func_write_single_register();
610 case FUNC_WRITE_MULTIPLE_COILS:
611 func_write_multiple_coils();
613 case FUNC_WRITE_MULTIPLE_REGISTERS:
614 func_write_multiple_registers();
616 case FUNC_MASK_WRITE_REGISTER:
617 func_mask_write_register();
619 case FUNC_READ_WRITE_MULTIPLE_REGISTERS:
620 func_read_write_multiple_registers();
622 case FUNC_ENCAPSULATED_INTERFACE_TRANSPORT:
623 func_encapsulated_interface_transport();
626 report_error(ERR_ILLEGAL_FUNCTION);
629 // Finish reply frame
630 write_u16(crc16(tx_buf, tx_size));
633 void modbus_loop(void)
635 if (state != STATE_RX_DONE)
637 state = STATE_PROCESSING;
639 if (!check_frame()) {
644 if (rx_buf[0] == MB_OUR_ADDRESS) {
645 // Frame addressed to us: process and reply
648 } else if (rx_buf[0] == 0x00) {
649 // Broadcast frame: process, but do not reply
653 // Somebody else's frame: discard
660 bool modbus_check_discrete_input(u16 addr UNUSED)
665 bool modbus_get_discrete_input(u16 addr UNUSED)
670 bool modbus_check_coil(u16 addr UNUSED)
675 bool modbus_get_coil(u16 addr UNUSED)
680 void modbus_set_coil(u16 addr UNUSED, bool value UNUSED)
684 bool modbus_check_input_register(u16 addr UNUSED)
689 u16 modbus_get_input_register(u16 addr UNUSED)
694 bool modbus_check_holding_register(u16 addr UNUSED)
699 u16 modbus_get_holding_register(u16 addr UNUSED)
704 void modbus_set_holding_register(u16 addr UNUSED, u16 value UNUSED)
708 const char * const modbus_id_strings[MODBUS_ID_MAX] = {
709 [MODBUS_ID_VENDOR_NAME] = "United Computer Wizards",
710 [MODBUS_ID_PRODUCT_CODE] = "42",
711 [MODBUS_ID_MAJOR_MINOR_REVISION] = "1.0",
712 [MODBUS_ID_VENDOR_URL] = "http://www.ucw.cz/",
713 [MODBUS_ID_PRODUCT_NAME] = "Magic Gadget",
714 [MODBUS_ID_USER_APP_NAME] = NULL,