Virtual Wire Library questions

Ive been studying instrument op-amps and ways to filter signals for EMG's in class. I want to do a side project where I can hook up my output of my EMG's to a transmitter/reciever unit to turn on motors for a RC car. I have two dual channel EMG's hooked up to my left and right hand. I plan on analogRead(2 EMG Outputs) and then compare these values to determine which motors to power(i.e. turn left-right-forward-backward) and then send the signal to my transmitter,say the letter "F" for forward to then transmit it to my reciever.And my reciever would pick up "F" and I would then define "F" in my reciever code to turn on my motors to go forward.

I just recieved my transmitter/reciever unit(a cheap 5$ 455MHz) off ebay. I downloaded the Virtual Wire library but have been having problems understanding it.

Ive gone through quite a few websites trying to figure this out. For starters :
Where I obtained the code below

My question are in bold.

#include <VirtualWire.h>
void setup()
{
// Initialize the IO and ISR
vw_setup(2000); // Bits per sec
}
void loop()
{
send("Hello there");
delay(1000);
}
void send (char *message) <- So this sets our funciton send with a parameter with the char type/string. Under the variable message which is used below in our vw_send function.
{
vw_send((uint8_t *)message, strlen(message)); <-So this sends our message which has been assigned the string array "Hello There" and sets its length to the number of chars in the string itself??
vw_wait_tx(); // Wait until the whole message is gone
}

I have more question but i figured i start off simple and one at a time. Any help would be great.

So this sets our funciton send with a parameter with the char type/string.

It doesn't "set" anything. It implements the function...

So this sends our message which has been assigned the string array "Hello There" and sets its length to the number of chars in the string itself??

The terminology is a bit strange, but, yes. The method sends the array in which the string "Hello There" has been stored. The second argument is the length of the string, so the method knows how many bytes to send.

This is a working example of sending the data from a 3-axis accelerometer to a receiver which then prints the values to the Serial monitor.

Transmitter:

#include <VirtualWire.h>

char Array[20];
int X,Y,Z,State;

void setup()
{
  Serial.begin(115200);	  // Debugging only
  Serial.println("Sending"); // Debugging only

  //vw_set_ptt_inverted(true); // Required for DR3100
  vw_setup(2000);	 // Bits per sec
  vw_set_tx_pin(7); //RF sending pin Arduino Uno

  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  digitalWrite(5, LOW); // RF ground
  digitalWrite(6,HIGH); // RF Vcc
}

void loop()
{ 
  X = analogRead(A2) / 4; // X raw data divided by 4 gives 0 - 255
  Y = analogRead(A1) / 4; // -------------------------------------
  Z = analogRead(A0) / 4; // -------------------------------------
  
  sprintf(Array, "%d,%d,%d.",X,Y,Z); // format the values into a string to then be sent out.
  vw_send((uint8_t*)Array, strlen(Array));
  vw_wait_tx(); 
}

Receiver:

#include <VirtualWire.h>

void setup()
{
  Serial.begin(115200);	// Debugging only
  Serial.println("Receiving");
  
  vw_setup(2000);	 // Bits per sec
  vw_set_rx_pin(8);
  vw_rx_start();       // Start the receiver PLL running
}

void loop()
{
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;
  
  if(vw_get_message(buf, &buflen))
  {
    int X = atoi(strtok((char*)buf, ","));  // Look for a comma, the return the data before it.
    int Y = atoi(strtok(NULL, ",")); // same as above
    int Z = atoi(strtok(NULL, ".")); // Look for a period, then return data before it.
    Serial.print(X); // X axis
    Serial.print(", ");
    Serial.print(Y); // Y axis
    Serial.print(", ");
    Serial.println(Z); // Z axis
  }
}

Read up on strtok() and atoi() to see what they are doing.

I looked up strtok() and atoi() and get the jest of it. But ive never seen

sprintf(Array, "%d,%d,%d.",X,Y,Z);

and im guessing

int X = atoi(strtok((char*)buf, ",")); // Look for a comma, the return the data before it.
int Y = atoi(strtok(NULL, ",")); // same as above
int Z = atoi(strtok(NULL, ".")); // Look for a period, then return data before it.

I can sorta understand, breaking them into tokens and then converting their types to int's. But in the sprintf above does this :sprintf(Array, "%d,%d,%d.",X,Y,Z); set each value x,y,z to the string "%d,%d,%d." since in our atoi(strtok part, we look for stuff before the first two commas and a period.And this is the only part of the string where i can see two commas and a period. And htis is helping so much. I can basically just get rid of the Z and copy and paste but id very much like to fully understand the fundmentals of htis.

Using sprintf() to convert the ints to strings, after using strtok() and atoi() to convert the string to int hardly makes sense.

sprintf()
What sprintf does is it takes your data(ints, chars, floats...) and convert it into individual elements in the array provided.

sprintf(Array, "%d,%d,%d.",X,Y,Z);

char Array[20]; <- declare an array with room for 20 elements (big enough for the created string)
"%d,%d,%d." <- this is the string that we will be creating, and then storing into "Array"
%d <- type of data. (%d = decimal or integer, %c = char, %s = String(if I remember correctly), %f = float...) More Here

Strtok() will split the incoming sting based on the delimiters, so the first and second were commas, and the last was a period. And the function will read through the string and find a delimiter and return whatever was before it, but after the previous delimiter (if there was one).

Atoi will convert an array of ascii characters into an integer.

Using sprintf() to convert the ints to strings, after using strtok() and atoi() to convert the string to int hardly makes sense.

Do you have something better?

I do know how to do the same thing with structs, but it does appear to be a slower method.

Do you have something better?

Maybe I'm missing something, but taking a string apart, diddling with the parts, and then putting the string back together exactly the way it was hardly seems useful.

sprintf(Array, "%d,%d,%d.",X,Y,Z); So looking at its syntax, if I just needed to store and place two seperate sensor values in the array it'd go sprintf(Array, "%d,%d.",X,Y,); correct?

and then putting the string back together exactly the way it was hardly seems useful.

I am splitting the string back into its original parts, then printing them out separately, just to show that they were separated.

I could have easily just done a Serial.println(buf) and that was it, but I wanted to show them individually.

@geryuu11
Correct.

Edit:
sprintf(Array, "%d,%d.",X,Y,); extra comma may be problematic.

There are other ways to split the sent string and convert it to integers with less overhead, but this way is, I guess you can say the quick and dirty way.

Thanks for the help everyone, cant wait to get home and try it on the arduino. Also couldnt i just use the atoi function to convert the string into ints and leave it at that?

Also couldnt i just use the atoi function to convert the string into ints and leave it at that?

the whole string or just after it is split?

If you do the whole string atoi(buf), then it will convert everything, even the commas and period into one integer.

If your talking about after the string was split into its individual parts then use atoi without showing it in the serial monitor, yes, you can do that.

Alternative:
Not tested, but if it doesn't work, I'm sure I hear about it from PaulS, AWOL, PeterH or someone else.

#include <VirtualWire.h>
unsigned int * myData;

void setup()
{
  Serial.begin(115200);	// Debugging only
  Serial.println("setup");
  vw_setup(2000);	 // Bits per sec
  vw_set_rx_pin(2);     // receiver pin
  vw_rx_start();       // Start the receiver PLL running
}

void loop()
{ 
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

  if (vw_get_message(buf, &buflen)) // Non-blocking
  {
    myData = Split_and_Convert(buf, 3, ',', '.');
    Serial.print(myData[0]);
    Serial.print(" ");
    Serial.print(myData[1]);
    Serial.print(" ");
    Serial.println(myData[2]);
  }
}

unsigned int * Split_and_Convert(uint8_t * data, byte Expecting_ints_returned, char Splitting_Delimiter, char End_Delimiter)
{
  uint16_t output[Expecting_ints_returned];
  uint8_t x = 0;
  boolean done = false;
  
  while(done == false) // while there is still things to do
  {
    if(*data != End_Delimiter) // if the current char is not the End_Delimiter, keep going
    {
      if(*data != Splitting_Delimiter)
        output[x] = output[x] * 10 + (*data - '0'); // convert the individual chars to an int
      
      else // if the Splitting_Delimiter was found, increment the output index for the next int
        x++;

      *data++; // increment the buffer
    }
    else //current char matched the End_Delimiter, stop
      done = true;
  }
  return output;  
}

But ive never seen

sprintf(Array, "%d,%d,%d.",X,Y,Z);

and im guessing

Don't guess. Look it up. Is google broken in your country ?

sprintf(Array, "%d,%d,%d.",X,Y,Z); So looking at its syntax, if I just needed to store and place two seperate sensor values in the array it'd go sprintf(Array, "%d,%d.",X,Y,); correct?

the point that you missed here, and hazardmind did a poor job of explaining, it that you are not putting the two sensor values X and Y into an array ( of sensor values ).

You are effectively "printing" them into an array of text.

So if your ( integer ) sensor values are 12 and 27, if you were putting them into an array, you would have something equivalent to

   int Array[2] = { 12, 27 };

But that is not what sprintf( ) does. sprintf( ) does the same thing as printf, except the output goes into a char buffer instead of your output/display device.

So, after you call sprintf, your result looks like this:

char Array[20] ;
sprintf( Array, "%d,%d,%d", X,Y,Z } ;

// is equivalent to

char Array[] = { '1', '2', ',' , '2', '7', ',' , '3', '4', ...  };

//   The elements in Array[] here are individual ascii characters representing the text representation
//   of your sensor value numbers.

if it doesn't work, I'm sure I hear about it

Probably, yes.

If you do the whole string atoi(buf), then it will convert everything, even the commas and period into one integer.

Wrong again.

atoi(buf) will look through buf, and try to find one or more characters which can be interpreted as an integer, and as soon as it comes to a character which cannot be part of an integer, it will stop looking, and return what it has.

sprintf( ) is NOT an alternative method to using atoi( )

sprintf( ) does the OPPOSITE of what atoi( ) does.

Were you talking to me, because that I know.

What is the speed difference between using strtok() and sscanf() ?

This would be much easier write but more confusing to another person:

sscanf (buf, "%d%*c%d%*c%d%*c", &X, &Y, &Z); // 12,345,6789.

There was an issue with the code, output should have been global and made sure every element was set to zero.

Version 1:

#include <VirtualWire.h>
unsigned int *myData;
unsigned int output[3];

void setup()
{
  Serial.begin(115200);	// Debugging only
  Serial.println("setup");
  vw_setup(2000);	 // Bits per sec
  vw_set_rx_pin(2);     // receiver pin
  vw_rx_start();       // Start the receiver PLL running
}

void loop()
{ 
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

  if (vw_get_message(buf, &buflen)) // Non-blocking
  {
    myData = Split_and_Convert(buf, 3, ',', '.');

    Serial.print(myData[0]);
    Serial.print(" ");
    Serial.print(myData[1]);
    Serial.print(" ");
    Serial.println(myData[2]);
  }
}

unsigned int * Split_and_Convert(uint8_t * data, byte Expecting_ints_returned, char Splitting_Delimiter, char End_Delimiter)
{
  uint8_t x = 0;
  boolean done = false;

  for(byte j = 0; j < Expecting_ints_returned; j++) // can't assume the array will be set to all zeros
    output[j] = 0;

  while(done == false) // while there is still things to do
  {
    if(*data != End_Delimiter) // if the current char is not the End_Delimiter, keep going
    {
      if(*data != Splitting_Delimiter)
        output[x] = output[x] * 10 + (*data - '0'); // convert the individual chars to an int

      else // if the Splitting_Delimiter was found, increment the output index for the next int
        x++;

      *data++; // increment the buffer
    }
    else //current char matched the End_Delimiter, stop
      done = true;
  }
  return output;  
}

Version 2: minor tweaks

#include <VirtualWire.h>
uint16_t myArray[3];

template<class E, size_t size>
size_t arraySize(E(&)[size])
{
  return size;
}

template<typename T, size_t N>
void Split_and_Convert(uint8_t * data, T (&output)[N], char Splitting_Delimiter, char End_Delimiter)
{
  uint8_t x = 0;
  boolean done = false;

  for(byte j = 0; j < N; j++) // can't assume the array will be all zeros
    output[j] = 0;

  while(done == false) // while there is still things to do
  {
    if(*data != End_Delimiter) // if the current char is not the End_Delimiter, keep going
    {
      if(*data != Splitting_Delimiter)
        output[x] = output[x] * 10 + (*data - '0'); // convert the individual chars to an int
      else // if the Splitting_Delimiter was found, increment the output index for the next int
        x++;

      *data++; // increment the buffer
    }
    else //current char matched the End_Delimiter, stop
      done = true;
  } 
}

void setup()
{
  Serial.begin(115200);	// Debugging only
  Serial.println("setup");
  vw_setup(2000);	 // Bits per sec
  vw_set_rx_pin(2);     // receiver pin
  vw_rx_start();       // Start the receiver PLL running

  Serial.println(arraySize(myArray));

}

void loop()
{ 
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

  if (vw_get_message(buf, &buflen)) // Non-blocking
  {
    Split_and_Convert(buf, myArray, ',', '.');

    Serial.print(myArray[0]);
    Serial.print(" ");
    Serial.print(myArray[1]);
    Serial.print(" ");
    Serial.println(myArray[2]);
  }
}

(It took me a while to figure out why the compiler couldn't find the functions, it was driving me nuts)

What is the speed difference between using strtok() and sscanf() ?

sscanf() can be used to parse well defined strings. strtok() is used to parse unknown strings, where a token defines what to expect next.

Given a string like "050, 100, 150", sscanf() can easily deal with that. Given a string like "X:050, Y:100, Z:150", sscanf() can deal with that, as well as strtok(). But, change that string to "X:050, Z:100, Y:150" and sscanf() will still assume that the values are for X, Y, and Z in that order, while strtok() can be used to tokenize and use the string, and correctly assign the right values to the right variables.