I am trying to take the sensor data from Arduino & Raspberry Pi using RS232 serial communication. I have searched for this small thing and found something related on this below link but was unable get the full idea.
The os (kernel) has an internal buffer of 4096 bytes. If this buffer is full and a new character arrives on the serial port, the oldest character in the buffer will be overwritten and thus will be lost. After a successful call to RS232_OpenComport(), the os will start to buffer incoming characters.
The values are properly coming from Arduino to Raspberry Pi (output attached below) and it is storing in a pointer to unsigned char[] which is defined as unsigned char *buf[4096].
int main()
{
int i, n,
cport_nr=0, /* /dev/ttyS0 (COM1 on windows) */
bdrate=9600; /* 9600 baud */
unsigned char buf[4096];
char mode[]={'8','N','1',0};
while(1)
{
n = RS232_PollComport(cport_nr, buf, 4095);
if(n > 0)
{
buf[n] = 0;
for(i=0; i < n; i++)
{
if(buf[i] < 32) /* replace unreadable control-codes by dots */
{
buf[i] = '.';
}
}
printf("received %i bytes: %s\n", n, (char *)buf);
}
}
Now I want to store these values in another float/double variable so that I can perform further operations on it. How to store a value suppose 0.01 to a float/double which is later used to create stuff.
From the output in the screenshot it looks like you are sending the string representation of the numbers rather than the actual numbers. You just need to detect those "unreadable control-codes" that you are just replacing with a . as they will probably tell you when a number ends and another begins. Just make QSerialPort * serial; a proper class member.
Also, check for errors on opening the port: serial->open(QIODevice::ReadWrite); Then, insert some qDebug() in serialreceived() to see if the slot is called at all and if the canReadLine() works. you should use QByteArray to read your data. If there's any char in the response, that is not String conform, the resulting QString will be prematurely terminated, use readLine() instead readAll() like this:
QByteArray data = serial -> readLine();
qDebug() < data.toHex(' '); // prints the hex representation of your char array
QString str(data);
qDebug() << str;
First, it will be better if you use some other ASCII character (e.g. space) to separate the numbers, because . dot is a part of floating point number. Then, you can construct std::string object from your raw unsigned char array, split it in a multiple strings and convert each string to float.
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
int main() {
// imagine that this buff is already after read and preprocessing
unsigned char buff[1024] = "13.60 13.60 -11.12 -0.3 and let's say that the rest is garbage";
int n = 28; // let's say that you received 28 bytes
std::string strBuff(reinterpret_cast<char*>(buff), n); // construct a string from buff using just first 28 bytes
std::vector<std::string> numbers;
boost::split(numbers, strBuff, boost::is_any_of(" "), boost::token_compress_on);
for (const auto& n : numbers) {
try {
std::cout << std::stof(n) << std::endl;
} catch (const std::exception& e) {
std::cout << n << " is not convertible to float: " << e.what() << std::endl;
}
}
return 0;
}
I took the string splitting method from this answer but you can use anything that works for you.
I used reinterpret_cast because std::string accepts char instead of unsigned char as a CTor arg.
Related
#include <iostream>
#include <cstring>
#include <cctype>
void go_lower(char *str);
void fill_str(char *mass);
int main()
{
char str_1[80];
char str_2[80];
char *pointer_1;
char *pointer_2;
pointer_1 = str_1;
pointer_2 = str_2;
fill_str(pointer_1);
fill_str(pointer_2);
if(strcmp(str_1, str_2))
{
std::cout << "This strings are equal\n";
}
else
{
std::cout << "This strings are not equal\n";
}
return 0;
}
void fill_str(char *mass)
{
std::cout << "Insert string to compare\n";
while(*mass)
{
std::cin >> *mass;
mass++;
}
std::cout << '\n';
}
void go_lower(char *str)
{
while(*str)
{
if(isupper(*str))
{
*str = tolower(*str);
}
str++;
}
}
Not so long ago I started studying programming and I try to understand the test tasks. In this program, I need to compare strings regardless of the case of the characters. when the program starts, it goes straight to filling in str_2 and the comparison block concludes that the strings are equal. What is the error?
While in C++, you should really use std::basic_string, you still need to know how to handle plain-old-arrays. When passing an plain-old-array to a function to be filled, you must pass an additional parameter to provide the array size to the function. Within the function you can only read size - 2 characters into the array (saving index size - 1 for the nul-terminating character '\0' (ASCII 0).
You should also avoid using MagicNumbers in your code. Instead, if you need a constant (such as for 80), #define a constant or use a const int in C++. Since you have a choice for your return type, you should return the number of characters read into each string. The information will be available when you leave your fill_str() function and there is no reason to have to calling function re-scan for end-of-string.
You cannot use std::cin effectively to read multi-word input in a function as you are attempting. std::cin discards whitespace, so you will never read the '\n' generated by the user pressing [Enter]. So you have know way to know when the user is done typing. Instead you must use std::cin.get(); which will read every character.
With those changes, you can write your function to use plain-old-arrays as follows:
#define MAXC 80 /* if you need a constant, #define one (or more) */
...
/* using plain-old-arrays, you must pass size of array */
size_t fill_str (char *mass, size_t size)
{
size_t n = 0; /* length counter */
std::cout << "\nInsert string to compare\n";
while (n < size - 1) /* read while array not full */
{
if ((*mass = std::cin.get()) && *mass != '\n') /* use get() to read whitespace */
n++; /* increment length counter */
if (*mass == '\n') /* if \n user pressed [Enter], input complete */
break;
mass++; /* increment pointer */
}
*mass = 0; /* nul-terminate when you leave read-loop */
return n; /* return length */
}
Your example, omitting your unused go_lower() function, could then be:
#include <iostream>
#include <cstring>
#define MAXC 80 /* if you need a constant, #define one (or more) */
size_t fill_str (char *mass, size_t size);
int main (void)
{
char str_1[MAXC];
char str_2[MAXC];
char *pointer_1;
char *pointer_2;
size_t len_1, len_2;
pointer_1 = str_1;
pointer_2 = str_2;
len_1 = fill_str (pointer_1, MAXC);
len_2 = fill_str (pointer_2, MAXC);
if (len_1 != len_2) {
std::cout << "\nstrings differ in length (" <<
len_1 << " != " << len_2 << ").\n";
return 0;
}
if (strcmp (str_1, str_2) == 0) /* only equal if return is 0 */
{
std::cout << "\nThis strings are equal\n";
}
else
{
std::cout << "\nThis strings are not equal\n";
}
return 0;
}
/* using plain-old-arrays, you must pass size of array */
size_t fill_str (char *mass, size_t size)
{
size_t n = 0; /* length counter */
std::cout << "\nInsert string to compare\n";
while (n < size - 1) /* read while array not full */
{
if ((*mass = std::cin.get()) && *mass != '\n') /* use get() to read whitespace */
n++; /* increment length counter */
if (*mass == '\n') /* if \n user pressed [Enter], input complete */
break;
mass++; /* increment pointer */
}
*mass = 0; /* nul-terminate when you leave read-loop */
return n; /* return length */
}
Example Use/Output
$ ./bin/golower
Insert string to compare
apples and bananas
Insert string to compare
apples and bananas
This strings are equal
$ ./bin/golower
Insert string to compare
pickle
Insert string to compare
pickles
strings differ in length (6 != 7).
As mentioned at the beginning, you should really use std::string and getline() to read the user input, but you should also be familiar with plain-old-arrays. Look things over and let me know if you have questions.
The while (*mass) in your fill_str function will not loop even once because * mass may be initialized to 0 here (it is actually undefined behavior and up to your compiler). Because of this none of your strings will ever get filled with anything.
strcmp evaluates your strings equal because they are initialized in such a way they are equal from the beginning and are never changed because of the reason stated above.
To read a string consider using getline or std::cin if you know that your string will not contain spaces, eg.
std::cin >> mass;
See more here https://www.cplusplus.com/doc/tutorial/basic_io. Also consider using std::string instead of char arrays.
I have an Arduino that controls timers. The settings for timers are stored in byte arrays. I need to convert the arrays to strings to SET a string on an external Redis server.
So, I have many arrays of bytes of different lengths that I need to convert to strings to pass as arguments to a function expecting char[]. I need the values to be separated by commas and terminated with '\0'.
byte timer[4] {1,5,23,120};
byte timer2[6] {0,0,0,0,0,0}
I have succeeded to do it manually for each array using sprintf() like this
char buf[30];
for (int i=0;i<5;i++){ buf[i] = (int) timer[i]; }
sprintf(buf, "%d,%d,%d,%d,%d",timer[0],timer[1],timer[2],timer[3],timer[4]);
That gives me an output string buf: 1,5,23,120
But I have to use a fixed number of 'placeholders' in sprintf().
I would like to come up with a function to which I could pass the name of the array (e.g. timer[]) and that would build a string, probably using a for loop of 'variable lengths' (depending of the particular array to to 'process') and many strcat() functions. I have tried a few ways to do this, none of them making sense to the compiler, nor to me!
Which way should I go looking?
Here is the low tech way you could do it in normal C.
char* toString(byte* bytes, int nbytes)
{
// Has to be static so it doesn't go out of scope at the end of the call.
// You could dynamically allocate memory based on nbytes.
// Size of 128 is arbitrary - pick something you know is big enough.
static char buffer[128];
char* bp = buffer;
*bp = 0; // means return will be valid even if nbytes is 0.
for(int i = 0; i < nbytes; i++)
{
if (i > 0) {
*bp = ','; bp++;
}
// sprintf can have errors, so probably want to check for a +ve
// result.
bp += sprintf(bp, "%d", bytes[i])
}
return buffer;
}
an implementation, assuming that timer is an array (else, size would have to be passed as a parameter) with the special handling of the comma.
Basically, print the integer in a temp buffer, then concatenate to the final buffer. Pepper with commas where needed.
The size of the output buffer isn't tested, mind.
#include <stdio.h>
#include <strings.h>
typedef unsigned char byte;
int main()
{
byte timer[4] = {1,5,23,120};
int i;
char buf[30] = "";
int first_item = 1;
for (i=0;i<sizeof(timer)/sizeof(timer[0]);i++)
{
char t[10];
if (!first_item)
{
strcat(buf,",");
}
first_item = 0;
sprintf(t,"%d",timer[i]);
strcat(buf,t);
}
printf(buf);
}
I'm trying to use ifstream/ofstream to read/write but for some reason, the data gets corrupted along the way. Heres the read/write methods and the test:
void FileWrite(const char* FilePath, std::vector<char> &data) {
std::ofstream os (FilePath);
int len = data.size();
os.write(reinterpret_cast<char*>(&len), 4);
os.write(&(data[0]), len);
os.close();
}
std::vector<char> FileRead(const char* FilePath) {
std::ifstream is(FilePath);
int len;
is.read(reinterpret_cast<char*>(&len), 4);
std::vector<char> ret(len);
is.read(&(ret[0]), len);
is.close();
return ret;
}
void test() {
std::vector<char> sample(1024 * 1024);
for (int i = 0; i < 1024 * 1024; i++) {
sample[i] = rand() % 256;
}
FileWrite("C:\\test\\sample", sample);
auto sample2 = FileRead("C:\\test\\sample");
int err = 0;
for (int i = 0; i < sample.size(); i++) {
if (sample[i] != sample2[i])
err++;
}
std::cout << err << "\n";
int a;
std::cin >> a;
}
It writes the length correctly, reads it correctly and starts reading the data correctly but at some point(depending on input, usually at around the 1000'th byte) it goes wrong and everything to follow is wrong. Why is that?
for starter, you should open the file stream for binary read and write :
std::ofstream os (FilePath,std::ios::binary);
(edit: assuming char really means "signed char")
Do notice that regular char can hold up to CHAR_MAX/2 value, which is 127.
If the random number is bigger - the result will wrap around, resulting negative value. the stream will try to write this character as a text character, which is invalid value to write. binary format should at least fix this problem.
Also, you shouldn't close the stream yourself here, the destructor does it for you.
Two more simple points:
1) &(data[0]) should be just &data[0], the () are redundant
2) try keep the same convention. you write upper-camel-case for FilePath variable, but lower-camel-case for all the other variables.
I need to serialize various structs to a file.
If possible I'd like the files to be pure ASCII. I could write some kind of serializer for each struct, but there are hundreds and many contain floats and doubles which I'd like to represent accurately.
I can't use a third-party serialization library and I don't have the time to write hundreds of serializiers.
How can I ASCII-safe serialize this data?
Also streams please, I hate the look of C-style printf("%02x",data).
I found this solution online and it addresses just this problem:
https://jdale88.wordpress.com/2009/09/24/c-anything-tofrom-a-hex-string/
Reproduced below:
#include <string>
#include <sstream>
#include <iomanip>
// ------------------------------------------------------------------
/*!
Convert a block of data to a hex string
*/
void toHex(
void *const data, //!< Data to convert
const size_t dataLength, //!< Length of the data to convert
std::string &dest //!< Destination string
)
{
unsigned char *byteData = reinterpret_cast<unsigned char*>(data);
std::stringstream hexStringStream;
hexStringStream << std::hex << std::setfill('0');
for(size_t index = 0; index < dataLength; ++index)
hexStringStream << std::setw(2) << static_cast<int>(byteData[index]);
dest = hexStringStream.str();
}
// ------------------------------------------------------------------
/*!
Convert a hex string to a block of data
*/
void fromHex(
const std::string &in, //!< Input hex string
void *const data //!< Data store
)
{
size_t length = in.length();
unsigned char *byteData = reinterpret_cast<unsigned char*>(data);
std::stringstream hexStringStream; hexStringStream >> std::hex;
for(size_t strIndex = 0, dataIndex = 0; strIndex < length; ++dataIndex)
{
// Read out and convert the string two characters at a time
const char tmpStr[3] = { in[strIndex++], in[strIndex++], 0 };
// Reset and fill the string stream
hexStringStream.clear();
hexStringStream.str(tmpStr);
// Do the conversion
int tmpValue = 0;
hexStringStream >> tmpValue;
byteData[dataIndex] = static_cast<unsigned char>(tmpValue);
}
}
This can be easily adapted to read/write to file streams, although the stringstream used in fromHex is still necessary, the conversion must be done two read characters at a time.
Any way you do it, you're going to need serialization code for
each struct type. You can't just bit-copy a struct to the
external world, and expect it to work.
And if you want pure ascii, don't bother with hex. For
serializing float and double, set the output stream to
scientific, and the precision to 8 for float, and 16 for
double. (It will take a few more bytes, but it will actually
work.)
For the rest: if the struct are written cleanly, according to
some in house programming guidelines, and only contain basic
types, you should be able to parse them directly. Otherwise,
the simplest solution is generally to design a very simple
descriptor language, describe each struct in it, and run a code
generator over it to get the serialization code.
I want to build a function to easily convert a string containing hex code (eg. "0ae34e") into a string containing the equivalent ascii values and vice versa.
Do I have to cut the Hex string in pairs of 2 values and gue them together again or is there a convenient way to do that?
thanks
Based on binascii_unhexlify() function from Python:
#include <cctype> // is*
int to_int(int c) {
if (not isxdigit(c)) return -1; // error: non-hexadecimal digit found
if (isdigit(c)) return c - '0';
if (isupper(c)) c = tolower(c);
return c - 'a' + 10;
}
template<class InputIterator, class OutputIterator> int
unhexlify(InputIterator first, InputIterator last, OutputIterator ascii) {
while (first != last) {
int top = to_int(*first++);
int bot = to_int(*first++);
if (top == -1 or bot == -1)
return -1; // error
*ascii++ = (top << 4) + bot;
}
return 0;
}
Example
#include <iostream>
int main() {
char hex[] = "7B5a7D";
size_t len = sizeof(hex) - 1; // strlen
char ascii[len/2+1];
ascii[len/2] = '\0';
if (unhexlify(hex, hex+len, ascii) < 0) return 1; // error
std::cout << hex << " -> " << ascii << std::endl;
}
Output
7B5a7D -> {Z}
An interesting quote from the comments in the source code:
While I was reading dozens of programs that encode or decode the
formats here (documentation? hihi:-) I have formulated Jansen's
Observation:
Programs that encode binary data in ASCII are written in such a style
that they are as unreadable as possible. Devices used include
unnecessary global variables, burying important tables in unrelated
sourcefiles, putting functions in include files, using
seemingly-descriptive variable names for different purposes, calls to
empty subroutines and a host of others.
I have attempted to break with this tradition, but I guess that that
does make the performance sub-optimal. Oh well, too bad...
Jack Jansen, CWI, July 1995.
If you want to use a more c++ native way, you can say
std::string str = "0x00f34" // for example
stringstream ss(str);
ss << hex;
int n;
ss >> n;
The sprintf and sscanf functions can already do that for you. This code is an example that should give you an idea. Please go through the function references and the safe alternatives before you use them
#include <stdio.h>
int main()
{
int i;
char str[80]={0};
char input[80]="0x01F1";
int output;
/* convert a hex input to integer in string */
printf ("Hex number: ");
scanf ("%x",&i);
sprintf (str,"%d",i,i);
printf("%s\n",str);
/* convert input in hex to integer in string */
sscanf(input,"%x",&output);
printf("%d\n",output);
}