-
Notifications
You must be signed in to change notification settings - Fork 7.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Register a callback when the last byte is written on UART or when the RTS pin become low #10343
Comments
Humm... so the feature request is about using UART in Half Duplex mode. You want a callback for when all data has left UART FIFO? |
Yes |
Currently when I get a cb about the data receive, the app immediately wants to write the next packet. It cant write since the uart is still busy. I have to wait until the uart is available so I can write the next packet. This results in a busy while waiting loop on one of the task. If there would be a cb about the rts pin I could write the next packet in that cb because that would signal to the app that the uart is available |
You can also look into hardware RS485 support of the ESP32. ... or do the same on the RTS pin (as suggested in the issue title) |
Oh that is a good idea. Thank you very much will definitely try this |
Well. This is not good because I literally can't do anything in an ISR routin. While this could work void IRAM_ATTR packetWriteDone(){
}
attachInterrupt(MBUS_RTS, packetWriteDone, FALLING); It would be even slower than the current implementation. I must set a flag inside the interrupt and watch that flag in the loop. |
Current flow
This would be better
|
It looks like you want implement the Modbus Protocol. It gaves a lot of working librarys for this. Have you tried Serial.flush()? It does similar like "uart_wait_tx_done", without a timeout. Why you don't use Serial.available() for the incoming Bytes? Serial.onReceive() is always a blocking Function. |
@device111 If you need to implement RS485, then the ESP32-xx all do have hardware support for toggling the DE/RE pin and even have collision detection in hardware. |
Ok, you can use it for auto-toggle the DE Pin, but the rest of the implementation must affect to this. 😉 |
Yes, I have implemented the modbus protocoll but not the traditional so I can't use any library for this. void Modbus::init() {
Serial1.begin(MBUS_BAUD, SERIAL_8N1, MBUS_RX, MBUS_TX);
Serial1.setPins(-1, -1, -1, MBUS_RTS);
Serial1.setMode(UART_MODE_RS485_HALF_DUPLEX);
Serial1.setRxTimeout(MBUS_RX_TIMEOUT);
Serial1.onReceive(
std::bind(&Modbus::handlePacket, this),
PACKET_TRIGGER_ONLY_ON_TIMEOUT
);
Serial1.onReceiveError(std::bind(&Modbus::handleReceiveError, this, std::placeholders::_1));
} This uses Serial1.begin(MBUS_BAUD, SERIAL_8N1, MBUS_RX, MBUS_TX);
Serial1.setPins(-1, -1, -1, MBUS_RTS); so I don't really have to do any pin toggling. In my application, when I got a packet, I immidiately send the next packet. ( it is a master on the bus ) bool Modbus::isUartBusy(){
return uart_wait_tx_done(UART_NUM_1, 0) == ESP_ERR_TIMEOUT;
} So it waits for the TX done. But I would prefer a callback style like |
This basically looks like this in a simple form void Modbus::init() {
Serial1.begin(MBUS_BAUD, SERIAL_8N1, MBUS_RX, MBUS_TX);
Serial1.setPins(-1, -1, -1, MBUS_RTS);
Serial1.setMode(UART_MODE_RS485_HALF_DUPLEX);
Serial1.setRxTimeout(MBUS_RX_TIMEOUT);
Serial1.onReceive(
std::bind(&Modbus::handlePacket, this),
PACKET_TRIGGER_ONLY_ON_TIMEOUT
);
Serial1.onReceiveError(std::bind(&Modbus::handleReceiveError, this, std::placeholders::_1));
}
void Modbus::handlePacket() {
// Check how many bytes are available
int available = Serial1.available();
// Check if it would overflow our buffer
if (available >= MAX_MBUS_DATA_LENGTH) {
ESP_LOGE(MBUS_DEBUG_TAG, "Packet is too big: %d bytes. Can't process it.", available);
lastPacketError = BUFF_OVERFLOW;
callErrorCb();
return;
}
// Get all the data
uint8_t rawPacket[available];
int readBytes = Serial1.readBytes(rawPacket, available);
// Check CRC and other error bytes.
if (!isPacketValid(rawPacket, readBytes)) {
ESP_LOGE(MBUS_DEBUG_TAG, "Invalid packet. Can't process it.");
printRawPacket(rawPacket, readBytes);
callErrorCb();
return;
}
// Parse the packet if it was a scan packet.
parseScanPacket(rawPacket, readBytes);
// Call the packet callback if it wasn't a scan and we have a valid callback
if (packetCallback && !isScanning) {
packetCallback(rawPacket, readBytes);
}
} After these checks are done, the modbus calls the |
Something like this would be really good void Modbus::init() {
Serial1.begin(MBUS_BAUD, SERIAL_8N1, MBUS_RX, MBUS_TX);
Serial1.setPins(-1, -1, -1, MBUS_RTS);
Serial1.setMode(UART_MODE_RS485_HALF_DUPLEX);
Serial1.setRxTimeout(MBUS_RX_TIMEOUT);
Serial1.onReceive(
std::bind(&Modbus::handlePacket, this),
PACKET_TRIGGER_ONLY_ON_TIMEOUT
);
Serial1.onReceiveError(std::bind(&Modbus::handleReceiveError, this, std::placeholders::_1));
Serial1.onTransmitDone(std::bind(&Modbus::handleTransmitDone, this, std::placeholders::_1));
} I could send the next packet inside Currently: Ideally: If the UART becomes free while a task parses the packet it would be event better because we don't have to wait for parsing the packet. We could send the next packet immidiately regardless of the latest data. |
Hi @hitecSmartHome did you find any solution for this?. I'm actually trying to adapt profibus stack for esp32 based on this project github. I need to adapt USART data register empty and uart receive interrupts for esp32. |
Well, as I said, I'm doing it like this now: Serial1.onReceive([](){
// Got a whole packet. That callback means the uart triggered a byte timeout.
// Read the whole UART buffer into my own.
int available = Serial1.available();
uint8_t rawPacket[available];
int readBytes = Serial1.readBytes(rawPacket, available);
// Do some checks on the packet like CRC and things like that...
if( isPacketValid() ){
// If the packet was valid, process it.
processValidPacket();
}
// Write the next packet but wait for uart busy in `writeNextPacket`
writeNextPacket();
},PACKET_TRIGGER_ONLY_ON_TIMEOUT); |
@hitecSmartHome uart receive interrupt is ok, but what about uart data register empty interrupt? |
There is none. |
Related area
UART
Hardware specification
UART
Is your feature request related to a problem?
Currently ( with arduino v3 and idf v5 ) I can register callback functions for
onReceive
andonReceiveError
.When I receive a packet I immidiately want to write the next. It is not possible because the
UART
is still busy.I had to implement a logic to wait for the
UART
to become available before I can write.A cb function would be good if the
UART
is available again.Describe the solution you'd like
Describe alternatives you've considered
Before I want to write I call this wrapper function
while( isUartBusy() ){}
This is the implementation
This is not ideal because it freezes the task which wants to write.
Additional context
Here is how I use the UART right now.
I have checked existing list of Feature requests and the Contribution Guide
The text was updated successfully, but these errors were encountered: