This might be a stupid way of doing what I'm doing but I'll try to explain my thought process.
I have 2 nrf24l01 wireless transceivers for a project. The code that the professor gave us uses a char buffer[32] (32 bytes) to send messages over. This code works and everything but I'm trying to alter it to work with what I'm trying to do. I'm trying to send. I'm trying to send numbers that go up to 50,000. Since the program sends the message in characters (1 byte) the max value is 255.
Is there a way to split an integer in 2 chars and then combine them in the other arduino.
For example: Suppose I'm trying to send the value "v[0] = 65535" in binary this would be 11111111 11111111. Can I somehow split it to be "buffer[0] = 255" (in binary 11111111) and "buffer[1] = 255" (in binary 11111111)? And when it reaches the other side, it'll combine buffer[0] and buffer[1] into v[0] again?
If yes, how can I do that? If this is too much to ask, then I'll just down scale and upscale. I was hoping I wouldn't do that because I'd be losing some resolution by doing this.
I'm sorry I'm not knowledgeable enough. I realize this was written for Java. Would it work for Arduino too? Also, can you help me out with combing them again, please?
UKHeliBob:
Why not just send it rather than messing around splitting it into bytes and putting it back together on the receiving side ?
In fact, if you want to send and receive an entire array then just do it as long as its size does not exceed 32 bytes
I'm trying to send 4 different int values. They're basically the controlling values that the user inputs from the computer. The way the professor did it, is he scaled down and sent them in array form using char. I'm not really sure why he does that. He just sent us the code and didn't bother explaining. He's very hard to reach now with quarantine and uni being closed. So I'm trying to make do with what I have rather than try and mess with something break it. This is a small part of the project, so not worth investing too much time trying and testing.
There must be some logic to what he's trying to do, because the original code between a c++ program and the arduino sent through serial communication was in an int array. So the fact that he took the time, means there is a reason, either something in the code or in the hardware. I can post his code, but I was trying to avoid putting too much information that would confuse people.
aarg:
Is is forbidden to post the professor's code? We need to know at least what code (e.g. library) is being used to operate the NRF.
No not forbidden, just has other stuff in it, so I didn't want to add more confusion. Here's the code.
Arduino (PC)
#include <Arduino.h>
#include <Servo.h>
#include <avr/wdt.h> // for sw reset function
#include <SPI.h>
#include "RF24.h"
#include "printf.h" // for print details function
// set up nRF24L01 radio on SPI bus plus pins 7 and 8
RF24 radio(7,8);
byte addresses[][6] = {"1Node","2Node"};
// note you should pick a special address for each node
// on your network -- avoids interference with other groups
// if they happen to choose the same channel by accident
// byte addresses[][6] = {"1GR01","2GR01"}; // for group #1, etc.
// suggest each group use:
// channel = 95 + 2*k, where k is your group number
void setup_RF24_wireless();
void setup()
{
int i;
char buffer[32]; // RF24 wirless module max buffer size = 32 bytes
int len; // length of input data
// initialize serial port communication (usb, etc.)
// note: you have to change the communication rate in the arduino program here,
// the VC++ program, and the device driver (with device manager), and
// the serial port monitor if you use it.
// it's also a good idea to unplug and plug back in the Arduino usb cable
// after change the device driver setting to restart the driver.
Serial.begin(9600);
// Serial.begin(115200);
// Serial.begin(500000);
// set serial timeout to 1000 ms
// Serial.readBytes(...) will return after this time
// (with the wrong number of bytes)
Serial.setTimeout(1000);
setup_RF24_wireless();
// wait for start messge from PC -- this section is neccessary
// as readBytes doesn't wait properly
// -> robot waits until the PC contacts it
// -> need to turn on arduino first then run PC program
// because of this
i = 0;
while( Serial.available() == 0 ) i++;
// read start message
len = 1; // number of bytes for message from PC
Serial.readBytes(buffer,len);
delay(100);
}
void loop()
{
static char buffer[32]; // RF24 wireless module max buffer size = 32 bytes
int len; // length of input data
int i,n;
unsigned char b;
unsigned char start_char = 255; // start character for message
// -- don't use this value for anything else
// read data from PC serial connection
// wait until new data is available
// this code is good for synchronization with
// completely reliable communication but it will
// hang here (ie infinite loop) of there if
// communication is stopped from the PC
// i = 0;
// while( Serial.available() == 0 ) i++;
// reset buffers to make sure data is new
for(i=0;i<3;i++) buffer[i] = 0;
// wait for message start byte
while(1) {
len = 1; // number of bytes for message from PC
n = Serial.readBytes(buffer,len);
// check for message start byte
// note: the purpose of the start byte
// is to synchronize the message
// in case communication is interrupted,
// otherwise the inputs can get out of sync
// because we assume the inputs are in a certain
// order in the wireless message.
b = buffer[0];
if(b == start_char) break;
}
// read the rest of the incoming message
len = 2; // number of bytes for message from PC
// use buffer+1 so the data read starts at element [1]
n = Serial.readBytes(buffer+1,len);
// the previous function will return an error if
// no message in 1000 ms (1s) as specified by the serial time out
// parameter in setup.
if(n != len) {
Serial.print("\nreadBytes error");
return; // try again
}
// send 3 byte message using RF24 wireless module
len = 3;
// blocking write / send to car arduino wireless module
// the write function will check if the autoacknowledgment
// is succesful or not -- if not it will retry a certain
// number of times -- if that fails then the function returns
// a zero value
if ( !radio.write(buffer,len) ) {
Serial.print("\nwrite failed -- wasn't received / acknowledged");
}
// we won't be a dog with a bone here -- let's finish loop
// check for serial time out (ie communication fault)
// and turn off robot if that happens
// note: default serial time out on Arduino is 1s
// TODO: optionally receive message from wireless module
// and send it back to PC via serial communication
// TODO: can power down / power up reset the module ?
// powerDown();
// powerUp();
// send back the data for error checking
// this is good for debugging but it will stop the robot
// when that happens which is not good for the final version
// which you want "robust" to errors.
// Serial.write(buffer,len);
}
void setup_RF24_wireless()
{
// printf_begin(); // for print details function
radio.begin();
radio.setPALevel(RF24_PA_MAX);
// Set Power Amplifier (PA) level to one of four levels:
// RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX
// The power levels correspond to the following output levels respectively:
// NRF24L01: -18dBm, -12dBm,-6dBM, and 0dBm
// Reset value is MAX
// radio.setDataRate(RF24_250KBPS);
// reduce data rate to 250 kbps for longer range better reliability (default = 1 Mbps)
// enable auto acknowledgements on transmit -- re-transmit if errors are encountered
// as configured below with setRetries
// false will disable checking -- faster but less chance to catch errors
radio.setAutoAck(true);
radio.setRetries(5,3); // total max delay = (5 + 1) * 3 * 250 us = 4.5 ms
// more than 3 retries doesn't seem to help much
// Set the number and delay of retries upon failed submit
// first argument -- How long to wait between each retry, in multiples of 250us,
// max is 15. 0 means 250us, 15 means 4000us. 5 means 1500 us. 5 is min for 250 kbps.
// second argument -- How many retries before giving up, max 15
// note 15 was default for 2nd argument instead of 6 but that's a bit too slow IMHO
radio.setChannel(107); // valid range 0 to 125
// can use scanner example to find free channels
// set reading and writing connections / pipes
radio.openWritingPipe(addresses[0]);
radio.openReadingPipe(1,addresses[1]);
// the first argument is the reading pipe number
// a wireless module can read / listen to up to 6 pipes
// to make a star like network topology
// note pipes 0 and 1 will store a full 5-byte address. Pipes 2-5 will technically
// only store a single byte, borrowing up to 4 additional bytes from pipe #1 per the
// assigned address width.
// warning pipe 0 is also used by the writing pipe. So if you open
// pipe 0 for reading, and then startListening(), it will overwrite the
// writing pipe, so call openWritingPipe() again before write().
// print a large block of debug information
// -- useful for checking parameters and their defaults
// radio.printDetails();
}
I'm limited by 9000 characters. So I'll post the receiving end of the code when I'm allowed to post again.
#include <Arduino.h>
#include <Servo.h>
#include <avr/wdt.h> // for sw reset function
#include <SPI.h>
#include "RF24.h"
#include "printf.h" // for print details function
// set up nRF24L01 radio on SPI bus plus pins 7 and 8
RF24 radio(7,8);
byte addresses[][6] = {"1Node","2Node"};
// hall encoder pins
const int ENC_A = 2;
const int ENC_B = 3;
// actuator servos pins
const int SER_1 = 5; // servo1 pin
const int SER_2 = 6; // servo2 pin
// servo library objects
Servo Servo1;
Servo Servo2;
void write_actuators(unsigned char pw1, unsigned char pw2);
// turn off robot and stop the program
void stop_robot();
// software reset function
void sw_reset();
void setup_RF24_wireless();
void setup()
{
int i;
char buffer[32]; // RF24 wireless module max buffer size = 32 bytes
int len; // length of input data
// encoder
pinMode(ENC_A,INPUT_PULLUP);
pinMode(ENC_B,INPUT_PULLUP);
// servo objects
Servo1.attach(SER_1);
Servo2.attach(SER_2);
// for debugging with serial monitor if needed
Serial.begin(9600);
setup_RF24_wireless();
// wait for start messge from PC -- this section is neccessary
// as readBytes doesn't wait properly
// -> robot waits until the PC contacts it
// -> need to turn on arduino first then run PC program
// because of this
// must start listening to read / receive data
radio.startListening();
// wait for message from RF24 wireless module
// no timeout here since it's just a start message
// and the robot hasn't been started
i = 0;
while ( ! radio.available() ) i++;
len = 3; // number of bytes for message
// read the start message
// note the read function doesn't block so you need
// to use available function as above to block
radio.read(buffer,len);
delay(100);
}
void loop()
{
static char buffer[32]; // RF24 wireless module max buffer size = 32 bytes
int len; // length of input data
int i,n;
unsigned char b;
unsigned char start_char = 255; // start character for message
// -- don't use this value for anything else
// Arduino inputs sent from PC
unsigned char pw1, pw2;
unsigned long start_time;
// read data from RF24 wireless module
// wait until new data is available
// this code is good for synchronization with
// completely reliable communication but it will
// hang here (ie infinite loop) of there if
// communication is stopped from the PC
// TODO: read 3 byte message and check 1st byte for start byte
// discard message and flush RX buffer if 1st byte is not start byte
// note that RX does not flush automatically but write will
// result in nothing in TX FIFO -- either flush on fail
// or empty on write ack
// must start listening to read / receive data
radio.startListening();
// wait for message from RF24 wireless module
start_time = millis();
while ( ! radio.available() ) {
if( millis() - start_time > 1000 ) { // check for timeout
Serial.print("\nwireless timeout error -- stop program");
stop_robot();
delay(100); // give time for printing before stopping program
exit(0); // stop program
}
}
len = 3; // number of bytes for message
// read the start message
// note the read function doesn't block so you need
// to use available function as above to block
radio.read(buffer,len);
// first character in message is the start character
b = buffer[0];
// check for message start byte
// note: the purpose of the start byte
// is to synchronize the message
// in case communication is interrupted,
// otherwise the inputs can get out of sync
// because we assume the inputs are in a certain
// order in the wireless message.
// check for start character
if(b != start_char) {
Serial.print("\nstart character missing from message");
delay(2);
return; // try again
}
// get inputs from buffer after the start byte
pw1 = buffer[1];
pw2 = buffer[2];
// for testing / debugging
Serial.print("\n");
Serial.print(pw1);
Serial.print(" ");
Serial.print(pw2);
// saturation of Arduino inputs -- protect actuators / robot
// prevent throttle from getting too large
// 90 should be zero throttle with the ESC
// once it's been configured properly
if(pw1 > 120) pw1 = 120;
if(pw1 < 60) pw1 = 60;
// prevent steering angle from getting too large
if(pw2 > 120) pw2 = 120;
if(pw2 < 60) pw2 = 60;
// send back the data for error checking
// this is good for debugging but it will stop the robot
// when that happens which is not good for the final version
// which you want "robust" to errors.
// Serial.write(buffer,len);
// repeatedly perform the following:
// 1. Read sensors / outputs
// 2. Calculate control input
// 3. Write actuators / inputs
// write actuators
write_actuators(pw1,pw2);
}
void setup_RF24_wireless()
{
// printf_begin(); // for print details function
radio.begin();
radio.setPALevel(RF24_PA_MAX);
// Set Power Amplifier (PA) level to one of four levels:
// RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX
// The power levels correspond to the following output levels respectively:
// NRF24L01: -18dBm, -12dBm,-6dBM, and 0dBm
// Reset value is MAX
// radio.setDataRate(RF24_250KBPS);
// reduce data rate to 250 kbps for longer range better reliability (default = 1 Mbps)
// enable auto acknowledgements on transmit -- re-transmit if errors are encountered
// as configured below with setRetries
// false will disable checking -- faster but less chance to catch errors
radio.setAutoAck(true);
radio.setRetries(5,3); // total max delay = (5 + 1) * 3 * 250 us = 4.5 ms
// more than 3 retries doesn't seem to help much
// Set the number and delay of retries upon failed submit
// first argument -- How long to wait between each retry, in multiples of 250us,
// max is 15. 0 means 250us, 15 means 4000us. 5 means 1500 us. 5 is min for 250 kbps.
// second argument -- How many retries before giving up, max 15
// note 15 was default for 2nd argument instead of 6 but that's a bit too slow IMHO
radio.setChannel(107); // valid range 0 to 125
// can use scanner example to find free channels
// set reading and writing connections / pipes
radio.openWritingPipe(addresses[1]); // use [0] for other module
radio.openReadingPipe(1,addresses[0]); // use [1] for other module
// note pipes 0 and 1 will store a full 5-byte address. Pipes 2-5 will technically
// only store a single byte, borrowing up to 4 additional bytes from pipe #1 per the
// assigned address width.
// warning pipe 0 is also used by the writing pipe. So if you open
// pipe 0 for reading, and then startListening(), it will overwrite the
// writing pipe, so call openWritingPipe() again before write().
// print a large block of debug information
// -- useful for checking parameters and their defaults
// radio.printDetails();
}
void write_actuators(unsigned char pw1, unsigned char pw2)
{
int th1, th2;
th1 = pw1;
th2 = pw2;
// set servos
Servo1.write(th1);
Servo2.write(th2);
}
// turn off robot and stop the program
void stop_robot()
{
unsigned char pw1, pw2;
// set inputs to zero / neutral values
pw1 = 90;
pw2 = 90;
// write actuators
write_actuators(pw1,pw2);
// different options available below
// cli(); // disable all interrupts to suspend program
// and allow uninterrupted turning off of the robot
// stop the program, note exit(0) does the same thing
// cli(); // turn off interrupts / background functions
// while(1); // infinite do nothing loop
// or
// exit(0); // stop program
}
void sw_reset()
// software reset function using watchdog timer
{
wdt_enable(WDTO_15MS); // sets a "watchdog" timer to 15 ms
// so the arduino will reset in 15 ms if the timer is not stopped
while(1); // infinite do nothing loop -- wait for the countdown
}
His code basically takes 2 numbers (each one byte) values from a c++ program through serial communication. Then It sends 3 numbers (each one byte) to the arduino (car). The first one is an initiation character. The other two are values for the car.
What I'd like to do is send 4 numbers overs each one 2 bytes. So I'll change the len 2 to 8 and len 3 to 9.
UKHeliBob:
So put them in an array and send them all at the same time
well since the data type is char. Wouldn't that end up ruining the data as the container won't be able to handle it. For example. If I want to send 300, and char can only hold 255. Would would happen with the rest?
I think I've got what I need, but I'm not sure. Will test it. Basically, I'd do like Java example. Save the into to char, that would save the low byte, shift them 8 steps, then save the new value, that would save the high byte. And on the second arduino, I'd combine both together using this "uint16_t value = (highByte << 8) | lowbyte;" I THINK. I gotta test it out to be sure.
I wonder would this increase the time to transfer the data by much or not. If it does take way longer to transfer, then I might be better off sticking with the low step count (servo steps) over losing time.
krusion:
I wonder would this increase the time to transfer the data by much or not. If it does take way longer to transfer, then I might be better off sticking with the low step count (servo steps) over losing time.
Compared to what? Sending one byte for each value? Have you investigated how fast the data is sent over the radio? It's very fast. The framing and message protocol overhead is probably already much bigger than your 8 byte message.
Yes. That will be 5 bytes vs 9 bytes. It's supposed to be controlling a car that's running with a 20,000 rpm motor. So timing has to be just right. I guess I just need to finish the code for data transmission then I'll test both scenarios, sending 5 bytes and 9 bytes, and compare if the time difference is big or not.