Lite kort hur detta bibliotek fungerar.
Steg 1:
Först anropas denna funktion
Koden som körs är denna. Vad denna gör är att den ska läsa ett meddelande för att kunna ta ett beslut.
Kod: Markera allt
bool modbus_polling(){
if(nmbs_server){
nmbs_error err = nmbs_server_poll(nmbs_server);
if (err != NMBS_ERROR_NONE) {
return false;
}
return true;
}
return false;
}
Steg 2:
Denna kod är själva polling-funktionen. Den läser och sedan skriver den
Kod: Markera allt
nmbs_error nmbs_server_poll(nmbs_t* nmbs) {
msg_state_reset(nmbs);
bool first_byte_received = false;
nmbs_error err = recv_req_header(nmbs, &first_byte_received);
if (err != NMBS_ERROR_NONE) {
if (!first_byte_received && err == NMBS_ERROR_TIMEOUT)
return NMBS_ERROR_NONE;
return err;
}
#ifdef NMBS_DEBUG
printf("%d ", nmbs->address_rtu);
printf("NMBS req <- ");
if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
if (nmbs->msg.broadcast)
printf("broadcast\t");
else
printf("address_rtu %d\t", nmbs->msg.unit_id);
}
#endif
err = handle_req_fc(nmbs);
if (err != NMBS_ERROR_NONE) {
if (err != NMBS_ERROR_TIMEOUT)
flush(nmbs);
return err;
}
return NMBS_ERROR_NONE;
}
Steg 3:
Låt oss börja först med läsningen. Den ska alltså läsa en header. Troligtvis är det en "data-frame". Vi går vidare in till funktionen
recv_msg_header.
Kod: Markera allt
static nmbs_error recv_req_header(nmbs_t* nmbs, bool* first_byte_received) {
nmbs_error err = recv_msg_header(nmbs, first_byte_received);
if (err != NMBS_ERROR_NONE)
return err;
if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
// Check if request is for us
if (nmbs->msg.unit_id == NMBS_BROADCAST_ADDRESS)
nmbs->msg.broadcast = true;
else if (nmbs->msg.unit_id != nmbs->address_rtu)
nmbs->msg.ignored = true;
else
nmbs->msg.ignored = false;
}
return NMBS_ERROR_NONE;
}
Steg 4:
Här är koden för att läsa. Det denna kod gör är att den läser ID och FUNKTION i dataramen. Alltså två byte. Den är något försiktig dock när den läser då den läser via
get_1 som betyder "get one byte". För varje "byte" den har läst, så "hoppar" den ett steg. Funktionen
recv(nmbs, 1); anropar alltså den där UART funktionen som du har ovan med storleken
size = 1.
Kod: Markera allt
static nmbs_error recv_msg_header(nmbs_t* nmbs, bool* first_byte_received) {
// We wait for the read timeout here, just for the first message byte
int32_t old_byte_timeout = nmbs->byte_timeout_ms;
nmbs->byte_timeout_ms = nmbs->read_timeout_ms;
msg_state_reset(nmbs);
*first_byte_received = false;
if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
nmbs_error err = recv(nmbs, 1);
nmbs->byte_timeout_ms = old_byte_timeout;
if (err != NMBS_ERROR_NONE)
return err;
*first_byte_received = true;
nmbs->msg.unit_id = get_1(nmbs);
err = recv(nmbs, 1);
if (err != NMBS_ERROR_NONE)
return err;
nmbs->msg.fc = get_1(nmbs);
}
else if (nmbs->platform.transport == NMBS_TRANSPORT_TCP) {
Denna kod är bortklippt för den visar bara TCP-IP.
}
return NMBS_ERROR_NONE;
}
Steg 5:
Efter vi har fått våra ID byte och FUNKTION byte. En sak förstår jag inte varför
nmbs->msg.unit_id har alltid en fixerad broadcast adress på 0 så fort vi läser ett meddelande.
Då är vill tillbaka till denna kod igen
Kod: Markera allt
static nmbs_error recv_req_header(nmbs_t* nmbs, bool* first_byte_received) {
nmbs_error err = recv_msg_header(nmbs, first_byte_received);
if (err != NMBS_ERROR_NONE)
return err;
if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
// Check if request is for us
if (nmbs->msg.unit_id == NMBS_BROADCAST_ADDRESS)
nmbs->msg.broadcast = true;
else if (nmbs->msg.unit_id != nmbs->address_rtu)
nmbs->msg.ignored = true;
else
nmbs->msg.ignored = false;
}
return NMBS_ERROR_NONE;
}
Steg 6:
Vi är tillbaka till denna kod igen och nu ska vi läsa våran funktionskod. Detta gör via via
handle_req_fc(nmbs);
Kod: Markera allt
nmbs_error nmbs_server_poll(nmbs_t* nmbs) {
msg_state_reset(nmbs);
bool first_byte_received = false;
nmbs_error err = recv_req_header(nmbs, &first_byte_received);
if (err != NMBS_ERROR_NONE) {
if (!first_byte_received && err == NMBS_ERROR_TIMEOUT)
return NMBS_ERROR_NONE;
return err;
}
#ifdef NMBS_DEBUG
printf("%d ", nmbs->address_rtu);
printf("NMBS req <- ");
if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
if (nmbs->msg.broadcast)
printf("broadcast\t");
else
printf("address_rtu %d\t", nmbs->msg.unit_id);
}
#endif
err = handle_req_fc(nmbs);
if (err != NMBS_ERROR_NONE) {
if (err != NMBS_ERROR_TIMEOUT)
flush(nmbs);
return err;
}
return NMBS_ERROR_NONE;
}
Steg 7:
Nu ska vi avgöra vad vi ska ta för beslut. Om vi har FUNKTION = 4 så betyder det att vi ska skicka ett register. Då anropar vi
err = handle_read_input_registers(nmbs);
Kod: Markera allt
static nmbs_error handle_req_fc(nmbs_t* nmbs) {
NMBS_DEBUG_PRINT("fc %d\t", nmbs->msg.fc);
nmbs_error err = NMBS_ERROR_NONE;
switch (nmbs->msg.fc) {
#ifndef NMBS_SERVER_READ_COILS_DISABLED
case 1:
err = handle_read_coils(nmbs);
break;
#endif
#ifndef NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED
case 2:
err = handle_read_discrete_inputs(nmbs);
break;
#endif
#ifndef NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED
case 3:
err = handle_read_holding_registers(nmbs);
break;
#endif
#ifndef NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED
case 4:
err = handle_read_input_registers(nmbs);
break;
#endif
#ifndef NMBS_SERVER_WRITE_SINGLE_COIL_DISABLED
case 5:
err = handle_write_single_coil(nmbs);
break;
#endif
#ifndef NMBS_SERVER_WRITE_SINGLE_REGISTER_DISABLED
case 6:
err = handle_write_single_register(nmbs);
break;
#endif
#ifndef NMBS_SERVER_WRITE_MULTIPLE_COILS_DISABLED
case 15:
err = handle_write_multiple_coils(nmbs);
break;
#endif
#ifndef NMBS_SERVER_WRITE_MULTIPLE_REGISTERS_DISABLED
case 16:
err = handle_write_multiple_registers(nmbs);
break;
#endif
#ifndef NMBS_SERVER_READ_FILE_RECORD_DISABLED
case 20:
err = handle_read_file_record(nmbs);
break;
#endif
#ifndef NMBS_SERVER_WRITE_FILE_RECORD_DISABLED
case 21:
err = handle_write_file_record(nmbs);
break;
#endif
#ifndef NMBS_SERVER_READ_WRITE_REGISTERS_DISABLED
case 23:
err = handle_read_write_registers(nmbs);
break;
#endif
#ifndef NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED
case 43:
err = handle_read_device_identification(nmbs);
break;
#endif
default:
flush(nmbs);
if (!nmbs->msg.ignored && !nmbs->msg.broadcast)
err = send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
}
return err;
}
Steg 8:
Här är liksom slutpunkten för meddelandet. Om inte
if (!nmbs->msg.broadcast) kör så blir det en timeout.
Kod: Markera allt
#if !defined(NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED) || !defined(NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED)
static nmbs_error handle_read_registers(nmbs_t* nmbs,
nmbs_error (*callback)(uint16_t, uint16_t, uint16_t*, uint8_t, void*)) {
nmbs_error err = recv(nmbs, 4);
if (err != NMBS_ERROR_NONE)
return err;
uint16_t address = get_2(nmbs);
uint16_t quantity = get_2(nmbs);
NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
err = recv_msg_footer(nmbs);
if (err != NMBS_ERROR_NONE)
return err;
if (!nmbs->msg.ignored) {
if (quantity < 1 || quantity > 125)
return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
if (callback) {
uint16_t regs[125] = {0};
err = callback(address, quantity, regs, nmbs->msg.unit_id, nmbs->callbacks.arg);
if (err != NMBS_ERROR_NONE) {
if (nmbs_error_is_exception(err))
return send_exception_msg(nmbs, err);
return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
}
// TODO check all these read request broadcast use cases
if (!nmbs->msg.broadcast) {
uint8_t regs_bytes = quantity * 2;
put_res_header(nmbs, 1 + regs_bytes);
put_1(nmbs, regs_bytes);
NMBS_DEBUG_PRINT("b %d\t", regs_bytes);
NMBS_DEBUG_PRINT("regs ");
for (int i = 0; i < quantity; i++) {
put_2(nmbs, regs[i]);
NMBS_DEBUG_PRINT("%d ", regs[i]);
}
err = send_msg(nmbs);
if (err != NMBS_ERROR_NONE)
return err;
}else{
int a = 0; /* Broroad cast! */
}
}
else {
return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
}
}
else {
return recv_read_registers_res(nmbs, quantity, NULL);
}
return NMBS_ERROR_NONE;
}