Class object seems to change address when it shouldn't

I'm trying to build an interface to enstablish serial communication between my board and a python script running on my computer to control a robotic arm. Every message is divided in a header (1 character) and a body. The body is composed of a series of keys and values, similar to a json file.

Message example:

Hvalue1:foo,value2:bar\n

The first request is made by the board and expects a confirm from the computer.
There are som functions which should help me with debugging, those are all static methods under the Debug class.
ENVIRONMENT: Platformio IDE
BOARD: Elegoo UNO R3 (ATMEGA328 U)
PROBLEM: There is something I'm probably doing wrong when accessing the object storing the last received serial message. I used Debug::logPointer to see what the address was at different points, for some reason I get a different value inside readSerialMessage than the one i get in loop. When I try to retrieve the value of the header with serialMessage.getHeader() execution just stops.

CODE:

//Main.cpp
#include <Arduino.h>
#include <Servo.h>
#include "pins.h"
#include "controls.h"
#include "interface.h"

//Object storing arm state
Arm arm(BASE_PINS, SERVO_ALFA_PIN, SERVO_BETA_PIN, SERVO_GAMMA_PIN); 

const int BAUD = 9600;

void setup()
{
    Serial.begin(BAUD);
}

unsigned long start_time, end_time; // Keep track of time taken by every iteration
unsigned int iteration = 0;

bool connected {false};

void loop()
{
    Message startCommunicationMessage('s');
    startCommunicationMessage.setIntValue("baud", BAUD);

    Message serialMessage('n');

    while (!connected){
        if (Serial.available() > 0){
            Debug::logPointer(serialMessage);
            readSerialRequest(&serialMessage);

            if (serialMessage.getHeader() != 'C'){ //AT THIS POINT THE EXECUTION STOPS
                Debug::log("Invalid");
                continue;
            } else {
                Debug::log("Received confirm");
                connected = true;
                break;
            }
        }
        sendSerialMessage(startCommunicationMessage);
        delay(1000);
    }

    Debug::log("Connected!");

    while (connected){
        start_time = micros();

        if (iteration % 50){
            //Check for serial commands every 12.5 ms
            if (Serial.available() > 0){
                readSerialRequest(&serialMessage);
                parseSerialRequest(serialMessage, arm);
            }
        }
    
        iteration += 1;
        end_time = micros();

        delayMicroseconds(min(max(0, 250 + start_time - end_time), 250));
    }
}
//Interface.cpp
int readSerialRequest(char *dest, char terminator='\n'){
    //Destination string MUST contain a number of characters >= MAX_REQUEST_LENGTH

    size_t counter {0};

    while (Serial.available() > 0){
        char character = Serial.read();
        if (character = terminator){
            *dest = character;
            dest++;
            counter++;
        } else {
            *dest = '\0';
            return counter;
        }
    }
}

void readSerialRequest(Message *message, char terminator='\n'){
    Debug::logPointer(serialMessage);
    char* content = message->getContent();

    char header = Serial.read();
    Debug::logPointer(*message);
    message->setHeader(header);

    readSerialRequest(content, terminator);
}

void sendSerialMessage(Message msg){
    Serial.print(msg.getHeader());
    Serial.println(msg.getContent());
}
//Message.cpp
class Message {
    char m_header = '\0';
    char m_content[MAX_REQUEST_LENGTH];

public:
    Message(char header)
    {
        m_header = header;
        char content[1]{""};
        strcpy(m_content, content);
    }

    void setHeader(char header){
        m_header = header;
    }
    char getHeader(){
        return m_header;
    }

    void setContent(char *content){
        strcpy(m_content, content);
    }

    char *getContent(){
        return m_content;
    }

    void addValue(char *content, const char *key, const char *value){
        char *ptr = &content[strlen(content)];

        if (strlen(content) == 1)
        {
            *ptr = ','; // Add item separator
            ptr++;
        }

        strcpy(ptr, key); // Copy key
        ptr += strlen(key);

        *ptr = ':'; // Add :
        ptr++;

        strcpy(ptr, value); // Copy value
    }
    void setValue(const char *key, const char *value){
        char *val_ptr{searchValue(m_content, key)};

        if (val_ptr == nullptr) // Value doesn't exist, must be added
        {
            addValue(m_content, key, value);
        }
        else
        {
            char *old_value_end = strstr(val_ptr, ","); // Locate the end of the old value
            char *new_value_end{&val_ptr[strlen(value)]};
            if (old_value_end == nullptr || old_value_end == new_value_end)
            { // Value is at the end or new value has the same length
                strcpy(val_ptr, value);
            }
            else
            { // Subsequent values must be shifted
                char *buffer = new char[strlen(old_value_end) + 1];
                strcpy(buffer, old_value_end); // Store part of string to be shifted in a temporary buffer

                strcpy(val_ptr, value);        // Copy new value
                strcpy(new_value_end, buffer); // Copy the buffer right after the new value

                delete[] buffer;
                buffer = nullptr;
            }
        }
    }

    //Not strictly relevant to the problem
    //Basically these methods encode int, floats and bools and then send the result to the setValue method
    void setIntValue(const char* key, const int &value){...}
    void setFloatValue(const char* key, const float &value){...}
    void setBoolValue(const char* key, const bool &value){...}
};
//Debug
template <typename T>
void Debug::log(T log){
    Serial.print("llog_message:");
    Serial.println(log);
}

template <typename T>
void Debug::logPointer(T log){
    Serial.print("laddress:");
    Serial.println((unsigned int)&log);
}

Please do not post in "Uncategorized"? See the sticky topics in Uncategorized - Arduino Forum.

Topic moved.

I've written a small tutorial on interfacing with Python. See Two ways communication between Python3 and Arduino

that could give you some ideas

Turns out I was lacking a lot of understanding about how serial communication is handled:

  1. I didn't account for the possibility of reading bytes faster than I was receiving them: Inside readSerialRequest() I looped until Serial.available() became zero, this would happen before encountering the terminator, thus returning without putting the NULL character at the end of the string. This is how i changed the function:
int readSerialRequest(char *dest, char terminator='\n', unsigned int timeout=3000){
    //Destination string MUST contain a number of characters >= MAX_REQUEST_LENGTH

    size_t counter {0};

    long unsigned int start {millis()};
    long unsigned int now {0};

    while (true){
        if (Serial.available() > 0){
            char character = Serial.read();
            if (character == terminator || counter == MAX_REQUEST_LENGTH){
                *dest = '\0';
                Debug::setLed(3, false);
                return counter;
            } else {
                *dest = character;
                dest++;
                counter++;
            }
        }

        now = millis();
        if (now - start > timeout){
            *dest = '\0';
            Debug::setLed(3, false);
            Debug::logError("Reading timed out");
            return counter;
        }
    }
}
  1. Serial.print() is NOT a blocking function: Since i was receiving neither "Invalid" nor "Received confirm" from the if else statement I thought the error must have been in serialMessage.getHeader(). The error was inside the else clause where I put a function (which i stupidly omitted since I thought it was not relevant), this function took serialMessage as an argument and did a bunch of things with the content, as the content was not null terminated it would overflow and mess up everything.

It actually is blocking. A serial.print / write places data in the underlaying buffer of the Serial object. If that buffer is full, the print / write functions will start blocking till there is space to put another byte in.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.