I am fairly inexperienced with Arduino Uno. I am working on a project to control a Simscape buck converter using Simulink Realtime. Simulink communicates with Arduino by serial. The conrol algorithm is written on the Arduino. I noticed that trying to integrate the error using something like:
integralError += error;
or
integralError = integralError + error;
was returning a NaN. So I made this simpler code to test it. In this code, a ramp function is fed to arduino, and read in as signalIn1 and signalIn2. I call the function myIncrement to perform the compound assignment (maybe not great name, sorry for that.) myIncrement does nothing with the first signal as a sanity check. It then attempts to perform the compound assignment on the second output signal. The results are then assigned and written out.
This works fine if there is no compound assignment, but however I try to do this, it delivers NaN. What am I missing? Thank you very much.
// Create a union to easily convert float to byte
typedef union {
float number;
uint8_t bytes[4];
} FLOATUNION_t;
// Create the variables you want to receive
FLOATUNION_t signalIn1;
FLOATUNION_t signalIn2;
// Create the variables to send
FLOATUNION_t signalOut1;
FLOATUNION_t signalOut2;
// Other Variables
float result1 = 1;
float result2 = 1;
float result3 = 0;
const float refreshRate = 3000;
void setup() {
// initialize serial, use the same boudrate in the Simulink Config block
Serial.begin(115200);
}
void loop() {
// Get the floats from serial
signalIn1.number = getFloat(); // Give your float a value
signalIn2.number = getFloat(); // Give your float a value
// Do Something
myIncrement();
signalOut1.number = result1;
signalOut2.number = result2;
// Send some variables back
// Print header: Important to avoid sync errors!
Serial.write('A');
for (int i = 0; i < 4; i++) {
Serial.write(signalOut1.bytes[i]);
}
for (int i = 0; i < 4; i++) {
Serial.write(signalOut2.bytes[i]);
}
// Print terminator
Serial.print('\n');
// Use the same delay in the Serial Receive block
delay(refreshRate);
}
void myIncrement(){
result1 = signalIn1.number;
result2 += signalIn2.number;
}
float getFloat(){
int cont = 0;
FLOATUNION_t f;
while (cont < 4 ){
f.bytes[cont] = Serial.read() ;
cont = cont +1;
}
return f.number;
}
Honestly spoke, I make this assumption by checking the values that I read back in Simulink. When I don't use any compound assignments, I can read values fine and I get the expected control dynamics, but when I try, I don't see the expected control dynamics (no integral portion,) and I additionally read NaNs out.
I did not consider this, maybe it is clear that I don't have a lot of experience here, and these two attempts are hack jobs. I will experiment with this, thank you.
even if there were 4 bytes waiting for you in the Serial buffer, this type of construct in C++ where you read a field of a union that was not the last written to is not legit. (see type punning articles)
I'd say you don't need the union type, have a small buffer to receive the bytes and then memcpy() that into a float (assuming your PC and the Arduino represent the float the same way in binary).
apparently the float is sent as a 4 byte binary - assuming IEEE-754 bit-representation for type float. So Serial.parseFloat() won't do as it expects an ASCII representation of the float.
if you want a blocking function to read the float you could do something like
bool getFloat(float &f) {
constexpr byte sizeOfFloat = sizeof(float); // assuming IEEE-754 bit-representation for type float (single-precision floating-point numbers = 4 bytes)
byte buffer[sizeOfFloat];
unsigned long startTime = millis();
for (byte i = 0; i < sizeOfFloat; i++) {
while (Serial.available() <= 0) {
if (millis() - startTime >= 5000) return false; // Timeout after 5 seconds
}
buffer[i] = Serial.read(); // Read each byte
startTime = millis(); // Reset timeout for each byte
}
// Convert bytes to float
memcpy(&f, buffer, sizeOfFloat);
return true;
}
you would test the reception this way
void setup() {
Serial.begin(115200);
Serial.println("Waiting for float value...");
}
void loop() {
float receivedValue;
if (getFloat(receivedValue)) {
Serial.print("Received float: "); Serial.println(receivedValue);
// here do something with the data
}
}
The function has a built in timeout of 5 seconds to not cripple Arduino in case nothing comes over the serial line (if getFloat() returns false, it means the code timed out and the 4 bytes were not received).
Great! I successfully performed the compound assignment operator on the second signal read in. Now I will try to implement this in the PI controller. I will update on this.
Do you also maybe have an idea what this solved? Sorry if this is maybe a not good question.