Hi,
I'm quite new to Arduino, so my apologies if i ask a stupid question.
I have a device (digital smart meter) that outputs serial data every 15 seconds. (a telegram that contains multiple lines)
Every line is terminated by a \r\n
Example of the data:
b'1-0:1.8.1(000858.642kWh)\r\n'
b'1-0:1.8.2(001264.926kWh)\r\n'
b'1-0:2.8.1(000421.788kWh)\r\n'
b'1-0:2.8.2(000162.755kWh)\r\n
I'm using Serial.readStringUntil('\n') and then have code process the string to get the code, value and metric.
But for some reason it is not reading he whole telegram, only a few lines are processed. I assume this is because my code to process the data is to slow to read the following lines.
How can i handle in the best way?
What i want to get out of it is for example:
If I find 1.7.0 then the i create a description saying "Real time power consumption" and a valuestring of "0.461" and a metric string "kW"
msg1 = Serial2.readStringUntil('\r');
Serial.print("msg1="); Serial.println(msg1);
codeString=msg1.substring(5,msg1.indexOf('('));
Serial.print("codeString="); Serial.println(codeString);
if (ValidCode(codeString)){ //the function ValidCode checks if the code, for example 2.8.0 is something I want to process
description = GetDescription(codeString); "returns a description for example "Real time energy consumption
Serial.print("description="); Serial.println(description);
//get the value
valueSensor = msg1.substring(msg1.indexOf('(')+1,msg1.indexOf('*')); //extracts the value out of the message, for example 846.57
Serial.print("Value="); Serial.println(valueSensor);
//get the metric
valueMetric = msg1.substring(msg1.indexOf('*')+1,msg1.indexOf(')')); //returns the metric, for example kW
Serial.print("Metric="); Serial.println(valueMetric);
packageMessage = description + " " + valueSensor + " " + valueMetric;
send_mqtt_message(packageMessage.c_str()); //sends MQTT message
}
Is there really a b' at the beginning of each line ?
Is it a P1 port telegram ?
There are a number of sketches for that, but not always good sketches.
Often the function sscanf() is used to get the numbers out of the telegram.
The .readStringUntil() has a timeout and the behavior of that function is not well documented. Therefor I prefer to use plain 'C' code to read data from the serial port.
Serial2.readStringUntil('\r'); can block for upto 1sec waiting for input
Try the non-blocking code below instead (reading from Serial for testing)
Here is some sample output
Sample data lines (terminated by '\r' '\n', Arduino monitor both NewLine and carrage Return ending)
b'1-0:1.8.1(000858.642*kWh)
b'1-0:1.8.2(001264.926*kWh)
got a line of input 'b'1-0:1.8.1(000858.642*kWh)'
codeString=1.8.1
Value=000858.642
Metric=kWh
Real time energy consumption 000858.642 kWh
See my tutorial on Taming Arduino Strings Use String& to pass Strings and use local Strings in processing methods to recover memory.
Your call to send_mqtt_message may also be blocking the loop and causing you to miss input data.
See my tutorial Arduino Serial I/O for the Real World for how to print debug output without blocking the loop.
It has code for increasing the input TX buffer so you can capture all the input data in one go and then process it a line at a time. Arduino Serial I/O for the Real World also has a loopTimer you can use to see how long your send_mqtt_message is taking.
/**
https://forum.arduino.cc/index.php?topic=732163.0
b'1-0:1.8.1(000858.642*kWh)\r\n
b'1-0:1.8.2(001264.926*kWh)\r\n
b'1-0:2.8.1(000421.788*kWh)\r\n
b'1-0:2.8.2(000162.755*kWh)\r\n
not sure about the ' after the \n in your example data?
*/
String msg1;
void setup() {
Serial.begin(9600);
for (int i = 10; i > 0; i--) {
Serial.print(' '); Serial.print(i);
delay(500);
}
Serial.println();
Serial.println(F("PowerReadings.ino"));
Serial.println(F("Sample data lines (terminated by '\\r' '\\n', Arduino monitor both NewLine and carrage Return ending)"));
Serial.println(F("b'1-0:1.8.1(000858.642*kWh)"));
Serial.println(F("b'1-0:1.8.2(001264.926*kWh)"));
msg1.reserve(40); // expected line size
}
// read Serial until until_c char found or limit char read, returns true when found/limited else false
// non-blocking, until_c is returned as last char in String, updates input String with chars read
bool readStringUntil(String& input, char until_c, size_t char_limit) {
while (Serial.available()) {
char c = Serial.read();
input += c;
if (c == until_c) {
return true;
}
if (input.length() >= char_limit) {
return true;
}
}
return false;
}
bool ValidCode(String& codeString) {
return (codeString.length() > 0); // for now
}
bool processInput(String& input) {
input.trim();
if (input.length() == 0) {
return false;
}
int startIdx = input.indexOf(':');
int endIdx = input.indexOf('(');
if (startIdx < 0) {
return false;
}
startIdx++; // step over :
String codeString = input.substring(startIdx, endIdx);
Serial.print("codeString="); Serial.println(codeString);
if (ValidCode(codeString)) { //the function ValidCode checks if the code, for example 2.8.0 is something I want to process
//String description = GetDescription(codeString); "returns a description for example "Real time energy consumption
//Serial.print("description="); Serial.println(description);
String description = "Real time energy consumption";
//get the value
if (endIdx < 0) {
return false; // invalid data
}
startIdx = endIdx + 1;
endIdx = msg1.indexOf('*');
String valueSensor = msg1.substring(startIdx, endIdx); //extracts the value out of the message, for example 846.57
Serial.print("Value="); Serial.println(valueSensor);
//get the metric
if (endIdx < 0) {
return false; // invalid data
}
startIdx = endIdx + 1;
endIdx = msg1.indexOf(')');
String valueMetric = msg1.substring(startIdx, endIdx); //returns the metric, for example kW
Serial.print("Metric="); Serial.println(valueMetric);
// dont use + as it uses extra memory see
// Taming Arduino Strings https://www.forward.com.au/pfod/ArduinoProgramming/ArduinoStrings/index.html
// packageMessage = description + " " + valueSensor + " " + valueMetric;
String packageMessage = description;
packageMessage += " ";
packageMessage += valueSensor;
packageMessage += " ";
packageMessage += valueMetric;
Serial.println(packageMessage.c_str());
//send_mqtt_message(packageMessage.c_str()); //sends MQTT message
}
return true;
}
void loop() {
if (readStringUntil(msg1, '\n', 40)) { // read until find newline or have read 40 chars
if (msg1.lastIndexOf('\n') >= 0) { // input terminated by '\n'
msg1.trim();
Serial.print(F(" got a line of input '")); Serial.print(msg1); Serial.println("'");
if (!processInput(msg1)) {
Serial.print(F(" Corrupted input '")); Serial.print(msg1); Serial.println("'");
}
} else {
Serial.print(F(" Corrupted input '")); Serial.print(msg1); Serial.println("'");
}
msg1 = ""; // clear after processing for next line
}
}
That b in front might come from python. When I played a little with python, I've seen it; but can't remember what the meaning was or how I got rid of it.
@OP, if you used a python script to read it, I suggest to start by using a a normal terminal program to show you the data. If the device sends that_b_, you will have to strip it of.