static void retransmit_current_message(modbus* bus) { modbus_message* message; list_peek_first(&bus->messageQueue, (void**)&message); if(--message->remainingTransmissionAttempts > 0) // retransmit message or drop it? { uint32_t compensatedReponseTimeout; if(message->deferredRetransmissions) { if(list_count(&bus->messageQueue) > 1) // should a deferred retransmission be attempted and does it make any sense to defer (only makes sense if more than one packet is in the queue) { if(list_append(&bus->messageQueue, message)) // add to end of queue { list_remove(&bus->messageQueue, message); // remove current/first instance of message in transfer queue message->state = MODBUS_MESSAGE_STATE_QUEUED; bus->state = BUS_STATE_IDLE; NABTO_LOG_TRACE(("Deferring retransmission of query (bus=%u, query=%u).", bus->identifier, (int)message)); return; } bus->state = BUS_STATE_IDLE; NABTO_LOG_TRACE(("Unable to defer query - retransmitting immediately (bus=%u, query=%u)!", bus->identifier, (int)message)); } else { NABTO_LOG_TRACE(("Query allowed deferrence but transfer queue is empty (bus=%u, query=%u).", bus->identifier, (int)message)); } } // retransmit immediately (also used as fallback if deferred retransmission fails) compensatedReponseTimeout = (uint32_t)message->maximumResponsetime + calculate_transmission_time(message->frameSize); uart_flush_receiver(bus->uartChannel); uart_write_buffer(bus->uartChannel, message->frame, message->frameSize); nabtoSetFutureStamp(&bus->responseTimeout, compensatedReponseTimeout); // time to wait for start of response: transmission time of outgoing frame + maximum response time bus->responseSize = 0; bus->responseOverrun = false; NABTO_LOG_TRACE(("Retransmitting query (bus=%u, remaining attempts=%u).", bus->identifier, (int)message->remainingTransmissionAttempts)); } else { // mark message as failed message->state = MODBUS_MESSAGE_STATE_FAILED; // move message from transfer queue to completed list list_remove(&bus->messageQueue, message); list_append(&completedList, message); bus->state = BUS_STATE_IDLE; NABTO_LOG_TRACE(("Dropped query due too many retransmissions (bus=%u, message=%u).", bus->identifier, (int)message)); } }
int fastcall write(int hnd, const void *buf, unsigned count) { static BYTE *b; static int c; b = buf; c = (int)count; hnd = hnd; if(!screen_initialized) return uart_write_buffer(buf, count); while(c) { char_out(*b); b++; c--; } return count; }
void modbus_rtu_master_tick(void) { modbus* bus; // tick all busses for(bus = busses; bus < (busses + MODBUS_NUMBER_OF_BUSSES); bus++) { modbus_state oldState; oldState = bus->state; switch(bus->state) { case BUS_STATE_IDLE: { modbus_message* message; if(list_peek_first(&bus->messageQueue, (void**)&message)) // is there a message waiting to be sent { if(message->state == MODBUS_MESSAGE_STATE_DISCARDED) // has message been discared by the application while waiting in the queue? { list_remove(&bus->messageQueue, message); modbus_rtu_master_release_message(message); NABTO_LOG_TRACE(("Completing discard request (bus=%u, query=%u).", bus->identifier, (int)message)); } else // start transferring the message { uint32_t compensatedReponseTime = message->maximumResponsetime + calculate_transmission_time(message->frameSize); NABTO_LOG_TRACE(("Sending query (bus=%u, timeout=%u, compensated timeout=%u).", bus->identifier, (int)message->maximumResponsetime, (int)compensatedReponseTime)); uart_flush_receiver(bus->uartChannel); uart_write_buffer(bus->uartChannel, message->frame, message->frameSize); message->state = MODBUS_MESSAGE_STATE_TRANSFERRING; nabtoSetFutureStamp(&bus->responseTimeout, compensatedReponseTime); // time to wait for start of response: transmission time of outgoing frame + maximum response time bus->responseSize = 0; bus->responseOverrun = false; bus->state = BUS_STATE_BUSY; NABTO_LOG_TRACE(("Query sent (bus=%u, timeout=%u, compensated timeout=%u).", bus->identifier, (int)message->maximumResponsetime, (int)compensatedReponseTime)); NABTO_LOG_BUFFER(NABTO_LOG_SEVERITY_LEVEL_TRACE, ("Query (length=%u): (shown with CRC)", (int)message->frameSize), message->frame, message->frameSize); } } } break; case BUS_STATE_BUSY: { uint16_t length; length = uart_can_read(bus->uartChannel); if(length > 0) // has data been received? { if(bus->responseOverrun == false) // ignore all data after an overrun has occurred { if((bus->responseSize + length) <= MODBUS_MAXIMUM_FRAME_SIZE) // is there room for the data? { // add data to response buffer uart_read_buffer(bus->uartChannel, bus->response + bus->responseSize, length); bus->responseSize += length; } else // response buffer overrun { uart_flush_receiver(bus->uartChannel); // discard data bus->responseOverrun = true; // mark response as over size NABTO_LOG_TRACE(("Receiving oversize response (bus=%u, oversize length=%u)!", bus->identifier, (int)((bus->responseSize + length) - MODBUS_MAXIMUM_FRAME_SIZE))); } } else { uart_flush_receiver(bus->uartChannel); // discard data NABTO_LOG_TRACE(("Dumping overrun data (bus=%u, length=%u)!", bus->identifier, (int)length)); } nabtoSetFutureStamp(&bus->frameCommitTimeout, FRAME_SILENT_DURATION); // reset packet commit timer } else if(bus->responseSize > 0) // has the response begun { if(nabtoIsStampPassed(&bus->frameCommitTimeout)) // has the frame ended { modbus_message* message; list_peek_first(&bus->messageQueue, (void**)&message); if(message->state == MODBUS_MESSAGE_STATE_DISCARDED) // has the message been discarded { list_remove(&bus->messageQueue, message); modbus_rtu_master_release_message(message); // perform actual release of discarded message bus->state = BUS_STATE_IDLE; NABTO_LOG_TRACE(("Received response for dumped query (bus=%u, query=%u, response length=%u).", bus->identifier, (int)message, (int)bus->responseSize)); } else { if(modbus_rtu_crc_verify_crc_field(bus->response, bus->responseSize)) // does reponse pass CRC check? { NABTO_LOG_BUFFER(NABTO_LOG_SEVERITY_LEVEL_TRACE, ("Received response (bus=%u, length=%u): (shown with CRC)", bus->identifier, (int)bus->responseSize), bus->response, bus->responseSize); // replace the query in the message with the response (removing CRC) memcpy(message->frame, bus->response, bus->responseSize - 2); message->frameSize = bus->responseSize - 2; message->state = MODBUS_MESSAGE_STATE_COMPLETED; // mark message as completed // move message from transfer queue to completed list list_remove(&bus->messageQueue, message); list_append(&completedList, message); bus->state = BUS_STATE_IDLE; NABTO_LOG_TRACE(("Received response for query (bus=%u, query=%u).", bus->identifier, (int)message)); } else { NABTO_LOG_TRACE(("Received bad response for query (bus=%u, query=%u)!", bus->identifier, (int)message)); retransmit_current_message(bus); } } } } else if(nabtoIsStampPassed(&bus->responseTimeout)) // the response has not begun within the time limit { modbus_message* message; list_peek_first(&bus->messageQueue, (void**)&message); NABTO_LOG_TRACE(("No response received (bus=%u, message=%u)!", bus->identifier, (int)message)); if(message->state == MODBUS_MESSAGE_STATE_DISCARDED) // has the application discarded the message? { list_remove(&bus->messageQueue, message); modbus_rtu_master_release_message(message); // perform actual release of discarded message bus->state = BUS_STATE_IDLE; NABTO_LOG_TRACE(("Completing discard request (bus=%u, query=%u).", bus->identifier, (int)message)); } else // no - just continue and retransmit the message { retransmit_current_message(bus); } } } break; } if(oldState != bus->state) { NABTO_LOG_TRACE(("State change in bus %u: %s -> %s", bus->identifier, modbusStateNames[oldState], modbusStateNames[bus->state])); } } }
static void flush_cache(void) { uart_write_buffer(0, transmitCache, transmitCachePointer); transmitCachePointer = 0; }
void uart_write(uint8_t channel, uint8_t value) { uart_write_buffer(channel, &value, 1); }