Sending pot values over 433mhz TX/RX for servo position control

Hey guys,

I have a problem hopefully yall could help me figure out. I recently printed a robotic hand and attached 5 servos in the forearm with a cable to each finger. I connected 5 push buttons to the motors w/ arduino pro mini. It works great.

I am almost done designing and printing a glove that uses tiny potentiometers above each knuckle to control the servos' positions. I have programmed pot values to servo position before so that's not an issue. And I have built from scratch 2 RC tanks using 433mhz TX/RX with pushbuttons to control the motors. The problem I am having is trying to figure out how to send pot values over the TX/RX.

I really don't know where to start. For the most part, I know how the TX/RX work. Turning an action into a code, sending that code to the receiver, then interpreting that code into an action. Pretty straight forward if using a sensor that only has on and off positions (like a pushbutton).

But how do I send the value of a pot to the receiver? Does the transmitter send a code or number for every value change on the pot? Does it send a code for every 10, 20, 50 values changed? That is a lot of code to write just to see if it works (which is why I am asking you guys before attempting it). If I can get this to work then I can use it on several other projects that I have on the waiting list.

On a different note, does anyone know of a GOOD tutorial or documentation on how to FULLY use VirtualWire? What I have learned so far was through trial and error, but I would like to understand and utilize the full capacity of VW. Or is there a better TX/RX library out there for arduino?

For every question or problem I have had in the past, you guys have always given great answers and nudged me in the right direction, and for that I thank you. That's why this is the first place I go to with an issue I can't find an answer to or figure out myself.

Any guidance or ideas that you can give would be well appreciated. Thank you for your time.

d_rhoades:
On a different note, does anyone know of a GOOD tutorial or documentation on how to FULLY use VirtualWire? What I have learned so far was through trial and error, but I would like to understand and utilize the full capacity of VW. Or is there a better TX/RX library out there for arduino?

I don't know about better, but on the VW site it mentions that it was superceded by RadioHead. I assumed that meant that ongoing development would continue more on RadioHead so I dutifully went there.

Not sure what you mean by "fully use", or "full capacity". It seems to do what it is commissioned to do, to provide a transport layer for packets. If you want to use those packets to implement higher level protocols, you can. But it would be in the realm of protocol design.

There does seem to be a common lack of understanding about how to send different data formats, to which the simplest answer is usually, "use a union of byte and whatever data type you wish to send". A C/C++ union allows different data formats to occupy the same memory locations. It has portability problems, but it works.

Below is some servo/pot tx and rx code that might be of interest. The tx code has a dead band included to limit tx/rx band width usage. You can try the code independent of each other.

Tx

//zoomkat multi pot/servo test 3-23-13
//includes dead band for testing and limit servo hunting
//view output using the serial monitor

#include <Servo.h> 
Servo myservo1;  //declare servos
Servo myservo2;
Servo myservo3;
Servo myservo4;
Servo myservo5;

int potpin1 = 0;  //analog input pin A0
int potpin2 = 1;
int potpin3 = 2;
int potpin4 = 3;
int potpin5 = 4;

int newval1, oldval1;  //pot input values
int newval2, oldval2;
int newval3, oldval3;
int newval4, oldval4;
int newval5, oldval5;

void setup() 
{
  Serial.begin(9600);  
  myservo1.attach(2);  
  myservo2.attach(3);
  myservo3.attach(4);
  myservo4.attach(5);
  myservo5.attach(6);
  Serial.println("testing multi pot servo");  
}

void loop()
{ 
  newval1 = analogRead(potpin1);           
  newval1 = map(newval1, 0, 1023, 0, 179); 
  if (newval1 < (oldval1-2) || newval1 > (oldval1+2)){ //dead band 
    myservo1.write(newval1); //position the servo
    Serial.print(newval1); //print the new value for testing 
    Serial.print("a,");
    oldval1=newval1; //set the current old value
  }

  newval2 = analogRead(potpin2);
  newval2 = map(newval2, 0, 1023, 0, 179);
  if (newval2 < (oldval2-2) || newval2 > (oldval2+2)){  
    myservo2.write(newval2);
    Serial.print(newval2);
    Serial.print("b,");
    oldval2=newval2;
  }

  newval3 = analogRead(potpin3);           
  newval3 = map(newval3, 0, 1023, 0, 179); 
  if (newval1 < (oldval3-2) || newval3 > (oldval3+2)){  
    myservo3.write(newval3);
    Serial.print(newval3);
    Serial.print("c,");
    oldval3=newval3;
  }

  newval4 = analogRead(potpin4);           
  newval4 = map(newval4, 0, 1023, 0, 179); 
  if (newval4 < (oldval4-2) || newval4 > (oldval4+2)){  
    myservo1.write(newval4);
    Serial.print(newval4);
    Serial.print("d,");
    oldval4=newval4;
  }

  newval5 = analogRead(potpin5);           
  newval5 = map(newval5, 0, 1023, 0, 179); 
  if (newval5 < (oldval5-2) || newval5 > (oldval5+2)){  
    myservo5.write(newval5);
    Serial.print(newval5);
    Serial.print("e,");
    oldval5=newval5;
  } 
  delay(50);  //to slow loop for testing, adjust as needed
}

Rx

//zoomkat 11-22-12 simple delimited ',' string parse 
//from serial port input (via serial monitor)
//and print result out serial port
//multi servos added 

String readString;
#include <Servo.h> 
Servo myservoa, myservob, myservoc, myservod, myservoe;  // create servo object to control a servo 

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

  //myservoa.writeMicroseconds(1500); //set initial servo position if desired

  myservoa.attach(6);  //the pin for the servoa control
  myservob.attach(7);  //the pin for the servob control
  myservoc.attach(8);  //the pin for the servoc control
  myservod.attach(9);  //the pin for the servod control 
  myservod.attach(10);  //the pin for the servoe control 
  Serial.println("multi-servo-delimit-test-dual-input-11-22-12"); // so I can keep track of what is loaded
}

void loop() {

  //expect single strings like 700a, or 1500c, or 2000d,
  //or like 30c, or 90a, or 180d,
  //or combined like 30c,180b,70a,120d,

  if (Serial.available())  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == ',') {
      if (readString.length() >1) {
        Serial.println(readString); //prints string to serial port out

        int n = readString.toInt();  //convert readString into a number

        // auto select appropriate value, copied from someone elses code.
        if(n >= 500)
        {
          Serial.print("writing Microseconds: ");
          Serial.println(n);
          if(readString.indexOf('a') >0) myservoa.writeMicroseconds(n);
          if(readString.indexOf('b') >0) myservob.writeMicroseconds(n);
          if(readString.indexOf('c') >0) myservoc.writeMicroseconds(n);
          if(readString.indexOf('d') >0) myservod.writeMicroseconds(n);
          if(readString.indexOf('e') >0) myservoe.writeMicroseconds(n);
        }
        else
        {   
          Serial.print("writing Angle: ");
          Serial.println(n);
          if(readString.indexOf('a') >0) myservoa.write(n);
          if(readString.indexOf('b') >0) myservob.write(n);
          if(readString.indexOf('c') >0) myservoc.write(n);
          if(readString.indexOf('d') >0) myservod.write(n);
          if(readString.indexOf('e') >0) myservoe.write(n);
        }
         readString=""; //clears variable for new input
      }
    }  
    else {     
      readString += c; //makes the string readString
    }
  }
}

Does the transmitter send a code or number for every value change on the pot? Does it send a code for every 10, 20, 50 values changed?

You control the transmission. Send what you want and when you want. You can send at a fixed interval, or you can send when data changes.

The basic model for Send and Receive is to declare equivalent constructs of the data to be sent and received. You then cast it into bytes for transmission. On receipt it will be reassembled into the original form. The two key commands are

vw_send((uint8_t*) &dataSent, sizeof(dataSent));
if (vw_get_message((uint8_t*)(&dataReceived),&buflen)
//do something with dataReceived;

Here’s an example of data configured in a struct. For your potentiometer data, your likely to be defining an integer array of 5 values. You can use the default max buflen, or reduce the size to fit your data and save memory.

#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;
  }
}
#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 buflen = VW_MAX_MESSAGE_LEN;

  if (vw_get_message((uint8_t*)(&dataReceived),&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;
    }
  }

}

If you are monitoring some pots, I suggest you always store the current value, and compare it to the last value you read. The ADC’s are not exact, so you need to pick a difference, say ±10 (determine by experiment), so that if the present value and the last value exceed that difference, you send the data.

Wow. I didn't know that was possible. I have only worked with sending push button statis over RF to control DC motors. This opens up a whole new array of possibilities for me. But this is why I love the arduino and why I got into microcontrollers in the first place. It gives me the excitement of discovery that I haven't felt since childhood.

I appreciate all your replies. You have given me something to think about and test for a few weeks (my day job keeps me very very busy). After some trial-and-error and some experimentation, hopefully, I will be able to better understand how wireless communication works and, better yet, how to fully use it to suit my needs.

Thank you for all the help. I am going to try this a few different ways and document each for future reference.