RAK13005 WisBlock LIN Module: listen/read not successful

Hi! I’m using a RAK13005 as LIN master (resistor re-soldered to controller mode position) together with a Base RAK5005 and Core RAK11200. Connected to the LIN bus is also separate LIN slave ECU. I’m able to send data to the bus using lin_master.write(masterID, sendData, 8). I’m also able to send a header to the LIN slave using lin_master.write(slaveID, writeHeaderData, 0) (uint8_t writeHeaderData[0]). With CANalyzer I’m verifying that the slave ECU responds to the header. But neither lin_master.listen(slaveID, readData, readDataSize) nor lin_master.read(readData, readDataSize) returns 1. lin_master.readStream(readData,readDataSize) returns 1, but the content of readData is shifterd to bytes. E.g. What I read in CANalyzer from LIN bus is 0x02 89 00 00 E3 FF FF and what readStream() returns is 0x55 9c 2 89 0 0 e3 ff. And finally; Every now an then, readStream() stalls for 1000ms. Normally is takes 3ms.
Hence, does anyone have any insight to why:

  1. read() and listen() are not successfully reading data?
  2. readStream() is byte shifting data?
  3. readStream() is randomly stalling?

Thanks, Olof

Welcome to RAK forum @ome ,

What is the baudrate you are operating? read() and listen() will only return 0 if it doesn’t see anything in the bus. How frequent you check the return?

Does the extra 0x55 0x9c in the readStream() consistent or it is changes in char/value and size?

When you say readStream randomly stalling, does it return 0? Or your code just won’t respond? like the whole code stall?

If possible to share your code, that can be helpful as well for me to get a better picture. Also if you can share some info with your ECU and the goal you are trying to accomplish.

Thanks for the welcome and reply!
The baudrate is 19200.

Regarding read() / listen() / readStream() they are called continuously in a while loop for e.g 200ms until there is a positive response or the time expires (pretty much a copy from the LED control master example)

The 0x55 0x9c can be consistent for a specific execution of the code. But e.g. if changing the delays in the scheduling of the write() and readStream() or changing debug level lin_bus.h, the data can be different e.g 0x55 5b e fe ff ff ff ff.

When readStream() is stalling it still returns 1. The stalling is detected by measuring the time the function call takes, see code below. When it stalls it is allways for 1000ms

readTimer = millis();
readOk = lin_master.readStream(readData,readDataSize);
if(DEBUG_READDATA)Serial.printf(“3. Read OK: %d, data read duration: %d ms\n”, readOk, (millis() - readTimer));

Basically what I want to achieve with the code is to simulate a LIN master on the LIN bus. Sending a header to a separate LIN ECU for it to respond. Then I want to read that response and look at some specific bits in the data and take different actions based on the value of the data (currently the “action” is represented by the LED). I presume the listen() function is the appropriate one to use in this case since I’m only interested in data from one specific ID. The reason for currently using readStream() is that is the only way I can read any data. Please find source code below.



    #include <Wire.h>
    #include “lin_bus.h”

    #ifdef RAK4630
    #define BOARD "RAK4631 "
    int lin_tx = 16;
    #elif ESP32
    #define BOARD "RAK11200 "
    int lin_tx = 21;
    #else
    #define BOARD "RAK11300 "
    int lin_tx = 0;
    #endif
    #define DEBUG 0
    #define DEBUG_READDATA 1
    #define DEBUG_WRITEDATA 1
    #define WB_LED1 12
    #define WB_LED2 2
    int lin_en = WB_IO6; //internal pulldown, EN=0 is sleep mode, EN=1 is normal operation mode.
    int lin_wk = WB_IO5; //low active

    // LIN Object
    lin_bus lin_master(Serial1,LIN_V2, lin_en, lin_wk, lin_tx);
    unsigned long baudrate = 19200;

    uint8_t masterID = 0x1B;
    uint8_t masterData[8] = {0x0e, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
    uint16_t dataWriteTime;

    uint8_t slaveID = 0x1C;
    uint8_t slaveHeaderData[0];
    const uint8_t schedulingDelay = 20;

    const int readDataSize = 8;
    uint8_t readData[readDataSize];
    boolean dataRead = false;
    uint8_t theInterestingBits;

    int readOk;

    unsigned long timer, readTimer;

    void setup() {

    pinMode(WB_LED1, OUTPUT);
    pinMode(WB_LED2, OUTPUT);
    pinMode(lin_wk,OUTPUT);
    digitalWrite(lin_wk,HIGH);
    if(DEBUG || DEBUG_READDATA || DEBUG_WRITEDATA){
    time_t timeout = millis();
    Serial.begin(115200);
    while (!Serial)
    {
    if ((millis() - timeout) < 5000)
    {
    delay(100);
    }
    else
    {
    break;
    }
    }
    }
    boolean masterInitiated = false;
    while(!masterInitiated)
    {
    masterInitiated = lin_master.master(baudrate);
    if(DEBUG)Serial.println(“LIN master waiting to initiate”);
    delay(100);
    }

    //Set board in 1=normal operation
    lin_master.sleep(1);
    if(DEBUG)Serial.println(“RAK13005 set in normal operation”);
    delay(100);
    lin_master.busWakeUp();
    if(DEBUG)Serial.println(“LIN-bus wakup sent”);
    delay(100);
    }

    void loop() {

    timer = millis();
    dataWriteTime = lin_master.write(masterID, masterData, 8); // Write data to LIN
    if(DEBUG_WRITEDATA)Serial.printf(“1. Wrote data to LIN bus: %x, %x, %x, %x, %x, %x, %x, %x Duration: %dms\n”, masterData[0], masterData[1], masterData[2], masterData[3], masterData[4], masterData[5], masterData[6], masterData[7], dataWriteTime);
    dataWriteTime = lin_master.write(slaveID, slaveHeaderData, sizeof(slaveHeaderData));
    if(DEBUG_WRITEDATA)Serial.printf(“2. Wrote header data to ID: %x, Duration: %dms\n”, slaveID, dataWriteTime);
    if(DEBUG_WRITEDATA)Serial.printf(“Total write time: %dms\n”, millis() - timer);
    //while( (millis() - timer) < schedulingDelay ) delay(1);
    delay(schedulingDelay);

    timer = millis();
    while( (millis() - timer) < schedulingDelay ) //Continously reads LIN bus until data is recieved or time expires
    {
    if(!dataRead)
    {
    readTimer = millis();
    readOk = lin_master.readStream(readData,readDataSize);
    if(DEBUG_READDATA)Serial.printf(“3. Read OK: %d, data read duration: %d ms\n”, readOk, (millis() - readTimer));

      if(readOk)
      {
        dataRead = true;
        digitalWrite(WB_LED1, HIGH);        
        if(DEBUG_READDATA)printReadData();
        if(readData[5] == 0)
        {
          theInterestingBits = readData[4] & B11100000;
          if(DEBUG_READDATA)Serial.printf("theInterestingBits: %x\n", theInterestingBits);
          if( theInterestingBits > 0 ){
            digitalWrite(WB_LED2, HIGH);
          }
          else
          {
            digitalWrite(WB_LED2, LOW);
          }
        }
      }
      else
      {
        if(DEBUG_READDATA)Serial.println("4. Read fail");  
      }
    }

    }
    digitalWrite(WB_LED1, LOW);
    dataRead = false;
    readOk = 0;
    }

    void printReadData()
    {
    Serial.print(“4. Data received: “);
    for( uint8_t i=0; i<readDataSize ;i++ )
    {
    Serial.printf(”%x “, readData[i]);
    }
    Serial.printf(”\n”);
    }


If you want to use an LINBus node send some datas to another device you can only need use the function of“write(uint8_t ident, uint8_t *data, uint8_t data_size)”for publish on BUS, “ident” is Identification code, all devices on the LINBus can receive BUS datas,all device can process data based on the ID value.
1.read() function be used in slaver node,if your device is master you can’t use “read()”,and you can use “listen(ID)”to get specified ID datas. “listen()” function can be used in slaver and master.
2.“readStream()” can get datas from LINBus include synch and checksum of LIN frame.
3.You can receive one entire LIN frame after wait for transmit time.

Hope this can help you !

Thanks for you reply! Your information is helpful and would make sense to include here: GitHub - RAKWireless/RAK13005-TLE7259-Library: Arduino library for RAK13005 LIN bus module with TLE7259 transceiver IC

However, as previously mentioned, using listen does always return 0. After investigating the library source code (lin_bus.cpp) I made some changes to listen()-method, which improved the situation.

First, when debug printing the number of available bytes with _serial->available() I get 11. This makes sense to me since I expect 8 bytes of data, the sync field, PID field and checksum. In the code however, data is only read if _serial->available() > (data_size + 3). I changed this to _serial->available() >= (data_size + 3). Now listen() will get past the if statement.

Secondly, when reading the header, the original code states:

g_head2 = g_head1;
g_head1 = _serial->read();

g_head1 is initiated as uint8_t g_head1 = 0x55; in the lin_bus.h
This means that when the code is executed g_head2 = 0x55 and g_head1 = 0x55, since the first byte read from the serial interface is the sync byte. Hence, when reaching the if statement:
if((g_head2==0x55)&&(g_head1 == protectID(ident))) it will evaluate false and return 0. (unless your slave id is 0x55)

I therefore changed:

g_head2 = _serial->read(); 
g_head1 = _serial->read();

Now if((g_head2==0x55)&&(g_head1 == protectID(ident))) will evaluate ture and it will go on to read the data bytes.

There still seems to be some timing issues though, which I have not fully investigated yet. When I turn off the debug printing in lin_bus.h, listen() still sometimes return 0.


The LINBus received one entire frame include Break byte、Synch byte、 ID byte、Data byte and Checksum byte. The Break byte is decoded to 0. The whole LINBus package’s bytes is 4+Data bytes numbers. So if you want to read one entire LIN package you need read 4+DataSize number s data from Serial buffer.If you need receive only LINBus head you can’t use “listen()”.You can use follow codes to decode LINBus head.
loop()
{
time_t time_start = millis();
while((millis()-time_start)<2000)
while(Serial1.available()>0)
{
uint8_t readch = Serial1.read();
Serial.printf(“Serial1 read is %x\r\n”,readch);
if(readch == 0x55)
{
uint8_t identity = Serial1.read();
Serial.printf(“identity is %x\r\n”,identity);
}
}
}

OK, It seems like I’m not receiving the 0 from the sync field, which causes the issue. With some minor tweaks, there are no timing issues anymore, so I have a working solution. So, I will not spend more time on finding the root cause.
Thanks for the support.