So I'm currently working on a project where it involves two lora devices (Arduino Mega + Dragino LoRa shield) exchanging data with each other.
How it works:
Client will send the speed of motor reading to the server, it will also receive the distance reading from the server.
Server will receive the speed of motor reading and also send its distance reading to the client.
I'm kinda new in C++ programming and here is the issue. The value i received from client is stored in char type buf. How can i compare it with an integer so that i can proceed with a threshold?
if ((char*)buf) < 10){ // (char*)buf contains the distance value sent by the server, I want to compare it with a certain value to trigger a buzzer
sound = 1000;
tone(buzzer,sound);
}
I recieved an error message error: ISO C++ forbids comparison between pointer and integer
Here is the sending code:
char data[3];
itoa(reading, data, 10);
rf95.send((uint8_t*)data,sizeof(data));
Any idea on how i can solve this issue.
Sender:
char data[3]; // I suggest making this bigger because it is
itoa(reading, data, 10); // now only safe if `reading` is in the range [-9, 99]
rf95.send((uint8_t*)data, strlen(data) + 1); // only send just enough
I suggest using sprintf(data, "%d", reading); instead of the non-standard itoa though.
If reading is a float as the comments suggest, you need to do this instead:
sprintf(data, "%.0f", reading); // there's no space for decimals in data
Receiver:
// read into buf until a \0 is encountered (and store that \0), then
int reading;
// if buf is an uint8_t[] as suggested in the comments, cast it:
if(sscanf(reinterpret_cast<const char*>(buf), "%d", &reading) == 1) {
// an int was decoded
if(reading < 10) {
sound = 1000;
tone(buzzer,sound);
}
}
If you have installed Arduino STL you may have access to a lot more functions and classes that could help - but afaik, Arduino STL is very outdated so I used sscanf that I think is included in the base Arduino package.
Since you probably want to send different types over the radio and you'll have to cast between the char* and uint8* a lot, you could add two helper function templates to do that:
template<uint8_t N> // returns true if sent ok
bool rf95send(const char(&buf)[N], uint8_t len) {
if(N < len) return false;
return rf95.send(reinterpret_cast<const uint8_t*>(buf), len);
}
template<uint8_t N> // returns true if received ok
bool rf95receive(char(&buf)[N]) {
uint8_t len = N;
return rf95.recv(reinterpret_cast<uint8_t*>(buf), &len);
}
You can then add helper functions for the types you'd like to support:
bool rf95send_float(float f) { // returns true if sent ok
char buf[45]; // room for all floats
int len = sprintf(buf, "%.2f", f);
return len > 0 && rf95send(buf, len + 1); // +1 for the null terminator
}
bool rf95receive_float(float& f) { // returns true if received ok
char buf[45];
if(rf95receive(buf)) {
return sscanf(buf, "%f", &f) == 1;
}
return false;
}
Then in your sender code:
float reading;
if(rf95send_float(reading)) { // sent ok
and the receiving side:
float reading;
if(rf95receive_float(reading)) { // received ok
if(reading < 10) {
sound = 1000;
tone(buzzer,sound);
}
}
Related
i'm trying to send data from a C++ program over serial communication to an Arduino. I formed a struct for sending the data as an object:
typedef struct
{
double width;
double height;
bool passBoard;
} MachineParameters;
I'm using this Serial library: http://wjwwood.github.com/serial/ for sending the data like this:
MachineParameters mp;
mp.width = 100;
mp.height = 200;
mp.passBoard = true;
ser.write((const uint8_t *)&mp, sizeof(mp));
The library makes it possible to send the data as uint8_t, std::vector or std::string.
The Arduino does receive data, but i don't know how to parse the data into the struct. I'm using the same struct as in the cpp code.
// In Arduio
MachineParameters mp;
int byte_size = 24;
loop() {
if(Serial.available() >= 24) {
Serial.readBytes((char*) &mp , 24);
}
}
// Goal: Read received mp data just like
// mp.width or mp.height
After hours of trying, i still cannot figure it out, how to send this struct to the arduino successfully. Is there another way of sending this data to the arduino? It worked sending the data as string, but that did not seem right.
I am pretty new to programming with C++, so please excuse any obvious questions...
Thank you for helping!
UPDATE: Working solution below
After a view more tries and thanks to your tips, i figured it out. Here is the code, which worked for me. I found out that my problem was the wrong byte size, used for parsing the buffer. The size of the struct in C++ is 12, whereas on the arduino it's 9. Using the original size (12) for parsing the buffer on the Arduino, the struct was parsed correctly.
/* --- C++ CODE --- */
typedef struct
{
double width;
double height;
bool passBoard;
} MachineParameters;
// sizeof(MachineParameters) returns 12.
MachineParameters mp;
mp.width = 11.1;
mp.passBoard = false;
mp.height = 22.2;
ser.write((uint8_t *)&mp, sizeof(mp));
/* --- END OF C++ --- */
/* --- Arduino Code --- */
#define BYTE_SIZE 12
char messageBuffer[BYTE_SIZE];
typedef struct
{
double width;
double height;
bool passBoard;
} MachineParameters;
MachineParameters mp;
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available() >= BYTE_SIZE) {
Serial.readBytes(messageBuffer , BYTE_SIZE);
memcpy(&mp, &messageBuffer, BYTE_SIZE);
// mp.width returns 11.1
// Success :)
}
}
/* --- END OF ARDUINO --- */
on the arduino you receive the data in a buffer as raw bytes
now you have to parse this, you can do this using memcpy to copy the data in the struct
however for doing this the data has to be aligned meaning that you have to know exactly where it begins. so you should send a synchronization byte ( start / stop bytes ) to be able to fix where the data begins
then you can use the code in parsing buffer data into struct :
struct abc {
char a;
char b;
char c;
char d[2];
};
int main() {
char arr[5] = { 'a', 'b', 'c', 'd', 'e' };
struct abc sa;
memcpy(&sa, arr, 5);
return 0;
}
here arr is incoming buffer, and with memcpy all the contents are copied appropriately.
ser.write((const uint8_t *)&mp, sizeof(mp)); is correct
you can create an identical structure mp_arduino on the arduino and copy the content of the receive buffer into this structure with
memcpy ( &mp_arduino, receive_buffer+n, sizeof(mp_arduino) ); where n indicates the position / byte in receive_buffer where your data begins and receive_buffer is defined as uint8_t receive_buffer[] ( or uint8_t* receive_buffer is the same... )
( http://www.cplusplus.com/reference/cstring/memcpy/ )
this is how a struct is stored in memory : How is a struct stored in memory?
on both systems a float should be 32 bit https://www.arduino.cc/reference/en/language/variables/data-types/float/
I'm trying to write a image via UART on Beaglebone Black. But when I use the write() function in library .
int write(int handle, void *buffer, int nbyte);
Regardless of the agurment nbyte has int type, I can not transfer 70kB at once. I displayed the number of byte which is transfered, and the result is the number of byte = 4111.
length = write(fd,body.c_str(),strlen(body.c_str())); //
cout<<length<<endl; // result length = 4111;
cout<<strlen(body.c_str())<<endl; // result strlen(body.c_str()) = 72255;
I hope to hear from you!
The write call does not assure that you can write the amount of data supplied, that's why it as an integer as its return, not a Boolean. The behavior you see is actually common among different operating systems, it may be due to the underline device might does not have sufficient buffer or storage for you to write 70kb. What you need is to write in a loop, each write will write the amount that is left unwritten:
int total = body.length(); // or strlen(body.c_str())
char *buffer = body.c_str();
int written = 0;
int ret;
while (written < total) {
ret = write(fd, buffer + written, total - written);
if (ret < 0) {
// error
break;
}
written += ret;
}
I am trying to send a message over Socket in c++. I have read many questions on stack overflow related to this but couldn't still figure out how it works. lets say i am sending following characters(M,a,r,t,i,n) to a local host server, people suggest that you can use 4 bytes as the length(i.e 32 bits, so that it can handle a message up to 4GB length).
I did the same thing at my client side but still dont know how can i figure out this thing at server side whether i want to receive only starting 3 bytes(M,a,r) or last 3 bytes(t,i,n) of my data.
I am posting my code please help me mainly in the server side, will be thankfull if can write few lines with relevance to code.
Client side code
std::vector<char> userbuffer(20);
std::cout<<"\nclient:"<<std::endl;
char* p = userbuffer.data();
*p = 'M';
++p; *p = 'a';
++p; *p = 'r';
++p; *p = 't';
++p; *p = 'i';
++p; *p = 'n';
size_t length = strlen(userbuffer.data());
uint32_t nlength = htonl(length);
//line containg message length information
int header_info = send(socketFD, (char*)&nlength, 4, 0);
// Data bytes send to the server
int bytes_sent = send(socketFD, userbuffer.data(), length, 0);
if(bytes_sent == SOCKET_ERROR){ //some errror handling}
Server Side Code
char receivebuffer[MAX_DATA] = { '\0' };
int bytesReceivedFromClientMsg = 1;
int length_bytes = 0;
uint32_t length, nlength;
//code to check length if we have received whole data length
while(length_bytes < 4){
int read = recv(clientSocket, ((char*)&nlength)+length_bytes, (4-length_bytes), 0);
if (read == -1) { //error handling}
length_bytes += read;}
// Most painfull section to understand.
// I implemented this code from some ideas on internet
//but still cant find how its extracting length and what i am reading :(
while(bytesReceivedFromClientMsg > 0){
int msgheader = recv(clientSocket,(char*)&nlength,6, 0);
length = ntohl(nlength);//leng value here is in severel thousand size
char *receivebuffer = new char(length+1);
bytesReceivedFromClientMsg = recv(clientSocket, receivebuffer, msgheader, 0);
receivebuffer[length] = 0 ;
std::cout<<"msg header is :"<<msgheader<<std::endl;
std::cout<<"msg data is :"<<bytesReceivedFromClientMsg<<std::endl;
if(bytesReceivedFromClientMsg == SOCKET_ERROR){//some error handling}
You need a design for your network protocol. There are protocols like SMTP that are text-like protocols. You have to read characters until you find a termination character like the new-line in a text-like protocol.
With a message based protocol you have better chances for high performance protocol. You define a header (that is used in your code but not defined). In the header you put information about the length and probably about the type of the next message. Then you send the header in front of the message body. The body is "Martin" in your example.
The receiver has a state "header received". When the header is not received complete (or nothting at all) it will use the size of the header as chunk size. It receives chunksize bytes into the header variable. When the header is received complete the receiver sets the chunksize to the sized the is set in the header and receives so many bytes to the payload buffer. When this has been complete the state "header received" is false again.
int receive(socket sock, char * buffer, int chunk_size)
{
int offset = 0;
while (chunk_size > 0)
{
// add select() here when you have a non-blocking socket.
int n = recv(sock, buffer+offset, chunk_size);
// TODO: error handling
offset += n;
chunk_size -= n;
}
// return amount of received bytes
return offset;
}
void do_receive(void)
{
struct {
int size;
// other message information
} header;
while (true)
{
receive(sock, &header, sizeof(header);
receive(sock, buffer, header.size);
process_message(buffer, header.size);
}
}
The code above will not pass any compiler. But it shows the idea..
I am having trouble transmitting a float across a simple 2 node Xbee Network.
I am aware that the Xbee system transmits packages via bytes, so I can send a char, but I am having trouble sending anything more than that, and I can't seem to find any documentation anywhere.
Here is my current (basic) code.
Sender:
(... appropriate setup...)
void loop()
{
sensorValue = analogRead(analogInPin);
sensorValueTemp = sensorValue / 9.31; //LM35 measurement into Centigrade
Serial.print(sensorValueTemp);
delay(1000);
}
Receiver:
(...appropriate setup...)
void loop() {
lcd.setCursor(0, 0);
if (Serial.available() > 0) {
lcd.print(incomingByte);
}
delay(1000);
}
Any hint as to get the float to be transmitted successfully would be great? Or if it is already being transmitted properly, how to read it properly?
Thanks
You can send the float in bytes and reconstruct the float at the receiver.
The following example may help you:
Sender side:
float x = 1128.476;
char b[sizeof(x)];
memcpy(b, &x, sizeof(x));
// Iterate over b and send bytes
// [...]
Receiver side:
float y = 0;
char b[sizeof(x)];
// Store 4 bytes representing the float into b
// [...]
// Rebuild the float
memcpy(&y, b, sizeof(y));
At the end, you have float y on the receiver side, which has the same representation of float x on the sender side.
You can send all the binary data you want between two machines of an identical architecture, memory layout, byte-endianness, etc. by simply taking a byte-sized pointer (char *) on the "sending" side, and iterating over the referenced object for the number of bytes in that object. On the "receiving" side, you allocate an object of the same size (float, long, struct foo), and receiving the bytes, one by one, into a byte-sized pointer which is post-incremented after each byte is received.
On the sending side --
void sendObject(const void *object, size_t size) {
char *outputCursor = (char *) object;
for (;size > 0;size--)
yourSendAByteFunction(*outputCursor++);
}
On the receiving side, assuming yourReceiveAByteFunction() return 0..255 for a valid byte and -1 for a "receiving error", you can do this --
int receiveObject(void *object, size_t size) {
char *inputCursor = (char *) object;
for (;size > 0;size--) {
int nextByte = yourReceiveAByteFunction();
if (nextByte < 0)
return FALSE;
*inputCursor++ = nextByte;
}
return TRUE;
}
You can do the same I/O error checking in the sendObject() function by declaring yourSendAByteFunction() so it returns TRUE or FALSE depending on whether or not an error occurred in the output. It all depends on how much complexity you can stand, and whether or not you have a reliable transmission link.
You can also do a bit of data encapsulation if you have bytes you can't transmit by having a "shift" byte and set of byte values that are prefixed by the "shift" byte to represent some other byte.
Your original sender is sending an ASCII string that represents the float value.
In order to receive and display the value you need to modify the lines as shown below:
(...appropriate setup...)
void loop() {
lcd.setCursor(0, 0);
while (Serial.available() > 0) { //Changed Line
incomingByte = Serial.read(); //Added Line
lcd.print(incomingByte);
}
delay(1000);
}
Note: If would be better to terminate the serial output with a CR to synchronize the devices instead of the delay(1000);
I am trying to implement a (T)LV protocol over TCP sending protocol buffers from a python client and receiving with a C++ server.
My code looks more or less like this:
char* buffer[RCVBUFSIZE];
int recvsize= 0;
// Filling my buffer with as much as possible.
while(true) {
if(recvsize == RCVBUFSIZE) {
break;
} else if(recvsize+= recv(sock, buffer+recvsize, sizeof(buffer)-recvsize, 0) < 1) {
break;
}
}
//Parsing LV protocol
while(true) {
unsigned short protosize= 0;
//Copy first two bytes into protosize
memcpy((char *) &protosize, buffer, sizeof(unsigned short));
if(protosize == 0) { break; } // Protocol indicates EOM be setting length to "0"
void* protomsg[protosize];
memcpy(protomsg, buffer+sizeof(unsigned short), protosize);
int msglength= sizeof(unsigned short)+protosize;
//Now I'll move the whole buffer to the left so that I don't have to keep track of where I'm at.
memmove(buffer, buffer+msglength, RCVBUFSIZE-msglength);
protoMsg pm;
if(!pm.ParseFromArray(protomsg, protosize)) { break; } // Parsing failed.
// Do stuff with parsed message.
}
Now I have several problems:
The while loop receiving the message never terminates. I suspect that the recv call blocks when there isn't any data left anymore while I expected it to return with an error. I have found the select function to check whether there's something to read. I will give that a try.
But when I call receive only once to skip this problem (The message received comes in at ~10 bytes, so I expect all to be collected in one call.) I get another problem:
memcpy and memmove don't seem to be working as expected. On the first loop the short is processed as expected (I receive the same value I send on the other side), but then everything from parsing of the protocol buffer fails. Have I misunderstood something?
Edit: Regarding the comment about ntohs -- I'm transmitting the short as little-endian currently, forgot to mention that. (I will change this still, btw.)
Third edit: The code now works, but I had to change the following:
char* buffer[RCVBUFSIZE];
int recvsize= 0;
// Filling my buffer with as much as possible.
while(true) {
if(recvsize == RCVBUFSIZE) {
break;
} else if((recvsize+= recv(sock, buffer+recvsize, sizeof(buffer)-recvsize, 0)) < 1) {
break;
} else if(recvsize > 1) {
unsigned short msglength= 0;
memcpy((char *) &msglength, buffer+recvsize-sizeof(unsigned short), sizeof(unsigned short));
if(msglength == 0) { break; } // Received a full transmission.
}
}
So first I needed to add brackets around the recvsize+= recv() statement and second as the non-blocking method didn't work for some reason I am now checking whether the last two bytes that were transmitted translate to a 0 when read a unsigned short. This probably leads to a problem if I read a 0 by chance that is not the length field. I'll start another question about this probably.
I also changed protomsg to char[], but I don't think this really changed anything. (I had parsing working with a void array already.. )
If the message you receive is always around 10 bytes, and RCVBUFSIZE is more than that, you will never terminate until there is a error reading data. Also, the buffer variable in your code is an array of RCVBUFSIZE pointers, probably not what you want.
Modify your code as follows:
#define MINIMALMESSAGESIZE 10 // Or what the size may be
char buffer[RCVBUFSIZE];
int totalrecvsize= 0;
int recvsize= 0;
while(true) {
if(totalrecvsize >= MINIMALMESSAGESIZE) {
break;
} else if(recvsize= recv(sock, buffer+totalrecvsize, sizeof(buffer)-totalrecvsize, 0) < 1) {
break;
} else {
totalrecvsize += recvsize;
}
}