0

I am implementing a MQTT communication. I want to send CAN frames over MQTT through a graphical interfaces (realized in python). I am able to send messages from the GUI to one topic and I am able to see messages arrived on the same topic (using paho library) when I use the board. The function is below and the topic is diagnostic_request/topic:

 void onMessageArrived
(
    const char* topic,
    const uint8_t* payload,
    size_t payloadLen,
    void* context
)
{
    //LE_FATAL("The publisher received a message!");
    //GIMP
    char payloadStr[payloadLen + 1];
    memcpy(payloadStr, payload, payloadLen);
    payloadStr[payloadLen] = '\0';
    LE_INFO("Received message! topic: \"%s\", payload: \"%s\"", topic, payloadStr);
    //GIMP principio di conversione

    if (strcmp(subscribeTopic, topic)==0)
        LE_INFO("Sei sul topic di richiesta diagnostica!");
        can_send();        
  }

At this point I have a difficulty. My can_send function (that works!) is:

int can_send(void)
{
    const char* subscribeTopic = "diagnostic_request/topic";
    LE_FATAL_IF(
            mqtt_Subscribe(MQTTSession, subscribeTopic, MQTT_QOS0_TRANSMIT_ONCE) != LE_OK,
            "failed to subscribe to %s",
            subscribeTopic);
    LE_INFO("Subscribed to topic (%s)", subscribeTopic);


           
        
int nbytesWrite;

// USE SEND STANDARD FRAME
frameWrite.can_id = 0x750; //codice identificativo di una richiesta diagnostica per centralina simulata
frameWrite.can_id &= CAN_SFF_MASK;

frameWrite.can_dlc = 8;
 //strcpy((char *)frameWrite.data, "MGATE");
frameWrite.data[0] = 0x03;
frameWrite.data[1] = 0x22;
frameWrite.data[2] = 0xF1;
frameWrite.data[3] = 0x89;
frameWrite.data[4] = 0x00;
frameWrite.data[5] = 0x00;
frameWrite.data[6] = 0x00;
frameWrite.data[7] = 0x00;
if ((nbytesWrite = write(sdw, &frameWrite, sizeof(frameWrite))) != sizeof(frameWrite))
{
    canwrite = false;
    LE_INFO ("Writing error, nbytesWrite = %d", nbytesWrite);
    return SOCK_WRITING_ERROR;
}

canwrite = true;
return 0;
}

So I have to call can_send in the onMessageArrived function when is statement is ok. I am able to see when I send a publish on diagnostic_request/topic. The only problem is to send the payloadStr value to the can_send function and unpackage it in the frameWrite.data[]. Can someone help me to understand how to modify the can_send function in order that the values

frameWrite.data[0] = 0x03;
frameWrite.data[1] = 0x22;
frameWrite.data[2] = 0xF1;
frameWrite.data[3] = 0x89;
frameWrite.data[4] = 0x00;
frameWrite.data[5] = 0x00;
frameWrite.data[6] = 0x00;
frameWrite.data[7] = 0x00;

are values that I send through mqtt in the payloadStr? I send a string of 8 bytes but I can't unpackage it. Any help will be appreciated Regards

9
  • Whats the typedef for frameWrite? IF the data is only 8 bytes then you could provide a char* payload and size_t len parameter to can_send, memset frameWrite.data to zeroes, cap the len so it doesn't exceed maximum and then use strncpy (probably with a typecast) to copy into the frame. Or exit the function if the maximum length is exceeded. Commented Jun 4, 2021 at 8:48
  • thanks for the quickly reply. It is a struct. So can I do: int can_send(const char* message) with return 0? And How could I unpack message (that is the payloadStr? In other words I want to give through a string the values 0x03, 0x22 ...etc that I wrote manually in the code... Commented Jun 4, 2021 at 8:50
  • Ah - so in this case are you sending a hex string over MQTT, e.g. "22F189" ? Commented Jun 4, 2021 at 9:14
  • yes...I have to send hex strings, such ad 220909, but the dimensions are fixed Commented Jun 4, 2021 at 9:16
  • So basically you need something like this -> stackoverflow.com/a/18724427/1607937 Commented Jun 4, 2021 at 9:19

1 Answer 1

0

Taking the approach of "write the test first".....

int convertNybble (char const hex) 
{
    if (hex >= '0' && hex <= '9')
    {
        return hex - '0';
    }
    else if (hex >= 'a' && hex <= 'f')
    {
        return 10 + (hex - 'a');
    }
    else if (hex >= 'A' && hex <= 'F')
    {
        return 10 + (hex - 'A');
    }
    
    return -1; // invalid hex character
}


#define ERR_NOT_EVEN_NUMBER_OF_DIGITS   -1
#define ERR_STRING_TOO_LONG             -2
#define ERR_INVALID_CHAR                -3


int preparePayload(char const* hexString, unsigned char* canBuffer, size_t const bufLen)
{
    size_t hexLen = strlen(hexString);

    if (0 != (hexLen % 2))
    {
        // Expect an even number of digits.
        return ERR_NOT_EVEN_NUMBER_OF_DIGITS;
    }

    size_t payloadLen = hexLen / 2;

    // buffer will contain payload-length, followed by payload data.
    //  so payloadLen+1 must be able to fit into bufLen - fail if payloadLen+1 > bufLen, which is the same as payloadLen >= bufLen
    if (payloadLen >= bufLen)
    {
        // will not be able to fit the decoded string into the payload buffer
        return ERR_STRING_TOO_LONG;
    }

    unsigned char* bufEnd = canBuffer;
    bufEnd += bufLen;

    *canBuffer++ = (unsigned char)payloadLen;

    while (payloadLen > 0)
    {
        payloadLen--;

        int hexDigit[2];
        hexDigit[0] = convertNybble(*hexString++);
        hexDigit[1] = convertNybble(*hexString++);

        if ((hexDigit[0] < 0) || (hexDigit[1] < 0))
        {
            return ERR_INVALID_CHAR;
        }

        *canBuffer++ = (hexDigit[0] << 4) | hexDigit[1];
    }

    // fill any leftovers with zero
    while (canBuffer < bufEnd)
    {
        *canBuffer++ = 0;
    }

    return (0);
}



int performTests()
{
    char const* testStr[5] =
    {
        "12345",
        "0123456789ABCDEF",
        "@ABC",
        "0AF9",
        "0123456789ABCD"
    };

#define MARKER 0xFF
#define PAYLOAD_LEN 8
    unsigned char payload[PAYLOAD_LEN+1];
    payload[PAYLOAD_LEN] = MARKER;

    int decodeResult = preparePayload(testStr[0], payload, PAYLOAD_LEN);

    if (ERR_NOT_EVEN_NUMBER_OF_DIGITS != decodeResult) { return -1; }       //not expected result

    decodeResult = preparePayload(testStr[1], payload, PAYLOAD_LEN);

    if (ERR_STRING_TOO_LONG != decodeResult) { return -1; }

    decodeResult = preparePayload(testStr[2], payload, PAYLOAD_LEN);

    if (ERR_INVALID_CHAR != decodeResult) { return -1; }

    // here we are checking the character decoding
    decodeResult = preparePayload(testStr[3], payload, PAYLOAD_LEN);

    if (0 != decodeResult) { return -1; }

    if (payload[0] != 2)    { return -1; }
    if (payload[1] != 0x0A) { return -1; }
    if (payload[2] != 0xF9) { return -1; }
    if (payload[3] != 0)    { return -1; }
    if (payload[7] != 0)    { return -1; }
    // payload contains - 02 0a f9 00 00 00 00 00 00

    // here we are checking the limit of the payload capacity
    decodeResult = preparePayload(testStr[4], payload, PAYLOAD_LEN);

    if (0 != decodeResult) { return -1; }
    // payload contains - 07 01 23 45 67 89 ab cd


    // check we haven't overrun the buffer
    if (payload[8] != MARKER) { return -1; }


    // all tests pass
    return 0;
}


int main()
{
    int testResult = performTests();
    return testResult;
}

You can then embody the decoder as follows:

convertNybble, preparePayload etc as above....


int canbusSend(unsigned char const* canbusPayload)
{
    //etc

    // - copy payloadBytes to frameWrite.data....

    //etc
}


void onMessageArrived
(
    const char* topic,
    const uint8_t* mqttPayload,
    size_t payloadLen,
    void* context
)
{
#define CANBUS_PAYLOAD_BUFFER_LEN   8
    unsigned char canbusPayload[CANBUS_PAYLOAD_BUFFER_LEN];
    int decodeResult = preparePayload((char const*)mqttPayload, canbusPayload, CANBUS_PAYLOAD_BUFFER_LEN);

    if (0 == decodeResult)
    {
        int canbusSendResult = canbusSend(canbusPayload);
        // TODO : report error if failed
    }
    else
    {
        //TODO : report error
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your answer, is very interesting studing your code. I have another question (I am sorry for bothering you). Is correct in can_send only declare payload and use it as an array of elements? for example: frameWrite.data[0] = payload[0];. If I do this way I obtain payload[0] may be used uninitialized in this function. How can I do to pass the value from preparePayload function?
You could do something like memcpy (frameWrite.data, canbusPayload, 8)
don't forget to upvote and accept the answer if it helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.