How do I give my robot proportional speed instead of full forward, full reverse?

Hi All,

I’ve managed to get TX and RX code working for a makeshift skid steering robot. The TX uses two analog joysticks, and both TX and RX use 315mhz (and 415mhz, i’m making two transmitters, one for niece and other for nephew).

I’ve searched the internet high and low, and I could not find anyone using these cheap RF modules and doing proportional speed on their robot. All are using analog joysticks BUT using them to generate full forward/full reverse signals that go to the motor driver.

Would someone be kind enough to take a peek at my below TX and RX code, and show me how to do the analog read on the TX, for the left and right joystick, and then transmit those two values to the RX on the robot that will analog write those values to the motor driver?

One thing that i don’t understand in the below code is in the void loop(). When its reading the left joystick position, and its of a certain value, it transmits a number 1, else a number 5. But now when the left joystick is being pushed the opposite direction, the code transmits a 2, else a 6. Why a 6? if the joystick is not moving, shouldn’t it be a 5 just like before? Anyhow, since the robot is working, i didn’t want to go and muck around with the code.

I’m really rusty on my arduino, i’ve been away from it for months, but i look forward to solving this problem.

Transmitter Code

#include <VirtualWire.h>

int LStick = A0; // assign analog pin 0 to LStick command
int Lnumber; // number to send for Left joystick position
int RStick = A1; // assign analog pin 1 to RStick command
int Rnumber; // number to send for Right joystick position

int hLight = 4;
//Variable to keep track of the number of button presses.
int counter = 1;

void setup()
{
  Serial.begin(9600);//Only for Debugging
  
  pinMode(hLight,INPUT);
  
  pinMode(LStick, INPUT); // set INPUT mode for Left joystick input pin
  pinMode(RStick, INPUT); // set INPUT mode for Right joystick input pin
  
  digitalWrite(hLight,HIGH);  //turn on pullup resistor for headlight button

vw_set_tx_pin(12); //tx config
vw_setup(3000); 
}

void loop()
{
   Lnumber = analogRead(LStick); // read Left joystick position
  if (Lnumber <= 50) // if position is 5% from 0 (exactly 50/1024)
    { // the "50" value could be increased to add joystick sensitivity
      char msg[1] = {'1'}; // send number "1"
      digitalWrite(13, true); // Flash a light to show transmitting
      vw_send((uint8_t *)msg, 1);
      vw_wait_tx();
      delay (5);
    }
 else
       {
      char msg[1] = {'5'}; // else send number "5"
      //digitalWrite(13, true); // Flash a light to show transmitting
      vw_send((uint8_t *)msg, 1);
      vw_wait_tx();
      delay (5);        
       }

   Lnumber = analogRead(LStick); // read Left joystick position
  if (Lnumber >= 974) // if position is 5% from 1024 (exactly 974/1024)
    { // the "974" value could be reduced to add joystick sensitivity
      char msg[1] = {'2'}; // send number "2"
      digitalWrite(13, true); // Flash a light to show transmitting
      vw_send((uint8_t *)msg, 1);
      vw_wait_tx();
      delay (5);
    }
 else
       {
      char msg[1] = {'6'}; // else send number "6"
      //digitalWrite(13, true); // Flash a light to show transmitting
      vw_send((uint8_t *)msg, 1);
      vw_wait_tx();
      delay (5);        
       }
  
   Rnumber = analogRead(RStick); // read Right joystick position
  if (Rnumber <= 50) // if position is 5% from 0 (exactly 50/1024)
    { // the "50" value could be increased to add joystick sensitivity
      char msg[1] = {'3'}; // send number "3"
      digitalWrite(13, true); // Flash a light to show transmitting
      vw_send((uint8_t *)msg, 1);
      vw_wait_tx();
      delay (5);
    }
 else
       {
      char msg[1] = {'7'}; // else send number "7"
      //digitalWrite(13, true); // Flash a light to show transmitting
      vw_send((uint8_t *)msg, 1);
      vw_wait_tx();
      delay (5);        
       }

   Rnumber = analogRead(RStick); // read joystick sail position
  if (Rnumber >= 974) // if position is 5% from 1024 (exactly 974/1024)
    { // the "974" value could be reduced to add joystick sensitivity
      char msg[1] = {'4'}; // send number "4"
      digitalWrite(13, true); // Flash a light to show transmitting
      vw_send((uint8_t *)msg, 1);
      vw_wait_tx();
      delay (5);
    }
 else
       {
      char msg[1] = {'8'}; // else send number "8"
      //digitalWrite(13, true); // Flash a light to show transmitting
      vw_send((uint8_t *)msg, 1);
      vw_wait_tx();
      delay (5);        
       }
       headLights();   
  } 
  
  void headLights()
{
  /*This function give the headlight on or off command based on the 
  number of button presses.
  
  The counter variable is the number of times the headlight button is pressed.
  
  I plan on improving this part of the code by using interrupts as
  this method isn't 100% accurate right now.
  
  */
  
  if(digitalRead(hLight)==LOW)
  {
    //Increase the value of counter every time the headlight button is pressed.
    counter++;
  }
  
  //If value of counter is even the Lights On command is sent.
  if(counter%2==0)
  {
    char *msg2 = "9";
    //digitalWrite(13, true); // Flash a light to show transmitting
    vw_send((uint8_t *)msg2, strlen(msg2));
    vw_wait_tx(); // Wait until the whole message is gone
    //digitalWrite(13, false);
    //Debugging
    //Serial.println("Lights On");
    //Serial.println("  ");
  }
  
  //If counter is odd the Light Off command is sent.
  else
  {
    char *msg2 = "0";
    //digitalWrite(13, true); // Flash a light to show transmitting
    vw_send((uint8_t *)msg2, strlen(msg2));
    vw_wait_tx(); // Wait until the whole message is gone
    //digitalWrite(13, false);
    //Debugging
    //Serial.println("Lights OFF");
    //Serial.println("  ");
  }
  delay(10);
}

I’ll post the Receiver code next.

Receiver code

#include <VirtualWire.h>
#include "pitches.h"

int LeftForward = 3; 			        // assign pin 3 to Right Motor Forward output pin
int LeftReverse = 4;			        // assign pin 4 to Right Motor Reverse output pin
int RightForward = 5; 		        // assign pin 5 to Left Motor Forward output pin
int RightReverse = 6; 		        // assign pin 6 to Left Motor Reverse output pin
const int receive_pin = 8;	                // assign pin 8 to RF receiver

int hLight =2;

int buz = 9;

// notes in the melody:
int melody[] = {
  NOTE_C5, NOTE_G3,NOTE_B5, NOTE_E4};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
  16, 16, 16, 16,};

void setup()
{
delay(100);
vw_set_rx_pin(receive_pin);		// rx config
vw_set_ptt_inverted(true);
vw_setup(3000);
vw_rx_start();
pinMode(RightForward, OUTPUT);	// set OUTPUT mode for output pins
pinMode(RightReverse, OUTPUT);
pinMode(LeftForward, OUTPUT);
pinMode(LeftReverse, OUTPUT);
pinMode(hLight,OUTPUT);
pinMode(buz,OUTPUT);


 for (int thisNote = 0; thisNote < 8; thisNote++) {

    // to calculate the note duration, take one second 
    // divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 800/noteDurations[thisNote];
    tone(9, melody[thisNote],noteDuration);

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    noTone(9);
  }

}


void loop()
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;
if (vw_get_message(buf, &buflen))
{
if(buf[0]=='1')						// if tx receive "1" 
   digitalWrite(LeftForward,HIGH);		// put Left Motor Forward pin to "HIGH"
}
if(buf[0]=='5')						// if tx receive "5"
{
  digitalWrite(LeftForward,LOW);			// put Left Motor Forward pin to "LOW"
}
if(buf[0]=='2')						// if tx receive "2"
{
  digitalWrite(LeftReverse,HIGH);		// put Left Motor Reverse pin to "HIGH"
}
if(buf[0]=='6')						// if tx receive "6"
{
  digitalWrite(LeftReverse,LOW);		// put Left Motor Reverse pin to "LOW"
}
if(buf[0]=='3')						// if tx receive "3"
{
  digitalWrite(RightForward,HIGH);	        // put Right Motor Forward pin to "HIGH"
}
if(buf[0]=='7')						// if tx receive "7"
{
  digitalWrite(RightForward,LOW);		// put Right Motor Forward pin to "LOW"
}
if(buf[0]=='4')						// if tx receive "4"
{
  digitalWrite(RightReverse,HIGH);	        // put Right Motor Reverse pin to "HIGH"
}
if(buf[0]=='8')						// if tx receive "8"
{
  digitalWrite(RightReverse,LOW);	        // put Right Motor Reverse pin to "LOW"
}
if(buf[0]=='9')						// if tx receive "9"
{
  digitalWrite(hLight,HIGH);
}
if(buf[0]=='0')						// if tx receive "0"
{
  digitalWrite(hLight,LOW);
}

}
int LStick = A0; // assign analog pin 0 to LStick command
int Lnumber; // number to send for Left joystick position
int RStick = A1; // assign analog pin 1 to RStick command

A0 is the name of the pin when it is used as a digital pin. 0 is the number of the pin when it is used as an analog pin.

 pinMode(LStick, INPUT); // set INPUT mode for Left joystick input pin
  pinMode(RStick, INPUT); // set INPUT mode for Right joystick input pin

You can't set the mode for an analog pin. They are input only.

You are reading the joystick position, and then sending '1' or '5'. Why? Why not send the joystick value (or the joystick value divided by 4)?

If you want the joystick value to control speed, sending '1' or '5' isn't going to make that happen.

I’m often surprised how many people seem to be satisfied with only on and off type remotes.

As Paul points out, if you want to use analog values, then send the analog values.

exwhyzed:
Anyhow, since the robot is working, i didn’t want to go and muck around with the code.

I hope you know how to use “save as”? I muck about with working code all the time. It’s the only way to make it better.

What I've come up with is this:

   Lnumber = analogRead(LStick);	// read Left joystick position (0 - 1023)
   
  
      char msg[4] = {Lnumber};				// send LStick analog value, ,which is the Lnumber
      digitalWrite(13, true); // Flash a light to show transmitting
      vw_send((uint8_t *)msg, 1);
      vw_wait_tx();
      delay (5);

I don't think its right because even though the array could be 4 digits long, it isn't always 4 digits. It could be 1, 2, 3 or 4 digits. So I've put in char msg[4] but what it really needs to be is flexible, and adapt to whatever quantity of digits the Lnumber is. But i don't know how to do that.

Hopefully someone who knows what they're doing will help but for now, I'll offer an ugly hack. You could add 1000 to the value you send and subtract 1000 from the received amount in order to force your number to four digits.

I'm embarrassed even to suggest this, but I haven't used the Arduino enough to know the correct way to doing what you want.

I suggest looking at Robin2's "Intro to Serial" tutorials (I think they're stickies in the programing forum (I was wrong)).

I’ve searched the internet high and low, and I could not find anyone using these cheap RF modules and doing proportional speed on their robot. All are using analog joysticks BUT using them to generate full forward/full reverse signals that go to the motor driver.

Below is some joystick/pot tx code and rx code that captures the values sent and converts them for servo control. Instead of mapping the pot value from 0-179, you could map 0-254 for analog writing to an H-bridge pin. The tx code is made to minimize network/wireless traffic by sending values only when they change significantly.

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 
// Powering a servo from the arduino usually *DOES NOT WORK*.

String readString;
#include <Servo.h> 
Servo myservoa, myservob, myservoc, myservod;  // 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 
  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);
        }
        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);
        }
         readString=""; //clears variable for new input
      }
    }  
    else {     
      readString += c; //makes the string readString
    }
  }
}

DuaneDegn:
I'm often surprised how many people seem to be satisfied with only on and off type remotes.

As Paul points out, if you want to use analog values, then send the analog values.

I hope you know how to use "save as"? I muck about with working code all the time. It's the only way to make it better.

It might not work 100% of the time, but usually, if you can make a working code better at all, you're doing good.

Thanks for the code zoomkat. Looking at unknown code and comparing it with code I know is the main way i'm learning to program arduino. Reading books on it start to get complicated after a few lessons, and all of the instructions as well as code end up looking like the wingdings font on apple/mac computers!

I'm going to see what i can do with it now...

And thanks for the input everyone else. That's a neat trick DuaneDegn, i'm going to keep that in mind.

Major progress has been made. I managed to send analog values to the receiving arduino, and print them out in a stable manner on the serial monitor. Transmission was choppy at first, but i lowered the number in brackets in

vw_setup(3000);

The serial monitor started printing out data consistently...and it wasn't a problem with my antennas as i thought. The next step is sending the right analog stick as well, and then writing both to their respective motors. I'll post my code shortly, when i run into the next roadblock, which is going to be the if statement that says if the analog value is THIS, then write to MotorForward, else write to MotorReverse, but don't include values in the deadzone. And do this for the other joystick. Time to get some sleep, its 4.23am!

After tweaking the code for so many days and hours, i kept having the same problem. The recevier can Serial print the joystick values that it is receiving just fine. But when i map those analog values to PWM values and write to the motors, i get wonky behaviour.

I slowly press forward on the joystick and the motor moves forward slowly, then full forward on the stick and motor is full. Yaay, have i accomplished proportional control? No. when i hit reverse on the joystick, motor sometimes moves forward or backwards. And even when i dont do anything with the joystick, the motor want to move forward by itself, for some reason. I’ve already found the joystick middle analog value using another sketch, and have created a deadzone where the motors should stay still (when the joystick is on or near its rest position), so i can’t figure out why the motor is moving by itself, and in different directions. When i reset both arduinos, and try reversing instead, it seems to work, but then the same erratic behaviour appears. Can anyone shed some light as to why this is happening?

I scrapped my code and just used the one on the bottom of this site, and changed it to analog write to 2 PWM pins that go to the motor driver: https://www.dipmicro.com/store/RF315PAIR

TX code

/* FILE:    MXFS03V_433MHZ_MODULE_HCMODU0007_TRANSMIT_EXAMPLE.pde
   DATE:    03/03/13
   VERSION: 0.1
   AUTHOR:  Andrew Davies

This is an example of how to use the 433MHz wireless transmitter module 
(HCMODU0007) which is the Tx part of the tranmitter and receiver module pair.
This example makes use of the VirtualWire library written by Mike McCauley.
The sketch will read a value from the analogue input A0 and transmit it as 
2 bytes to the receiver module once every second.

Tx MODULE CONNECTIONS:

PIN  DESCRIPTION      ARDUINO PIN
1    GND              GND
2    VCC (3.5-12V)    VCC
3    TX DATA          D2


You may copy, alter and reuse this code in any way you like, but please leave 
reference to HobbyComponents.com in your comments if you redistribute this code. 

THIS SOFTWARE IS PROVIDED "AS IS". HOBBY COMPONENTS LTD MAKES NO WARRANTIES, WHETHER 
EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF 
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ACCURACY OR LACK OF NEGLIGENCE.
HOBBY COMPONENTS SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR ANY DAMAGES, 
INCLUDING, BUT NOT LIMITED TO, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY 
REASON WHATSOEVER.
*/


/*Include the VirtualWire library */
#include <VirtualWire.h> 

/* Digital IO pin that will be used for sending data to the transmitter */
const int TX_DIO_Pin = 12;


void setup()
{
  pinMode(13, OUTPUT);
  /* Initialises the DIO pin used to send data to the Tx module */
  vw_set_tx_pin(TX_DIO_Pin);
  /* Set the transmit logic level (LOW = transmit for this 
     version of module)*/ 
  vw_set_ptt_inverted(true); 
  
  /* Transmit at 2000 bits per second */
  vw_setup(2000);    // Bits per sec
}

/* Main program */
void loop()
{
  /* Temporarily holds the value read from analogue input A0 */
  unsigned int Data;
  /* The transmit buffer that will hold the data to be 
     transmitted. */
  byte TxBuffer[2];
  
  /* Read the analogue input A0... */
  Data = analogRead(A0);
  /* ...and store it as high and low bytes in the transmit 
     buffer */
  TxBuffer[0] = Data >> 8;
  TxBuffer[1] = Data;
  
  /* Turn on the LED on pin 13 to indicate that we are about 
    to transmit data */
  digitalWrite(13, HIGH); 
  /* Send the data (2 bytes) */
  vw_send((byte *)TxBuffer, 2);
  /* Wait until the data has been sent */
  vw_wait_tx(); 
  
  /* Turn off the LED on pin 13 to indicate that we have 
     now sent the data */
  digitalWrite(13, LOW); 
  
  /* Do nothing for a second. Lower this delay to send 
     data quicker */
  //delay(100);
}

RX code

/* FILE:    MX05V_433MHZ_MODULE_HCMODU0007_RECEIVE_EXAMPLE.pde
   DATE:    03/03/13
   VERSION: 0.1
   AUTHOR:  Andrew Davies

This is an example of how to use the 433MHz wireless reciever module 
(HCMODU0007) which is the Rx part of the tranmitter and reciver module pair.
This example makes use of the VirtualWire library written by Mike McCauley.
This sketch in intended to be used with the Tx example code to recive analogue
input data sent from the transmitting Arduino. The received data is then output 
to the UART.

Rx MODULE CONNECTIONS:

PIN  DESCRIPTION      ARDUINO PIN
1    GND              GND
2    RX DATA          D2
3    RX DATA          N/A
4    VCC (5V)         VCC


You may copy, alter and reuse this code in any way you like, but please leave 
reference to HobbyComponents.com in your comments if you redistribute this code. 

THIS SOFTWARE IS PROVIDED "AS IS". HOBBY COMPONENTS LTD MAKES NO WARRANTIES, WHETHER 
EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF 
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ACCURACY OR LACK OF NEGLIGENCE.
HOBBY COMPONENTS SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR ANY DAMAGES, 
INCLUDING, BUT NOT LIMITED TO, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY 
REASON WHATSOEVER.
*/


/*Include the VirtualWire library */
#include <VirtualWire.h> 

int LeftForward = 3;
int LeftReverse = 5;

/* Digital IO pin that will be used for receiving data from the receiver */
const int RX_DIO_Pin = 8;

void setup()
{
    pinMode(13, OUTPUT);
    //Serial.begin(9600);
    
    pinMode(LeftForward, OUTPUT);	// set OUTPUT mode for output pins
    pinMode(LeftReverse, OUTPUT);
    digitalWrite(RX_DIO_Pin,HIGH);  //turn on pullup resistor for RXpin

    /* Initialises the DIO pin used to receive data from the Rx module */
    vw_set_rx_pin(RX_DIO_Pin);
    
    /* Receive at 2000 bits per second */
    vw_setup(2000);
    
    /* Enable the receiver */
    vw_rx_start(); 
}

/* Main program */
void loop()
{
  /* Set the receive buffer size to 2 bytes */
  uint8_t Buffer_Size = 2;
  
  /* Holds the recived data */
  unsigned int Data;
  
  /* The receive buffer */
  uint8_t RxBuffer[Buffer_Size];

    /* Has a message been received? */
    if (vw_get_message(RxBuffer, &Buffer_Size)) // Non-blocking
    {
        /* If so, then turn on the LED connected to DIO 13 
           to indicate this */
        digitalWrite(13, HIGH); 
   
        /* Store the received high and low byte data */
        Data = RxBuffer[0] << 8 | RxBuffer[1];

        /* Output this data to the UART */
   //Serial.print("Analogue pin A0: ");
   //     Serial.println(Data);
        
   if (Data <= 507){
    Data = map(Data, 507,0, 0, 254);  
    analogWrite(LeftForward,Data);
  }
  
   if (Data >= 517){
    Data = map(Data, 517,1023, 0, 254);  
    analogWrite(LeftReverse,Data);
  } 
   
        /* Turn off the LED on pin 13 to indicate that the 
           data has now been received */
        digitalWrite(13, LOW);
    }
}

I added an “else do nothing” statement to the above code and it solved the problem. I was able to get a 100% accurate joystick movement to motor speed relationship. Funny how the serial monitor worked fine without this else statement.

Anyhow, my next hurdle is reading two joysticks. I’ve made much progress and am only stumped at this strange occurrence…I get proportional speed control for Left-Forward, Left-Reverse, Right-Forward…but as soon as I pull the right joystick back, the receiving arduino freezes, and its code-driven flashing LED even stops blinking, and RX permanently stops responding until I reset the Arduino. I don’t understand why only this joystick movement is causing a crash when all others are working fine. Even the serial monitor freezes. Whether or not I serial print during the loop or not doesn’t make a difference either.

I look forward to any help i can get into solving this huge problem.

Below is the code I am using.

TX code

#include <VirtualWire.h>

char Array[32];
int X,Y;

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

  // Initialise the IO and ISR
  //vw_set_ptt_inverted(true); // Required for DR3100
  vw_setup(2000); // Bits per sec
  vw_set_tx_pin(12); //RF sending pin
 }

void loop()
{ 
  X = analogRead(A0);
  Y = analogRead(A1);
  
  sprintf(Array, "%d,%d.",X,Y);
  vw_send((uint8_t*)Array, strlen(Array));
  vw_wait_tx(); 
}

RX code

#include <VirtualWire.h>

int LeftForward = 3;
int LeftReverse = 5;
int RightForward = 6;
int RightReverse = 9;


uint8_t data[32];

void setup()
{
  Serial.begin(9600); // Debugging only
  Serial.println("Receiving");
  pinMode(13,OUTPUT);
  // Initialise the IO and ISR
  //vw_set_ptt_inverted(true); // Required for DR3100
  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)) // Non-blocking
  {
    int X = atoi(strtok((char*)buf, ","));  // Look for a comma, the return the data before it.
    int Y = atoi(strtok(NULL, ".")); // Look for a period, then return data before it.


    if (X <= 507){
      X = map(X, 507,0, 0, 254);  
      analogWrite(LeftForward,X);
    }

    else
    {
      analogWrite(LeftForward,0);

      if (X >= 517){
        X = map(X, 517,1023, 0, 254);  
        analogWrite(LeftReverse,X);
      } 
      else
      {
        analogWrite(LeftReverse,0);
        //digitalWrite(13, LOW);

        if (Y <= 503){ //right joystick rest position is 508, so go 5 up and 5 down
          Y = map(Y, 503,0, 0, 255);  
          analogWrite(RightForward,Y);
        }

        else
        {
          analogWrite(RightForward,0);

          if (Y >= 513){
            Y = map(Y, 513,1023, 0, 255);  
            analogWrite(RightReverse,Y);
          } 
          else
          {
            analogWrite(RightReverse,0);    
          }  

          Serial.print(X); // X axis
          Serial.print(", ");
          Serial.print(Y); // Y axis
          Serial.print(", ");
          Serial.println();


        }
      }
    }
  }
}

SOLVED!!

PaulS:
...

The virtual wire library may be using a timer. If it does, it will interfere with PWM on some pins. Can you try other PWM pins on the receiver, for the motors?

It seems pins 9 and 10 were giving me problems. So instead of using pins 3, 5, 6, 9 to drive the motors, i use 3, 5, 6, 11 and now all works fine, YESSSS!!!

One small issue has now arisen, I’m trying to add some code that presses a button on the TX (on pin 4), and it turns on an led on the RX (on pin 4). It worked when i was doing a full-forward full-reverse coded robot. But now that the robot driving code has drastically changed, i can’t seem to get it to work.

I know that i need to increase the array that is sent by the TX, so i increased it by 2. And for the RX, i’m stuck on an error saying -invalid conversion from ‘const char*’ to ‘char’-

It would be greatly appreciated if someone could help me debug this. I’m just not sure how to send a digitalRead of a button press, along with 2 analog joystick reads, and then how to extract those on the receiving end and make them do what they are supposed to do.

Below is my TX and RX code.

TX

#include <VirtualWire.h>

char Array[34];
int X,Y,Z;

const int hLight = 4; //headlight pushbutton on pin 4
const char *msgs = "0"; // initial value for msgs

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

  // Initialise the IO and ISR
  //vw_set_ptt_inverted(true); // Required for DR3100
  vw_setup(2000); // Bits per sec
  vw_set_tx_pin(12); //RF sending pin
  
    pinMode(hLight,INPUT_PULLUP);
 }

void loop()
{ 
 
 int buttonState; // declare button state, will either be HIGH or LOW
 buttonState = digitalRead(hLight); // Reads button state
 
     if(buttonState == LOW) 
    {
      if(msgs == "A")  // If button is pressed and msgs value is A, change to B
      {
        msgs = "B";
      }
      else if(msgs == "B")  // If button is pressed and msgs value is B, change to A
      {
        msgs = "A"; 
      }
 
  X = analogRead(0);
  Y = analogRead(1);
  Z = msgs;
  
  sprintf(Array, "%d,%d,%d.",X,Y,Z);
  vw_send((uint8_t*)Array, strlen(Array));
  vw_wait_tx(); 
}
}

RX code

#include <VirtualWire.h>

int LeftForward = 3;
int LeftReverse = 5;
int RightForward = 6;
int RightReverse = 11;
int hLight =4;

uint8_t data[34];

void setup()
{
  Serial.begin(9600); // Debugging only
  Serial.println("Receiving");
  pinMode(13,OUTPUT);
  pinMode(hLight,OUTPUT);
  // Initialise the IO and ISR
  //vw_set_ptt_inverted(true); // Required for DR3100
  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)) // Non-blocking
  {
    int X = atoi(strtok((char*)buf, ","));  // Look for a comma, the return the data before it.
    int Y = atoi(strtok((char*)buf, ","));  // Look for a comma, the return the data before it.
    char Z = atoi(strtok(NULL, ".")); // Look for a period, then return data before it.


    if (X <= 507){
      X = map(X, 507,0, 0, 254);  
      analogWrite(LeftForward,X);
    }

    else
    {
      analogWrite(LeftForward,0);

      if (X >= 517){
        X = map(X, 517,1023, 0, 254);  
        analogWrite(LeftReverse,X);
      } 
      else
      {
        analogWrite(LeftReverse,0);
        //digitalWrite(13, LOW);

        if (Y <= 503){ //right joystick rest position is 508, so go 5 up and 5 down
          Y = map(Y, 503,0, 0, 255);  
          analogWrite(RightForward,Y);
        }

        else
        {
          analogWrite(RightForward,0);

          if (Y >= 513){
            Y = map(Y, 513,1023, 0, 255);  
            analogWrite(RightReverse,Y);
          } 
          else
          {
            analogWrite(RightReverse,0);    
          }  


    if (Z = "A"){ 
      digitalWrite(hLight,HIGH);
    }

    else
    {
      digitalWrite(hLight,LOW);   
          /*
          Serial.print(X); // X axis
           Serial.print(", ");
           Serial.print(Y); // Y axis
           Serial.print(", ");
           Serial.println();
           */
        }
      }
    }
  }
}

After spending all night on this one, I managed to put in the “textbook” button debouncing code (and commented out the previous WORKING but bouncing code).

I’m just trying to read things out on the serial monitor…its displays the joystick values fine, but when i press the button, the LED on pin 13 goes off, but when i release the button it goes back on again! Why doesn’t it stay off, and then only turn on again when i press the button again? I understand that I need to get this part of the TX code perfect before i can move on to getting the LED on the RX’s arduino working properly.

Would someone be kind enough to take a look at this new TX code and see where the problem lies?

TX code

#include <VirtualWire.h>

char Array[24];
int X,Y,Z;

const int buttonPin = 4;  // pushbutton  pin
const int ledPin = 13;      // the number of the LED pin
//const char *msgs = "0"; // initial value for msgs

// Variables will change:
int ledState = LOW;         // the current state of the output pin
int buttonState;             // the current reading from the input pin
int lastButtonState = HIGH;   // the previous reading from the input pin

// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers


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

  // Initialise the IO and ISR
  //vw_set_ptt_inverted(true); // Required for DR3100
  vw_setup(2000); // Bits per sec
  vw_set_tx_pin(12); //RF sending pin

  //pinMode(hLight,INPUT_PULLUP);
  pinMode(buttonPin,INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, ledState);
}

void loop()
{ 

  // read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH),  and you've waited
  // long enough since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // only toggle the LED if the new button state is HIGH
     // if (buttonState == LOW) {
      //  {
          ledState = !ledState;
    //    }
      }
    }
      
    

    // set the LED:
    digitalWrite(ledPin, ledState);

    // save the reading.  Next time through the loop,
    // it'll be the lastButtonState:
    lastButtonState = reading;


    /*
  if(reading == LOW) 
     {
     if(msgs == "0") // If button is pressed and msgs value is 0, change to 1
     {
     msgs = "1";
     }
     else if(msgs == "1")
     {
     msgs = "0"; // If button is pressed and msgs value is 1, change to 0
     }
     */

    X = analogRead(0);
    Y = analogRead(1);
    Z = buttonState;

    sprintf(Array, "%d,%d,%d.",X,Y,Z);
    vw_send((uint8_t*)Array, strlen(Array)); 
    vw_wait_tx(); 

    Serial.print(X); // X axis
    Serial.print(", ");
    Serial.print(Y); // Y axis
    Serial.print(", ");
    Serial.print(Z); // Z axis
    Serial.print(", ");
    Serial.println();
    delay(10);
     }

hoping there’s more to add to this thread, have been scouring the internet for similar code but there’s not much out there. my application requires 2 Arduinos connected by RS232 serial but this is definitely helpful for a noob like me. so thanks exwhyzed :slight_smile:

Time stamp variables must be unsigned long type.