How to send large amounts of data with the help of Virtual Wire?

Dear Arduino Community,

Nowadays I have started deal with Arduino projects. I would like to parse high-rated raw sensor readings in my computer.
I have got two Arduino Uno board, one MPU-6050 sensor and one RF link unit. The sensor and RF transmitter are attached one of the Arduino board, which won’t be connected to the PC. The RF receiver is soldered to the other board, which is linked to the PC so that I can parse the raw readings.

I would like to send the whole sensor measurements to my computer. Surfing on the Internet everybody suggested the Virtual Wire for this case. But my problem is that the Virtual Wire didn’t able to transmit the whole sensor records. There is a limitation in the code (see: [1]).

Could anybody help me or suggest a hint how to solve/avoid this problem?

Her is my code which is based on the sketch of JohnChi [2] and Palet J M [3] with slight change of modification.

#include <Wire.h>
#include <VirtualWire.h>                        // RF transmitter library

#define MPU 0x68 // I2C address of the MPU-6050
const int time_step=200; // time interval in msec
unsigned long Time=0;  // Time in msec
unsigned long Count=1; // Data counter
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;

void setup(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
  pinMode(13,OUTPUT);                           // Port 13, LED to check data transmission
  vw_set_ptt_inverted(true); 
  vw_set_tx_pin(12);                            // Emitter digital data pin
  vw_setup(4800);                               // Data transfer velocity (bps)
}

void loop(){
  Time=millis();
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  
  String separator="|";
  String Signal;
  Signal = Count + separator + AcX + separator + AcY + separator + AcZ + separator + GyX + separator + GyY + separator + GyZ + separator + Tmp + separator + millis();
  int Signal_length = Signal.length() + 1;
  char Signal_Char[Signal_length];  // Variable which content will be sent through the antenna
  Signal.toCharArray(Signal_Char,Signal_length);

  digitalWrite(13, HIGH); // Flash a light to show transmitting  
  vw_send((uint8_t *)Signal_Char,strlen(Signal_Char)); // Variable content sending
  int a=strlen(Signal_Char);

  Serial.print(Signal_Char); Serial.print("|"); Serial.print(Signal_length); Serial.print("|"); Serial.println(a);

  vw_wait_tx(); // Pause for letting the emitter complete the transmission
  digitalWrite(13,LOW); // Turn off LED on port 13 for checking data transmission
  Count=Count+1; // Increase the data counter by 1
  delay(time_step);
}

( I tried that to set VW_MAX_MESSAGE_LEN variable up to 100 (The default was 30) in the VirtualWire.h file, but nothing has changed.)

[1] http://forum.arduino.cc/index.php?topic=185688.0
[2] http://playground.arduino.cc/Main/MPU-6050#short
[3] GitHub - jmpalet/ArduinoRemoteSensorDataTransmission: Data transmission of a temperature sensor between two Arduino boards and live plotting in Matlab

Why do you think the message length is the problem?

Use of Strings is a bad idea as this can cause memory problems, and I don't think the idea of concatenating Strings with binary variables will work anyway. Take a closer look at what the code in your reference [3] is doing.

I suggest to pack the 18 or so data bytes into an array and send that.

How the hell much data do you need to send over?!

Oh, I see... you're doing it like you were sending it over serial to a human reader (I'm also not sure that concatenation is right - it certainly is in a civilized language, but this is C), in the process adding massive overhead - don't do that.

1) When using virtualwire, as well as any other method of using those el-cheapo OOK RF modules, there's a premium on every byte you send. Minimizing the number of bytes sent is important. You are only sending 14 bytes of data. So make an array of 14 bytes, and send it.

2) does it start sending in the background (with interrupts) when you do vw_send()? If so, I'd avoid printing to the serial until it's done, since serial is done using interrupts too.

Have you done a basic test of virtualwire to make sure you're using it correctly, and that you can receive simple packets?

Here is a sketch with your data organized in a struct and sent as bytes. The struct is put back together using memcpy.

Send

#include <VirtualWire.h>

struct {
  unsigned long Count;
  int AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;
  unsigned long Time;
}
data;

void setup()
{
  vw_setup(2000); // Bits per sec
}
void loop()
{
  data.Count;
  data.AcX = 123;
  data.AcY = 234;
  data.AcZ = 345;
  data.Tmp = 456;
  data.GyX = 567;
  data.GyY = 678;
  data.GyZ = 789;
  data.Time = millis();

  long interval = 1000; //send every second
  static unsigned long millisLast = millis();

  if (millis() - millisLast > interval) {
    vw_send((uint8_t*) &data, sizeof(data));
    vw_wait_tx();

    millisLast += interval;
    ++data.Count;
  }
}

Receive

#include <VirtualWire.h>

struct {
  unsigned long Count;
  int AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;
  unsigned long Time;
}
dataReceived;

unsigned long lastCount;

void setup()
{
  Serial.begin(9600);
  Serial.println("setup");

  vw_setup(2000); // Bits per sec
  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
  {
    memcpy (&dataReceived, buf, buflen);
  }
  if (dataReceived.Count != lastCount) {
    Serial.print("Count:");
    Serial.println(dataReceived.Count);
    Serial.println(dataReceived.AcX);
    Serial.println(dataReceived.AcY);
    Serial.println(dataReceived.AcZ);
    Serial.println(dataReceived.Tmp);
    Serial.println(dataReceived.GyX);
    Serial.println(dataReceived.GyY);
    Serial.println(dataReceived.GyZ);
    Serial.println(dataReceived.Time);
    lastCount = dataReceived.Count;
  }
}

Why the memcpy? Why not a simple cast for the destination struct?

Why the memcpy? Why not a simple cast for the destination struct?

AWOL-- I don't know how to do that. Can you please demonstrate the cast into the destination struct. Thanks.

Thank you very much your help and advice.

OK. I will avoid the string concatenating idea.

(1) [Take a closer look at what the code in your reference [3] is doing.] I checked ref [3] in detail but when I used the code: char Signal[24]; sprintf(Signal,"%d,%d,%d,%d,%d,%d,%d,%d",Cnt,AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ); vw_send((uint8_t *)Signal,strlen(Signal)); I found the same that the Virtual Wire does not transmitted/received the whole data.

(3) [does it start sending in the background (with interrupts) when you do vw_send()?] No it doesn't. (I used serial only for debugging.)

(4) [Have you done a basic test of virtualwire to make sure you're using it correctly, and that you can receive simple packets?] Yes I have. If I concatenate fewer variables then I got these data correctly.

The code which was written by cattledog serves very well my purpose to continue my project.

Thanks again your help and advice.

 char Signal[24];
 sprintf(Signal,"%d,%d,%d,%d,%d,%d,%d,%d",Cnt,AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ);

Are you absolutely certain that 24 bytes is large enough for the string created by sprintf, including the terminating zero? For three digits plus a comma per variable, it most certainly is not.

Ok I understand.

An another problem has occurred about the data sending.

In my sensor there is a HMC5883L magnetometer too.
I would like to send these data also to create a gait tracking program in another program language.

I find a magnetometer related code in the [1] website, which I built in my script.

I extended the struct function with more three int variables to assign the raw magnetometer readings in the main loop.
But then in this case the virtual wire does not transmit or receive the data at all.
The “transmitter” Arduino reads the measurements correctly (checked by serial printing)
Also the the size of data with the extended content is 28 byte.

Could anybody tell me please what can cause the bloomer?

Here is my sensor&TX code:

#include <Wire.h>
#include <VirtualWire.h>

#define MPU6050 0x68 // I2C address of the MPU-6050
#define HMC5883 0x1E // 0011110b, I2C 7bit address of HMC5883

const int time_step=200; // time interval in msec
struct {
  unsigned long Count=1;
  int AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ, MgX, MgY, MgZ;
  unsigned long Time;
}
data;

void setup(){
  Wire.begin();

  Wire.beginTransmission(MPU6050);
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0); // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

  //Put the HMC5883 IC into the correct operating mode
  Wire.beginTransmission(HMC5883); //open communication with HMC5883
  Wire.write(0x02); //select mode register
  Wire.write(0x00); //continuous measurement mode
  Wire.endTransmission();
  
  Serial.begin(115200); // For debugging
  pinMode(13,OUTPUT); // Port 13, LED to check data transmission
  
  vw_setup(4800); // Bits per sec
  vw_set_ptt_inverted(true); 
  vw_set_tx_pin(12); // Emitter digital data pin
}

void loop(){
  Wire.beginTransmission(MPU6050);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU6050,14,true);  // request a total of 14 registers
  data.Count;
  data.AcX = Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  data.AcY = Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  data.AcZ = Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  data.Tmp = Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  data.GyX = Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  data.GyY = Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  data.GyZ = Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  data.Time = millis();

  //Tell the HMC5883L where to begin reading data
  Wire.beginTransmission(HMC5883);
  Wire.write(0x03); //select register 3, X MSB register
  Wire.endTransmission(false);
  Wire.requestFrom(HMC5883,6,true); //Read data from each axis, 2 registers per axis
  if(6<=Wire.available()){
  data.MgX = Wire.read()<<8; //X msb
  data.MgX|= Wire.read(); //X lsb
  data.MgZ = Wire.read()<<8; //Z msb
  data.MgZ|= Wire.read(); //Z lsb
  data.MgY = Wire.read()<<8; //Y msb
  data.MgY|= Wire.read(); //Y lsb
  }
  
  digitalWrite(13, HIGH); // Flash a light to show transmitting  
    vw_send((uint8_t*) &data, sizeof(data));
    vw_wait_tx();
  digitalWrite(13,LOW); // Turn off LED on port 13 for checking data transmission

  ++data.Count;
  // For debugging
  Serial.print(data.AcX); Serial.print("|");
  Serial.print(data.AcY); Serial.print("|");
  Serial.print(data.AcZ); Serial.print("|");
  Serial.print(data.GyX); Serial.print("|");
  Serial.print(data.GyY); Serial.print("|");
  Serial.print(data.GyZ); Serial.print("|");
  Serial.print(data.MgX); Serial.print("|");
  Serial.print(data.MgY); Serial.print("|");
  Serial.print(data.MgZ); Serial.print("|");
  Serial.print(sizeof(data)); Serial.println(); 
  
  delay(time_step);
}

And RX code:

#include <VirtualWire.h>

struct {
  unsigned long Count;
  int AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ, MgX, MgY, MgZ;
  unsigned long Time;
}
dataReceived;

unsigned long lastCount;

void setup()
{
  Serial.begin(115200);
//  Serial.println("setup");
//  Serial.println("A");
  vw_setup(4800); // Bits per sec
  vw_rx_start(); // Start the receiver PLL running
  vw_set_rx_pin(11);
}

void loop()
{

  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

  if (vw_get_message(buf, &buflen)) // Non-blocking
  {
    memcpy (&dataReceived, buf, buflen);
  }
  if (dataReceived.Count != lastCount) {
    Serial.print(dataReceived.Count); Serial.print(" ");
    Serial.print(dataReceived.AcX); Serial.print(" ");
    Serial.print(dataReceived.AcY); Serial.print(" ");
    Serial.print(dataReceived.AcZ); Serial.print(" ");
    Serial.print(dataReceived.Tmp); Serial.print(" ");
    Serial.print(dataReceived.GyX); Serial.print(" ");
    Serial.print(dataReceived.GyY); Serial.print(" ");
    Serial.print(dataReceived.GyZ); Serial.print(" ");
    Serial.print(dataReceived.MgX); Serial.print(" ");
    Serial.print(dataReceived.MgY); Serial.print(" ");
    Serial.print(dataReceived.MgZ); Serial.print(" ");    
    Serial.println(dataReceived.Time);
    lastCount = dataReceived.Count;
  }
}

I did some test and found something. If I exclude any variable from the struct function then Receiver unit works correctly.

If I exclude any variable from the struct function

What "struct function"?

What is the value of VW_MAX_MESSAGE_LEN in the libraries that you are using? You can print it out in setup()