Serial communication printing issue - serial-port

I am working on serial communication between two MCU's particularly teensy(similar to Arduino) for generating fake GPS data. I have been able to write GPS data and read from the other MCU fine but if u look closely, the data that is printed has some ambiguity. The last values are changed somehow and I don't understand why is this because of sprintf command or conversion of float to string or what?
Some help will be appreciated.
Below are the working code and snippet of the serial terminal.
Thank you
float lat = 37.4980608;
char str1[21];
void setup()
{
Serial3.begin(115200);
Serial.begin(115200); // Config serial port (USB)
while(!Serial);
while(!Serial3);
Serial.println("Sending gps data");
}
void loop()
{
sprintf(str1, "%.7f%.7f", lon, lat);
Serial.println(str1);
Serial3.write(str1);
Serial3.flush();
delay(500);
}

What you are seeing is the compiler's approximation of your floats because their values are exceeding the precision possible with a float (4-bytes). Using a double won't help unless your MCU supports 8-byte doubles; I've not used a teensy but I highly doubt it supports 8-byte doubles.
This is not a clever solution but it should get you pointed in the right direction.
Define a struct that can represent large real number values
typedef struct {
int whole;
unsigned long fraction;
} BigNumber;
The you may declare/initialize latitude and longitude l
BigNumber latitude { 126, 9653503 };
BigNumber longitude { 37, 4980608 };
Then printing is easy:
sprintf(strbuf, "%i.%lu %i.%lu",
latitude.whole, latitude.fraction,
longitude.whole, longitude.fraction
);
However if mathematical operations are necessary - add, subtract, etc. - this won't cut it; find an arbitrary big number library like Nick Gammon's
Lastly, have a care: in your code str1 is too small - there is no accounting for the null terminator appended by sprintf, so you're getting plain lucky your program is not crashing.

Related

Cut out part of the data shown in serial monitor of Arduino

I'm new member here and also a joiner in programming
i try to extract information from my vehicle using OBD-II cable adapter. I try simple code to read the RPM and successfully got it and print it in serial monitor but i face a simple problem. Serial monitor display the PID-Code + current value of RPM as shown below:
010C849 where 010C: refer to RPM-PID used and 849: current value of RPM
so can i cutout the HEX number from the result and just display the value of RPM such as (849)
i used the following code:
Here is an example of the result `
#include <OBD2UART.h>
COBD obd;
void setup()
{
pinMode(13, OUTPUT);
obd.begin();
while (!obd.init());
}
void loop()
{
int value;
if (obd.readPID(PID_RPM, value)) {
Serial.println(value);
delay(1000);
}
}
`
You could use Regex, to cut out a pattern, but Arduino (and, in general, C++) does not provide support for regular expression parsing.
Once you have the string, you can use indexOf() and substring() to extract substrings. Once you have a substring, you can use toCharArray() to extract the character array, and atoi() to convert that to an integer.
Once you have integer values you can print them out.

Arduino: while (Serial.available()==0) gives input

I am trying to input GPS coordinate into the serial monitor to use in my drone project
However, whenever I try to input GPS coordinate, it automatically writes one of the GPS coordinates without my input. For example, GPS latitude is shown as 0.00, but the program waits for GPS Longitude info.
For a detailed situation please look at the picture attached.
int GPSNumCor;
void setup() {
// put your setup code here, to run once:
Serial.begin (115200);
Serial.print("What is the number of your GPS Coordinate? ");
while (Serial.available() == 0);
GPSNumCor = Serial.parseInt();
Serial.println(GPSNumCor);
delay (200);
float GPSLat[GPSNumCor], GPSLon[GPSNumCor];
for (int i = 0; i < GPSNumCor; i++)
{
if (i == 0)
{
Serial.println("What is your 1st GPS Coordinate");
}
if (i == 1)
{
Serial.println("What is your 2nd GPS Coordinate");
}
if (i == 2)
{
Serial.println("What is your 3rd GPS Coordinate");
}
if (i > 2)
{
Serial.print("What is your ");
Serial.print(i + 1);
Serial.println(" th GPS Coordinate");
}
delay(200);
Serial.print ("Latitude: ");
while (Serial.available() == 0);
GPSLat[i] = Serial.parseFloat();
Serial.println(GPSLat[i]);
Serial.print("Longitude: ");
while (Serial.available() == 0);
GPSLon[i] = Serial.parseFloat();
Serial.println(GPSLon[i]);
}
}
It has to wait for all input until I make an input to the program, but it does not wait.
I know while (Serial.available()==0) is a way to go, but I do not know why it would not work.
First, there's no reason to use while (Serial.available() == 0);. The parseFloat function you are about to use waits for data to be available and, if it didn't, merely checking for zero wouldn't be sufficient anyway because that would stop waiting as soon as a single character was available.
So here's why that while loop is a bad idea:
If you really do need to wait for the input before calling parseFloat, this won't do it. It only waits until at least one character is received and the coordinates may be more than one character.
The parseFloat function doesn't return until it has read an entire float anyway. So it already waits for you.
But that's not your problem. Think about the input stream, say it's "11.0<newline>22.0newline44.0". Where is the code to read the spaces between those numbers? When parseFloat tries to read a space, it returns a zero, as the documentation says. That's why you're getting zeroes -- you don't have any code to do anything with the separators between the floats.
Think about how parseFloat must work when it reads "12.34newline". First it reads the 1 and has no idea whether that's the whole number of not, so it keeps checking. Then it reads the "2.34" and still has no idea it has the whole number. Not until it sees the newline does it know that 12.34 is the correct float to return. But it does not consume the newline. Why? Because that might mean something.
With the code you showed, your next call to parseFloat will then try to read the newline and see that this is not a valid character to be part of a floating point number. So, as the documentation says, it will return zero.
Look closely at parseFloat's documentation to find out how to correctly match the delimiters in your serial stream. The parseFloat function has the ability to behave differently, consuming and ignoring delimeters rather than returning zero.
I don't know how it work, I just add Serial.read() in every time I want to read.
If u don't want to add Serial.read(), u can also use old version like 1.6.0, it still work fine but it don't work when u make like C# Serial app.
Just add Serial.read(), it work fine every place.
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
while(Serial.available()==0){}
int r=Serial.parseInt();
Serial.println(r);
Serial.read(); // it work fine
while(Serial.available()==0){}
int g=Serial.parseInt();
Serial.println(g);
Serial.read();
}
In the Serial Monitor window, in the drop-down menu on the bottom-right, change from "Newline" to "No line ending" and that will solve the problem (by preventing the Serial Monitor from automatically entering zero value(s)).
Both the parseInt() and parseFloat() have a hard time reading other data types (this also includes white spaces such as new lines) than the ones specified, and as a result they automatically return zero.
Reference: This page on Programming electronics offers valuable, detailed explanations (look for a paragraph with bold text):
https://www.programmingelectronics.com/parseint/

Type casting operation

I am using NodeMCU & Energy meter. Energy meter is Modbus RTU device which display parameter in 32 bit. With below piece of code I could able to read the data from slave but need method to type cast from into 32 bit floating & display
When I change the value to unsigned decimal in ModScan software the values showing properly. But I need to display value in 32 bit floating point.
#include <ModbusMaster232.h>
#include <SoftwareSerial.h>
float data[100];
ModbusMaster232 node(1);
// Define one address for reading
#define address 1
// Define the number of bits to read
#define bitQty 70
void setup()
{
Serial.begin(9600);
// Initialize Modbus communication baud rate
node.begin(9600);
}
void loop()
{
int result = node.readHoldingRegisters(address, bitQty);
data[0] = (float)node.getResponseBuffer(0);
data[1] = (float)node.getResponseBuffer(1);
data[2] = (float)node.getResponseBuffer(2);
data[3] = (float)node.getResponseBuffer(3);
data[4] = (float)node.getResponseBuffer(4);
data[5] = (float)node.getResponseBuffer(5);
for (int i = 0; i < 100; i++)
{
//data[i] = node.getResponseBuffer(i);
Serial.println(data[i]);
}
Serial.println("............");
}
I would like to display the reading as shown in Modbus with type casting.
Actual Modbus device output from salve:
Arduino output while read data from energy meter:
You are casting a 16-bit word into a float. Check the slave documentation to find how they map a 32-bit floating point into two Modbus registers. Basically you need to know where the least significant word is (first or second register), load them into memory (shift if necessary) and cast to float.
I am looking and trying to solve exact same problem (with same results). This may help:
function read() {
client.readHoldingRegisters(0000, 12)
.then(function(d) {
var floatA = d.buffer.readFloatBE(0);
console.log("Total kWh: ", floatA); })
.catch(function(e) {
console.log(e.message); })
.then(close);
}
This is NodeJS and javascript version, which works and Arduino does not. Complete example can be found here and it works on Raspberry Pi https://github.com/yaacov/node-modbus-serial/blob/master/examples/buffer.js

My Qt app does not recieve all the data sent by arduino

I'll go right to the point. My arduino reads values from the adc port and send them via serial port(values from 0 to 255). I save them in a byte type vector. After sending an specific signal to arduino, it starts to send to Qt app the data saved in the vector. Everything is working except that the arduino should send 800 values and the app receives less values than that. If I set the serial baud rate to 9600, I get 220 values. If, instead, I set the baud rate to 115200, I get only 20 values. Can you guys help me to fix this? I would like to use 115200 baud rate, because I need a good trasmision speed at this project(Real time linear CCD). I'll leave some code below:
Arduino code:
void sendData(void)
{
int x;
for (x = 0; x < 800; ++x)
{
Serial.print(buffer[x]);
}
}
This is the function that sends the values. I think is enough information, so I summarized it. If you need more code, please let me know.
Qt serial port setting code:
...
// QDialog windows private variables and constants
QSerialPort serial;
QSerialPortInfo serialInfo;
QList<QSerialPortInfo> listaPuertos;
bool estadoPuerto;
bool dataAvailable;
const QSerialPort::BaudRate BAUDRATE = QSerialPort::Baud9600;
const QSerialPort::DataBits DATABITS = QSerialPort::Data8;
const QSerialPort::Parity PARITY = QSerialPort::NoParity;
const QSerialPort::StopBits STOPBITS = QSerialPort::OneStop;
const QSerialPort::FlowControl FLOWCONTROL = QSerialPort::NoFlowControl;
const int pixels = 800;
QVector<double> data;
unsigned int dataIndex;
QByteArray values;
double maximo;
...
// Signal and slot connection.
QObject::connect(&serial, SIGNAL(readyRead()), this,SLOT(fillDataBuffer()));
...
// Method called when there's data available to read at the serial port.
void Ventana::fillDataBuffer()
{
dataIndex++;
data.append(QString::fromStdString(serial.readAll().toStdString()).toDouble());
if(data.at(dataIndex-1) > maximo) maximo = data.at(dataIndex-1);
/* The following qDebug is the one I use to test the recieved values,
* where I check that values. */
qDebug() << data.at(dataIndex-1);
}
Thanks and sorry if it's not so clear, it has been an exhausting day :P
Ok... I see two probelms here:
Arduino side: you send your data in a decimal form (so x = 100 will be sent as 3 characters - 1, 0 and 0. You have no delimiter between your data, so how your receiver will know that it received value 100 not three values 1, 0 and 0? Please see my answer here for further explanation on how to send ADC data from Arduino.
QT side: There is no guarantee on the moment when readyRead() signal will be triggered. It may be immediately after first sample arrives, but it may be raised after there are already couple of samples inside the serial port buffer. If that happens, your method fillDataBuffer() may process string 12303402 instead of four separate strings 123, 0, 340 and 2, because between two buffer reads four samples arrived. The bigger the baudrate, the more samples will arrive between the reads, which is why you observe less values with a bigger baud rate.
Solution for both of your problems is to append some delimiting byte for your data, and split the string in the buffer on that delimiting byte. If you don't want to have maximum data throughput, you can just do
Serial.print(buffer[x]);
Serial.print('\n');
and then, split incoming string on \n character.
Thank you very much! I did what you said about my arduino program and after solving that, I was still not getting the entire amount of data. So the the problem was in Qt. How you perfectly explain, the serial buffer was accumulating the values too fast, so the slot function "fillDataBuffer()" was too slow to process the arriving data. I simplified that function:
void Ventana::fillDataBuffer()
{
dataIndex++;
buffer.append(serial.readAll());
}
After saving all the values in the QByteArray buffer, I process the data separately.
Thanks again man. Your answer was really helpful.

Need help on understanding Writefile

I would like your assistance to understand a bit of code that would hugely help me in my project. Without going into too much details, here is what is causing me so much problems :
bool Serial::WriteData(char *buffer, unsigned int nbChar)
{
DWORD bytesSend;
//Try to write the buffer on the Serial port
if(!WriteFile(this->hSerial, (void *)buffer, nbChar, &bytesSend, 0))
{
//In case it don't work get comm error and return false
ClearCommError(this->hSerial, &this->errors, &this->status);
return false;
}
else
return true;
}
I use this function to send a variable to my Arduino Uno over Serial port like this :
snprintf(Data, sizeof(Data) - 1, "%3.1f", (int)(pf->speedKmh)*1.0);
SP->WriteData(Data, sizeof(Data) - 1); printf("\nData\n");
Some helpful info :
speedkmh is a float
char Data[8] = "";
So my question is : I would like to know exactly what is being sent to the Arduino. At the moment I don't really know if it is sending an array, bits one at a time, if it sends a float etc... Could you help me understand this?
Thanks!
The following line:
snprintf(Data, sizeof(Data) - 1, "%3.1f", (int)(pf->speedKmh)*1.0);
converts your floating point number to a string (an array of characters). Then
SP->WriteData(Data, sizeof(Data) - 1); printf("\nData\n");
sends these characters, one at a time, over the serial port.
BTW, it is not very clear why you convert your float to an integer, then back to a float (by multiplying it by 1.0), then to a string. As I see it, this would have the result of truncating the fractional part of the float, then appending a misleading ".0" to the string (from the ".1f" part of the control string). That is, both 1.0 and 1.4 will be converted to "1.0".

Resources