├── Arduino Sketches ├── modbus_rtu_master.pde └── modbus_rtu_slave.pde ├── Makefile ├── ModbusSlave ├── ModbusSlave.cpp ├── ModbusSlave.cpp~ ├── ModbusSlave.h ├── ModbusSlave.h~ └── keywords.txt ├── README ├── mbm.c ├── mbslave_lib_example └── mbslave_lib_example.pde ├── mbslave_pde_example └── mbslave_pde_example.pde ├── modbus_rtu.c └── modbus_rtu.h /Arduino Sketches/modbus_rtu_master.pde: -------------------------------------------------------------------------------- 1 | /* 2 | Modbus over serial line - RTU Master Arduino Sketch 3 | 4 | These functions implement functions 3 and 16 (read holding registers 5 | and preset multiple registers) of the Modbus RTU Protocol, to be used 6 | over the Arduino serial connection. 7 | 8 | This implementation DOES NOT fully comply with the Modbus specifications. 9 | 10 | This Arduino adaptation is derived from the work 11 | By P.Costigan email: phil@pcscada.com.au http://pcscada.com.au 12 | 13 | 14 | This code has its origins with 15 | paul@pmcrae.freeserve.co.uk (http://www.pmcrae.freeserve.co.uk) 16 | who wrote a small program to read 100 registers from a modbus slave. 17 | 18 | I have used his code as a catalist to produce this more functional set 19 | of functions. Thanks paul. 20 | 21 | */ 22 | /* FIXME: not yet being used */ 23 | unsigned long interframe_delay = 2; /* Modbus t3.5 = 2 ms */ 24 | 25 | /* 26 | * preset_multiple_registers: Modbus function 16. Write the data from an 27 | * array into the holding registers of a slave. 28 | * INPUTS 29 | * slave: modbus slave id number 30 | * start_addr: address of the slave's first register (+1) 31 | * reg_count: number of consecutive registers to preset 32 | * data: array of words (ints) with the data to write into the slave 33 | * RETURNS: the number of bytes received as response on success, or 34 | * 0 if no bytes received (i.e. response timeout) 35 | * -1 to -4 (modbus exception code) 36 | * -5 for other errors (port error, etc.). 37 | */ 38 | 39 | int preset_multiple_registers(int slave, int start_addr, 40 | int reg_count, int *data); 41 | 42 | /* 43 | * read_holding_registers: Modbus function 3. Read the holding registers 44 | * in a slave and put the data into an array 45 | * INPUTS 46 | * slave: modbus slave id number 47 | * start_addr: address of the slave's first register (+1) 48 | * count: number of consecutive registers to read 49 | * dest: array of words (ints) on which the read data is to be stored 50 | * dest_size: size of the array, which should be at least 'count' 51 | * RETURNS: the number of bytes received as response on success, or 52 | * 0 if no valid response received (i.e. response timeout, bad crc) 53 | * -1 to -4 (modbus exception code) 54 | * -5 for other errors (port error, etc.). 55 | */ 56 | 57 | int read_holding_registers(int slave, int start_addr, int count, 58 | int *dest, int dest_size); 59 | 60 | void setup() 61 | { 62 | const int baudrate = 9600; 63 | if (baudrate <= 19200) 64 | interframe_delay = (unsigned long)(3.5 * 11 / baudrate); /* Modbus t3.5 */ 65 | Serial.begin(baudrate); /* format 8N1, DOES NOT comply with Modbus spec. */ 66 | } 67 | 68 | /* example data */ 69 | int retval; 70 | int data[10]; 71 | 72 | void loop() 73 | { 74 | /* example, this will write some data in the first 10 registers of slave 1 */ 75 | retval = preset_multiple_registers(1,1,10, data); 76 | data[0] = retval; 77 | data[1]++; 78 | data[8]=0xdead; 79 | data[9] = 0xbeaf; 80 | delay(500); 81 | } 82 | 83 | /**************************************************************************** 84 | * BEGIN MODBUS RTU MASTER FUNCTIONS 85 | ****************************************************************************/ 86 | 87 | #define TIMEOUT 1000 /* 1 second */ 88 | #define MAX_READ_REGS 125 89 | #define MAX_WRITE_REGS 125 90 | #define MAX_RESPONSE_LENGTH 256 91 | #define PRESET_QUERY_SIZE 256 92 | /* errors */ 93 | #define PORT_ERROR -5 94 | 95 | /* 96 | CRC 97 | 98 | INPUTS: 99 | buf -> Array containing message to be sent to controller. 100 | start -> Start of loop in crc counter, usually 0. 101 | cnt -> Amount of bytes in message being sent to controller/ 102 | OUTPUTS: 103 | temp -> Returns crc byte for message. 104 | COMMENTS: 105 | This routine calculates the crc high and low byte of a message. 106 | Note that this crc is only used for Modbus, not Modbus+ etc. 107 | ****************************************************************************/ 108 | 109 | unsigned int crc(unsigned char *buf, int start, int cnt) 110 | { 111 | int i, j; 112 | unsigned temp, temp2, flag; 113 | 114 | temp = 0xFFFF; 115 | 116 | for (i = start; i < cnt; i++) { 117 | temp = temp ^ buf[i]; 118 | 119 | for (j = 1; j <= 8; j++) { 120 | flag = temp & 0x0001; 121 | temp = temp >> 1; 122 | if (flag) 123 | temp = temp ^ 0xA001; 124 | } 125 | } 126 | 127 | /* Reverse byte order. */ 128 | 129 | temp2 = temp >> 8; 130 | temp = (temp << 8) | temp2; 131 | temp &= 0xFFFF; 132 | 133 | return (temp); 134 | } 135 | 136 | 137 | /*********************************************************************** 138 | * 139 | * The following functions construct the required query into 140 | * a modbus query packet. 141 | * 142 | ***********************************************************************/ 143 | 144 | #define REQUEST_QUERY_SIZE 6 /* the following packets require */ 145 | #define CHECKSUM_SIZE 2 /* 6 unsigned chars for the packet plus */ 146 | /* 2 for the checksum. */ 147 | 148 | void build_request_packet(int slave, int function, int start_addr, 149 | int count, unsigned char *packet) 150 | { 151 | packet[0] = slave; 152 | packet[1] = function; 153 | start_addr -= 1; 154 | packet[2] = start_addr >> 8; 155 | packet[3] = start_addr & 0x00ff; 156 | packet[4] = count >> 8; 157 | packet[5] = count & 0x00ff; 158 | } 159 | 160 | /************************************************************************* 161 | * 162 | * modbus_query( packet, length) 163 | * 164 | * Function to add a checksum to the end of a packet. 165 | * Please note that the packet array must be at least 2 fields longer than 166 | * string_length. 167 | **************************************************************************/ 168 | 169 | void modbus_query(unsigned char *packet, size_t string_length) 170 | { 171 | int temp_crc; 172 | 173 | temp_crc = crc(packet, 0, string_length); 174 | 175 | packet[string_length++] = temp_crc >> 8; 176 | packet[string_length++] = temp_crc & 0x00FF; 177 | packet[string_length] = 0; 178 | } 179 | 180 | 181 | 182 | /*********************************************************************** 183 | * 184 | * send_query(query_string, query_length ) 185 | * 186 | * Function to send a query out to a modbus slave. 187 | ************************************************************************/ 188 | 189 | int send_query(unsigned char *query, size_t string_length) 190 | { 191 | 192 | int i; 193 | 194 | modbus_query(query, string_length); 195 | string_length += 2; 196 | 197 | for (i = 0; i < string_length; i++) { 198 | Serial.print(query[i], BYTE); 199 | } 200 | /* without the following delay, the reading of the response might be wrong 201 | * apparently, */ 202 | delay(200); /* FIXME: value to use? */ 203 | 204 | return i; /* it does not mean that the write was succesful, though */ 205 | } 206 | 207 | 208 | /*********************************************************************** 209 | * 210 | * receive_response( array_for_data ) 211 | * 212 | * Function to monitor for the reply from the modbus slave. 213 | * This function blocks for timeout seconds if there is no reply. 214 | * 215 | * Returns: Total number of characters received. 216 | ***********************************************************************/ 217 | 218 | int receive_response(unsigned char *received_string) 219 | { 220 | 221 | int bytes_received = 0; 222 | int i = 0; 223 | /* wait for a response; this will block! */ 224 | 225 | while(Serial.available() == 0) { 226 | delay(1); 227 | if (i++ > TIMEOUT) 228 | return bytes_received; 229 | } 230 | 231 | /* FIXME: does Serial.available wait 1.5T or 3.5T before exiting the loop? */ 232 | while(Serial.available()) { 233 | received_string[bytes_received] = Serial.read(); 234 | bytes_received++; 235 | if (bytes_received >= MAX_RESPONSE_LENGTH) 236 | return PORT_ERROR; 237 | } 238 | 239 | return (bytes_received); 240 | } 241 | 242 | 243 | /********************************************************************* 244 | * 245 | * modbus_response( response_data_array, query_array ) 246 | * 247 | * Function to the correct response is returned and that the checksum 248 | * is correct. 249 | * 250 | * Returns: string_length if OK 251 | * 0 if failed 252 | * Less than 0 for exception errors 253 | * 254 | * Note: All functions used for sending or receiving data via 255 | * modbus return these return values. 256 | * 257 | **********************************************************************/ 258 | 259 | int modbus_response(unsigned char *data, unsigned char *query) 260 | { 261 | int response_length; 262 | 263 | unsigned int crc_calc = 0; 264 | unsigned int crc_received = 0; 265 | unsigned char recv_crc_hi; 266 | unsigned char recv_crc_lo; 267 | 268 | do { // repeat if unexpected slave replied 269 | response_length = receive_response(data); 270 | } while ((response_length > 0) && (data[0] != query[0])); 271 | 272 | if (response_length) { 273 | 274 | 275 | crc_calc = crc(data, 0, response_length - 2); 276 | 277 | recv_crc_hi = (unsigned) data[response_length - 2]; 278 | recv_crc_lo = (unsigned) data[response_length - 1]; 279 | 280 | crc_received = data[response_length - 2]; 281 | crc_received = (unsigned) crc_received << 8; 282 | crc_received = 283 | crc_received | (unsigned) data[response_length - 1]; 284 | 285 | 286 | /*********** check CRC of response ************/ 287 | 288 | if (crc_calc != crc_received) { 289 | response_length = 0; 290 | } 291 | 292 | 293 | 294 | /********** check for exception response *****/ 295 | 296 | if (response_length && data[1] != query[1]) { 297 | response_length = 0 - data[2]; 298 | } 299 | } 300 | return (response_length); 301 | } 302 | 303 | 304 | /************************************************************************ 305 | * 306 | * read_reg_response 307 | * 308 | * reads the response data from a slave and puts the data into an 309 | * array. 310 | * 311 | ************************************************************************/ 312 | 313 | int read_reg_response(int *dest, int dest_size, unsigned char *query) 314 | { 315 | 316 | unsigned char data[MAX_RESPONSE_LENGTH]; 317 | int raw_response_length; 318 | int temp, i; 319 | 320 | raw_response_length = modbus_response(data, query); 321 | if (raw_response_length > 0) 322 | raw_response_length -= 2; 323 | 324 | if (raw_response_length > 0) { 325 | /* FIXME: data[2] * 2 ???!!! data[2] isn't already the byte count (number of registers * 2)?! */ 326 | for (i = 0; 327 | i < (data[2] * 2) && i < (raw_response_length / 2); 328 | i++) { 329 | 330 | /* shift reg hi_byte to temp */ 331 | temp = data[3 + i * 2] << 8; 332 | /* OR with lo_byte */ 333 | temp = temp | data[4 + i * 2]; 334 | 335 | dest[i] = temp; 336 | } 337 | } 338 | return (raw_response_length); 339 | } 340 | 341 | 342 | /*********************************************************************** 343 | * 344 | * preset_response 345 | * 346 | * Gets the raw data from the input stream. 347 | * 348 | ***********************************************************************/ 349 | 350 | int preset_response(unsigned char *query) 351 | { 352 | unsigned char data[MAX_RESPONSE_LENGTH]; 353 | int raw_response_length; 354 | 355 | raw_response_length = modbus_response(data, query); 356 | 357 | return (raw_response_length); 358 | } 359 | 360 | 361 | /************************************************************************ 362 | * 363 | * read_holding_registers 364 | * 365 | * Read the holding registers in a slave and put the data into 366 | * an array. 367 | * 368 | *************************************************************************/ 369 | 370 | int read_holding_registers(int slave, int start_addr, int count, 371 | int *dest, int dest_size) 372 | { 373 | int function = 0x03; /* Function: Read Holding Registers */ 374 | int ret; 375 | 376 | unsigned char packet[REQUEST_QUERY_SIZE + CHECKSUM_SIZE]; 377 | 378 | if (count > MAX_READ_REGS) { 379 | count = MAX_READ_REGS; 380 | } 381 | 382 | build_request_packet(slave, function, start_addr, count, packet); 383 | 384 | if (send_query(packet, REQUEST_QUERY_SIZE) > -1) { 385 | ret = read_reg_response(dest, dest_size, packet); 386 | } 387 | else { 388 | 389 | ret = -1; 390 | } 391 | 392 | return (ret); 393 | } 394 | 395 | 396 | /************************************************************************ 397 | * 398 | * preset_multiple_registers 399 | * 400 | * Write the data from an array into the holding registers of a 401 | * slave. 402 | * 403 | *************************************************************************/ 404 | 405 | int preset_multiple_registers(int slave, int start_addr, 406 | int reg_count, int *data) 407 | { 408 | int function = 0x10; /* Function 16: Write Multiple Registers */ 409 | int byte_count, i, packet_size = 6; 410 | int ret; 411 | 412 | unsigned char packet[PRESET_QUERY_SIZE]; 413 | 414 | if (reg_count > MAX_WRITE_REGS) { 415 | reg_count = MAX_WRITE_REGS; 416 | } 417 | 418 | build_request_packet(slave, function, start_addr, reg_count, packet); 419 | byte_count = reg_count * 2; 420 | packet[6] = (unsigned char)byte_count; 421 | 422 | for (i = 0; i < reg_count; i++) { 423 | packet_size++; 424 | packet[packet_size] = data[i] >> 8; 425 | packet_size++; 426 | packet[packet_size] = data[i] & 0x00FF; 427 | } 428 | 429 | packet_size++; 430 | if (send_query(packet, packet_size) > -1) { 431 | ret = preset_response(packet); 432 | } 433 | else { 434 | ret = -1; 435 | } 436 | 437 | return (ret); 438 | } 439 | 440 | 441 | 442 | -------------------------------------------------------------------------------- /Arduino Sketches/modbus_rtu_slave.pde: -------------------------------------------------------------------------------- 1 | /* 2 | Modbus over serial line - RTU Slave Arduino Sketch 3 | 4 | 5 | These functions implement functions 3, 6, and 16 (read holding registers, 6 | preset single register and preset multiple registers) of the 7 | Modbus RTU Protocol, to be used over the Arduino serial connection. 8 | 9 | This implementation DOES NOT fully comply with the Modbus specifications. 10 | 11 | The functions included here have been derived from the 12 | Modicon Modbus Protocol Reference Guide 13 | which can be obtained from Schneider at www.schneiderautomation.com. 14 | 15 | This code has its origins with 16 | paul@pmcrae.freeserve.co.uk (http://www.pmcrae.freeserve.co.uk) 17 | who wrote a small program to read 100 registers from a modbus slave. 18 | 19 | */ 20 | 21 | 22 | /* 23 | * configure_mb_slave(baud, parity, tx_en_pin) 24 | * 25 | * sets the communication parameters for of the serial line. 26 | * 27 | * baud: baudrate in bps (typical values 9600, 19200... 115200) 28 | * parity: a single character sets the parity mode (character frame format): 29 | * 'n' no parity (8N1); 'e' even parity (8E1), 'o' for odd parity (8O1). 30 | * tx_en_pin: arduino pin number that controls transmision/reception 31 | * of an external half-duplex device (e.g. a RS485 interface chip). 32 | * 0 or 1 disables this function (for a two-device network) 33 | * >2 for point-to-multipoint topology (e.g. several arduinos) 34 | */ 35 | void configure_mb_slave(long baud, char parity, char txenpin); 36 | 37 | /* 38 | * update_mb_slave(slave_id, holding_regs_array, number_of_regs) 39 | * 40 | * checks if there is any valid request from the modbus master. If there is, 41 | * performs the action requested 42 | * 43 | * slave: slave id (1 to 127) 44 | * regs: an array with the holding registers. They start at address 1 (master point of view) 45 | * regs_size: total number of holding registers. 46 | * returns: 0 if no request from master, 47 | * NO_REPLY (-1) if no reply is sent to the master 48 | * an exception code (1 to 4) in case of a modbus exceptions 49 | * the number of bytes sent as reply ( > 4) if OK. 50 | */ 51 | 52 | int update_mb_slave(unsigned char slave, int *regs, 53 | unsigned int regs_size); 54 | 55 | 56 | /* Modbus RTU common parameters, the Master MUST use the same parameters */ 57 | enum { 58 | MB_SLAVE = 1, /* modbus slave id */ 59 | }; 60 | /* slave registers example */ 61 | enum { 62 | MB_REG0, 63 | MB_REG1, 64 | MB_REGS /* total number of registers on slave */ 65 | }; 66 | 67 | int regs[MB_REGS]; /* this is the slave's modbus data map */ 68 | 69 | void setup() 70 | { 71 | /* Modbus setup example, the master must use the same COM parameters */ 72 | /* 115200 bps, 8N1, two-device network */ 73 | configure_mb_slave(115200, 'n', 0); 74 | } 75 | 76 | 77 | void loop() 78 | { 79 | /* This is all for the Modbus slave */ 80 | update_mb_slave(MB_SLAVE, regs, MB_REGS); 81 | 82 | /* your code goes here */ 83 | } 84 | 85 | /**************************************************************************** 86 | * BEGIN MODBUS RTU SLAVE FUNCTIONS 87 | ****************************************************************************/ 88 | 89 | /* global variables */ 90 | unsigned int Txenpin = 0; /* Enable transmission pin, used on RS485 networks */ 91 | 92 | 93 | /* enum of supported modbus function codes. If you implement a new one, put its function code here ! */ 94 | enum { 95 | FC_READ_REGS = 0x03, //Read contiguous block of holding register 96 | FC_WRITE_REG = 0x06, //Write single holding register 97 | FC_WRITE_REGS = 0x10 //Write block of contiguous registers 98 | }; 99 | 100 | /* supported functions. If you implement a new one, put its function code into this array! */ 101 | const unsigned char fsupported[] = { FC_READ_REGS, FC_WRITE_REG, FC_WRITE_REGS }; 102 | 103 | /* constants */ 104 | enum { 105 | MAX_READ_REGS = 0x7D, 106 | MAX_WRITE_REGS = 0x7B, 107 | MAX_MESSAGE_LENGTH = 256 108 | }; 109 | 110 | 111 | enum { 112 | RESPONSE_SIZE = 6, 113 | EXCEPTION_SIZE = 3, 114 | CHECKSUM_SIZE = 2 115 | }; 116 | 117 | /* exceptions code */ 118 | enum { 119 | NO_REPLY = -1, 120 | EXC_FUNC_CODE = 1, 121 | EXC_ADDR_RANGE = 2, 122 | EXC_REGS_QUANT = 3, 123 | EXC_EXECUTE = 4 124 | }; 125 | 126 | /* positions inside the query/response array */ 127 | enum { 128 | SLAVE = 0, 129 | FUNC, 130 | START_H, 131 | START_L, 132 | REGS_H, 133 | REGS_L, 134 | BYTE_CNT 135 | }; 136 | 137 | 138 | /* 139 | CRC 140 | 141 | INPUTS: 142 | buf -> Array containing message to be sent to controller. 143 | start -> Start of loop in crc counter, usually 0. 144 | cnt -> Amount of bytes in message being sent to controller/ 145 | OUTPUTS: 146 | temp -> Returns crc byte for message. 147 | COMMENTS: 148 | This routine calculates the crc high and low byte of a message. 149 | Note that this crc is only used for Modbus, not Modbus+ etc. 150 | ****************************************************************************/ 151 | 152 | unsigned int crc(unsigned char *buf, unsigned char start, 153 | unsigned char cnt) 154 | { 155 | unsigned char i, j; 156 | unsigned temp, temp2, flag; 157 | 158 | temp = 0xFFFF; 159 | 160 | for (i = start; i < cnt; i++) { 161 | temp = temp ^ buf[i]; 162 | 163 | for (j = 1; j <= 8; j++) { 164 | flag = temp & 0x0001; 165 | temp = temp >> 1; 166 | if (flag) 167 | temp = temp ^ 0xA001; 168 | } 169 | } 170 | 171 | /* Reverse byte order. */ 172 | temp2 = temp >> 8; 173 | temp = (temp << 8) | temp2; 174 | temp &= 0xFFFF; 175 | 176 | return (temp); 177 | } 178 | 179 | 180 | 181 | 182 | /*********************************************************************** 183 | * 184 | * The following functions construct the required query into 185 | * a modbus query packet. 186 | * 187 | ***********************************************************************/ 188 | 189 | /* 190 | * Start of the packet of a read_holding_register response 191 | */ 192 | void build_read_packet(unsigned char slave, unsigned char function, 193 | unsigned char count, unsigned char *packet) 194 | { 195 | packet[SLAVE] = slave; 196 | packet[FUNC] = function; 197 | packet[2] = count * 2; 198 | } 199 | 200 | /* 201 | * Start of the packet of a preset_multiple_register response 202 | */ 203 | void build_write_packet(unsigned char slave, unsigned char function, 204 | unsigned int start_addr, 205 | unsigned char count, 206 | unsigned char *packet) 207 | { 208 | packet[SLAVE] = slave; 209 | packet[FUNC] = function; 210 | packet[START_H] = start_addr >> 8; 211 | packet[START_L] = start_addr & 0x00ff; 212 | packet[REGS_H] = 0x00; 213 | packet[REGS_L] = count; 214 | } 215 | 216 | /* 217 | * Start of the packet of a write_single_register response 218 | */ 219 | void build_write_single_packet(unsigned char slave, unsigned char function, 220 | unsigned int write_addr, unsigned int reg_val, unsigned char* packet) 221 | { 222 | packet[SLAVE] = slave; 223 | packet[FUNC] = function; 224 | packet[START_H] = write_addr >> 8; 225 | packet[START_L] = write_addr & 0x00ff; 226 | packet[REGS_H] = reg_val >> 8; 227 | packet[REGS_L] = reg_val & 0x00ff; 228 | } 229 | 230 | 231 | /* 232 | * Start of the packet of an exception response 233 | */ 234 | void build_error_packet(unsigned char slave, unsigned char function, 235 | unsigned char exception, unsigned char *packet) 236 | { 237 | packet[SLAVE] = slave; 238 | packet[FUNC] = function + 0x80; 239 | packet[2] = exception; 240 | } 241 | 242 | 243 | /************************************************************************* 244 | * 245 | * modbus_query( packet, length) 246 | * 247 | * Function to add a checksum to the end of a packet. 248 | * Please note that the packet array must be at least 2 fields longer than 249 | * string_length. 250 | **************************************************************************/ 251 | 252 | void modbus_reply(unsigned char *packet, unsigned char string_length) 253 | { 254 | int temp_crc; 255 | 256 | temp_crc = crc(packet, 0, string_length); 257 | packet[string_length] = temp_crc >> 8; 258 | string_length++; 259 | packet[string_length] = temp_crc & 0x00FF; 260 | } 261 | 262 | 263 | 264 | /*********************************************************************** 265 | * 266 | * send_reply( query_string, query_length ) 267 | * 268 | * Function to send a reply to a modbus master. 269 | * Returns: total number of characters sent 270 | ************************************************************************/ 271 | 272 | int send_reply(unsigned char *query, unsigned char string_length) 273 | { 274 | unsigned char i; 275 | 276 | if (Txenpin > 1) { // set MAX485 to speak mode 277 | UCSR0A=UCSR0A |(1 << TXC0); 278 | digitalWrite( Txenpin, HIGH); 279 | delay(1); 280 | } 281 | 282 | modbus_reply(query, string_length); 283 | string_length += 2; 284 | 285 | for (i = 0; i < string_length; i++) { 286 | Serial.print(query[i], BYTE); 287 | } 288 | 289 | if (Txenpin > 1) {// set MAX485 to listen mode 290 | while (!(UCSR0A & (1 << TXC0))); 291 | digitalWrite( Txenpin, LOW); 292 | } 293 | 294 | return i; /* it does not mean that the write was succesful, though */ 295 | } 296 | 297 | /*********************************************************************** 298 | * 299 | * receive_request( array_for_data ) 300 | * 301 | * Function to monitor for a request from the modbus master. 302 | * 303 | * Returns: Total number of characters received if OK 304 | * 0 if there is no request 305 | * A negative error code on failure 306 | ***********************************************************************/ 307 | 308 | int receive_request(unsigned char *received_string) 309 | { 310 | int bytes_received = 0; 311 | 312 | /* FIXME: does Serial.available wait 1.5T or 3.5T before exiting the loop? */ 313 | while (Serial.available()) { 314 | received_string[bytes_received] = Serial.read(); 315 | bytes_received++; 316 | if (bytes_received >= MAX_MESSAGE_LENGTH) 317 | return NO_REPLY; /* port error */ 318 | } 319 | 320 | return (bytes_received); 321 | } 322 | 323 | 324 | /********************************************************************* 325 | * 326 | * modbus_request(slave_id, request_data_array) 327 | * 328 | * Function to the correct request is returned and that the checksum 329 | * is correct. 330 | * 331 | * Returns: string_length if OK 332 | * 0 if failed 333 | * Less than 0 for exception errors 334 | * 335 | * Note: All functions used for sending or receiving data via 336 | * modbus return these return values. 337 | * 338 | **********************************************************************/ 339 | 340 | int modbus_request(unsigned char slave, unsigned char *data) 341 | { 342 | int response_length; 343 | unsigned int crc_calc = 0; 344 | unsigned int crc_received = 0; 345 | unsigned char recv_crc_hi; 346 | unsigned char recv_crc_lo; 347 | 348 | response_length = receive_request(data); 349 | 350 | if (response_length > 0) { 351 | crc_calc = crc(data, 0, response_length - 2); 352 | recv_crc_hi = (unsigned) data[response_length - 2]; 353 | recv_crc_lo = (unsigned) data[response_length - 1]; 354 | crc_received = data[response_length - 2]; 355 | crc_received = (unsigned) crc_received << 8; 356 | crc_received = 357 | crc_received | (unsigned) data[response_length - 1]; 358 | 359 | /*********** check CRC of response ************/ 360 | if (crc_calc != crc_received) { 361 | return NO_REPLY; 362 | } 363 | 364 | /* check for slave id */ 365 | if (slave != data[SLAVE]) { 366 | return NO_REPLY; 367 | } 368 | } 369 | return (response_length); 370 | } 371 | 372 | /********************************************************************* 373 | * 374 | * validate_request(request_data_array, request_length, available_regs) 375 | * 376 | * Function to check that the request can be processed by the slave. 377 | * 378 | * Returns: 0 if OK 379 | * A negative exception code on error 380 | * 381 | **********************************************************************/ 382 | 383 | int validate_request(unsigned char *data, unsigned char length, 384 | unsigned int regs_size) 385 | { 386 | int i, fcnt = 0; 387 | unsigned int regs_num = 0; 388 | unsigned int start_addr = 0; 389 | unsigned char max_regs_num; 390 | 391 | /* check function code */ 392 | for (i = 0; i < sizeof(fsupported); i++) { 393 | if (fsupported[i] == data[FUNC]) { 394 | fcnt = 1; 395 | break; 396 | } 397 | } 398 | if (0 == fcnt) 399 | return EXC_FUNC_CODE; 400 | 401 | if (FC_WRITE_REG == data[FUNC]) { 402 | /* For function write single reg, this is the target reg.*/ 403 | regs_num = ((int) data[START_H] << 8) + (int) data[START_L]; 404 | if (regs_num >= regs_size) 405 | return EXC_ADDR_RANGE; 406 | return 0; 407 | } 408 | 409 | /* For functions read/write regs, this is the range. */ 410 | regs_num = ((int) data[REGS_H] << 8) + (int) data[REGS_L]; 411 | 412 | /* check quantity of registers */ 413 | if (FC_READ_REGS == data[FUNC]) 414 | max_regs_num = MAX_READ_REGS; 415 | else if (FC_WRITE_REGS == data[FUNC]) 416 | max_regs_num = MAX_WRITE_REGS; 417 | 418 | if ((regs_num < 1) || (regs_num > max_regs_num)) 419 | return EXC_REGS_QUANT; 420 | 421 | /* check registers range, start address is 0 */ 422 | start_addr = ((int) data[START_H] << 8) + (int) data[START_L]; 423 | if ((start_addr + regs_num) > regs_size) 424 | return EXC_ADDR_RANGE; 425 | 426 | return 0; /* OK, no exception */ 427 | } 428 | 429 | 430 | 431 | /************************************************************************ 432 | * 433 | * write_regs(first_register, data_array, registers_array) 434 | * 435 | * writes into the slave's holding registers the data in query, 436 | * starting at start_addr. 437 | * 438 | * Returns: the number of registers written 439 | ************************************************************************/ 440 | 441 | int write_regs(unsigned int start_addr, unsigned char *query, int *regs) 442 | { 443 | int temp; 444 | unsigned int i; 445 | 446 | for (i = 0; i < query[REGS_L]; i++) { 447 | /* shift reg hi_byte to temp */ 448 | temp = (int) query[(BYTE_CNT + 1) + i * 2] << 8; 449 | /* OR with lo_byte */ 450 | temp = temp | (int) query[(BYTE_CNT + 2) + i * 2]; 451 | 452 | regs[start_addr + i] = temp; 453 | } 454 | return i; 455 | } 456 | 457 | /************************************************************************ 458 | * 459 | * preset_multiple_registers(slave_id, first_register, number_of_registers, 460 | * data_array, registers_array) 461 | * 462 | * Write the data from an array into the holding registers of the slave. 463 | * 464 | *************************************************************************/ 465 | 466 | int preset_multiple_registers(unsigned char slave, 467 | unsigned int start_addr, 468 | unsigned char count, 469 | unsigned char *query, 470 | int *regs) 471 | { 472 | unsigned char function = FC_WRITE_REGS; /* Preset Multiple Registers */ 473 | int status = 0; 474 | unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE]; 475 | 476 | build_write_packet(slave, function, start_addr, count, packet); 477 | 478 | if (write_regs(start_addr, query, regs)) { 479 | status = send_reply(packet, RESPONSE_SIZE); 480 | } 481 | 482 | return (status); 483 | } 484 | 485 | 486 | /************************************************************************ 487 | * 488 | * write_single_register(slave_id, write_addr, data_array, registers_array) 489 | * 490 | * Write a single int val into a single holding register of the slave. 491 | * 492 | *************************************************************************/ 493 | 494 | int write_single_register(unsigned char slave, 495 | unsigned int write_addr, unsigned char *query, int *regs) 496 | { 497 | unsigned char function = FC_WRITE_REG; /* Function: Write Single Register */ 498 | int status = 0; 499 | unsigned int reg_val; 500 | unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE]; 501 | 502 | reg_val = query[REGS_H] << 8 | query[REGS_L]; 503 | build_write_single_packet(slave, function, write_addr, reg_val, packet); 504 | regs[write_addr] = (int) reg_val; 505 | /* 506 | written.start_addr=write_addr; 507 | written.num_regs=1; 508 | */ 509 | status = send_reply(packet, RESPONSE_SIZE); 510 | 511 | return (status); 512 | } 513 | 514 | 515 | /************************************************************************ 516 | * 517 | * read_holding_registers(slave_id, first_register, number_of_registers, 518 | * registers_array) 519 | * 520 | * reads the slave's holdings registers and sends them to the Modbus master 521 | * 522 | *************************************************************************/ 523 | 524 | int read_holding_registers(unsigned char slave, unsigned int start_addr, 525 | 526 | unsigned char reg_count, int *regs) 527 | { 528 | unsigned char function = 0x03; /* Function 03: Read Holding Registers */ 529 | int packet_size = 3; 530 | int status; 531 | unsigned int i; 532 | unsigned char packet[MAX_MESSAGE_LENGTH]; 533 | 534 | build_read_packet(slave, function, reg_count, packet); 535 | 536 | for (i = start_addr; i < (start_addr + (unsigned int) reg_count); 537 | i++) { 538 | packet[packet_size] = regs[i] >> 8; 539 | packet_size++; 540 | packet[packet_size] = regs[i] & 0x00FF; 541 | packet_size++; 542 | } 543 | 544 | status = send_reply(packet, packet_size); 545 | 546 | return (status); 547 | } 548 | 549 | 550 | void configure_mb_slave(long baud, char parity, char txenpin) 551 | { 552 | Serial.begin(baud); 553 | 554 | switch (parity) { 555 | case 'e': // 8E1 556 | UCSR0C |= ((1< 1) { // pin 0 & pin 1 are reserved for RX/TX 572 | Txenpin = txenpin; /* set global variable */ 573 | pinMode(Txenpin, OUTPUT); 574 | digitalWrite(Txenpin, LOW); 575 | } 576 | 577 | return; 578 | } 579 | 580 | /* 581 | * update_mb_slave(slave_id, holding_regs_array, number_of_regs) 582 | * 583 | * checks if there is any valid request from the modbus master. If there is, 584 | * performs the action requested 585 | */ 586 | 587 | unsigned long Nowdt = 0; 588 | unsigned int lastBytesReceived; 589 | const unsigned long T35 = 5; 590 | 591 | int update_mb_slave(unsigned char slave, int *regs, 592 | unsigned int regs_size) 593 | { 594 | unsigned char query[MAX_MESSAGE_LENGTH]; 595 | unsigned char errpacket[EXCEPTION_SIZE + CHECKSUM_SIZE]; 596 | unsigned int start_addr; 597 | int exception; 598 | int length = Serial.available(); 599 | unsigned long now = millis(); 600 | 601 | if (length == 0) { 602 | lastBytesReceived = 0; 603 | return 0; 604 | } 605 | 606 | if (lastBytesReceived != length) { 607 | lastBytesReceived = length; 608 | Nowdt = now + T35; 609 | return 0; 610 | } 611 | if (now < Nowdt) 612 | return 0; 613 | 614 | lastBytesReceived = 0; 615 | 616 | length = modbus_request(slave, query); 617 | if (length < 1) 618 | return length; 619 | 620 | 621 | exception = validate_request(query, length, regs_size); 622 | if (exception) { 623 | build_error_packet(slave, query[FUNC], exception, 624 | errpacket); 625 | send_reply(errpacket, EXCEPTION_SIZE); 626 | return (exception); 627 | } 628 | 629 | 630 | start_addr = ((int) query[START_H] << 8) + 631 | (int) query[START_L]; 632 | switch (query[FUNC]) { 633 | case FC_READ_REGS: 634 | return read_holding_registers(slave, 635 | start_addr, 636 | query[REGS_L], 637 | regs); 638 | break; 639 | case FC_WRITE_REGS: 640 | return preset_multiple_registers(slave, 641 | start_addr, 642 | query[REGS_L], 643 | query, 644 | regs); 645 | break; 646 | case FC_WRITE_REG: 647 | write_single_register(slave, 648 | start_addr, 649 | query, 650 | regs); 651 | break; 652 | } 653 | } 654 | 655 | 656 | 657 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # compiler and its flags 2 | CC = gcc 3 | FLAGS = -Wall 4 | 5 | all: mbm 6 | 7 | # main application 8 | mbm: mbm.o modbus_rtu.o 9 | $(CC) $(FLAGS) -o mbm mbm.o modbus_rtu.o 10 | 11 | mbm.o: mbm.c 12 | $(CC) $(FLAGS) -c mbm.c 13 | 14 | modbus_rtu.o: modbus_rtu.c modbus_rtu.h 15 | $(CC) $(CFLAGS) -c modbus_rtu.c 16 | 17 | -------------------------------------------------------------------------------- /ModbusSlave/ModbusSlave.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Modbus over serial line - RTU Slave Arduino Sketch 3 | 4 | 5 | These functions implement functions 3, 6, and 16 (read holding registers, 6 | preset single register and preset multiple registers) of the 7 | Modbus RTU Protocol, to be used over the Arduino serial connection. 8 | 9 | This implementation DOES NOT fully comply with the Modbus specifications. 10 | 11 | This code has its origins with 12 | paul@pmcrae.freeserve.co.uk (http://www.pmcrae.freeserve.co.uk) 13 | who wrote a small program to read 100 registers from a modbus slave. 14 | 15 | I have used his code as a catalist to produce this more functional set 16 | of functions. Thanks paul. 17 | */ 18 | 19 | #include "WProgram.h" 20 | #include "ModbusSlave.h" 21 | 22 | /**************************************************************************** 23 | * BEGIN MODBUS RTU SLAVE FUNCTIONS 24 | ****************************************************************************/ 25 | 26 | /* constants */ 27 | enum { 28 | MAX_READ_REGS = 0x7D, 29 | MAX_WRITE_REGS = 0x7B, 30 | MAX_MESSAGE_LENGTH = 256 31 | }; 32 | 33 | 34 | enum { 35 | RESPONSE_SIZE = 6, 36 | EXCEPTION_SIZE = 3, 37 | CHECKSUM_SIZE = 2 38 | }; 39 | 40 | /* exceptions code */ 41 | enum { 42 | NO_REPLY = -1, 43 | EXC_FUNC_CODE = 1, 44 | EXC_ADDR_RANGE = 2, 45 | EXC_REGS_QUANT = 3, 46 | EXC_EXECUTE = 4 47 | }; 48 | 49 | /* positions inside the query/response array */ 50 | enum { 51 | SLAVE = 0, 52 | FUNC, 53 | START_H, 54 | START_L, 55 | REGS_H, 56 | REGS_L, 57 | BYTE_CNT 58 | }; 59 | 60 | 61 | /* enum of supported modbus function codes. If you implement a new one, put its function code here ! */ 62 | enum { 63 | FC_READ_REGS = 0x03, //Read contiguous block of holding register 64 | FC_WRITE_REG = 0x06, //Write single holding register 65 | FC_WRITE_REGS = 0x10 //Write block of contiguous registers 66 | }; 67 | 68 | /* supported functions. If you implement a new one, put its function code into this array! */ 69 | const unsigned char fsupported[] = { FC_READ_REGS, FC_WRITE_REG, FC_WRITE_REGS }; 70 | 71 | 72 | /* 73 | CRC 74 | 75 | INPUTS: 76 | buf -> Array containing message to be sent to controller. 77 | start -> Start of loop in crc counter, usually 0. 78 | cnt -> Amount of bytes in message being sent to controller/ 79 | OUTPUTS: 80 | temp -> Returns crc byte for message. 81 | COMMENTS: 82 | This routine calculates the crc high and low byte of a message. 83 | Note that this crc is only used for Modbus, not Modbus+ etc. 84 | ****************************************************************************/ 85 | 86 | unsigned int ModbusSlave::crc(unsigned char *buf, unsigned char start, 87 | unsigned char cnt) 88 | { 89 | unsigned char i, j; 90 | unsigned temp, temp2, flag; 91 | 92 | temp = 0xFFFF; 93 | 94 | for (i = start; i < cnt; i++) { 95 | temp = temp ^ buf[i]; 96 | 97 | for (j = 1; j <= 8; j++) { 98 | flag = temp & 0x0001; 99 | temp = temp >> 1; 100 | if (flag) 101 | temp = temp ^ 0xA001; 102 | } 103 | } 104 | 105 | /* Reverse byte order. */ 106 | temp2 = temp >> 8; 107 | temp = (temp << 8) | temp2; 108 | temp &= 0xFFFF; 109 | 110 | return (temp); 111 | } 112 | 113 | 114 | 115 | 116 | /*********************************************************************** 117 | * 118 | * The following functions construct the required query into 119 | * a modbus query packet. 120 | * 121 | ***********************************************************************/ 122 | 123 | /* 124 | * Start of the packet of a read_holding_register response 125 | */ 126 | void ModbusSlave::build_read_packet(unsigned char function, 127 | unsigned char count, unsigned char *packet) 128 | { 129 | packet[SLAVE] = slave; 130 | packet[FUNC] = function; 131 | packet[2] = count * 2; 132 | } 133 | 134 | /* 135 | * Start of the packet of a preset_multiple_register response 136 | */ 137 | void ModbusSlave::build_write_packet(unsigned char function, 138 | unsigned int start_addr, 139 | unsigned char count, 140 | unsigned char *packet) 141 | { 142 | packet[SLAVE] = slave; 143 | packet[FUNC] = function; 144 | packet[START_H] = start_addr >> 8; 145 | packet[START_L] = start_addr & 0x00ff; 146 | packet[REGS_H] = 0x00; 147 | packet[REGS_L] = count; 148 | } 149 | 150 | /* 151 | * Start of the packet of a write_single_register response 152 | */ 153 | void ModbusSlave::build_write_single_packet(unsigned char function, 154 | unsigned int write_addr, unsigned int reg_val, unsigned char* packet) 155 | { 156 | packet[SLAVE] = slave; 157 | packet[FUNC] = function; 158 | packet[START_H] = write_addr >> 8; 159 | packet[START_L] = write_addr & 0x00ff; 160 | packet[REGS_H] = reg_val >> 8; 161 | packet[REGS_L] = reg_val & 0x00ff; 162 | } 163 | 164 | 165 | /* 166 | * Start of the packet of an exception response 167 | */ 168 | void ModbusSlave::build_error_packet( unsigned char function, 169 | unsigned char exception, unsigned char *packet) 170 | { 171 | packet[SLAVE] = slave; 172 | packet[FUNC] = function + 0x80; 173 | packet[2] = exception; 174 | } 175 | 176 | 177 | /************************************************************************* 178 | * 179 | * modbus_query( packet, length) 180 | * 181 | * Function to add a checksum to the end of a packet. 182 | * Please note that the packet array must be at least 2 fields longer than 183 | * string_length. 184 | **************************************************************************/ 185 | 186 | void ModbusSlave::modbus_reply(unsigned char *packet, unsigned char string_length) 187 | { 188 | int temp_crc; 189 | 190 | temp_crc = crc(packet, 0, string_length); 191 | packet[string_length] = temp_crc >> 8; 192 | string_length++; 193 | packet[string_length] = temp_crc & 0x00FF; 194 | } 195 | 196 | 197 | 198 | /*********************************************************************** 199 | * 200 | * send_reply( query_string, query_length ) 201 | * 202 | * Function to send a reply to a modbus master. 203 | * Returns: total number of characters sent 204 | ************************************************************************/ 205 | 206 | int ModbusSlave::send_reply(unsigned char *query, unsigned char string_length) 207 | { 208 | unsigned char i; 209 | 210 | if (txenpin > 1) { // set MAX485 to speak mode 211 | UCSR0A=UCSR0A |(1 << TXC0); 212 | digitalWrite( txenpin, HIGH); 213 | delay(1); 214 | } 215 | 216 | modbus_reply(query, string_length); 217 | string_length += 2; 218 | 219 | for (i = 0; i < string_length; i++) { 220 | Serial.print(query[i], BYTE); 221 | } 222 | 223 | if (txenpin > 1) {// set MAX485 to listen mode 224 | while (!(UCSR0A & (1 << TXC0))); 225 | digitalWrite( txenpin, LOW); 226 | } 227 | 228 | return i; /* it does not mean that the write was succesful, though */ 229 | } 230 | 231 | /*********************************************************************** 232 | * 233 | * receive_request( array_for_data ) 234 | * 235 | * Function to monitor for a request from the modbus master. 236 | * 237 | * Returns: Total number of characters received if OK 238 | * 0 if there is no request 239 | * A negative error code on failure 240 | ***********************************************************************/ 241 | 242 | int ModbusSlave::receive_request(unsigned char *received_string) 243 | { 244 | int bytes_received = 0; 245 | 246 | /* FIXME: does Serial.available wait 1.5T or 3.5T before exiting the loop? */ 247 | while (Serial.available()) { 248 | received_string[bytes_received] = Serial.read(); 249 | bytes_received++; 250 | if (bytes_received >= MAX_MESSAGE_LENGTH) 251 | return NO_REPLY; /* port error */ 252 | } 253 | 254 | return (bytes_received); 255 | } 256 | 257 | 258 | /********************************************************************* 259 | * 260 | * modbus_request(request_data_array) 261 | * 262 | * Function to the correct request is returned and that the checksum 263 | * is correct. 264 | * 265 | * Returns: string_length if OK 266 | * 0 if failed 267 | * Less than 0 for exception errors 268 | * 269 | * Note: All functions used for sending or receiving data via 270 | * modbus return these return values. 271 | * 272 | **********************************************************************/ 273 | 274 | int ModbusSlave::modbus_request(unsigned char *data) 275 | { 276 | int response_length; 277 | unsigned int crc_calc = 0; 278 | unsigned int crc_received = 0; 279 | unsigned char recv_crc_hi; 280 | unsigned char recv_crc_lo; 281 | 282 | response_length = receive_request(data); 283 | 284 | if (response_length > 0) { 285 | crc_calc = crc(data, 0, response_length - 2); 286 | recv_crc_hi = (unsigned) data[response_length - 2]; 287 | recv_crc_lo = (unsigned) data[response_length - 1]; 288 | crc_received = data[response_length - 2]; 289 | crc_received = (unsigned) crc_received << 8; 290 | crc_received = 291 | crc_received | (unsigned) data[response_length - 1]; 292 | 293 | /*********** check CRC of response ************/ 294 | if (crc_calc != crc_received) { 295 | return NO_REPLY; 296 | } 297 | 298 | /* check for slave id */ 299 | if (slave != data[SLAVE]) { 300 | return NO_REPLY; 301 | } 302 | } 303 | return (response_length); 304 | } 305 | 306 | /********************************************************************* 307 | * 308 | * validate_request(request_data_array, request_length, available_regs) 309 | * 310 | * Function to check that the request can be processed by the slave. 311 | * 312 | * Returns: 0 if OK 313 | * A negative exception code on error 314 | * 315 | **********************************************************************/ 316 | 317 | int ModbusSlave::validate_request(unsigned char *data, unsigned char length, 318 | unsigned int regs_size) 319 | { 320 | int i, fcnt = 0; 321 | unsigned int regs_num = 0; 322 | unsigned int start_addr = 0; 323 | unsigned char max_regs_num; 324 | 325 | /* check function code */ 326 | for (i = 0; i < sizeof(fsupported); i++) { 327 | if (fsupported[i] == data[FUNC]) { 328 | fcnt = 1; 329 | break; 330 | } 331 | } 332 | if (0 == fcnt) 333 | return EXC_FUNC_CODE; 334 | 335 | if (FC_WRITE_REG == data[FUNC]) { 336 | /* For function write single reg, this is the target reg.*/ 337 | regs_num = ((int) data[START_H] << 8) + (int) data[START_L]; 338 | if (regs_num >= regs_size) 339 | return EXC_ADDR_RANGE; 340 | return 0; 341 | } 342 | 343 | /* For functions read/write regs, this is the range. */ 344 | regs_num = ((int) data[REGS_H] << 8) + (int) data[REGS_L]; 345 | 346 | /* check quantity of registers */ 347 | if (FC_READ_REGS == data[FUNC]) 348 | max_regs_num = MAX_READ_REGS; 349 | else if (FC_WRITE_REGS == data[FUNC]) 350 | max_regs_num = MAX_WRITE_REGS; 351 | 352 | if ((regs_num < 1) || (regs_num > max_regs_num)) 353 | return EXC_REGS_QUANT; 354 | 355 | /* check registers range, start address is 0 */ 356 | start_addr = ((int) data[START_H] << 8) + (int) data[START_L]; 357 | if ((start_addr + regs_num) > regs_size) 358 | return EXC_ADDR_RANGE; 359 | 360 | return 0; /* OK, no exception */ 361 | } 362 | 363 | 364 | 365 | /************************************************************************ 366 | * 367 | * write_regs(first_register, data_array, registers_array) 368 | * 369 | * writes into the slave's holding registers the data in query, 370 | * starting at start_addr. 371 | * 372 | * Returns: the number of registers written 373 | ************************************************************************/ 374 | 375 | int ModbusSlave::write_regs(unsigned int start_addr, unsigned char *query, int *regs) 376 | { 377 | int temp; 378 | unsigned int i; 379 | 380 | for (i = 0; i < query[REGS_L]; i++) { 381 | /* shift reg hi_byte to temp */ 382 | temp = (int) query[(BYTE_CNT + 1) + i * 2] << 8; 383 | /* OR with lo_byte */ 384 | temp = temp | (int) query[(BYTE_CNT + 2) + i * 2]; 385 | 386 | regs[start_addr + i] = temp; 387 | } 388 | return i; 389 | } 390 | 391 | /************************************************************************ 392 | * 393 | * preset_multiple_registers(first_register, number_of_registers, 394 | * data_array, registers_array) 395 | * 396 | * Write the data from an array into the holding registers of the slave. 397 | * 398 | *************************************************************************/ 399 | 400 | int ModbusSlave::preset_multiple_registers(unsigned int start_addr, 401 | unsigned char count, 402 | unsigned char *query, 403 | int *regs) 404 | { 405 | unsigned char function = FC_WRITE_REGS; /* Preset Multiple Registers */ 406 | int status = 0; 407 | unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE]; 408 | 409 | build_write_packet(function, start_addr, count, packet); 410 | 411 | if (write_regs(start_addr, query, regs)) { 412 | status = send_reply(packet, RESPONSE_SIZE); 413 | } 414 | 415 | return (status); 416 | } 417 | 418 | /************************************************************************ 419 | * 420 | * write_single_register(slave_id, write_addr, data_array, registers_array) 421 | * 422 | * Write a single int val into a single holding register of the slave. 423 | * 424 | *************************************************************************/ 425 | 426 | int ModbusSlave::write_single_register(unsigned int write_addr, unsigned char *query, int *regs) 427 | { 428 | unsigned char function = FC_WRITE_REG; /* Function: Write Single Register */ 429 | int status = 0; 430 | unsigned int reg_val; 431 | unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE]; 432 | 433 | reg_val = query[REGS_H] << 8 | query[REGS_L]; 434 | build_write_single_packet(function, write_addr, reg_val, packet); 435 | regs[write_addr] = (int) reg_val; 436 | /* 437 | written.start_addr=write_addr; 438 | written.num_regs=1; 439 | */ 440 | status = send_reply(packet, RESPONSE_SIZE); 441 | 442 | return (status); 443 | } 444 | 445 | /************************************************************************ 446 | * 447 | * read_holding_registers(first_register, number_of_registers, 448 | * registers_array) 449 | * 450 | * reads the slave's holdings registers and sends them to the Modbus master 451 | * 452 | *************************************************************************/ 453 | 454 | int ModbusSlave::read_holding_registers( unsigned int start_addr, 455 | 456 | unsigned char reg_count, int *regs) 457 | { 458 | unsigned char function = FC_READ_REGS; /* Read Holding Registers */ 459 | int packet_size = 3; 460 | int status; 461 | unsigned int i; 462 | unsigned char packet[MAX_MESSAGE_LENGTH]; 463 | 464 | build_read_packet(function, reg_count, packet); 465 | 466 | for (i = start_addr; i < (start_addr + (unsigned int) reg_count); 467 | i++) { 468 | packet[packet_size] = regs[i] >> 8; 469 | packet_size++; 470 | packet[packet_size] = regs[i] & 0x00FF; 471 | packet_size++; 472 | } 473 | 474 | status = send_reply(packet, packet_size); 475 | 476 | return (status); 477 | } 478 | 479 | /* 480 | * configure(slave, baud, parity, txenpin) 481 | * 482 | * sets the communication parameters for of the serial line. 483 | * 484 | * slave: identification number of the slave in the Modbus network (1 to 127) 485 | * baud: baudrate in bps (typical values 9600, 19200... 115200) 486 | * parity: a single character sets the parity mode (character frame format): 487 | * 'n' no parity (8N1); 'e' even parity (8E1), 'o' for odd parity (8O1). 488 | * txenpin: arduino pin number that controls transmision/reception 489 | * of an external half-duplex device (e.g. a RS485 interface chip). 490 | * 0 or 1 disables this function (for a two-device network) 491 | * >2 for point-to-multipoint topology (e.g. several arduinos) 492 | */ 493 | 494 | void ModbusSlave::configure(unsigned char slave, long baud, char parity, char txenpin) 495 | { 496 | this->slave = slave; 497 | this->txenpin = txenpin; 498 | 499 | Serial.begin(baud); 500 | 501 | switch (parity) { 502 | case 'e': // 8E1 503 | UCSR0C |= ((1< 1) { // pin 0 & pin 1 are reserved for RX/TX 519 | pinMode(txenpin, OUTPUT); 520 | digitalWrite(txenpin, LOW); 521 | } 522 | 523 | return; 524 | } 525 | 526 | 527 | /* 528 | * update(regs, regs_size) 529 | * 530 | * checks if there is any valid request from the modbus master. If there is, 531 | * performs the requested action 532 | * 533 | * regs: an array with the holding registers. They start at address 1 (master point of view) 534 | * regs_size: total number of holding registers, i.e. the size of the array regs. 535 | * returns: 0 if no request from master, 536 | * NO_REPLY (-1) if no reply is sent to the master 537 | * an exception code (1 to 4) in case of a modbus exceptions 538 | * the number of bytes sent as reply ( > 4) if OK. 539 | */ 540 | unsigned long Nowdt = 0; 541 | unsigned int lastBytesReceived; 542 | const unsigned long T35 = 5; 543 | 544 | int ModbusSlave::update(int *regs, 545 | unsigned int regs_size) 546 | { 547 | unsigned char query[MAX_MESSAGE_LENGTH]; 548 | unsigned char errpacket[EXCEPTION_SIZE + CHECKSUM_SIZE]; 549 | unsigned int start_addr; 550 | int exception; 551 | int length = Serial.available(); 552 | unsigned long now = millis(); 553 | 554 | 555 | if (length == 0) { 556 | lastBytesReceived = 0; 557 | return 0; 558 | } 559 | 560 | if (lastBytesReceived != length) { 561 | lastBytesReceived = length; 562 | Nowdt = now + T35; 563 | return 0; 564 | } 565 | if (now < Nowdt) 566 | return 0; 567 | 568 | lastBytesReceived = 0; 569 | 570 | length = modbus_request(query); 571 | if (length < 1) 572 | return length; 573 | 574 | exception = validate_request(query, length, regs_size); 575 | if (exception) { 576 | build_error_packet( query[FUNC], exception, 577 | errpacket); 578 | send_reply(errpacket, EXCEPTION_SIZE); 579 | return (exception); 580 | } 581 | 582 | start_addr = 583 | ((int) query[START_H] << 8) + 584 | (int) query[START_L]; 585 | 586 | switch (query[FUNC]) { 587 | case FC_READ_REGS: 588 | return read_holding_registers( 589 | start_addr, 590 | query[REGS_L], 591 | regs); 592 | break; 593 | case FC_WRITE_REGS: 594 | return preset_multiple_registers( 595 | start_addr, 596 | query[REGS_L], 597 | query, 598 | regs); 599 | break; 600 | case FC_WRITE_REG: 601 | write_single_register( 602 | start_addr, 603 | query, 604 | regs); 605 | break; 606 | } 607 | 608 | } 609 | 610 | 611 | 612 | -------------------------------------------------------------------------------- /ModbusSlave/ModbusSlave.cpp~: -------------------------------------------------------------------------------- 1 | /* 2 | Modbus over serial line - RTU Slave Arduino Sketch 3 | 4 | By Juan Pablo Zometa : jpmzometa@gmail.com 5 | http://sites.google.com/site/jpmzometa/ 6 | Samuel Marco: sammarcoarmengol@gmail.com 7 | and Andras Tucsni. 8 | 9 | These functions implement functions 3, 6, and 16 (read holding registers, 10 | preset single register and preset multiple registers) of the 11 | Modbus RTU Protocol, to be used over the Arduino serial connection. 12 | 13 | This implementation DOES NOT fully comply with the Modbus specifications. 14 | 15 | This Arduino adaptation is derived from the work 16 | By P.Costigan email: phil@pcscada.com.au http://pcscada.com.au 17 | 18 | These library of functions are designed to enable a program send and 19 | receive data from a device that communicates using the Modbus protocol. 20 | 21 | Copyright (C) 2000 Philip Costigan P.C. SCADA LINK PTY. LTD. 22 | 23 | This program is free software; you can redistribute it and/or modify 24 | it under the terms of the GNU General Public License as published by 25 | the Free Software Foundation; either version 2 of the License, or 26 | (at your option) any later version. 27 | 28 | This program is distributed in the hope that it will be useful, 29 | but WITHOUT ANY WARRANTY; without even the implied warranty of 30 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 | GNU General Public License for more details. 32 | 33 | You should have received a copy of the GNU General Public License 34 | along with this program; if not, write to the Free Software 35 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 36 | 37 | The functions included here have been derived from the 38 | Modicon Modbus Protocol Reference Guide 39 | which can be obtained from Schneider at www.schneiderautomation.com. 40 | 41 | This code has its origins with 42 | paul@pmcrae.freeserve.co.uk (http://www.pmcrae.freeserve.co.uk) 43 | who wrote a small program to read 100 registers from a modbus slave. 44 | 45 | I have used his code as a catalist to produce this more functional set 46 | of functions. Thanks paul. 47 | */ 48 | 49 | #include "WProgram.h" 50 | #include "ModbusSlave.h" 51 | 52 | /**************************************************************************** 53 | * BEGIN MODBUS RTU SLAVE FUNCTIONS 54 | ****************************************************************************/ 55 | 56 | /* constants */ 57 | enum { 58 | MAX_READ_REGS = 0x7D, 59 | MAX_WRITE_REGS = 0x7B, 60 | MAX_MESSAGE_LENGTH = 256 61 | }; 62 | 63 | 64 | enum { 65 | RESPONSE_SIZE = 6, 66 | EXCEPTION_SIZE = 3, 67 | CHECKSUM_SIZE = 2 68 | }; 69 | 70 | /* exceptions code */ 71 | enum { 72 | NO_REPLY = -1, 73 | EXC_FUNC_CODE = 1, 74 | EXC_ADDR_RANGE = 2, 75 | EXC_REGS_QUANT = 3, 76 | EXC_EXECUTE = 4 77 | }; 78 | 79 | /* positions inside the query/response array */ 80 | enum { 81 | SLAVE = 0, 82 | FUNC, 83 | START_H, 84 | START_L, 85 | REGS_H, 86 | REGS_L, 87 | BYTE_CNT 88 | }; 89 | 90 | 91 | /* enum of supported modbus function codes. If you implement a new one, put its function code here ! */ 92 | enum { 93 | FC_READ_REGS = 0x03, //Read contiguous block of holding register 94 | FC_WRITE_REG = 0x06, //Write single holding register 95 | FC_WRITE_REGS = 0x10 //Write block of contiguous registers 96 | }; 97 | 98 | /* supported functions. If you implement a new one, put its function code into this array! */ 99 | const unsigned char fsupported[] = { FC_READ_REGS, FC_WRITE_REG, FC_WRITE_REGS }; 100 | 101 | 102 | /* 103 | CRC 104 | 105 | INPUTS: 106 | buf -> Array containing message to be sent to controller. 107 | start -> Start of loop in crc counter, usually 0. 108 | cnt -> Amount of bytes in message being sent to controller/ 109 | OUTPUTS: 110 | temp -> Returns crc byte for message. 111 | COMMENTS: 112 | This routine calculates the crc high and low byte of a message. 113 | Note that this crc is only used for Modbus, not Modbus+ etc. 114 | ****************************************************************************/ 115 | 116 | unsigned int ModbusSlave::crc(unsigned char *buf, unsigned char start, 117 | unsigned char cnt) 118 | { 119 | unsigned char i, j; 120 | unsigned temp, temp2, flag; 121 | 122 | temp = 0xFFFF; 123 | 124 | for (i = start; i < cnt; i++) { 125 | temp = temp ^ buf[i]; 126 | 127 | for (j = 1; j <= 8; j++) { 128 | flag = temp & 0x0001; 129 | temp = temp >> 1; 130 | if (flag) 131 | temp = temp ^ 0xA001; 132 | } 133 | } 134 | 135 | /* Reverse byte order. */ 136 | temp2 = temp >> 8; 137 | temp = (temp << 8) | temp2; 138 | temp &= 0xFFFF; 139 | 140 | return (temp); 141 | } 142 | 143 | 144 | 145 | 146 | /*********************************************************************** 147 | * 148 | * The following functions construct the required query into 149 | * a modbus query packet. 150 | * 151 | ***********************************************************************/ 152 | 153 | /* 154 | * Start of the packet of a read_holding_register response 155 | */ 156 | void ModbusSlave::build_read_packet(unsigned char function, 157 | unsigned char count, unsigned char *packet) 158 | { 159 | packet[SLAVE] = slave; 160 | packet[FUNC] = function; 161 | packet[2] = count * 2; 162 | } 163 | 164 | /* 165 | * Start of the packet of a preset_multiple_register response 166 | */ 167 | void ModbusSlave::build_write_packet(unsigned char function, 168 | unsigned int start_addr, 169 | unsigned char count, 170 | unsigned char *packet) 171 | { 172 | packet[SLAVE] = slave; 173 | packet[FUNC] = function; 174 | packet[START_H] = start_addr >> 8; 175 | packet[START_L] = start_addr & 0x00ff; 176 | packet[REGS_H] = 0x00; 177 | packet[REGS_L] = count; 178 | } 179 | 180 | /* 181 | * Start of the packet of a write_single_register response 182 | */ 183 | void ModbusSlave::build_write_single_packet(unsigned char function, 184 | unsigned int write_addr, unsigned int reg_val, unsigned char* packet) 185 | { 186 | packet[SLAVE] = slave; 187 | packet[FUNC] = function; 188 | packet[START_H] = write_addr >> 8; 189 | packet[START_L] = write_addr & 0x00ff; 190 | packet[REGS_H] = reg_val >> 8; 191 | packet[REGS_L] = reg_val & 0x00ff; 192 | } 193 | 194 | 195 | /* 196 | * Start of the packet of an exception response 197 | */ 198 | void ModbusSlave::build_error_packet( unsigned char function, 199 | unsigned char exception, unsigned char *packet) 200 | { 201 | packet[SLAVE] = slave; 202 | packet[FUNC] = function + 0x80; 203 | packet[2] = exception; 204 | } 205 | 206 | 207 | /************************************************************************* 208 | * 209 | * modbus_query( packet, length) 210 | * 211 | * Function to add a checksum to the end of a packet. 212 | * Please note that the packet array must be at least 2 fields longer than 213 | * string_length. 214 | **************************************************************************/ 215 | 216 | void ModbusSlave::modbus_reply(unsigned char *packet, unsigned char string_length) 217 | { 218 | int temp_crc; 219 | 220 | temp_crc = crc(packet, 0, string_length); 221 | packet[string_length] = temp_crc >> 8; 222 | string_length++; 223 | packet[string_length] = temp_crc & 0x00FF; 224 | } 225 | 226 | 227 | 228 | /*********************************************************************** 229 | * 230 | * send_reply( query_string, query_length ) 231 | * 232 | * Function to send a reply to a modbus master. 233 | * Returns: total number of characters sent 234 | ************************************************************************/ 235 | 236 | int ModbusSlave::send_reply(unsigned char *query, unsigned char string_length) 237 | { 238 | unsigned char i; 239 | 240 | if (txenpin > 1) { // set MAX485 to speak mode 241 | UCSR0A=UCSR0A |(1 << TXC0); 242 | digitalWrite( txenpin, HIGH); 243 | delay(1); 244 | } 245 | 246 | modbus_reply(query, string_length); 247 | string_length += 2; 248 | 249 | for (i = 0; i < string_length; i++) { 250 | Serial.print(query[i], BYTE); 251 | } 252 | 253 | if (txenpin > 1) {// set MAX485 to listen mode 254 | while (!(UCSR0A & (1 << TXC0))); 255 | digitalWrite( txenpin, LOW); 256 | } 257 | 258 | return i; /* it does not mean that the write was succesful, though */ 259 | } 260 | 261 | /*********************************************************************** 262 | * 263 | * receive_request( array_for_data ) 264 | * 265 | * Function to monitor for a request from the modbus master. 266 | * 267 | * Returns: Total number of characters received if OK 268 | * 0 if there is no request 269 | * A negative error code on failure 270 | ***********************************************************************/ 271 | 272 | int ModbusSlave::receive_request(unsigned char *received_string) 273 | { 274 | int bytes_received = 0; 275 | 276 | /* FIXME: does Serial.available wait 1.5T or 3.5T before exiting the loop? */ 277 | while (Serial.available()) { 278 | received_string[bytes_received] = Serial.read(); 279 | bytes_received++; 280 | if (bytes_received >= MAX_MESSAGE_LENGTH) 281 | return NO_REPLY; /* port error */ 282 | } 283 | 284 | return (bytes_received); 285 | } 286 | 287 | 288 | /********************************************************************* 289 | * 290 | * modbus_request(request_data_array) 291 | * 292 | * Function to the correct request is returned and that the checksum 293 | * is correct. 294 | * 295 | * Returns: string_length if OK 296 | * 0 if failed 297 | * Less than 0 for exception errors 298 | * 299 | * Note: All functions used for sending or receiving data via 300 | * modbus return these return values. 301 | * 302 | **********************************************************************/ 303 | 304 | int ModbusSlave::modbus_request(unsigned char *data) 305 | { 306 | int response_length; 307 | unsigned int crc_calc = 0; 308 | unsigned int crc_received = 0; 309 | unsigned char recv_crc_hi; 310 | unsigned char recv_crc_lo; 311 | 312 | response_length = receive_request(data); 313 | 314 | if (response_length > 0) { 315 | crc_calc = crc(data, 0, response_length - 2); 316 | recv_crc_hi = (unsigned) data[response_length - 2]; 317 | recv_crc_lo = (unsigned) data[response_length - 1]; 318 | crc_received = data[response_length - 2]; 319 | crc_received = (unsigned) crc_received << 8; 320 | crc_received = 321 | crc_received | (unsigned) data[response_length - 1]; 322 | 323 | /*********** check CRC of response ************/ 324 | if (crc_calc != crc_received) { 325 | return NO_REPLY; 326 | } 327 | 328 | /* check for slave id */ 329 | if (slave != data[SLAVE]) { 330 | return NO_REPLY; 331 | } 332 | } 333 | return (response_length); 334 | } 335 | 336 | /********************************************************************* 337 | * 338 | * validate_request(request_data_array, request_length, available_regs) 339 | * 340 | * Function to check that the request can be processed by the slave. 341 | * 342 | * Returns: 0 if OK 343 | * A negative exception code on error 344 | * 345 | **********************************************************************/ 346 | 347 | int ModbusSlave::validate_request(unsigned char *data, unsigned char length, 348 | unsigned int regs_size) 349 | { 350 | int i, fcnt = 0; 351 | unsigned int regs_num = 0; 352 | unsigned int start_addr = 0; 353 | unsigned char max_regs_num; 354 | 355 | /* check function code */ 356 | for (i = 0; i < sizeof(fsupported); i++) { 357 | if (fsupported[i] == data[FUNC]) { 358 | fcnt = 1; 359 | break; 360 | } 361 | } 362 | if (0 == fcnt) 363 | return EXC_FUNC_CODE; 364 | 365 | if (FC_WRITE_REG == data[FUNC]) { 366 | /* For function write single reg, this is the target reg.*/ 367 | regs_num = ((int) data[START_H] << 8) + (int) data[START_L]; 368 | if (regs_num >= regs_size) 369 | return EXC_ADDR_RANGE; 370 | return 0; 371 | } 372 | 373 | /* For functions read/write regs, this is the range. */ 374 | regs_num = ((int) data[REGS_H] << 8) + (int) data[REGS_L]; 375 | 376 | /* check quantity of registers */ 377 | if (FC_READ_REGS == data[FUNC]) 378 | max_regs_num = MAX_READ_REGS; 379 | else if (FC_WRITE_REGS == data[FUNC]) 380 | max_regs_num = MAX_WRITE_REGS; 381 | 382 | if ((regs_num < 1) || (regs_num > max_regs_num)) 383 | return EXC_REGS_QUANT; 384 | 385 | /* check registers range, start address is 0 */ 386 | start_addr = ((int) data[START_H] << 8) + (int) data[START_L]; 387 | if ((start_addr + regs_num) > regs_size) 388 | return EXC_ADDR_RANGE; 389 | 390 | return 0; /* OK, no exception */ 391 | } 392 | 393 | 394 | 395 | /************************************************************************ 396 | * 397 | * write_regs(first_register, data_array, registers_array) 398 | * 399 | * writes into the slave's holding registers the data in query, 400 | * starting at start_addr. 401 | * 402 | * Returns: the number of registers written 403 | ************************************************************************/ 404 | 405 | int ModbusSlave::write_regs(unsigned int start_addr, unsigned char *query, int *regs) 406 | { 407 | int temp; 408 | unsigned int i; 409 | 410 | for (i = 0; i < query[REGS_L]; i++) { 411 | /* shift reg hi_byte to temp */ 412 | temp = (int) query[(BYTE_CNT + 1) + i * 2] << 8; 413 | /* OR with lo_byte */ 414 | temp = temp | (int) query[(BYTE_CNT + 2) + i * 2]; 415 | 416 | regs[start_addr + i] = temp; 417 | } 418 | return i; 419 | } 420 | 421 | /************************************************************************ 422 | * 423 | * preset_multiple_registers(first_register, number_of_registers, 424 | * data_array, registers_array) 425 | * 426 | * Write the data from an array into the holding registers of the slave. 427 | * 428 | *************************************************************************/ 429 | 430 | int ModbusSlave::preset_multiple_registers(unsigned int start_addr, 431 | unsigned char count, 432 | unsigned char *query, 433 | int *regs) 434 | { 435 | unsigned char function = FC_WRITE_REGS; /* Preset Multiple Registers */ 436 | int status = 0; 437 | unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE]; 438 | 439 | build_write_packet(function, start_addr, count, packet); 440 | 441 | if (write_regs(start_addr, query, regs)) { 442 | status = send_reply(packet, RESPONSE_SIZE); 443 | } 444 | 445 | return (status); 446 | } 447 | 448 | /************************************************************************ 449 | * 450 | * write_single_register(slave_id, write_addr, data_array, registers_array) 451 | * 452 | * Write a single int val into a single holding register of the slave. 453 | * 454 | *************************************************************************/ 455 | 456 | int ModbusSlave::write_single_register(unsigned int write_addr, unsigned char *query, int *regs) 457 | { 458 | unsigned char function = FC_WRITE_REG; /* Function: Write Single Register */ 459 | int status = 0; 460 | unsigned int reg_val; 461 | unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE]; 462 | 463 | reg_val = query[REGS_H] << 8 | query[REGS_L]; 464 | build_write_single_packet(function, write_addr, reg_val, packet); 465 | regs[write_addr] = (int) reg_val; 466 | /* 467 | written.start_addr=write_addr; 468 | written.num_regs=1; 469 | */ 470 | status = send_reply(packet, RESPONSE_SIZE); 471 | 472 | return (status); 473 | } 474 | 475 | /************************************************************************ 476 | * 477 | * read_holding_registers(first_register, number_of_registers, 478 | * registers_array) 479 | * 480 | * reads the slave's holdings registers and sends them to the Modbus master 481 | * 482 | *************************************************************************/ 483 | 484 | int ModbusSlave::read_holding_registers( unsigned int start_addr, 485 | 486 | unsigned char reg_count, int *regs) 487 | { 488 | unsigned char function = FC_READ_REGS; /* Read Holding Registers */ 489 | int packet_size = 3; 490 | int status; 491 | unsigned int i; 492 | unsigned char packet[MAX_MESSAGE_LENGTH]; 493 | 494 | build_read_packet(function, reg_count, packet); 495 | 496 | for (i = start_addr; i < (start_addr + (unsigned int) reg_count); 497 | i++) { 498 | packet[packet_size] = regs[i] >> 8; 499 | packet_size++; 500 | packet[packet_size] = regs[i] & 0x00FF; 501 | packet_size++; 502 | } 503 | 504 | status = send_reply(packet, packet_size); 505 | 506 | return (status); 507 | } 508 | 509 | /* 510 | * configure(slave, baud, parity, txenpin) 511 | * 512 | * sets the communication parameters for of the serial line. 513 | * 514 | * slave: identification number of the slave in the Modbus network (1 to 127) 515 | * baud: baudrate in bps (typical values 9600, 19200... 115200) 516 | * parity: a single character sets the parity mode (character frame format): 517 | * 'n' no parity (8N1); 'e' even parity (8E1), 'o' for odd parity (8O1). 518 | * txenpin: arduino pin number that controls transmision/reception 519 | * of an external half-duplex device (e.g. a RS485 interface chip). 520 | * 0 or 1 disables this function (for a two-device network) 521 | * >2 for point-to-multipoint topology (e.g. several arduinos) 522 | */ 523 | 524 | void ModbusSlave::configure(unsigned char slave, long baud, char parity, char txenpin) 525 | { 526 | this->slave = slave; 527 | this->txenpin = txenpin; 528 | 529 | Serial.begin(baud); 530 | 531 | switch (parity) { 532 | case 'e': // 8E1 533 | UCSR0C |= ((1< 1) { // pin 0 & pin 1 are reserved for RX/TX 549 | pinMode(txenpin, OUTPUT); 550 | digitalWrite(txenpin, LOW); 551 | } 552 | 553 | return; 554 | } 555 | 556 | 557 | /* 558 | * update(regs, regs_size) 559 | * 560 | * checks if there is any valid request from the modbus master. If there is, 561 | * performs the requested action 562 | * 563 | * regs: an array with the holding registers. They start at address 1 (master point of view) 564 | * regs_size: total number of holding registers, i.e. the size of the array regs. 565 | * returns: 0 if no request from master, 566 | * NO_REPLY (-1) if no reply is sent to the master 567 | * an exception code (1 to 4) in case of a modbus exceptions 568 | * the number of bytes sent as reply ( > 4) if OK. 569 | */ 570 | unsigned long Nowdt = 0; 571 | unsigned int lastBytesReceived; 572 | const unsigned long T35 = 5; 573 | 574 | int ModbusSlave::update(int *regs, 575 | unsigned int regs_size) 576 | { 577 | unsigned char query[MAX_MESSAGE_LENGTH]; 578 | unsigned char errpacket[EXCEPTION_SIZE + CHECKSUM_SIZE]; 579 | unsigned int start_addr; 580 | int exception; 581 | int length = Serial.available(); 582 | unsigned long now = millis(); 583 | 584 | 585 | if (length == 0) { 586 | lastBytesReceived = 0; 587 | return 0; 588 | } 589 | 590 | if (lastBytesReceived != length) { 591 | lastBytesReceived = length; 592 | Nowdt = now + T35; 593 | return 0; 594 | } 595 | if (now < Nowdt) 596 | return 0; 597 | 598 | lastBytesReceived = 0; 599 | 600 | length = modbus_request(query); 601 | if (length < 1) 602 | return length; 603 | 604 | exception = validate_request(query, length, regs_size); 605 | if (exception) { 606 | build_error_packet( query[FUNC], exception, 607 | errpacket); 608 | send_reply(errpacket, EXCEPTION_SIZE); 609 | return (exception); 610 | } 611 | 612 | start_addr = 613 | ((int) query[START_H] << 8) + 614 | (int) query[START_L]; 615 | 616 | switch (query[FUNC]) { 617 | case FC_READ_REGS: 618 | return read_holding_registers( 619 | start_addr, 620 | query[REGS_L], 621 | regs); 622 | break; 623 | case FC_WRITE_REGS: 624 | return preset_multiple_registers( 625 | start_addr, 626 | query[REGS_L], 627 | query, 628 | regs); 629 | break; 630 | case FC_WRITE_REG: 631 | write_single_register( 632 | start_addr, 633 | query, 634 | regs); 635 | break; 636 | } 637 | 638 | } 639 | 640 | 641 | 642 | -------------------------------------------------------------------------------- /ModbusSlave/ModbusSlave.h: -------------------------------------------------------------------------------- 1 | #ifndef MODBUS_SLAVE_H 2 | #define MODBUS_SLAVE_H 3 | #include "WProgram.h" 4 | 5 | class ModbusSlave { 6 | private: 7 | unsigned char slave; 8 | char txenpin; 9 | 10 | unsigned int crc(unsigned char *buf, unsigned char start, unsigned char cnt); 11 | void build_read_packet(unsigned char function, unsigned char count, unsigned char *packet); 12 | void build_write_packet(unsigned char function, unsigned int start_addr, unsigned char count, unsigned char *packet); 13 | void build_write_single_packet(unsigned char function, unsigned int write_addr, unsigned int reg_val, unsigned char* packet); 14 | void build_error_packet(unsigned char function,unsigned char exception, unsigned char *packet); 15 | void modbus_reply(unsigned char *packet, unsigned char string_length); 16 | int send_reply(unsigned char *query, unsigned char string_length); 17 | int receive_request(unsigned char *received_string); 18 | int modbus_request(unsigned char *data); 19 | int validate_request(unsigned char *data, unsigned char length, unsigned int regs_size); 20 | int write_regs(unsigned int start_addr, unsigned char *query, int *regs); 21 | int preset_multiple_registers(unsigned int start_addr,unsigned char count,unsigned char *query,int *regs); 22 | int read_holding_registers(unsigned int start_addr, unsigned char reg_count, int *regs); 23 | int write_single_register(unsigned int write_addr, unsigned char *query, int *regs); 24 | void configure(long baud, char parity, char txenpin); 25 | 26 | public: 27 | /* 28 | * configure(slave, baud, parity, txenpin) 29 | * 30 | * sets the communication parameters for of the serial line. 31 | * 32 | * slave: identification number of the slave in the Modbus network (1 to 127) 33 | * baud: baudrate in bps (typical values 9600, 19200... 115200) 34 | * parity: a single character sets the parity mode (character frame format): 35 | * 'n' no parity (8N1); 'e' even parity (8E1), 'o' for odd parity (8O1). 36 | * txenpin: arduino pin number that controls transmision/reception 37 | * of an external half-duplex device (e.g. a RS485 interface chip). 38 | * 0 or 1 disables this function (for a two-device network) 39 | * >2 for point-to-multipoint topology (e.g. several arduinos) 40 | */ 41 | void configure(unsigned char slave, long baud, char parity, char txenpin); 42 | 43 | /* 44 | * update(regs, regs_size) 45 | * 46 | * checks if there is any valid request from the modbus master. If there is, 47 | * performs the requested action 48 | * 49 | * regs: an array with the holding registers. They start at address 1 (master point of view) 50 | * regs_size: total number of holding registers, i.e. the size of the array regs. 51 | * returns: 0 if no request from master, 52 | * NO_REPLY (-1) if no reply is sent to the master 53 | * an exception code (1 to 4) in case of a modbus exceptions 54 | * the number of bytes sent as reply ( > 4) if OK. 55 | */ 56 | int update(int *regs, unsigned int regs_size); 57 | 58 | // empty constructor 59 | ModbusSlave() 60 | { 61 | 62 | } 63 | 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /ModbusSlave/ModbusSlave.h~: -------------------------------------------------------------------------------- 1 | #ifndef MODBUS_SLAVE_H 2 | #define MODBUS_SLAVE_H 3 | /**************************************************************************** 4 | * 5 | * ModbusSlave library implementing a Modbus RTU Slave for Arduino 6 | * Modified by S.Marco. mailto:sammarcoarmengol@gmail.com 7 | * Based on the work published by jpmzometa at 8 | * http://sites.google.com/site/jpmzometa/arduino-mbrt 9 | * 10 | * Based also on http://pcscada.com.au by P.Costigan email: phil@pcscada.com.au 11 | * 12 | * These library of functions are designed to enable a program send and 13 | * receive data from a device that communicates using the Modbus protocol. 14 | * 15 | * Copyright (C) 2000 Philip Costigan P.C. SCADA LINK PTY. LTD. 16 | * 17 | * This file is part of ModbusSlave. 18 | * 19 | * ModbusSlave is free software; you can redistribute it and/or modify 20 | * it under the terms of the GNU General Public License as published by 21 | * the Free Software Foundation; either version 2 of the License, or 22 | * (at your option) any later version. 23 | * 24 | * This program is distributed in the hope that it will be useful, 25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | * GNU General Public License for more details. 28 | * 29 | * You should have received a copy of the GNU General Public License 30 | * along with this program; if not, write to the Free Software 31 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 32 | * 33 | * 34 | * The functions included here have been derived from the 35 | * Modbus Specifications and Implementation Guides 36 | * 37 | * http://www.modbus.org/docs/Modbus_over_serial_line_V1_02.pdf 38 | * http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf 39 | * http://www.modbus.org/docs/PI_MBUS_300.pdf 40 | * 41 | ****************************************************************************/ 42 | 43 | /**************************************************************************** 44 | * BEGIN MODBUS RTU SLAVE FUNCTIONS 45 | ****************************************************************************/ 46 | #include "WProgram.h" 47 | 48 | class ModbusSlave { 49 | private: 50 | unsigned char slave; 51 | char txenpin; 52 | 53 | unsigned int crc(unsigned char *buf, unsigned char start, unsigned char cnt); 54 | void build_read_packet(unsigned char function, unsigned char count, unsigned char *packet); 55 | void build_write_packet(unsigned char function, unsigned int start_addr, unsigned char count, unsigned char *packet); 56 | void build_write_single_packet(unsigned char function, unsigned int write_addr, unsigned int reg_val, unsigned char* packet); 57 | void build_error_packet(unsigned char function,unsigned char exception, unsigned char *packet); 58 | void modbus_reply(unsigned char *packet, unsigned char string_length); 59 | int send_reply(unsigned char *query, unsigned char string_length); 60 | int receive_request(unsigned char *received_string); 61 | int modbus_request(unsigned char *data); 62 | int validate_request(unsigned char *data, unsigned char length, unsigned int regs_size); 63 | int write_regs(unsigned int start_addr, unsigned char *query, int *regs); 64 | int preset_multiple_registers(unsigned int start_addr,unsigned char count,unsigned char *query,int *regs); 65 | int read_holding_registers(unsigned int start_addr, unsigned char reg_count, int *regs); 66 | int write_single_register(unsigned int write_addr, unsigned char *query, int *regs); 67 | void configure(long baud, char parity, char txenpin); 68 | 69 | public: 70 | /* 71 | * configure(slave, baud, parity, txenpin) 72 | * 73 | * sets the communication parameters for of the serial line. 74 | * 75 | * slave: identification number of the slave in the Modbus network (1 to 127) 76 | * baud: baudrate in bps (typical values 9600, 19200... 115200) 77 | * parity: a single character sets the parity mode (character frame format): 78 | * 'n' no parity (8N1); 'e' even parity (8E1), 'o' for odd parity (8O1). 79 | * txenpin: arduino pin number that controls transmision/reception 80 | * of an external half-duplex device (e.g. a RS485 interface chip). 81 | * 0 or 1 disables this function (for a two-device network) 82 | * >2 for point-to-multipoint topology (e.g. several arduinos) 83 | */ 84 | void configure(unsigned char slave, long baud, char parity, char txenpin); 85 | 86 | /* 87 | * update(regs, regs_size) 88 | * 89 | * checks if there is any valid request from the modbus master. If there is, 90 | * performs the requested action 91 | * 92 | * regs: an array with the holding registers. They start at address 1 (master point of view) 93 | * regs_size: total number of holding registers, i.e. the size of the array regs. 94 | * returns: 0 if no request from master, 95 | * NO_REPLY (-1) if no reply is sent to the master 96 | * an exception code (1 to 4) in case of a modbus exceptions 97 | * the number of bytes sent as reply ( > 4) if OK. 98 | */ 99 | int update(int *regs, unsigned int regs_size); 100 | 101 | // empty constructor 102 | ModbusSlave() 103 | { 104 | 105 | } 106 | 107 | }; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /ModbusSlave/keywords.txt: -------------------------------------------------------------------------------- 1 | ModbusSlave KEYWORD1 2 | update KEYWORD2 3 | configure KEYWORD2 4 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 1 - Open either sketch on your Arduino IDE: 2 | - mbslave_pde_example.pde is programed in C. The complete modbus implementation is inside the sketch. Nothing else is needed. 3 | - mbslave_lib_example.pde uses the ModbusSlave library. The library is included in this directory as the directory ModbusSlave. See Arduino library reference on the internet to see how to install the library. 4 | 5 | 2 - upload your selected sketch into your arduino. 6 | 7 | 3 - on your linux console, change to the directory where you unpacked mbslave_examples.tar.gz file, and type: 8 | make 9 | this will create the Modbus master file mbm. 10 | 11 | 4 - Run the executable: 12 | ./mbm 13 | 14 | You will see the led of your arduino turn off (if it was on), then turn on 15 | for 2 seconds, then blink quite fast for 3 secs, and then blink slower for 16 | 6 seconds. 17 | 18 | You can of course use other Modbus master implementations for your test. 19 | 20 | Other things to be noticed is the PDE Sketches folder. This is version 1.0 implementation of MODBUS protocol. The c file in the folder can be used with certain modifications as a universal file that can run on virutally any microcontroller. 21 | -------------------------------------------------------------------------------- /mbm.c: -------------------------------------------------------------------------------- 1 | #include "modbus_rtu.h" 2 | #include 3 | #include 4 | 5 | #define COMM_PORT "/dev/ttyUSB0" 6 | #define COMM_PARITY "even" 7 | 8 | /* Modbus RTU common parameters, the Slave MUST use the same parameters */ 9 | enum { 10 | COMM_BPS = 115200, 11 | MB_SLAVE = 1, /* modbus slave id */ 12 | }; 13 | /* slave 1 registers */ 14 | enum { 15 | MB_CTRL, /* Led control on, off or blink */ 16 | MB_TIME, /* blink time in milliseconds */ 17 | MB_CNT, /* count the number of blinks */ 18 | MB_REGS /* total number of registers on slave */ 19 | }; 20 | 21 | int main(int argc, char *argv[]) 22 | { 23 | int err = 0; 24 | int val = 0; 25 | int blink[MB_REGS]; 26 | int fd = set_up_comms(COMM_PORT, COMM_BPS, COMM_PARITY); 27 | 28 | /* turn off Arduino led */ 29 | preset_single_register(MB_SLAVE,(MB_CTRL+1), val, fd); 30 | 31 | sleep(1); 32 | 33 | /* turn it on for 2 secs */ 34 | val = 1; 35 | preset_single_register(MB_SLAVE,(MB_CTRL+1), val, fd); 36 | sleep(2); 37 | 38 | /* blink for 2 secs */ 39 | blink[MB_CTRL] = 2; 40 | blink[MB_TIME] = 200; 41 | preset_multiple_registers(MB_SLAVE,(MB_CTRL+1), 2, blink, fd); 42 | sleep(2); 43 | 44 | /* read the slave's registers */ 45 | err = read_holding_registers( MB_SLAVE, (MB_CTRL+1), MB_REGS, blink, MB_REGS,fd); 46 | printf("\n The blink count at this time is %d \n\n", blink[MB_CNT]); 47 | 48 | /* blink one more second */ 49 | sleep(1); 50 | 51 | /* blink a little bit slower until the watchdog turns off the LED */ 52 | blink[MB_CTRL] = 2; 53 | blink[MB_TIME] = 500; 54 | preset_multiple_registers(MB_SLAVE,(MB_CTRL+1), 2, blink, fd); 55 | sleep(1); 56 | 57 | /* read the slave's registers */ 58 | err = read_holding_registers( MB_SLAVE, (MB_CTRL+1), MB_REGS, blink, MB_REGS,fd); 59 | printf("\n The blink count at this time is %d \n\n", blink[MB_CNT]); 60 | 61 | /* On ending this program, the slave's watchdog will start. 62 | * 10 secs later the led will turn off 63 | */ 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /mbslave_lib_example/mbslave_lib_example.pde: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* First step MBS: create an instance */ 4 | ModbusSlave mbs; 5 | 6 | /* slave registers */ 7 | enum { 8 | MB_CTRL, /* Led control on, off or blink */ 9 | MB_TIME, /* blink time in milliseconds */ 10 | MB_CNT, /* count the number of blinks */ 11 | MB_REGS /* total number of registers on slave */ 12 | }; 13 | 14 | int regs[MB_REGS]; 15 | int ledPin = 13; 16 | unsigned long wdog = 0; /* watchdog */ 17 | unsigned long tprev = 0; /* previous time*/ 18 | 19 | void setup() 20 | { 21 | 22 | /* the Modbus slave configuration parameters */ 23 | const unsigned char SLAVE = 1; 24 | const long BAUD = 115200; 25 | const char PARITY = 'e'; 26 | const char TXENPIN = 0; 27 | 28 | /* Second step MBS: configure */ 29 | mbs.configure(SLAVE,BAUD,PARITY,TXENPIN); 30 | 31 | pinMode(ledPin, OUTPUT); 32 | 33 | } 34 | 35 | void loop() 36 | { 37 | /* Third and las step MBS: update in loop*/ 38 | if(mbs.update(regs, MB_REGS)) 39 | wdog = millis(); 40 | 41 | if ((millis() - wdog) > 5000) { /* no comms in 5 sec */ 42 | regs[MB_CTRL] = 0; /* turn off led */ 43 | } 44 | 45 | /* the values in regs are set by the modbus master */ 46 | switch(regs[MB_CTRL]) { 47 | case 0: 48 | digitalWrite(ledPin, LOW); 49 | break; 50 | case 1: 51 | digitalWrite(ledPin, HIGH); 52 | break; 53 | default: /* blink */ 54 | if (millis() - tprev > regs[MB_TIME]) { 55 | if (LOW == digitalRead(ledPin)) { 56 | digitalWrite(ledPin, HIGH); 57 | /* this is how you change your holding registers 58 | so the master can read values from the slave */ 59 | regs[MB_CNT]++; 60 | } else { 61 | digitalWrite(ledPin, LOW); 62 | } 63 | tprev = millis(); 64 | } 65 | } 66 | 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /mbslave_pde_example/mbslave_pde_example.pde: -------------------------------------------------------------------------------- 1 | /* 2 | Modbus over serial line - RTU Slave Arduino Sketch 3 | 4 | By Juan Pablo Zometa : jpmzometa@gmail.com 5 | http://sites.google.com/site/jpmzometa/ 6 | and Samuel Marco: sammarcoarmengol@gmail.com 7 | and Andras Tucsni. 8 | 9 | These functions implement functions 3, 6, and 16 (read holding registers, 10 | preset single register and preset multiple registers) of the 11 | Modbus RTU Protocol, to be used over the Arduino serial connection. 12 | 13 | This implementation DOES NOT fully comply with the Modbus specifications. 14 | 15 | This Arduino adaptation is derived from the work 16 | By P.Costigan email: phil@pcscada.com.au http://pcscada.com.au 17 | 18 | These library of functions are designed to enable a program send and 19 | receive data from a device that communicates using the Modbus protocol. 20 | 21 | Copyright (C) 2000 Philip Costigan P.C. SCADA LINK PTY. LTD. 22 | 23 | This program is free software; you can redistribute it and/or modify 24 | it under the terms of the GNU General Public License as published by 25 | the Free Software Foundation; either version 2 of the License, or 26 | (at your option) any later version. 27 | 28 | This program is distributed in the hope that it will be useful, 29 | but WITHOUT ANY WARRANTY; without even the implied warranty of 30 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 | GNU General Public License for more details. 32 | 33 | You should have received a copy of the GNU General Public License 34 | along with this program; if not, write to the Free Software 35 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 36 | 37 | The functions included here have been derived from the 38 | Modicon Modbus Protocol Reference Guide 39 | which can be obtained from Schneider at www.schneiderautomation.com. 40 | 41 | This code has its origins with 42 | paul@pmcrae.freeserve.co.uk (http://www.pmcrae.freeserve.co.uk) 43 | who wrote a small program to read 100 registers from a modbus slave. 44 | 45 | I have used his code as a catalist to produce this more functional set 46 | of functions. Thanks paul. 47 | */ 48 | 49 | 50 | /* 51 | * configure_mb_slave(baud, parity, tx_en_pin) 52 | * 53 | * sets the communication parameters for of the serial line. 54 | * 55 | * baud: baudrate in bps (typical values 9600, 19200... 115200) 56 | * parity: a single character sets the parity mode (character frame format): 57 | * 'n' no parity (8N1); 'e' even parity (8E1), 'o' for odd parity (8O1). 58 | * tx_en_pin: arduino pin number that controls transmision/reception 59 | * of an external half-duplex device (e.g. a RS485 interface chip). 60 | * 0 or 1 disables this function (for a two-device network) 61 | * >2 for point-to-multipoint topology (e.g. several arduinos) 62 | */ 63 | void configure_mb_slave(long baud, char parity, char txenpin); 64 | 65 | /* 66 | * update_mb_slave(slave_id, holding_regs_array, number_of_regs) 67 | * 68 | * checks if there is any valid request from the modbus master. If there is, 69 | * performs the action requested 70 | * 71 | * slave: slave id (1 to 127) 72 | * regs: an array with the holding registers. They start at address 1 (master point of view) 73 | * regs_size: total number of holding registers. 74 | * returns: 0 if no request from master, 75 | * NO_REPLY (-1) if no reply is sent to the master 76 | * an exception code (1 to 4) in case of a modbus exceptions 77 | * the number of bytes sent as reply ( > 4) if OK. 78 | */ 79 | 80 | int update_mb_slave(unsigned char slave, int *regs, 81 | unsigned int regs_size); 82 | 83 | /* HERE BEGINS THE EXAMPLE CODE */ 84 | 85 | /* Modbus RTU common parameters, the Master MUST use the same parameters */ 86 | enum { 87 | COMM_BPS = 115200, /* baud rate */ 88 | MB_SLAVE = 1, /* modbus slave id */ 89 | PARITY = 'e' /* even parity */ 90 | }; 91 | 92 | /* slave registers */ 93 | enum { 94 | MB_CTRL, /* Led control on, off or blink */ 95 | MB_TIME, /* blink time in milliseconds */ 96 | MB_CNT, /* count the number of blinks */ 97 | MB_REGS /* total number of registers on slave */ 98 | }; 99 | 100 | int regs[MB_REGS]; 101 | int ledPin = 13; 102 | unsigned long wdog = 0; /* watchdog */ 103 | unsigned long tprev = 0; /* previous time*/ 104 | 105 | void setup() 106 | { 107 | /* configure modbus communication 108 | * 115200 bps, 8E1, two-device network */ 109 | configure_mb_slave(COMM_BPS, PARITY, 0); 110 | 111 | pinMode(ledPin, OUTPUT); 112 | } 113 | 114 | 115 | void loop() 116 | { 117 | /* check for master requests*/ 118 | if(update_mb_slave(MB_SLAVE, regs, MB_REGS)) 119 | wdog = millis(); 120 | 121 | if ((millis() - wdog) > 5000) { /* no comms in 5 sec */ 122 | regs[MB_CTRL] = 0; /* turn off led */ 123 | } 124 | 125 | /* the values in regs are set by the modbus master */ 126 | switch(regs[MB_CTRL]) { 127 | case 0: 128 | digitalWrite(ledPin, LOW); 129 | break; 130 | case 1: 131 | digitalWrite(ledPin, HIGH); 132 | break; 133 | default: /* blink */ 134 | if (millis() - tprev > regs[MB_TIME]) { 135 | if (LOW == digitalRead(ledPin)) { 136 | digitalWrite(ledPin, HIGH); 137 | /* this is how you change your holding registers 138 | so the master can read values from the slave */ 139 | regs[MB_CNT]++; 140 | } else { 141 | digitalWrite(ledPin, LOW); 142 | } 143 | tprev = millis(); 144 | } 145 | } 146 | } 147 | 148 | /**************************************************************************** 149 | * BEGIN MODBUS RTU SLAVE FUNCTIONS 150 | ****************************************************************************/ 151 | 152 | /* global variables */ 153 | unsigned int Txenpin = 0; /* Enable transmission pin, used on RS485 networks */ 154 | 155 | 156 | /* enum of supported modbus function codes. If you implement a new one, put its function code here ! */ 157 | enum { 158 | FC_READ_REGS = 0x03, //Read contiguous block of holding register 159 | FC_WRITE_REG = 0x06, //Write single holding register 160 | FC_WRITE_REGS = 0x10 //Write block of contiguous registers 161 | }; 162 | 163 | /* supported functions. If you implement a new one, put its function code into this array! */ 164 | const unsigned char fsupported[] = { FC_READ_REGS, FC_WRITE_REG, FC_WRITE_REGS }; 165 | 166 | /* constants */ 167 | enum { 168 | MAX_READ_REGS = 0x7D, 169 | MAX_WRITE_REGS = 0x7B, 170 | MAX_MESSAGE_LENGTH = 256 171 | }; 172 | 173 | 174 | enum { 175 | RESPONSE_SIZE = 6, 176 | EXCEPTION_SIZE = 3, 177 | CHECKSUM_SIZE = 2 178 | }; 179 | 180 | /* exceptions code */ 181 | enum { 182 | NO_REPLY = -1, 183 | EXC_FUNC_CODE = 1, 184 | EXC_ADDR_RANGE = 2, 185 | EXC_REGS_QUANT = 3, 186 | EXC_EXECUTE = 4 187 | }; 188 | 189 | /* positions inside the query/response array */ 190 | enum { 191 | SLAVE = 0, 192 | FUNC, 193 | START_H, 194 | START_L, 195 | REGS_H, 196 | REGS_L, 197 | BYTE_CNT 198 | }; 199 | 200 | 201 | /* 202 | CRC 203 | 204 | INPUTS: 205 | buf -> Array containing message to be sent to controller. 206 | start -> Start of loop in crc counter, usually 0. 207 | cnt -> Amount of bytes in message being sent to controller/ 208 | OUTPUTS: 209 | temp -> Returns crc byte for message. 210 | COMMENTS: 211 | This routine calculates the crc high and low byte of a message. 212 | Note that this crc is only used for Modbus, not Modbus+ etc. 213 | ****************************************************************************/ 214 | 215 | unsigned int crc(unsigned char *buf, unsigned char start, 216 | unsigned char cnt) 217 | { 218 | unsigned char i, j; 219 | unsigned temp, temp2, flag; 220 | 221 | temp = 0xFFFF; 222 | 223 | for (i = start; i < cnt; i++) { 224 | temp = temp ^ buf[i]; 225 | 226 | for (j = 1; j <= 8; j++) { 227 | flag = temp & 0x0001; 228 | temp = temp >> 1; 229 | if (flag) 230 | temp = temp ^ 0xA001; 231 | } 232 | } 233 | 234 | /* Reverse byte order. */ 235 | temp2 = temp >> 8; 236 | temp = (temp << 8) | temp2; 237 | temp &= 0xFFFF; 238 | 239 | return (temp); 240 | } 241 | 242 | 243 | 244 | 245 | /*********************************************************************** 246 | * 247 | * The following functions construct the required query into 248 | * a modbus query packet. 249 | * 250 | ***********************************************************************/ 251 | 252 | /* 253 | * Start of the packet of a read_holding_register response 254 | */ 255 | void build_read_packet(unsigned char slave, unsigned char function, 256 | unsigned char count, unsigned char *packet) 257 | { 258 | packet[SLAVE] = slave; 259 | packet[FUNC] = function; 260 | packet[2] = count * 2; 261 | } 262 | 263 | /* 264 | * Start of the packet of a preset_multiple_register response 265 | */ 266 | void build_write_packet(unsigned char slave, unsigned char function, 267 | unsigned int start_addr, 268 | unsigned char count, 269 | unsigned char *packet) 270 | { 271 | packet[SLAVE] = slave; 272 | packet[FUNC] = function; 273 | packet[START_H] = start_addr >> 8; 274 | packet[START_L] = start_addr & 0x00ff; 275 | packet[REGS_H] = 0x00; 276 | packet[REGS_L] = count; 277 | } 278 | 279 | /* 280 | * Start of the packet of a write_single_register response 281 | */ 282 | void build_write_single_packet(unsigned char slave, unsigned char function, 283 | unsigned int write_addr, unsigned int reg_val, unsigned char* packet) 284 | { 285 | packet[SLAVE] = slave; 286 | packet[FUNC] = function; 287 | packet[START_H] = write_addr >> 8; 288 | packet[START_L] = write_addr & 0x00ff; 289 | packet[REGS_H] = reg_val >> 8; 290 | packet[REGS_L] = reg_val & 0x00ff; 291 | } 292 | 293 | 294 | /* 295 | * Start of the packet of an exception response 296 | */ 297 | void build_error_packet(unsigned char slave, unsigned char function, 298 | unsigned char exception, unsigned char *packet) 299 | { 300 | packet[SLAVE] = slave; 301 | packet[FUNC] = function + 0x80; 302 | packet[2] = exception; 303 | } 304 | 305 | 306 | /************************************************************************* 307 | * 308 | * modbus_query( packet, length) 309 | * 310 | * Function to add a checksum to the end of a packet. 311 | * Please note that the packet array must be at least 2 fields longer than 312 | * string_length. 313 | **************************************************************************/ 314 | 315 | void modbus_reply(unsigned char *packet, unsigned char string_length) 316 | { 317 | int temp_crc; 318 | 319 | temp_crc = crc(packet, 0, string_length); 320 | packet[string_length] = temp_crc >> 8; 321 | string_length++; 322 | packet[string_length] = temp_crc & 0x00FF; 323 | } 324 | 325 | 326 | 327 | /*********************************************************************** 328 | * 329 | * send_reply( query_string, query_length ) 330 | * 331 | * Function to send a reply to a modbus master. 332 | * Returns: total number of characters sent 333 | ************************************************************************/ 334 | 335 | int send_reply(unsigned char *query, unsigned char string_length) 336 | { 337 | unsigned char i; 338 | 339 | if (Txenpin > 1) { // set MAX485 to speak mode 340 | UCSR0A=UCSR0A |(1 << TXC0); 341 | digitalWrite( Txenpin, HIGH); 342 | delay(1); 343 | } 344 | 345 | modbus_reply(query, string_length); 346 | string_length += 2; 347 | 348 | for (i = 0; i < string_length; i++) { 349 | Serial.print(query[i], BYTE); 350 | } 351 | 352 | if (Txenpin > 1) {// set MAX485 to listen mode 353 | while (!(UCSR0A & (1 << TXC0))); 354 | digitalWrite( Txenpin, LOW); 355 | } 356 | 357 | return i; /* it does not mean that the write was succesful, though */ 358 | } 359 | 360 | /*********************************************************************** 361 | * 362 | * receive_request( array_for_data ) 363 | * 364 | * Function to monitor for a request from the modbus master. 365 | * 366 | * Returns: Total number of characters received if OK 367 | * 0 if there is no request 368 | * A negative error code on failure 369 | ***********************************************************************/ 370 | 371 | int receive_request(unsigned char *received_string) 372 | { 373 | int bytes_received = 0; 374 | 375 | /* FIXME: does Serial.available wait 1.5T or 3.5T before exiting the loop? */ 376 | while (Serial.available()) { 377 | received_string[bytes_received] = Serial.read(); 378 | bytes_received++; 379 | if (bytes_received >= MAX_MESSAGE_LENGTH) 380 | return NO_REPLY; /* port error */ 381 | } 382 | 383 | return (bytes_received); 384 | } 385 | 386 | 387 | /********************************************************************* 388 | * 389 | * modbus_request(slave_id, request_data_array) 390 | * 391 | * Function to the correct request is returned and that the checksum 392 | * is correct. 393 | * 394 | * Returns: string_length if OK 395 | * 0 if failed 396 | * Less than 0 for exception errors 397 | * 398 | * Note: All functions used for sending or receiving data via 399 | * modbus return these return values. 400 | * 401 | **********************************************************************/ 402 | 403 | int modbus_request(unsigned char slave, unsigned char *data) 404 | { 405 | int response_length; 406 | unsigned int crc_calc = 0; 407 | unsigned int crc_received = 0; 408 | unsigned char recv_crc_hi; 409 | unsigned char recv_crc_lo; 410 | 411 | response_length = receive_request(data); 412 | 413 | if (response_length > 0) { 414 | crc_calc = crc(data, 0, response_length - 2); 415 | recv_crc_hi = (unsigned) data[response_length - 2]; 416 | recv_crc_lo = (unsigned) data[response_length - 1]; 417 | crc_received = data[response_length - 2]; 418 | crc_received = (unsigned) crc_received << 8; 419 | crc_received = 420 | crc_received | (unsigned) data[response_length - 1]; 421 | 422 | /*********** check CRC of response ************/ 423 | if (crc_calc != crc_received) { 424 | return NO_REPLY; 425 | } 426 | 427 | /* check for slave id */ 428 | if (slave != data[SLAVE]) { 429 | return NO_REPLY; 430 | } 431 | } 432 | return (response_length); 433 | } 434 | 435 | /********************************************************************* 436 | * 437 | * validate_request(request_data_array, request_length, available_regs) 438 | * 439 | * Function to check that the request can be processed by the slave. 440 | * 441 | * Returns: 0 if OK 442 | * A negative exception code on error 443 | * 444 | **********************************************************************/ 445 | 446 | int validate_request(unsigned char *data, unsigned char length, 447 | unsigned int regs_size) 448 | { 449 | int i, fcnt = 0; 450 | unsigned int regs_num = 0; 451 | unsigned int start_addr = 0; 452 | unsigned char max_regs_num; 453 | 454 | /* check function code */ 455 | for (i = 0; i < sizeof(fsupported); i++) { 456 | if (fsupported[i] == data[FUNC]) { 457 | fcnt = 1; 458 | break; 459 | } 460 | } 461 | if (0 == fcnt) 462 | return EXC_FUNC_CODE; 463 | 464 | if (FC_WRITE_REG == data[FUNC]) { 465 | /* For function write single reg, this is the target reg.*/ 466 | regs_num = ((int) data[START_H] << 8) + (int) data[START_L]; 467 | if (regs_num >= regs_size) 468 | return EXC_ADDR_RANGE; 469 | return 0; 470 | } 471 | 472 | /* For functions read/write regs, this is the range. */ 473 | regs_num = ((int) data[REGS_H] << 8) + (int) data[REGS_L]; 474 | 475 | /* check quantity of registers */ 476 | if (FC_READ_REGS == data[FUNC]) 477 | max_regs_num = MAX_READ_REGS; 478 | else if (FC_WRITE_REGS == data[FUNC]) 479 | max_regs_num = MAX_WRITE_REGS; 480 | 481 | if ((regs_num < 1) || (regs_num > max_regs_num)) 482 | return EXC_REGS_QUANT; 483 | 484 | /* check registers range, start address is 0 */ 485 | start_addr = ((int) data[START_H] << 8) + (int) data[START_L]; 486 | if ((start_addr + regs_num) > regs_size) 487 | return EXC_ADDR_RANGE; 488 | 489 | return 0; /* OK, no exception */ 490 | } 491 | 492 | 493 | 494 | /************************************************************************ 495 | * 496 | * write_regs(first_register, data_array, registers_array) 497 | * 498 | * writes into the slave's holding registers the data in query, 499 | * starting at start_addr. 500 | * 501 | * Returns: the number of registers written 502 | ************************************************************************/ 503 | 504 | int write_regs(unsigned int start_addr, unsigned char *query, int *regs) 505 | { 506 | int temp; 507 | unsigned int i; 508 | 509 | for (i = 0; i < query[REGS_L]; i++) { 510 | /* shift reg hi_byte to temp */ 511 | temp = (int) query[(BYTE_CNT + 1) + i * 2] << 8; 512 | /* OR with lo_byte */ 513 | temp = temp | (int) query[(BYTE_CNT + 2) + i * 2]; 514 | 515 | regs[start_addr + i] = temp; 516 | } 517 | return i; 518 | } 519 | 520 | /************************************************************************ 521 | * 522 | * preset_multiple_registers(slave_id, first_register, number_of_registers, 523 | * data_array, registers_array) 524 | * 525 | * Write the data from an array into the holding registers of the slave. 526 | * 527 | *************************************************************************/ 528 | 529 | int preset_multiple_registers(unsigned char slave, 530 | unsigned int start_addr, 531 | unsigned char count, 532 | unsigned char *query, 533 | int *regs) 534 | { 535 | unsigned char function = FC_WRITE_REGS; /* Preset Multiple Registers */ 536 | int status = 0; 537 | unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE]; 538 | 539 | build_write_packet(slave, function, start_addr, count, packet); 540 | 541 | if (write_regs(start_addr, query, regs)) { 542 | status = send_reply(packet, RESPONSE_SIZE); 543 | } 544 | 545 | return (status); 546 | } 547 | 548 | 549 | /************************************************************************ 550 | * 551 | * write_single_register(slave_id, write_addr, data_array, registers_array) 552 | * 553 | * Write a single int val into a single holding register of the slave. 554 | * 555 | *************************************************************************/ 556 | 557 | int write_single_register(unsigned char slave, 558 | unsigned int write_addr, unsigned char *query, int *regs) 559 | { 560 | unsigned char function = FC_WRITE_REG; /* Function: Write Single Register */ 561 | int status = 0; 562 | unsigned int reg_val; 563 | unsigned char packet[RESPONSE_SIZE + CHECKSUM_SIZE]; 564 | 565 | reg_val = query[REGS_H] << 8 | query[REGS_L]; 566 | build_write_single_packet(slave, function, write_addr, reg_val, packet); 567 | regs[write_addr] = (int) reg_val; 568 | /* 569 | written.start_addr=write_addr; 570 | written.num_regs=1; 571 | */ 572 | status = send_reply(packet, RESPONSE_SIZE); 573 | 574 | return (status); 575 | } 576 | 577 | 578 | /************************************************************************ 579 | * 580 | * read_holding_registers(slave_id, first_register, number_of_registers, 581 | * registers_array) 582 | * 583 | * reads the slave's holdings registers and sends them to the Modbus master 584 | * 585 | *************************************************************************/ 586 | 587 | int read_holding_registers(unsigned char slave, unsigned int start_addr, 588 | 589 | unsigned char reg_count, int *regs) 590 | { 591 | unsigned char function = 0x03; /* Function 03: Read Holding Registers */ 592 | int packet_size = 3; 593 | int status; 594 | unsigned int i; 595 | unsigned char packet[MAX_MESSAGE_LENGTH]; 596 | 597 | build_read_packet(slave, function, reg_count, packet); 598 | 599 | for (i = start_addr; i < (start_addr + (unsigned int) reg_count); 600 | i++) { 601 | packet[packet_size] = regs[i] >> 8; 602 | packet_size++; 603 | packet[packet_size] = regs[i] & 0x00FF; 604 | packet_size++; 605 | } 606 | 607 | status = send_reply(packet, packet_size); 608 | 609 | return (status); 610 | } 611 | 612 | 613 | void configure_mb_slave(long baud, char parity, char txenpin) 614 | { 615 | Serial.begin(baud); 616 | 617 | switch (parity) { 618 | case 'e': // 8E1 619 | UCSR0C |= ((1< 1) { // pin 0 & pin 1 are reserved for RX/TX 635 | Txenpin = txenpin; /* set global variable */ 636 | pinMode(Txenpin, OUTPUT); 637 | digitalWrite(Txenpin, LOW); 638 | } 639 | 640 | return; 641 | } 642 | 643 | /* 644 | * update_mb_slave(slave_id, holding_regs_array, number_of_regs) 645 | * 646 | * checks if there is any valid request from the modbus master. If there is, 647 | * performs the action requested 648 | */ 649 | 650 | unsigned long Nowdt = 0; 651 | unsigned int lastBytesReceived; 652 | const unsigned long T35 = 5; 653 | 654 | int update_mb_slave(unsigned char slave, int *regs, 655 | unsigned int regs_size) 656 | { 657 | unsigned char query[MAX_MESSAGE_LENGTH]; 658 | unsigned char errpacket[EXCEPTION_SIZE + CHECKSUM_SIZE]; 659 | unsigned int start_addr; 660 | int exception; 661 | int length = Serial.available(); 662 | unsigned long now = millis(); 663 | 664 | if (length == 0) { 665 | lastBytesReceived = 0; 666 | return 0; 667 | } 668 | 669 | if (lastBytesReceived != length) { 670 | lastBytesReceived = length; 671 | Nowdt = now + T35; 672 | return 0; 673 | } 674 | if (now < Nowdt) 675 | return 0; 676 | 677 | lastBytesReceived = 0; 678 | 679 | length = modbus_request(slave, query); 680 | if (length < 1) 681 | return length; 682 | 683 | 684 | exception = validate_request(query, length, regs_size); 685 | if (exception) { 686 | build_error_packet(slave, query[FUNC], exception, 687 | errpacket); 688 | send_reply(errpacket, EXCEPTION_SIZE); 689 | return (exception); 690 | } 691 | 692 | 693 | start_addr = ((int) query[START_H] << 8) + 694 | (int) query[START_L]; 695 | switch (query[FUNC]) { 696 | case FC_READ_REGS: 697 | return read_holding_registers(slave, 698 | start_addr, 699 | query[REGS_L], 700 | regs); 701 | break; 702 | case FC_WRITE_REGS: 703 | return preset_multiple_registers(slave, 704 | start_addr, 705 | query[REGS_L], 706 | query, 707 | regs); 708 | break; 709 | case FC_WRITE_REG: 710 | write_single_register(slave, 711 | start_addr, 712 | query, 713 | regs); 714 | break; 715 | } 716 | } 717 | 718 | 719 | 720 | -------------------------------------------------------------------------------- /modbus_rtu.c: -------------------------------------------------------------------------------- 1 | /* modbus_rtu.c 2 | 3 | 4 | 5 | These library of functions are designed to enable a program send and 6 | receive data from a device that communicates using the Modbus protocol. 7 | 8 | Copyright (C) 2000 Philip Costigan P.C. SCADA LINK PTY. LTD. 9 | 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software 22 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, US 23 | 24 | */ 25 | 26 | #include /* File control definitions */ 27 | #include /* Standard input/output */ 28 | #include 29 | #include 30 | #include /* POSIX terminal control definitions */ 31 | #include /* Time structures for select() */ 32 | #include /* POSIX Symbolic Constants */ 33 | #include /* Error definitions */ 34 | #include "modbus_rtu.h" 35 | 36 | #define DEBUG /* uncomment to see the data sent and received */ 37 | // #define DEBUG_CHITO /* mas comentarios para encontrar el error en recepcion */ 38 | 39 | int char_interval_timeout; 40 | 41 | enum { 42 | FALSE = 0, 43 | TRUE 44 | }; 45 | 46 | /************************************************************************* 47 | 48 | modbus_query( packet, length) 49 | 50 | Function to add a checksum to the end of a packet. 51 | Please note that the packet array must be at least 2 fields longer than 52 | string_length. 53 | **************************************************************************/ 54 | 55 | void modbus_query(unsigned char *packet, size_t string_length) 56 | { 57 | int temp_crc; 58 | 59 | /* declaration */ 60 | unsigned int crc(unsigned char buf[], int start, int cnt); 61 | 62 | 63 | 64 | temp_crc = crc(packet, 0, string_length); 65 | 66 | packet[string_length++] = temp_crc >> 8; 67 | packet[string_length++] = temp_crc & 0x00FF; 68 | } 69 | 70 | 71 | 72 | 73 | 74 | /*********************************************************************** 75 | 76 | send_query( file_descriptor, query_string, query_length ) 77 | 78 | Function to send a query out to a modbus slave. 79 | ************************************************************************/ 80 | 81 | int send_query(int ttyfd, unsigned char *query, size_t string_length) 82 | { 83 | int write_stat; 84 | 85 | int status; 86 | #ifdef DEBUG 87 | int i; 88 | #endif 89 | 90 | modbus_query(query, string_length); 91 | string_length += 2; 92 | 93 | #ifdef DEBUG 94 | // Print to stderr the hex value of each character that is about to be 95 | // sent to the modbus slave. 96 | 97 | for (i = 0; i < string_length; i++) { 98 | fprintf(stderr, "[%0.2X]", query[i]); 99 | } 100 | fprintf(stderr, " - string length = %d", string_length); 101 | fprintf(stderr, "\n"); 102 | #endif 103 | 104 | tcflush(ttyfd, TCIOFLUSH); /* flush the input & output streams */ 105 | 106 | /* configura la linea RTS para transmision */ 107 | ioctl(ttyfd, TIOCMGET, &status); 108 | status |= TIOCM_RTS; 109 | ioctl(ttyfd, TIOCMSET, &status); 110 | write_stat = write(ttyfd, query, string_length); 111 | tcflush(ttyfd, TCIFLUSH); /* maybe not neccesary */ 112 | 113 | return (write_stat); 114 | } 115 | 116 | 117 | /********************************************************************* 118 | 119 | modbus_response( response_data_array, query_array ) 120 | 121 | Function to the correct response is returned and that the checksum 122 | is correct. 123 | 124 | Returns: string_length if OK 125 | 0 if failed 126 | Less than 0 for exception errors 127 | 128 | Note: All functions used for sending or receiving data via 129 | modbus return these return values. 130 | 131 | **********************************************************************/ 132 | 133 | int modbus_response(unsigned char *data, unsigned char *query, int fd) 134 | { 135 | int response_length; 136 | 137 | unsigned short crc_calc = 0; 138 | unsigned short crc_received = 0; 139 | unsigned char recv_crc_hi; 140 | unsigned char recv_crc_lo; 141 | 142 | 143 | /* local declaration */ 144 | int receive_response(unsigned char *received_string, int ttyfd); 145 | unsigned int crc(unsigned char buf[], int start, int cnt); 146 | 147 | 148 | response_length = receive_response(data, fd); 149 | if (response_length) { 150 | crc_calc = crc(data, 0, response_length - 2); 151 | 152 | recv_crc_hi = (unsigned) data[response_length - 2]; 153 | recv_crc_lo = (unsigned) data[response_length - 1]; 154 | 155 | crc_received = data[response_length - 2]; 156 | crc_received = (unsigned) crc_received << 8; 157 | crc_received = 158 | crc_received | (unsigned) data[response_length - 1]; 159 | 160 | 161 | /*********** check CRC of response ************/ 162 | 163 | if (crc_calc != crc_received) { 164 | 165 | fprintf(stderr, "crc error received "); 166 | fprintf(stderr, "%0X - ", crc_received); 167 | fprintf(stderr, "crc_calc %0X\n", crc_calc); 168 | 169 | response_length = 0; 170 | 171 | } 172 | 173 | 174 | 175 | /********** check for exception response *****/ 176 | 177 | if (response_length && data[1] != query[1]) { 178 | response_length = 0 - data[2]; 179 | } 180 | } 181 | /* FIXME: it does not check for the slave id; jpz */ 182 | return (response_length); 183 | } 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | /*********************************************************************** 193 | 194 | receive_response( array_for_data ) 195 | 196 | Function to monitor for the reply from the modbus slave. 197 | This function blocks for timeout seconds if there is no reply. 198 | 199 | Returns: Total number of characters received. 200 | ***********************************************************************/ 201 | 202 | int receive_response(unsigned char *received_string, int ttyfd) 203 | { 204 | 205 | int rxchar = PORT_FAILURE; 206 | int data_avail = FALSE; 207 | int bytes_received = 0; 208 | int read_stat; 209 | 210 | int timeout = 1; /* 1 second */ 211 | 212 | fd_set rfds; 213 | 214 | struct timeval tv; 215 | 216 | tv.tv_sec = timeout; 217 | tv.tv_usec = 0; 218 | 219 | FD_ZERO(&rfds); 220 | FD_SET(ttyfd, &rfds); 221 | 222 | 223 | 224 | 225 | #ifdef DEBUG 226 | fprintf(stderr, "Waiting for response.\n"); 227 | #endif 228 | 229 | /* wait for a response */ 230 | data_avail = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); 231 | 232 | if (!data_avail) { 233 | bytes_received = 0; 234 | fprintf(stderr, "Comms time out\n"); 235 | } 236 | 237 | 238 | tv.tv_sec = 0; 239 | tv.tv_usec = char_interval_timeout; 240 | 241 | FD_ZERO(&rfds); 242 | FD_SET(ttyfd, &rfds); 243 | 244 | while (data_avail) { 245 | /* antes de leer un byte, hacer una pausa de "un byte", 246 | * de lo contrario, otro proceso se apodera de la linea RTS */ 247 | //esperaRTS(1); 248 | /* if no character at the buffer wait char_interval_timeout */ 249 | /* before accepting end of response */ 250 | 251 | if (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) { 252 | 253 | 254 | read_stat = read(ttyfd, &rxchar, 1); 255 | 256 | if (read_stat < 0) { 257 | bytes_received = PORT_FAILURE; 258 | data_avail = FALSE; 259 | } else { 260 | rxchar = rxchar & 0xFF; 261 | received_string[bytes_received++] = rxchar; 262 | } 263 | 264 | 265 | if (bytes_received >= MAX_RESPONSE_LENGTH) { 266 | bytes_received = PORT_FAILURE; 267 | data_avail = FALSE; 268 | } 269 | #ifdef DEBUG 270 | /* display the hex code of each character received */ 271 | fprintf(stderr, "<%0.2X>", rxchar); 272 | #endif 273 | 274 | } else { 275 | data_avail = FALSE; 276 | } 277 | 278 | } 279 | 280 | #ifdef DEBUG 281 | fprintf(stderr, "\n"); 282 | #endif 283 | /* Al restar 2 a bytes_received, el crc de retorno es calculado a partir de 284 | * 2 bytes menos, lo que resulta en crc_error en lectura. ¡ELIMINADO! 285 | */ 286 | #if 0 287 | if (bytes_received > 2) { 288 | bytes_received -= 2; 289 | } 290 | #endif 291 | #ifdef DEBUG_CHITO 292 | fprintf(stderr, "receive_response: bytes recibidos: %d\n", 293 | bytes_received); 294 | #endif 295 | 296 | return (bytes_received); 297 | } 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | /*********************************************************************** 306 | 307 | The following functions construct the required query into 308 | a modbus query packet. 309 | 310 | ***********************************************************************/ 311 | 312 | #define REQUEST_QUERY_SIZE 6 /* the following packets require */ 313 | #define CHECKSUM_SIZE 2 /* 6 unsigned chars for the packet plus */ 314 | /* 2 for the checksum. */ 315 | 316 | void build_request_packet(int slave, int function, int start_addr, 317 | int count, unsigned char *packet) 318 | { 319 | packet[0] = slave, packet[1] = function; 320 | start_addr -= 1; 321 | packet[2] = start_addr >> 8; 322 | packet[3] = start_addr & 0x00ff; 323 | packet[4] = count >> 8; 324 | packet[5] = count & 0x00ff; 325 | 326 | } 327 | 328 | 329 | 330 | 331 | 332 | 333 | /************************************************************************ 334 | 335 | read_IO_status 336 | 337 | read_coil_stat_query and read_coil_stat_response interigate 338 | a modbus slave to get coil status. An array of coils shall be 339 | set to TRUE or FALSE according to the response from the slave. 340 | 341 | *************************************************************************/ 342 | 343 | int read_IO_status(int function, int slave, int start_addr, int count, 344 | int *dest, int dest_size, int ttyfd) 345 | { 346 | /* local declaration */ 347 | int read_IO_stat_response(int *dest, int dest_size, int coil_count, 348 | unsigned char *query, int fd); 349 | 350 | int status; 351 | 352 | 353 | unsigned char packet[REQUEST_QUERY_SIZE + CHECKSUM_SIZE]; 354 | build_request_packet(slave, function, start_addr, count, packet); 355 | 356 | if (send_query(ttyfd, packet, REQUEST_QUERY_SIZE) > -1) { 357 | status = read_IO_stat_response(dest, dest_size, 358 | count, packet, ttyfd); 359 | } else { 360 | status = PORT_FAILURE; 361 | } 362 | 363 | 364 | 365 | return (status); 366 | } 367 | 368 | 369 | 370 | /************************************************************************ 371 | 372 | read_coil_status 373 | 374 | reads the boolean status of coils and sets the array elements 375 | in the destination to TRUE or FALSE 376 | 377 | *************************************************************************/ 378 | 379 | int read_coil_status(int slave, int start_addr, int count, 380 | int *dest, int dest_size, int ttyfd) 381 | { 382 | int function = 0x01; 383 | int status; 384 | 385 | status = read_IO_status(function, slave, start_addr, count, 386 | dest, dest_size, ttyfd); 387 | 388 | return (status); 389 | } 390 | 391 | 392 | 393 | 394 | 395 | /************************************************************************ 396 | 397 | read_input_status 398 | 399 | same as read_coil_status but reads the slaves input table. 400 | 401 | ************************************************************************/ 402 | 403 | int read_input_status(int slave, int start_addr, int count, 404 | int *dest, int dest_size, int ttyfd) 405 | { 406 | int function = 0x02; /* Function: Read Input Status */ 407 | int status; 408 | 409 | status = read_IO_status(function, slave, start_addr, count, 410 | dest, dest_size, ttyfd); 411 | 412 | return (status); 413 | } 414 | 415 | 416 | 417 | 418 | /************************************************************************** 419 | 420 | read_IO_stat_response 421 | 422 | this function does the work of setting array elements to TRUE 423 | or FALSE. 424 | 425 | **************************************************************************/ 426 | 427 | int read_IO_stat_response(int *dest, int dest_size, int coil_count, 428 | unsigned char *query, int fd) 429 | { 430 | 431 | unsigned char data[MAX_RESPONSE_LENGTH]; 432 | int raw_response_length; 433 | int temp, i, bit, dest_pos = 0; 434 | int coils_processed = 0; 435 | 436 | raw_response_length = modbus_response(data, query, fd); 437 | 438 | 439 | if (raw_response_length > 0) { 440 | for (i = 0; i < (data[2]) && i < dest_size; i++) { 441 | /* shift reg hi_byte to temp */ 442 | temp = data[3 + i]; 443 | for (bit = 0x01; bit & 0xff && 444 | coils_processed < coil_count;) { 445 | if (temp & bit) { 446 | dest[dest_pos] = TRUE; 447 | } else { 448 | dest[dest_pos] = FALSE; 449 | } 450 | coils_processed++; 451 | dest_pos++; 452 | bit = bit << 1; 453 | } 454 | } 455 | } 456 | 457 | return (raw_response_length); 458 | } 459 | 460 | 461 | 462 | 463 | 464 | /************************************************************************ 465 | 466 | read_registers 467 | 468 | read the data from a modbus slave and put that data into an array. 469 | 470 | ************************************************************************/ 471 | 472 | int read_registers(int function, int slave, int start_addr, int count, 473 | int *dest, int dest_size, int ttyfd) 474 | { 475 | /* local declaration */ 476 | int read_reg_response(int *dest, int dest_size, 477 | unsigned char *query, int fd); 478 | 479 | int status; 480 | 481 | 482 | unsigned char packet[REQUEST_QUERY_SIZE + CHECKSUM_SIZE]; 483 | build_request_packet(slave, function, start_addr, count, packet); 484 | 485 | if (send_query(ttyfd, packet, REQUEST_QUERY_SIZE) > -1) { 486 | status = read_reg_response(dest, dest_size, packet, ttyfd); 487 | } else { 488 | status = PORT_FAILURE; 489 | } 490 | 491 | return (status); 492 | 493 | } 494 | 495 | 496 | 497 | /************************************************************************ 498 | 499 | read_holding_registers 500 | 501 | Read the holding registers in a slave and put the data into 502 | an array. 503 | 504 | *************************************************************************/ 505 | 506 | int read_holding_registers(int slave, int start_addr, int count, 507 | int *dest, int dest_size, int ttyfd) 508 | { 509 | int function = 0x03; /* Function: Read Holding Registers */ 510 | int status; 511 | 512 | if (count > MAX_READ_REGS) { 513 | count = MAX_READ_REGS; 514 | #ifdef DEBUG 515 | fprintf(stderr, "Too many registers requested.\n"); 516 | #endif 517 | } 518 | 519 | status = read_registers(function, slave, start_addr, count, 520 | dest, dest_size, ttyfd); 521 | 522 | return (status); 523 | } 524 | 525 | 526 | 527 | 528 | 529 | /************************************************************************ 530 | 531 | read_input_registers 532 | 533 | Read the inputg registers in a slave and put the data into 534 | an array. 535 | 536 | *************************************************************************/ 537 | 538 | int read_input_registers(int slave, int start_addr, int count, 539 | int *dest, int dest_size, int ttyfd) 540 | { 541 | int function = 0x04; /* Function: Read Input Reqisters */ 542 | int status; 543 | 544 | if (count > MAX_INPUT_REGS) { 545 | count = MAX_INPUT_REGS; 546 | #ifdef DEBUG 547 | fprintf(stderr, "Too many input registers requested.\n"); 548 | #endif 549 | } 550 | 551 | status = read_registers(function, slave, start_addr, count, 552 | dest, dest_size, ttyfd); 553 | 554 | return (status); 555 | } 556 | 557 | 558 | 559 | 560 | 561 | /************************************************************************ 562 | 563 | read_reg_response 564 | 565 | reads the response data from a slave and puts the data into an 566 | array. 567 | 568 | ************************************************************************/ 569 | 570 | int read_reg_response(int *dest, int dest_size, unsigned char *query, 571 | int fd) 572 | { 573 | 574 | unsigned char data[MAX_RESPONSE_LENGTH]; 575 | int raw_response_length; 576 | int temp, i; 577 | 578 | 579 | 580 | raw_response_length = modbus_response(data, query, fd); 581 | if (raw_response_length > 0) 582 | raw_response_length -= 2; 583 | 584 | 585 | if (raw_response_length > 0) { 586 | for (i = 0; 587 | i < (data[2] * 2) && i < (raw_response_length / 2); 588 | i++) { 589 | /* shift reg hi_byte to temp */ 590 | temp = data[3 + i * 2] << 8; 591 | /* OR with lo_byte */ 592 | temp = temp | data[4 + i * 2]; 593 | 594 | dest[i] = temp; 595 | } 596 | } 597 | return (raw_response_length); 598 | } 599 | 600 | 601 | 602 | 603 | 604 | /*********************************************************************** 605 | 606 | preset_response 607 | 608 | Gets the raw data from the input stream. 609 | 610 | ***********************************************************************/ 611 | 612 | int preset_response(unsigned char *query, int fd) 613 | { 614 | unsigned char data[MAX_RESPONSE_LENGTH]; 615 | int raw_response_length; 616 | 617 | raw_response_length = modbus_response(data, query, fd); 618 | 619 | return (raw_response_length); 620 | } 621 | 622 | 623 | 624 | 625 | 626 | 627 | /************************************************************************* 628 | 629 | set_single 630 | 631 | sends a value to a register in a slave. 632 | 633 | **************************************************************************/ 634 | 635 | int set_single(int function, int slave, int addr, int value, int fd) 636 | { 637 | 638 | int status; 639 | 640 | unsigned char packet[REQUEST_QUERY_SIZE]; 641 | packet[0] = slave; 642 | packet[1] = function; 643 | addr -= 1; 644 | packet[2] = addr >> 8; 645 | packet[3] = addr & 0x00FF; 646 | packet[4] = value >> 8; 647 | packet[5] = value & 0x00FF; 648 | 649 | if (send_query(fd, packet, 6) > -1) { 650 | status = preset_response(packet, fd); 651 | } else { 652 | status = PORT_FAILURE; 653 | } 654 | 655 | return (status); 656 | } 657 | 658 | 659 | 660 | 661 | 662 | 663 | /************************************************************************* 664 | 665 | force_single_coil 666 | 667 | turn on or off a single coil on the slave device 668 | 669 | *************************************************************************/ 670 | 671 | int force_single_coil(int slave, int coil_addr, int state, int fd) 672 | { 673 | int function = 0x05; 674 | int status; 675 | 676 | if (state) 677 | state = 0xFF00; 678 | 679 | status = set_single(function, slave, coil_addr, state, fd); 680 | 681 | return (status); 682 | } 683 | 684 | 685 | 686 | 687 | 688 | /************************************************************************* 689 | 690 | preset_single_register 691 | 692 | sets a value in one holding register in the slave device 693 | 694 | *************************************************************************/ 695 | 696 | int preset_single_register(int slave, int reg_addr, int value, int fd) 697 | { 698 | int function = 0x06; 699 | int status; 700 | 701 | status = set_single(function, slave, reg_addr, value, fd); 702 | 703 | return (status); 704 | } 705 | 706 | 707 | 708 | 709 | 710 | /************************************************************************ 711 | 712 | set_multiple_coils 713 | 714 | Takes an array of ints and sets or resets the coils on a slave 715 | appropriatly. 716 | 717 | *************************************************************************/ 718 | 719 | #define PRESET_QUERY_SIZE 210 720 | 721 | int set_multiple_coils(int slave, int start_addr, int coil_count, 722 | int *data, int fd) 723 | { 724 | int byte_count; 725 | int i, bit, packet_size = 6; 726 | int coil_check = 0; 727 | int data_array_pos = 0; 728 | int status; 729 | 730 | unsigned char packet[PRESET_QUERY_SIZE]; 731 | 732 | if (coil_count > MAX_WRITE_COILS) { 733 | coil_count = MAX_WRITE_COILS; 734 | #ifdef DEBUG 735 | fprintf(stderr, "Writing to too many coils.\n"); 736 | #endif 737 | } 738 | packet[0] = slave; 739 | packet[1] = 0x0F; 740 | start_addr -= 1; 741 | packet[2] = start_addr >> 8; 742 | packet[3] = start_addr & 0x00FF; 743 | packet[4] = coil_count >> 8; 744 | packet[5] = coil_count & 0x00FF; 745 | byte_count = (coil_count / 8) + 1; 746 | packet[6] = byte_count; 747 | 748 | bit = 0x01; 749 | 750 | for (i = 0; i < byte_count; i++) { 751 | packet[++packet_size] = 0; 752 | 753 | while (bit & 0xFF && coil_check++ < coil_count) { 754 | if (data[data_array_pos++]) { 755 | packet[packet_size] |= bit; 756 | } else { 757 | packet[packet_size] &= ~bit; 758 | } 759 | bit = bit << 1; 760 | } 761 | bit = 0x01; 762 | } 763 | 764 | if (send_query(fd, packet, ++packet_size) > -1) { 765 | status = preset_response(packet, fd); 766 | } else { 767 | status = PORT_FAILURE; 768 | } 769 | 770 | return (status); 771 | } 772 | 773 | 774 | 775 | 776 | 777 | /************************************************************************* 778 | 779 | preset_multiple_registers 780 | 781 | copy the values in an array to an array on the slave. 782 | 783 | ***************************************************************************/ 784 | 785 | int preset_multiple_registers(int slave, int start_addr, 786 | int reg_count, int *data, int fd) 787 | { 788 | int byte_count, i, packet_size = 6; 789 | int status; 790 | 791 | unsigned char packet[PRESET_QUERY_SIZE]; 792 | 793 | if (reg_count > MAX_WRITE_REGS) { 794 | reg_count = MAX_WRITE_REGS; 795 | #ifdef DEBUG 796 | fprintf(stderr, 797 | "Trying to write to too many registers.\n"); 798 | #endif 799 | } 800 | 801 | packet[0] = slave; 802 | packet[1] = 0x10; 803 | start_addr -= 1; 804 | packet[2] = start_addr >> 8; 805 | packet[3] = start_addr & 0x00FF; 806 | packet[4] = reg_count >> 8; 807 | packet[5] = reg_count & 0x00FF; 808 | byte_count = reg_count * 2; 809 | packet[6] = byte_count; 810 | 811 | for (i = 0; i < reg_count; i++) { 812 | packet[++packet_size] = data[i] >> 8; 813 | packet[++packet_size] = data[i] & 0x00FF; 814 | } 815 | 816 | if (send_query(fd, packet, ++packet_size) > -1) { 817 | status = preset_response(packet, fd); 818 | } else { 819 | status = PORT_FAILURE; 820 | } 821 | 822 | return (status); 823 | } 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | /**************************************************************************** 836 | ***************************** [ BEGIN: crc ] ****************************** 837 | ***************************************************************************** 838 | INPUTS: 839 | buf -> Array containing message to be sent to controller. start -> Start of loop in crc counter, usually 0. 840 | cnt -> Amount of bytes in message being sent to controller/ 841 | OUTPUTS: 842 | temp -> Returns crc byte for message. 843 | COMMENTS: 844 | This routine calculates the crc high and low byte of a message. 845 | Note that this crc is only used for Modbus, not Modbus+ etc. 846 | ****************************************************************************/ 847 | 848 | unsigned int crc(unsigned char *buf, int start, int cnt) 849 | { 850 | int i, j; 851 | unsigned temp, temp2, flag; 852 | 853 | temp = 0xFFFF; 854 | #ifdef DEBUG_CHITO 855 | fprintf(stderr, "crc: string length %d\n", cnt); 856 | #endif 857 | for (i = start; i < cnt; i++) { 858 | temp = temp ^ buf[i]; 859 | 860 | for (j = 1; j <= 8; j++) { 861 | flag = temp & 0x0001; 862 | temp = temp >> 1; 863 | if (flag) 864 | temp = temp ^ 0xA001; 865 | } 866 | } 867 | 868 | /* Reverse byte order. */ 869 | 870 | temp2 = temp >> 8; 871 | temp = (temp << 8) | temp2; 872 | temp &= 0xFFFF; 873 | 874 | #ifdef DEBUG_CHITO 875 | fprintf(stderr, "crc: temp crc %0.2X\n", temp); 876 | #endif 877 | 878 | return (temp); 879 | } 880 | 881 | 882 | 883 | /************************************************************************ 884 | 885 | set_up_comms 886 | 887 | This function sets up a serial port for RTU communications to 888 | modbus. 889 | 890 | **************************************************************************/ 891 | 892 | int set_up_comms(char *device, int baud_i, char *parity) 893 | { 894 | int ttyfd; 895 | struct termios settings; 896 | int k, n, status; // jpz 897 | 898 | speed_t baud_rate; 899 | 900 | #ifdef DEBUG 901 | fprintf(stderr, "opening %s\n", device); 902 | #endif 903 | 904 | switch (baud_i) { 905 | case 110: 906 | baud_rate = B110; 907 | char_interval_timeout = TO_B110; 908 | break; 909 | case 300: 910 | baud_rate = B300; 911 | char_interval_timeout = TO_B300; 912 | break; 913 | case 600: 914 | baud_rate = B600; 915 | char_interval_timeout = TO_B600; 916 | break; 917 | case 1200: 918 | baud_rate = B1200; 919 | char_interval_timeout = TO_B1200; 920 | break; 921 | case 2400: 922 | baud_rate = B2400; 923 | char_interval_timeout = TO_B2400; 924 | break; 925 | case 4800: 926 | baud_rate = B4800; 927 | char_interval_timeout = TO_B4800; 928 | break; 929 | case 9600: 930 | case 0: 931 | baud_rate = B9600; 932 | char_interval_timeout = TO_B9600; 933 | break; 934 | case 19200: 935 | baud_rate = B19200; 936 | char_interval_timeout = TO_B19200; 937 | break; 938 | case 38400: 939 | baud_rate = B38400; 940 | char_interval_timeout = TO_B38400; 941 | break; 942 | case 57600: 943 | baud_rate = B57600; 944 | char_interval_timeout = TO_B57600; 945 | break; 946 | case 115200: 947 | baud_rate = B115200; 948 | char_interval_timeout = TO_B115200; 949 | break; 950 | default: 951 | baud_rate = B9600; 952 | char_interval_timeout = TO_B9600; 953 | fprintf(stderr, "Unknown baud rate %d for %s.", baud_i, 954 | device); 955 | } 956 | 957 | 958 | 959 | if ((ttyfd = open(device, O_RDWR)) < 0) { 960 | fprintf(stderr, "Error opening device %s. ", device); 961 | 962 | fprintf(stderr, "Error no. %d \n", errno); 963 | exit(1); /* stop the program. This maybe should */ 964 | /* be a bit kinder but will do for now. */ 965 | } 966 | #ifdef DEBUG 967 | fprintf(stderr, "%s open\n", device); 968 | #endif 969 | 970 | /* read your man page for the meaning of all this. # man termios */ 971 | /* Its a bit to involved to comment here */ 972 | 973 | 974 | cfsetispeed(&settings, baud_rate); /* Set the baud rate */ 975 | cfsetospeed(&settings, baud_rate); 976 | 977 | settings.c_line = 0; 978 | 979 | settings.c_iflag |= IGNBRK; 980 | settings.c_iflag |= IGNPAR; 981 | settings.c_iflag &= ~PARMRK; 982 | settings.c_iflag &= ~INPCK; 983 | settings.c_iflag &= ~ISTRIP; 984 | settings.c_iflag &= ~INLCR; 985 | settings.c_iflag &= ~IGNCR; 986 | settings.c_iflag &= ~ICRNL; 987 | settings.c_iflag &= ~IUCLC; 988 | settings.c_iflag &= ~IXON; 989 | settings.c_iflag |= IXANY; 990 | settings.c_iflag &= ~IXOFF; 991 | settings.c_iflag &= ~IMAXBEL; 992 | 993 | settings.c_oflag |= OPOST; 994 | settings.c_oflag &= ~OLCUC; 995 | settings.c_oflag &= ~ONLCR; 996 | settings.c_oflag &= ~OCRNL; 997 | settings.c_oflag |= ONOCR; 998 | settings.c_oflag &= ~ONLRET; 999 | settings.c_oflag &= ~OFILL; 1000 | settings.c_oflag &= ~OFDEL; 1001 | 1002 | settings.c_cflag &= ~CSIZE; 1003 | settings.c_cflag |= CS8; 1004 | settings.c_cflag &= ~CSTOPB; 1005 | settings.c_cflag |= CREAD; 1006 | 1007 | if (strncmp(parity, "none", 4) == 0) { 1008 | settings.c_cflag &= ~PARENB; 1009 | settings.c_cflag &= ~PARODD; 1010 | } else if (strncmp(parity, "even", 4) == 0) { 1011 | settings.c_cflag |= PARENB; 1012 | settings.c_cflag &= ~PARODD; 1013 | } else { 1014 | settings.c_cflag |= PARENB; 1015 | settings.c_cflag |= PARODD; 1016 | } 1017 | 1018 | settings.c_cflag &= ~HUPCL; 1019 | settings.c_cflag |= CLOCAL; 1020 | settings.c_cflag &= ~CRTSCTS; 1021 | 1022 | settings.c_lflag &= ~ISIG; 1023 | settings.c_lflag &= ~ICANON; 1024 | settings.c_lflag &= ~ECHO; 1025 | settings.c_lflag |= IEXTEN; 1026 | 1027 | settings.c_cc[VMIN] = 0; 1028 | settings.c_cc[VTIME] = 0; 1029 | 1030 | if (tcsetattr(ttyfd, TCSANOW, &settings) < 0) { 1031 | fprintf(stderr, "tcsetattr failed\n"); 1032 | exit(1); 1033 | } 1034 | 1035 | return (ttyfd); 1036 | } 1037 | -------------------------------------------------------------------------------- /modbus_rtu.h: -------------------------------------------------------------------------------- 1 | /* modbus_rtu.h 2 | 3 | By P.Costigan email: phil@pcscada.com.au http://pcscada.com.au 4 | 5 | These library of functions are designed to enable a program send and 6 | receive data from a device that communicates using the Modbus protocol. 7 | 8 | Copyright (C) 2000 Philip Costigan P.C. SCADA LINK PTY. LTD. 9 | 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software 22 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 | */ 24 | 25 | 26 | #ifndef MODBUS_RTU_H 27 | #define MODBUS_RTU_H 28 | 29 | #define MAX_DATA_LENGTH 246 30 | #define MAX_QUERY_LENGTH 256 31 | #define MAX_RESPONSE_LENGTH 256 32 | 33 | 34 | /*********************************************************************** 35 | 36 | Note: All functions used for sending or receiving data via 37 | modbus return these return values. 38 | 39 | 40 | Returns: string_length if OK 41 | 0 if failed 42 | Less than 0 for exception errors 43 | 44 | ***********************************************************************/ 45 | 46 | #define COMMS_FAILURE 0 47 | #define ILLEGAL_FUNCTION -1 48 | #define ILLEGAL_DATA_ADDRESS -2 49 | #define ILLEGAL_DATA_VALUE -3 50 | #define SLAVE_DEVICE_FAILURE -4 51 | #define ACKNOWLEDGE -5 52 | #define SLAVE_DEVICE_BUSY -6 53 | #define NEGATIVE_ACKNOWLEDGE -7 54 | #define MEMORY_PARITY_ERROR -8 55 | 56 | #define PORT_FAILURE -11 57 | 58 | 59 | 60 | /************************************************************************ 61 | 62 | read_coil_status() 63 | 64 | reads the boolean status of coils and sets the array elements 65 | in the destination to TRUE or FALSE. 66 | 67 | *************************************************************************/ 68 | 69 | int read_coil_status( int slave, int start_addr, int count, 70 | int *dest, int dest_size, int fd ); 71 | 72 | 73 | 74 | 75 | 76 | /************************************************************************ 77 | 78 | read_input_status() 79 | 80 | same as read_coil_status but reads the slaves input table. 81 | 82 | ************************************************************************/ 83 | 84 | int read_input_status( int slave, int start_addr, int count, 85 | int *dest, int dest_size, int fd ); 86 | 87 | 88 | 89 | 90 | 91 | /*********************************************************************** 92 | 93 | read_holding_registers() 94 | 95 | Read the holding registers in a slave and put the data into 96 | an array. 97 | 98 | ************************************************************************/ 99 | 100 | #define MAX_READ_REGS 100 101 | 102 | int read_holding_registers( int slave, int start_addr, int count, 103 | int *dest, int dest_size, int fd ); 104 | 105 | 106 | 107 | 108 | /*********************************************************************** 109 | 110 | read_input_registers() 111 | 112 | Read the inputg registers in a slave and put the data into 113 | an array. 114 | 115 | ***********************************************************************/ 116 | 117 | #define MAX_INPUT_REGS 100 118 | 119 | int read_input_registers( int slave, int start_addr, int count, 120 | int *dest, int dest_size, int fd ); 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | /************************************************************************ 129 | 130 | force_single_coil() 131 | 132 | turn on or off a single coil on the slave device. 133 | 134 | ************************************************************************/ 135 | 136 | int force_single_coil( int slave, int addr, int state, int fd ); 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | /************************************************************************* 145 | 146 | preset_single_register() 147 | 148 | sets a value in one holding register in the slave device. 149 | 150 | *************************************************************************/ 151 | 152 | int preset_single_register( int slave, int reg_addr, int value, int fd ); 153 | 154 | 155 | 156 | 157 | 158 | 159 | /************************************************************************* 160 | 161 | set_multiple_coils() 162 | 163 | Takes an array of ints and sets or resets the coils on a slave 164 | appropriatly. 165 | 166 | **************************************************************************/ 167 | 168 | #define MAX_WRITE_COILS 800 169 | 170 | int set_multiple_coils( int slave, int start_addr, 171 | int coil_count, int *data, int fd ); 172 | 173 | 174 | 175 | 176 | 177 | 178 | /************************************************************************* 179 | 180 | preset_multiple_registers() 181 | 182 | copy the values in an array to an array on the slave. 183 | 184 | *************************************************************************/ 185 | 186 | #define MAX_WRITE_REGS 100 187 | 188 | int preset_multiple_registers( int slave, int start_addr, 189 | int reg_count, int *data, int fd ); 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | /*************************************************************************** 199 | 200 | set_up_comms 201 | 202 | This function sets up a serial port for RTU communications to 203 | modbus. 204 | 205 | 206 | ***************************************************************************/ 207 | 208 | int set_up_comms( char *device, int baud, char *parity ); 209 | /* baud should be the plain baud rate, eg 2400; zero for the default 9600. 210 | * If an unsupported baud rate is specified, prints a message to stderr and 211 | * uses 9600. */ 212 | 213 | 214 | #define TO_B110 3200000 /* These values are the timeout delays */ 215 | #define TO_B300 1600000 /* at the end of packets of data. */ 216 | #define TO_B600 800000 /* At this stage a true calculation */ 217 | #define TO_B1200 400000 /* has not been worked out. So these */ 218 | #define TO_B2400 200000 /* values are just a guess. */ 219 | #define TO_B4800 100000 /* */ 220 | #define TO_B9600 50000 /* The spec says that a message frame */ 221 | #define TO_B19200 25000 /* starts after a silent interval of */ 222 | #define TO_B38400 12500 /* at least 3.5 character times. */ 223 | #define TO_B57600 8333 /* These are uS times. */ 224 | #define TO_B115200 4167 225 | 226 | 227 | 228 | 229 | #endif /* MODBUS_RTU_H */ 230 | --------------------------------------------------------------------------------