Open NeliusNDL opened 3 years ago
This error happens when you don´t have a Connection to the Network. If you have It terminated right it won´t hang.
I know it its a bad habit to have this kind of scenario in the code, but i haven´t had any issues with this behavior.
I found some page on the web that had a janky workarround for this.
@NeliusNDL
EDIT: ESP32SJA1000Class::endPacket()
int ESP32SJA1000Class::endPacket() { if (!CANControllerClass::endPacket()) { return 0; } const uint8_t TIMEOUTVALUE = 50; uint8_t uiTimeOut = 0;
// wait for TX buffer to free while (((readRegister(REG_SR) & 0x04) != 0x04) && (uiTimeOut < TIMEOUTVALUE)) { yield(); uiTimeOut++; } if(uiTimeOut == TIMEOUTVALUE) return -1; / get tx buff time out / uiTimeOut = 0;
int dataReg;
if (_txExtended) { writeRegister(REG_EFF, 0x80 | (_txRtr ? 0x40 : 0x00) | (0x0f & _txLength)); writeRegister(REG_EFF + 1, _txId >> 21); writeRegister(REG_EFF + 2, _txId >> 13); writeRegister(REG_EFF + 3, _txId >> 5); writeRegister(REG_EFF + 4, _txId << 3);
dataReg = REG_EFF + 5;
} else { writeRegister(REG_SFF, (_txRtr ? 0x40 : 0x00) | (0x0f & _txLength)); writeRegister(REG_SFF + 1, _txId >> 3); writeRegister(REG_SFF + 2, _txId << 5);
dataReg = REG_SFF + 3;
}
for (int i = 0; i < _txLength; i++) { writeRegister(dataReg + i, _txData[i]); }
if ( _loopback) { // self reception request modifyRegister(REG_CMR, 0x1f, 0x10); } else { // transmit request modifyRegister(REG_CMR, 0x1f, 0x01); }
// wait for TX complete while (((readRegister(REG_SR) & 0x08) != 0x08) && (uiTimeOut < TIMEOUTVALUE)) { if (readRegister(REG_ECC) == 0xd9) { modifyRegister(REG_CMR, 0x1f, 0x02); // error, abort return 0; } yield(); uiTimeOut++; } if(uiTimeOut == TIMEOUTVALUE) return -2; / send msg timeout /
return 1; }
//-------------------------------------------------------------
IN PROGRAM:
CAN.beginPacket(id); byte anzahl = CAN.write(sdata, 8); Serial.print(anzahl); Serial.print(" Byte\t"); int ERROR_CODE = CAN.endPacket(); // must be a int Serial.print(ERROR_CODE); Serial.print('\t'); switch (ERROR_CODE) { case 1: Serial.print("Buf sent!\r\n"); break; case -1: Serial.print("Get tx buff time out!\r\n"); break; case -2: Serial.print("Send msg timeout!\r\n"); break; default: Serial.print("Buf send failed!\r\n"); }
//-------------------------------------------------------------
Console:
Message to send: 00 01 09 03 04 05 06 07 8 Byte 1 Buf sent! Message to send: 00 01 0A 03 04 05 06 07 8 Byte 1 Buf sent! Message to send: 00 01 0B 03 04 05 06 07 8 Byte -2 Send msg timeout! Message to send: 00 01 0C 03 04 05 06 07 8 Byte -1 Get tx buff time out! Message to send: 00 01 0D 03 04 05 06 07 8 Byte -1 Get tx buff time out! Message to send: 00 01 0E 03 04 05 06 07 8 Byte -1 Get tx buff time out! Message to send: 00 01 0F 03 04 05 06 07 8 Byte -1 Get tx buff time out! Message to send: 00 01 10 03 04 05 06 07 8 Byte -1 Get tx buff time out! Message to send: 00 01 11 03 04 05 06 07 8 Byte -1 Get tx buff time out! Message to send: 00 01 12 03 04 05 06 07 8 Byte -1 Get tx buff time out! Message to send: 00 01 13 03 04 05 06 07 8 Byte 1 Buf sent! Message to send: 00 01 15 03 04 05 06 07 8 Byte 1 Buf sent! Message to send: 00 01 14 03 04 05 06 07 8 Byte 1 Buf sent!
@Petros144 thank you, you are most helpful !!!! I have implemented this and it is working as expected. If there is a device plugged in to listen and RX, then all is well. If I leave the CAN bus unconnected (but still properly terminated) while I continually try to send keep alive ID's I get send errors as expected. When I then reconnect a device everything works well.
But, if I leave the CAN bus disconnected for at least an hour, it seems the TX buffer gets backed up. When I then reconnect a device to the CAN bus I get the " Get tx buff time out!" error. This also confirms now that before, when the task was stuck in the while loop is was because of this error "tx buff time out".
I need a way to flush the CAN bus when this happens, not even ESP.restart() can do the trick, only a power reset (or pressing reset button) can resolve the problem.
@NeliusNDL i just had a look at the Datasheet, and I found this:
/ address and bit definitions for the Command Register /
//--------------------------
also see here: https://www.nxp.com/docs/en/application-note/AN97076.pdf (Page 29)
Example for the PeliCAN mode: Definitions for the different constants and variables, etc., are given in the Appendix. Variables may be interpreted different in BasicCAN and PeliCAN mode, e.g., “InterruptEnReg” points to the Control Register in BasicCAN mode but to the Interrupt Enable Register in PeliCAN mode. The language “C” is used for programming. After having initialized the CAN controller according to the example given in chapter 4.2.1, normal communication can be started. . . / wait until the Transmit Buffer is released / do { / start a polling timer and run some tasks while waiting break the loop and signal an error if time too long / } while((StatusReg & TBS_Bit ) != TBS_Bit ); / Transmit Buffer is released, a message may be written into the buffer / / in this example a Standard Frame message shall be transmitted / TxFrameInfo = 0x08; / SFF (data), DLC=8 / TxBuffer1 = 0xA5; / ID1 = A5, (1010 0101) / TxBuffer2 = 0x20; / ID2 = 20, (0010 0000) / TxBuffer3 = 0x51; / data1 = 51 / . . TxBuffer10 = 0x58; / data8 = 58 / / Start the transmission / CommandReg = TR_Bit ; / Set Transmission Request bit / . . The TS and RS flags in the Status Register can be used for detecting, that the CAN controller has reached the idle-state. The TBS- and TCS-flags can be checked for a successful transmission.
Im not a professional in CAN or c++ mabe you can see some helpfull stuff here.
@Petros144 I have added a the following line: vTaskDelay(5/portTICK_PERIOD_MS); // wait in 5ms chunks inside the 2 x while loops just before the yield(); command. I think that the yield(); is not very predictable and maybe sometimes there is nothing to yield(); to and that this time will just be too fast for the CAN TX buffer to become ready and maybe I get a false return of error code of -1
ie // wait for TX buffer to free while (((readRegister(REG_SR) & 0x04) != 0x04) && (uiTimeOut < TIMEOUTVALUE)) { vTaskDelay(5/portTICK_PERIOD_MS); // wait in 5ms chunks yield(); uiTimeOut++; }
note that I have also made const uint8_t TIMEOUTVALUE = 10; // x 5ms = 50ms note there is an additional delay regulated by the yield(); I know that this is perhaps a bit long
Thanks to @Petros144 I was able to fix the hanging problem, however, from that point forward, I only got Send msg timeout. So I put a
case -2:
Serial.print("Send msg timeout!\r\n");
CAN.sleep();
delay(50);
CAN.wakeup();
break;
and the problem was solved.
I encountered a similar situation. When I sent packets while the bus was in an unstable state, the ESP32 froze. (For example, when there was no termination resistance on the bus) I made changes to the library and created a sample code based on the code above. Please refer to pull request #118 for details.
Hi all, I tried implementing all the above solutions but my problem still persists. I have connected ESP 32 with MCP 2551. Initially, the code was stuck at CAN.endPacket(); after implementing the above solution I get "tx buff time out!" continuously. The issue is only during transmission, as it receives data properly from CAN analyzer Please help.
I'm surprised no fix for the MCP2515 module as this issue exists in that version also.
Hello, it seems that these lines
// wait for TX buffer to free while ((readRegister(REG_SR) & 0x04) != 0x04) { yield(); }
and
// wait for TX complete while ((readRegister(REG_SR) & 0x08) != 0x08) { if (readRegister(REG_ECC) == 0xd9) { modifyRegister(REG_CMR, 0x1f, 0x02); // error, abort return 0; } yield(); }
can make the device hang, I think that the TX buffer is full when there is nothing connected to the bus to RX and ack the data. Then the buffer gets full and the device hangs.
How can the TX buffer be cleared ? or there should be a timeout for these while loops and then return an error code