Below is the code being used by Arduino to extract two integers at a time, from formatted text in the serial. The format being two numbers. each followed by a comma, eg 51,7, After reading in the ints, it is supposed to do stuff with them, then loop back and go again, unless there is no data, then it is supposed to skip past the if and do stuff using the current int values.
void setup() {
Serial.begin(9600);
}
char rx_byte = 0;
String rx_str[2] = {""};
int nums[2] = {0}, a;
int PIN = 13;
boolean not_read = true;
void loop() {
if (Serial.available() > 0) { // is a character available?
memset(nums, 0, sizeof(nums));
rx_str[0] = "";
rx_str[1] = "";
a = 0;
while( a < 2 ){
not_read = true;
while(not_read){
rx_byte = Serial.read(); // get the character
if ((rx_byte >= '0') && (rx_byte <= '9')) {
rx_str[a] += rx_byte;
} // if ((rx_byte >= '0') && (rx_byte <= '9'))
else if (rx_byte == ',') {
not_read = false;
a++;
} // else if (rx_byte == ',')
} // while(not_read)
} // while(int a < 2)
nums[0] = rx_str[0].toInt();
nums[1] = rx_str[1].toInt();
} // if (Serial.available() > 0)
// do stuff with ints
} // void loop
This turnout works fine when the Arduino is plugged into the usb and the right data is entered into the serial monitor. The problem is, when the data is written by another program in c, using bluetooth with an HC-05-1 module, when there is no data, arduino hangs at the if statement waiting for serial input, instead of skipping past and doing stuff. Below is the C++ code for writing the data.
void right( double times[3] ) {
nums[0] = (int) times[0];
nums[1] = (int) times[1];
int bytes_written = 0; // bytes_read = 0;
printf( "\n nums[0]: %d ", nums[0]);
printf( "\n nums[1]: %d ", nums[1]);
sprintf( buf, "%d,%d,", nums[0],nums[1] );
printf( "\n writing ");
puts(buf);
bytes_written = write(s, buf, sizeof(buf));
printf("\n %d Bytes written to bluetooth", bytes_written);
printf("\n +----------------------------------+\n\n");
}
And this is sample output from function right()
nums[0]: 4
nums[1]: 2
writing 4,2,
15 Bytes written to bluetooth
+----------------------------------+
I dont kmow how relevant this is but here some output from function right() when the bluetooth and board are not powered up.
nums[0]: 16
nums[1]: 5
writing 16,5,
16,5,
15 Bytes written to bluetooth
+----------------------------------+
Not sure where that extra line of output comes from. Last week it used to output 0 Bytes written to bluetooth without the extra line, but changes have been made since then.
Edit....
Patrick Trentin's diagnosis was right.
sprintf( buf, "%d,%d,", nums[0],nums[1] );
needed to be changed to
int str_len = sprintf( buf, "%d,%d,", nums[0],nums[1] );
and
bytes_written = write(s, buf, sizeof(buf));
needed to be changed to
bytes_written = write(s, buf, str_len);
It was a bit disappointing how long the read process took, so some pretty serious optimization is needed, but it does seem to do what was intended... eventually.
Related
right now, I am currently trying to output the contents of buf.mtext so I can make sure take the correct input before moving on with my program. Everything seems to work fine, except one thing; msgrcv() puts garbage characters into the buffer, and the reciever process outputs garbage characters.
Here is my sender process:
int main (void)
{
int i; // loop counter
int status_01; // result status
int msqid_01; // message queue ID (#1)
key_t msgkey_01; // message-queue key (#1)
unsigned int rand_num;
float temp_rand;
unsigned char eight_bit_num;
unsigned char counter = 0;
unsigned char even_counter = 0;
unsigned char odd_counter = 0;
srand(time(0));
struct message {
long mtype;
char mtext[BUFFER_SIZE];
} buf_01;
msgkey_01 = MSG_key_01; // defined at top of file
msqid_01 = msgget(msgkey_01, 0666 | IPC_CREAT)
if ((msqid_01 <= -1) { exit(1); }
/* wait for a key stroke at the keyboard ---- */
eight_bit_num = getchar();
buf_01.mtype = 1;
/* send one eight-bit number, one at a time ------------ */
for (i = 0; i < NUM_REPEATS; i++)
{
temp_rand = ((float)rand()/(float)RAND_MAX)*255.0;
rand_num = (int)temp_rand;
eight_bit_num = (unsigned char)rand_num;
if ((eight_bit_num % 2) == 0)
{
printf("Even number: %d\n", eight_bit_num);
even_counter = even_counter + eight_bit_num;
}
else
{
printf("Odd number: %d\n", eight_bit_num);
odd_counter = odd_counter + eight_bit_num;
}
/* update the counters ------------------------------ */
counter = counter + eight_bit_num;
if((eight_bit_num % 2) == 0) { even_counter = even_counter + eight_bit_num; }
else { odd_counter = odd_counter + eight_bit_num; }
buf_01.mtext[0] = eight_bit_num; // copy the 8-bit number
buf_01.mtext[1] = '\0'; // null-terminate it
status_01 = msgsnd(msqid_01, (struct msgbuf *)&buf_01, sizeof(buf_01.mtext), 0);
status_01 = msgctl(msqid_01, IPC_RMID, NULL);
}
Here is my receiver process:
int main() {
struct message {
long mtype;
char mtext[BUFFER_SIZE];
} buf;
int msqid;
key_t msgkey;
msgkey = MSG_key_01;
msqid = msgget(msgkey, 0666); // connect to message queue
if (msqid < 0) {
printf("Failed\n");
exit(1);
}
else {
printf("Connected\n");
}
if (msgrcv(msqid, &buf, BUFFER_SIZE, 0, 0) < 0) { // read message into buf
perror("msgrcv");
exit(1);
}
printf("Data received is: %s \n", buf.mtext);
printf("Done receiving messages.\n");
return 0;
}
The output is usually something like as follows:
Data received is: ▒
Done receiving messages.
I have made sure to clear my message queues each time after running the sender and receiver processes, as well, since I have come to find out this can cause issues. Thanks in advance for your help.
Turns out neither of the suggested solutions were the issue, as I suspected; the sender process actually works just fine. The problem was that I was trying to print buf.mtext instead of buf.mtext[0] which isn't an actual integer value. I fixed the issue by just doing this:
int temp_num = buf.mtext[0];
printf("Data recieved is %d \n", temp_num);
I assume that for messages that are of only 1 byte (a char), I will use read() and write() directly.
For those messages having size > 1 bytes, I use two subfunctions to read and write them over sockets.
For example, I have the server construct a string called strcities (list of city) and print it out --> nothing strange. Then send the number of bytes of this string to the client, and then the actual string.
The client will first read the number of bytes, then the actual city list.
For some reason my code sometimes work and sometimes doesn't. If it works, it also prints out some extra characters that I have no idea where they come from. If it doesn't, it hangs and forever waits in the client, while the server goes back to the top of the loop and wait for next command from the client. Could you please take a look at my codes below and let me know where I did wrong?
Attempt_read
string attempt_read(int rbytes) { // rbytes = number of bytes of message to be read
int count1, bytes_read;
char buffer[rbytes+1];
bool notdone = true;
count1 = read(sd, buffer, rbytes);
while (notdone) {
if (count1 == -1){
perror("Error on write call");
exit(1);
}
else if (count1 < rbytes) {
rbytes = rbytes - count1; // update remaining bytes to be read
count1 = read(sd, buffer, rbytes);
}
else {notdone = false;}
} // end while
string returnme;
returnme = string(buffer);
return returnme;
}
Attempt_write
void attempt_write(string input1, int wbytes) { // wbytes = number of bytes of message
int count1;
bool notdone = true;
count1 = write(sd, input1.c_str(), wbytes);
while (notdone) {
if (count1 == -1){
perror("Error on write call");
exit(1);
}
else if (count1 < wbytes) {
wbytes = wbytes - count1;
count1 = write(sd, input1.c_str(), wbytes);
}
else {notdone = false;}
} // end while
return;
}
1) string class has a method size() that will return the length of the string, so you do not actually need a second attempt_write parameter.
2) You can transfer length of message before message or you can transfer a terminating 0 after, if you only will sent an ASCII strings. Because your connection could terminate at any time, it is better to send exact length before sending the string, so your client could know, what to expect.
3) What compilator do you use, that would allow char buffer[rbytes+1]; ? A standard c++ would require char buffer = new char[rbytes+1]; and corresponding delete to avoid a memory leaks.
4) In your code, the second read function call use same buffer with no adjustment to length, so you, practically, overwrite the already received data and the function will only work, if all data will be received in first function call. Same goes for write function
I would suggest something like this:
void data_read(unsigned char * buffer, int size) {
int readed, total = 0;
do {
readed = read(sd, buffer + total, size - total);
if (-1 == writted) {
perror("Error on read call");
exit(1);
}
total += readed;
} while (total < size);
}
string attempt_read() {
int size = 0;
data_read((unsigned char *) &size, sizeof(int));
string output(size, (char) 0x0);
data_read((unsigned char *) output.c_str(), size);
return output;
}
void data_write(unsigned char * buffer, int size) {
int writted, total = 0;
do {
writted = write(sd, buffer + total, size - total);
if (-1 == writted) {
perror("Error on write call");
exit(1);
}
total += writted;
} while (total < size);
}
void attempt_write(string input) {
int size = input.size();
data_write((unsigned char *) &size, sizeof(int));
data_write((unsigned char *) input.c_str(), size);
}
I have the following simple code to read commands sent from the computer to an Arduino Mega board:
void get_command()
{
char received;
int index = 0;
while (Serial.available() > 0) {
if (index < BUFFER_SIZE -1) {
received = Serial.read();
if (received == '\n') {
buffer[index] = '\0';
parse_command(buffer);
} else {
buffer[index++] = received;
}
} else {
Serial.println('666'); // buffer overflow
}
}
}
The commands are like A123 B123 C123 D123\n
Basically a sequence of space separated instructions, were each instruction starts by a letter and follows by a number. A command ends with a "\n".
Reading this seems to be very unstable. Sometimes I get the command perfectly, sometimes I miss the first letter, sometimes a pair of them, sometimes it starts by the number,...
I am sending the commands through the serial monitor, set to newline.
Before having this code, I'd simply check for the size using Serial.available, then Id wait a second to fill the buffer, and then I'd copy the buffer to a char*. This worked flawlessly. I am doing now this loop waiting for a \n, which seems more elegant, but it is being very unstable.
Code works by simply adding a delay(100) after the first Serial.available()
This is the code:
void get_command()
{
char received;
if (Serial.available() > 0) {
delay(100); // let the buffer fill up
int index = 0;
while (Serial.available() > 0) {
if (index < BUFFER_SIZE -1) {
received = Serial.read();
if (received != '\n') {
buffer[index] = received;
index++;
} else {
buffer[index] = '\0';
parse_command(buffer);
}
} else {
Serial.println('666'); // buffer overflow
}
}
}
}
I am trying to read data from an Arduino serial port. My current code:
if(Serial.available()>0){
if(Serial.available()==9){...}
When I type 9 characters in the serial monitor it works fine.
So when I add a second if in the
if(Serial.available()>0)
after the
if(Serial.available()==9){}
it recognizes each character as a single reading. For example when i type 4 characters it says Serial.available()=1 4 times.
real code:
if(Serial.available() > 0){
if(Serial.available()==9){
for(int i =0;i<9;i++){
RGB[i]=Serial.read() - '0';
}
//get the data from the integer array
R= RGB[0]*100+RGB[1]*10+RGB[2];
G= RGB[3]*100+RGB[4]*10+RGB[5];
B= RGB[6]*100+RGB[7]*10+RGB[8];
for(int q=0; q<=255; q++){
if(R>Rp){
Rp+=1;
analogWrite(8, Rp);
}else if(R<Rp){
Rp-=1;
analogWrite(8, Rp);
}
if(G>Gp){
Gp+=1;
analogWrite(9, Gp);
}else if(G<Gp){
Gp-=1;
analogWrite(9, Gp);
}
if(B>Bp){
Bp+=1;
analogWrite(10, Bp);
}else if(B<Bp){
Bp-=1;
analogWrite(10, Bp);
}
delay(10);
}
}
// if(Serial.read()=='r'){
// if(readinglstate==0){
// analogWrite(readinglight, 5);
// readinglstate=1;
// }else if(readinglstate==1){
// analogWrite(readinglight, 70);
// readinglstate=2;
// }else if(readinglstate==2){
// analogWrite(readinglight, 255);
// readinglstate=3;
// }else if(readinglstate==3){
// analogWrite(readinglight, 0);
// readinglstate=0;
// }
// }
}
The commented code is the one that changes the things...
The
Serial.read()=='r'
is popping the byte off the receive buffer. Think of it more like...
input = Serial.read(); // pop the next byte off, regardless of its value.
if (input == 'r') {
hence all your bytes in the buffer are read off the buffer. until "Serial.available() == 0"
I recommend the peek function.
if(Serial.peek()=='r'){
Serial.read(); // already know it, so pop it.
if(readinglstate==0){
...
Input data can arrive in several chunks, so you need to slightly rework approach to read data, please check the following code for reference:
int bytesToRead = 0;
int currBytePtr = 0;
int RGB[9];
while (9 > currBytePtr) { // we need 9 bytes of data
if (0 < (bytesToRead = Serial.available())) { // have something to read
for (int i = 0; i < bytesToRead; ++i ) {
RGB[currBytePtr++] = Serial.read();
if (9 == currBytePtr) { // we have enough data
break;
}
}
} else {
delay(1000); // sleep a bit
}
} // while
// process data received
...
why not just read the data till you have 9 bytes then process.
char buffer[10];
int index=0;
while (Serial.available()){
buffer[index++]=Serial.read();
if (index>8) {
ProcessData(buffer);
index=0;
}
}
I am trying to send large amounts of data over a socket, sometimes when I call send (on Windows) it won't send all the data I requested, as expected. So, I wrote a little function that should have solved my problems- but it's causing problems where the data isn't being sent correctly and causing the images to be corrupted. I'm making a simple chat room where you can send images (screenshots) to each other.
Why is my function not working?
How can I make it work?
void _internal_SendFile_alignment_512(SOCKET sock, BYTE *data, DWORD datasize)
{
Sock::Packet packet;
packet.DataSize = datasize;
packet.PacketType = PACKET_FILETRANSFER_INITIATE;
DWORD until = datasize / 512;
send(sock, (const char*)&packet, sizeof(packet), 0);
unsigned int pos = 0;
while( pos != datasize )
{
pos += send(sock, (char *)(data + pos), datasize - pos, 0);
}
}
My receive side is:
public override void OnReceiveData(TcpLib.ConnectionState state)
{
if (state.fileTransfer == true && state.waitingFor > 0)
{
byte[] buffer = new byte[state.AvailableData];
int readBytes = state.Read(buffer, 0, state.AvailableData);
state.waitingFor -= readBytes;
state.bw.Write(buffer);
state.bw.Flush();
if (state.waitingFor == 0)
{
state.bw.Close();
state.hFile.Close();
state.fileTransfer = false;
IPEndPoint ip = state.RemoteEndPoint as IPEndPoint;
Program.MainForm.log("Ended file transfer with " + ip);
}
}
else if( state.AvailableData > 7)
{
byte[] buffer = new byte[8];
int readBytes = state.Read(buffer, 0, 8);
if (readBytes == 8)
{
Packet packet = ByteArrayToStructure<Packet>(buffer);
if (packet.PacketType == PACKET_FILETRANSFER_INITIATE)
{
IPEndPoint ip = state.RemoteEndPoint as IPEndPoint;
String filename = getUniqueFileName("" + ip.Address);
if (filename == null)
{
Program.MainForm.log("Error getting filename for " + ip);
state.EndConnection();
return;
}
byte[] data = new byte[state.AvailableData];
readBytes = state.Read(data, 0, state.AvailableData);
state.waitingFor = packet.DataSize - readBytes;
state.hFile = new FileStream(filename, FileMode.Append);
state.bw = new BinaryWriter(state.hFile);
state.bw.Write(data);
state.bw.Flush();
state.fileTransfer = true;
Program.MainForm.log("Initiated file transfer with " + ip);
}
}
}
}
It receives all the data, when I debug my code and see that send() does not return the total data size (i.e. it has to be called more than once) and the image gets yellow lines or purple lines in it — I suspect there's something wrong with sending the data.
I mis-understood the question and solution intent. Thanks #Remy Lebeau for the comment to clarify that. Based on that, you can write a sendall() function as given in section 7.3 of http://beej.us/guide/bgnet/output/print/bgnet_USLetter.pdf
int sendall(int s, char *buf, int *len)
{
int total = 0; // how many bytes we've sent
int bytesleft = *len; // how many we have left to send
int n = 0;
while(total < *len) {
n = send(s, buf+total, bytesleft, 0);
if (n == -1) {
/* print/log error details */
break;
}
total += n;
bytesleft -= n;
}
*len = total; // return number actually sent here
return n==-1?-1:0; // return -1 on failure, 0 on success
}
You need to check the returnvalue of send(). In particular, you can't simply assume that it is the number of bytes sent, there is also the case that there was an error. Try this instead:
while(datasize != 0)
{
n = send(...);
if(n == SOCKET_ERROR)
throw exception("send() failed with errorcode #" + to_string(WSAGetLastEror()));
// adjust pointer and remaining number of bytes
datasize -= n;
data += n;
}
BTW:
Make that BYTE const* data, you're not going to modify what it points to.
The rest of your code seems too complicated, in particular you don't solve things by aligning to magic numbers like 512.