Virtual Wire uint8_t Transfer Volume

hey there, im sending values from one arduino to another using the virtual wire library and some 433Mhz RxTx modules.

Tx code in question

vw_send((uint8_t *)pos, sizeof(pos));

pos is an array consisting 3 values. from what i understand, the above code will transfer 6 bytes of information, 3 for the values and 3 for their polarity (positive or negative).

so for example i say:

pos = 100;
vw_send((uint8_t *)pos, sizeof(pos));

it will send 2 bytes ---> 100(value) and 0(positive polarity)

on the Rx side i receive 100 0 when i Serial.print it.

My question is::: how do i prevent the code from sending the polarity byte, thereby reducing the total volume transferred? does it have something to do with the unsigned int?

thanks in advance!
JP

Receivers require a burst of training pulses to synchronize the transmitter and receiver, and also requires good balance between 0s and 1s in the message stream in order to maintain the DC balance of the message

So messages are sent with a training preamble (36 bit training preamble consisting of 0-1 bit pairs + 12 bit start symbol 0xB38) then the message length, message and checksum.

Messages are sent with 4-to-6 bit encoding for good DC balance, and a CRC checksum for message integrity, so everything after the start symbol is encoded 4 to 6 bits.

Therefore a byte in the message is split into two blocks of 4 bits, encoded with integrity information as 2x6 bit symbols and sent hi nibble, low nibble, LSBit first.

That's how the virtual wire library works and this design takes into account the high level of interference occurring in the 433Mhz space and hardware requirements. You have to be verbose to be robust.

Also VirtualWire uses Arduino Timer1, and this will affect the PWM capabilities of the digital pins 9 and 10. hopefully you have nothing connected there.

So the question is more - why do you find this too slow for your needs?

1/ have you tried to counter-intuitively reduce the speed of transmission to 2000 bps for example (if you bumped it)? you'd be surprised that this sometimes speeds up things because if you go too fast you increase the failure rate and things needs to be retransmitted.

2/ there are other radio that are faster, operating in the 2.4Ghz range.

thanks for the quick and in depth response,

i understand how the library takes care of the communication and the sync'ing.

some extra info:

i am using an arduino nano as Tx, it has an IMU on it giving me roll, pitch and yaw values ranging from -180 to 180. these values i mapped from 0-256 for simplicity while figuring out the code.

these are send using the virtual wire library to an arduino uno which is controlling 3 servos (on pins 9,10,11). i need the servos to move in "realtime" or as close as possible, with 2000bps the servos stuttered and it would not receive every data package sent, so i increased the value until i got a relatively smooth movement (about 6000bps) with least amount of loss.

so i need 3 values to be sent, the code sends 6 bytes of info that is strictly sensor related (excluding all the other bits and bytes that come with the transfer)

response to your question:

input to Tx - x,y,z
what i get on Rx - x,0,y,0,z,0
0 represents positive

i feel that if i somehow prevent the 0's bytes from occurring, it will decrease the total volume sent and maybe increase transfer speed. having less values received also means i have less code on the Rx side too,

wont have to "roll = pos[0], pitch = pos[ 2], yaw = pos[4]"
that kind of business

thanks!
JP

I'm not sure I'm following you

At 2000bps, your message will be sent 20 times per second. are your servos that responsive?

Also, you understand that this is wrong right?

pos = 100;
vw_send((uint8_t *)pos, sizeof(pos));

as you rightly say, pos needs to be an array. but it's not an array of int, it's an array of uint8_t (that is 8 bits).

So if you declared

int pos[3];

then you have actually declared a storage space of 6 uint8_t because each int on your arduino is represented on 2 bytes.

so if you do

int pos[3]; 
...
pos[0] = 130;
pos[1] = 180;
pos[2] = 25;
vw_send((uint8_t *)pos, sizeof(pos));

You are not sending 3 bytes, you are sending 6 (sizeof(pos)) but because your values are always between 0 and 255 they fit in the low byte of the int and thus the most significant byte is always 0.

if you declare

uint8_t pos[3]; 
...
pos[0] = 130;
pos[1] = 180;
pos[2] = 25;
vw_send((uint8_t *)pos, sizeof(pos));

then you only send 3 bytes because sizeof(pos) is 3.

makes sense?

i meant the pos bit as an example, the code im using( i changed int pos[6] to uint8_t as you pointed out! it did what i wanted):

int sendRoll;
int sendPitch;
int sendYaw;

uint8_t pos[6];


sendRoll =  map(ToDeg(roll), -180, 180, 0, 360);
sendPitch = map(ToDeg(pitch), -180, 180, 0, 360);
sendYaw = map(ToDeg(yaw), -180, 180, 0, 360);

pos[0] = sendRoll / 256;
pos[1] = sendRoll % 256;
pos[2] = sendPitch / 256;
pos[3] = sendPitch % 256;
pos[4] = sendYaw / 256;
pos[5] = sendYaw % 256;

vw_send((uint8_t *)pos, sizeof(pos));
vw_wait_tx();

i did not understand how the sizeof(uint8_t(pos)) worked, but you cleared that up for me!

2000bps makes the servos stutter, refresh rate of 50hz, if i understand "realtime" correctly i need to send the servos values at a rate of at least 50hz for them to be responsive?

when i bumped up the rate to 6000bps, in 500 increments, the stuttering was reduced.

OK - as long as the data is not lost, that you don't have a human body in the middle of the waves and you are not sending that too far, yes 6000 can work.

Some things to take into account


sendRoll =  map(ToDeg(roll), -180, 180, 0, 360);
sendPitch = map(ToDeg(pitch), -180, 180, 0, 360);
sendYaw = map(ToDeg(yaw), -180, 180, 0, 360);

so you have a value between -180 and 180 and you want that value between 0 and 360, right? you don't need to call the map function for this, just add 180 given there is no scaling factor. that will be much faster.

sendRoll = ToDeg(roll)+180;
sendPitch = ToDeg(pitch)+180;
sendYaw = ToDeg(yaw)+180;

While this seems weird at first, I would not do the vw_wait_tx() just after vw_send((uint8_t *)pos, sizeof(pos)); but before. The reason is that you have plenty to do to go get your next Roll, Pitch and Yaw values, so why wait without doing anything for the communication to occur while you could already be acquiring the next values. if by the time you come back the transmission is not over, then you'll wait before sending the new values but if it is done, then you send right away. In any case you are faster than the current version.

As the array is not duplicated by the send function, you can't mess with it so I would write

sendRoll = ToDeg(roll)+180;
sendPitch = ToDeg(pitch)+180;
sendYaw = ToDeg(yaw)+180;

vw_wait_tx(); // wait for the previous transmit to possibly terminate

// now load the buffer
pos[0] = sendRoll / 256;
pos[1] = sendRoll % 256;
pos[2] = sendPitch / 256;
pos[3] = sendPitch % 256;
pos[4] = sendYaw / 256;
pos[5] = sendYaw % 256;

vw_send((uint8_t *)pos, sizeof(pos)); // send the info

what do you do with the data on the receiving end? are you using servo.write(angle)? if so the angle can only be between 0 and 180, so there is no need to send a value between 0 and 360 which needs 2 bytes and then do some math with it on the receiving end to map that between 0 and 180. do the math before sending so that you only send something that you can use with the servo.

if you bring the value between 0 and 180, then only 1 byte is needed and you increased your transmission speed.


On the receiving end, do you test for proper reception? vw_get_message(buf, &buflen)) reads the last received message and the function returns true if the message was verified correct, or false if a message was received but appears to have been corrupted.

it would be interesting to see if you are at 100% quality, that could create the stuttering you see if some of the values are sent to the servos but are weird numbers.

Hey, thanks for the response!

true i could just add 180 instead of mapping! i was in a map hype haha.

i will also move the wait_tx as you suggested, it makes sense doing that way too!

As for the receiving end, i am using servoTimer2 library which sends pwm from like 700-2250micro seconds or something like that.

    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;

    if (vw_get_message(buf, &buflen)) // Non-blocking
    {
      values[0] = buf[0]*256+buf[1];
      values[1] = buf[2]*256+buf[3];
      values[2] = buf[4]*256+buf[5];


    }


//example mapping

mapout1 = map(values[0], 0, 360, 700, 2200);

myservo1.write(mapout1;

after that i map the values to the pwn for the servo.write

two of my servos i will limit before hand to 180 degrees, instead of the 360 ( as you also explained, was doing all the same just for testing reasons)

one of my servos is a wide angle servo that has positional rotation up to 330 degrees, i am looking for a positional full turn servo though, might have to settle for a sail winch.

OK this is equivalent to writeMicroseconds()

if you shrink the value before sending from 0 to 180, then you can map to 700 - 2200. you've lost in precision but is it that precise anyway...would see bytes in the transmission so make your code faster

thank-you for all your help, the whole system works accurately!

the data i am receiving is perfect, roll position for example is 90 degrees, it then writes 90 degrees to the servo (mapped to micro seconds) and it moves to that position..

i only have one little snag...

roll and pitch servos work perfect, no jitter or extra movements of any sorts.. except the yaw servo.. it moves a few degrees left and right lets say about 10-15 degrees left from 0 and then right, doing some wiggle dance.. ive checked the serial monitor for the values being sent to the servo, they have no deviations... and when i tell the servo to stay in that position for a while, it starts the dance once again..
as well as moving and then stopping, it does a little dance

do you have any idea of what could cause servo jitter? although it only happens on one of the 3 servos...

i have swapped the pins to see if it is a data fault, roll datapin now controls the yaw servo, and the yaw servo still dances about.

ive checked the serial monitor for the values being sent to the servo, they have no deviations... and when i tell the servo to stay in that position for a while, it starts the dance once again..
as well as moving and then stopping, it does a little dance

we will need to see some code...

the code to the Tx is what youve seen before,

this is the entire Rx code (some parts are maybe redundant). buf[2], buf[3], value[2] and myservo3 are all to do with the servo in question. i built in a filter to make the movements more smooth when moving. but the servo dances left and right a bit when it is not supposed to be moving

#include <ServoTimer2.h>
#include <VirtualWire.h>


ServoTimer2 myservo1;  // create servo object to control a servo
ServoTimer2 myservo2;  // create servo object to control a servo
ServoTimer2 myservo3;  // create servo object to control a servo

int mapout1, mapout2, mapout3;
float mapout1_filter, mapout2_filter, mapout3_filter;       //filter coeff
const int n = 5;
int values[3];

void setup()
{
  Serial.begin(115200);
  vw_set_rx_pin(12);
  vw_setup(7500);   // Bits per sec
  vw_rx_start();       // Start the receiver PLL running

  myservo1.attach(9);  // attaches the servo on pin 9 to the servo object
  myservo2.attach(10);
  myservo3.attach(11);

  myservo1.write(1450);     //init position
  myservo2.write(1450);     //init position
  myservo3.write(1450); //init position
  delay(5000);

  
}

void loop()
{

    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;

    if (vw_get_message(buf, &buflen)) // Non-blocking
    {
      values[0] = buf[0];
      values[1] = buf[1];
      values[2] = buf[2]*256+buf[3];
    }

  mapout1 = map(values[0], 0, 180, 700, 2200); 
  mapout1_filter = (mapout1_filter * (n - 1) + mapout1) / n; // filter test
  myservo1.write((int)mapout1_filter);                 

  mapout2 = map(values[1], 0, 180, 2200, 700);
  mapout2_filter = (mapout2_filter * (n - 1) + mapout2) / n; // filter test
  myservo2.write((int)mapout2_filter); 


  mapout3 = map(values[2], 0, 330, 2200, 700);
  mapout3_filter = (mapout3_filter * (n - 1) + mapout3) / n; // filter test
  myservo3.write((int)mapout3_filter); 
//
  Serial.print(mapout1_filter);
  Serial.print(" ");
  Serial.print(mapout2_filter);
  Serial.print(" ");
  Serial.print(mapout3_filter);
  Serial.println(" ");
//  Serial.println(millis());

}
  mapout3 = map(values[2], 0, 330, 2200, 700);

are you sure values[2] is in-between 0 and 330?

yeah it definitely is, the values for it in the Tx are between -180 and 180, which i mapped to 0 to 330, which i split up into two bytes for the transfer, and then merged them again.

using the serial monitor i can clearly see the values and those are always between 0 and 330, they do not jump around, the value is very stable. it does not correlate to the movement of the servo when it should be holding its position.