Methods of remote control?

I’ve built several robots before on the common RF system. This is my first foray into autonomy.

I want to do one of the following:

  1. use a USB gamepad on my laptop, to control an arduino over bluetooth.
    1. Natively via processing (using analog joystick, center off, with intermediate positions between the axis’s) ← would prefer (similar to this)
    1. mapping keyboard letters to each button into putty (press button to move, press another for stop) ← seems easiest for this newb to program, and would also allow me to use my phone on the run, when I don’t have laptop.
  1. have the arduino to recognize all 6 channels of my RF receiver separately, which I don’t think is possible? (plus I read that it can create electrical noise?)

Additionally, I want to be able to flip it from manual control to auto-pilot, on the fly, then flip it back to manual, still on the fly (i dont mind if I have to stop movement- i just dont want to have to interact with it by hand). In essence, I’m building a platform I’m used to (with having remote control), but I also want it to be autonomous if I send a command to run a stored program. I.E., since it will be a transport bot, I could drive it to a location by hand, then run a stored program that would remove the cargo container from the chassis, set it on the ground, and open it, hold it open for X amount of time, then close it, return it to the chassis, and drive off (and restore manual control). OR, if an anomaly occurred during the auto-pilot, I could do an instant interrupt (canceling the stored program), and do the same commands manually.

I would think it wouldn’t be too difficult to do, but I’m new to this, so I don’t know what’s simple and what’s difficult.

How can I do this?

The robot I plan to build will consist of the following (tank style):

Output:
2- 12VDC 2 amp drive/4 amp stall drive motors (using suitable PWM-input motor drivers)
1- 12VDC 2 amp drive/4 amp stall winch motor (using suitable PWM-input motor driver)
3- standard RC servos
1- stepper motor

Input:
2 proximity sensors
3 limit switches
power (or “kill” switch) to physically remove battery power

What rf receiver are you using? Why can't you use all 6 channels?

maniacbug: What rf receiver are you using? Why can't you use all 6 channels?

Thanks for replying at this hour. This should probably get moved to the "Will it work" forum...

The RF receiver is a Hitec RCD3500.

I thought only 1 channel can be read, because the Arduino only has 1 pin that supports PulseIn. I also read that the reciever can create alot of electrical noise in the system. Did I read wrong?

That said, I only need proportional control for the 2 drive motors and the camera pan. The other 2 servos, stepper motor, and winch motor will pretty much be solid state. The servos and stepper are limited duration that I'll set in the program, and the winch motor will have limit switches to stop it.

Finally, after some triple-refined googling, I found this article: http://diydrones.com/profiles/blog/show?id=705844:BlogPost:38418

I understand that the PulseIn command can only harbor 2 commands @ 30% per command, which would be fine, because I'd only be working with the 2 drive motors 95% of the time. It is the camera pan servo that I am unsure how I would communicate with. The other 3 channels, if I understand correctly, I could simply run to a digital pin, since those will be solid-state only, and I could use an IF statement to allow those functions to run only if the drive motors were at zero.

Since the camera would be a non-integral object, I could have that servo run directly off the receiver.

Left joystick: Up/Down- left motor power Left/Right- Open Bay doors and lower tailgate (actuates both servos plus stepper motor at once)

Right joystick: Up/Down- right motor power Left/Right- Run winch motor

trim knob: Camera pan left/right

Landing gear switch: Engage/disengage autopilot

Based on the link i found above, I modified that person’s source for differential drive…I think? (Remember, I’m a newb trying to learn)

I believe “Scale1” and “Scale2” would be proprietary numbers, as they are the conversion factor from the input to output. They were originally 0.15, but I’m not sure where the value comes from. I understand the part of converting from a -0.5, 0, +0.5 delta pulse to a 0, +100 scale for the PWM motor driver. Of course, in my simple mind, I would just adjust with (x100)+50, as shown below. -0.5 = 0, 0=50, +0.5 = 100.

I also added in the statements/loops that will allow operation of the bay doors, as well as the autopilot (just a basic spin, move, spin, move maneuver).

This is all based on the code I had + what little knowledge I’ve gained in the last 48 hours, representative of this code. If I understand right (Arduino Uno), there are 8 digital pins, 6 PWM pins, and 6 analog pins.

I’m still not sure what the analog pins are good for. I do know, that based on my understanding, I have 3 devices that don’t have homes- The cargo limit switches and the right door servo. Will I have to piggyback another Arduino through the I2C protocol? I haven’t gotten that far in research. What puzzles me most, is that analogWrite is used to run a PWM command, but the PWM is run off a digital pin. So that has me confused.

Pin0- Rx CH1
Pin1- Rx CH2
Pin2- Rx CH3
Pin3- Left motor drive
Pin4- Rx CH4
Pin5- Right motor drive
Pin6- Stepper Motor drive ( http://www.robotshop.com/cytron-3-40v-2a-unipolar-bipolar-stepper-motor-controller.html )
Pin7- Rx CH5
Pin8- Left Motor Direction
Pin9- Winch Motor drive
Pin10- Left Servo drive
Pin11- Right Motor Direction
Pin12- Cargo Gate Direction
Pin13- Cargo Winch Direction

Without a home:
INPUT Limit Switch on front of cargo container
INPUT Limit Switch on bottom of cargo container
OUTPUT Right Servo

(note that this code isn’t complete, i’m still filling in blanks and researching methods)

#include <Servo.h>

// Pins 3, 5, 6, 9, 10, 11 are PWM.
// Pins 2, 4, 7, 8, 12, 13 are Digital.
// Pins A0, A1, A2, A3, A4, A5 are analog.

unsigned long Channel1;
unsigned long Channel2;
unsigned long lastgood1;
unsigned long lastgood2;
unsigned long Initial1;
unsigned long Initial2;
unsigned long autotimer1;
unsigned long autotimer2;
unsigned long autotimer3;
unsigned long autotimer4;
unsigned long autotimer5;
unsigned long autotimer6;
Servo leftServo;
Servo rightServo;

// INPUTS
const int LeftMotorIn = 0; // INPUT Rx CH1 Left Motor
const int RightMotorIn = 1; // INPUT Rx CH2 Right Motor
const int cargoDoorIn = 2; // INPUT Rx CH3 cargo doors
const int cargoBoxIn = 4; // INPUT Rx CH4 cargo winch
const int autopilot = 7; // INPUT Rx CH5 Autopilot
const int cargoIn = ??; // INPUT Limit switch on front of cargo container
const int cargoOut = ??; // INPUT Limit switch on bottom of cargo container

// Solid State Outputs
const int LeftDir = 8; // SS OUTPUT Left Motor Direction Guide
const int RightDir = 11; // SS OUTPUT Right Motor Direction Guide
const int gateDir = 12; // SS OUTPUT Cargo Gate Direction Guide
const int winchDir = 13; // SS OUTPUT Cargo Winch Direction Guide

// PWM Outputs
const int LeftMotorOut = 3; // PWM OUTPUT Left Motor Output Drive
const int RightMotorOut = 5; // PWM OUTPUT Right Motor Output Drive
const int gateOut = 6; // PWM OUTPUT Stepper Motor Speed
const int winchOut = 9; // PWM OUTPUT Winch Motor Speed
leftServo.attach(10);
rightServo.attach(??);

float Scale1;
float Scale2;
boolean Forward1;
boolean Forward2;
boolean neutral1;
boolean neutral2;
boolean cargoIsOpen = FALSE;
boolean cargoIsOut = FALSE;
//motor stuff
int Left;
int Right;
int Motor1;
int Motor2;

/**************************************************************
 * Subroutine to control right motor
 ***************************************************************/
void motorLeft(byte PWM, boolean Dir1)
{
    if (Dir1){
      digitalWrite(LeftDir, LOW);
    } else {
      digitalWrite(LeftDir, HIGH);
    }
    analogWrite(LeftMotorOut, PWM); 
    
}  

/**************************************************************
 * Subroutine to control left motor
 ***************************************************************/
void motorRight(byte PWM, boolean Dir2)
{ 
    if (Dir1){
      digitalWrite(RightDir, LOW);
    } else {
      digitalWrite(RightDir, HIGH);
    }
    analogWrite(RightMotorOut, PWM); 
}  

void cargoDoor()
{
  if (cargoIsOpen){
    leftServo.write(0); // Turn servo to 0 degrees
    rightServo.write(0); // Turn servo to 0 degree
    digitalWrite(gateDir, LOW);
    analogWrite(gateOut,50); // Turn Stepper half duty backwards to home position
    cargoDoorIsOpen = FALSE;
    delay(500); // Delay half a second to let them do their things
  } else {
    leftServo.write(90); // Turn servo to 90 degrees
    rightServo.write(-90); // Turn servo to -90 degree
    digitalWrite(gateDir, HIGH);
    analogWrite(gateOut,50); // Turn Stepper 600 steps to open tailgate
    cargoDoorIsOpen = TRUE;
    delay(500); // Delay half a second to let them do their things
  }
}

void cargoBox()
{
  if (cargoIsOut){
    while (cargoIn = LOW){
      digitalWrite(winchDir, LOW);
      analogWrite(winchOut, 100); // Draw the cargo box in at full speed for full torque
    }
    cargoIsOut = FALSE;
    delay(1500); // After the box is all the way in, wait for it to settle    
  } else {
    while (cargoOut = LOW){
      digitalWrite(winchDir, HIGH);
      analogWrite(winchOut, 75); // push the cargo box out at half power
    }
    cargoIsOut = TRUE;
    delay(1500); // After the box is all the way out, wait for it to settle
  }
}

void autopilot(){
 // Goes into autopilot mode
  autotimer1 = millis(); // Get start time when going into autopilot
  autotimer2 = autotimer1 + 5000;
  autotimer3 = autotimer2 + 5000;
  autotimer4 = autotimer3 + 5000;
  autotimer5 = autotimer4 + 5000;
  
  while (autopilot = HIGH){ // If autopilot goes low, die.
    autotimer6 = millis();   
    while ( autotimer6 < autotimer2){
      digitalWrite(LeftDir = HIGH);
      analogWrite(LeftMotorOut, 100);
      digitalWrite(RightDir = LOW);
      analogWrite(RightMotorOut, 100);
    }
    while ( autotimer6 < autotimer3){
      digitalWrite(LeftDir = LOW);
      analogWrite(LeftMotorOut, 25);
      digitalWrite(RightDir = LOW);
      analogWrite(RightMotorOut, 25);
    } 
    while ( autotimer6 < autotimer4){
      digitalWrite(LeftDir = LOW);
      analogWrite(LeftMotorOut, 100);
      digitalWrite(RightDir = HIGH);
      analogWrite(RightMotorOut, 100);
    }
    while ( autotimer6 < autotimer5){
      digitalWrite(LeftDir = HIGH);
      analogWrite(LeftMotorOut, 25);
      digitalWrite(RightDir = HIGH);
      analogWrite(RightMotorOut, 25);
    }  
 
  } 
}

void peripherals()
{
  delay(500); //wait for things to stop
  
  if (autopilot = HIGH){
    autopilot();
  } else if (cargoDoorIn = HIGH){
    cargoDoor();
  } else if (cargoBoxIn = HIGH){
    cargoBox();
  } else {
    loop();
  }
}

void setup()
{
  Serial.begin(19200);
  pinMode (LeftMotorIn, INPUT); 
  pinMode (RightMotorIn, INPUT);
  pinMode(LeftMotorOut, OUTPUT); 
  pinMode(RightMotorOut, OUTPUT); 
  pinMode(LeftDir, OUTPUT);
  pinMode(RightDir, OUTPUT);
  pinMode(cargoDoorIn, INPUT);
  pinMode(cargoDoorOut, OUTPUT);
  pinMode(cargoBoxIn, INPUT);
  pinMode(cargoBoxOut, OUTPUT);
  InitialLeft = pulseIn (LeftMotorIn, HIGH); //read RC channel 1 (Left)
  InitialRight = pulseIn (RightMotorIn, HIGH); //read RC channel 2 (Right)
}

void loop()
{
  //read RC channel 1
  Channel1 = pulseIn (LeftMotorIn, HIGH, 20000); 
    if (Channel1 < Initial1) {
      Forward1 = false;
    } else {
      Forward1 = true;
    }
    
    if (Channel1 == Initial1){
      neutral1 = true;
     }
     
     Left = Channel1 - Initial1;

     Left = (Left * 100) + 50; // convert to 0-100 range
     constrain (Left, 0, 100); //just in case
  
  //read RC channel 2
  Channel2 = pulseIn (RightMotorIn, HIGH, 20000); 
    if (Channel2 < Initial2) {
      Forward2 = false;
    } else {
      Forward2 = true;
    }

    if (Channel2 == Initial2){
      neutral2 = true;
    }
    Right = Channel2 - Initial2;

    Right = (Right * 100) + 50; // convert to 0-100 range
    constrain (Right, 0, 100); //just in case

    // If motors are neutral, go to Peripherals Fxn & stop motors
    if (neutral1 && neutral2){
      digitalWrite(LeftDir, LOW);
      digitalWrite(LeftMotorOut, LOW); 
      digitalWrite(RightDir, LOW);
      digitalWrite(RightMotorOut, LOW);
      peripherals();
    } else {
      motorLeft(Left, Forward1);
      motorRight(Right, Forward2);
    }

}

I thought only 1 channel can be read, because the Arduino only has 1 pin that supports PulseIn.

Which pin would that be? The pulseIn() function times how long it takes the specified digital pin to go LOW then HIGH or HIGH then LOW, based on the direction requested. It works for any digital pin, including the analog pins used as digital pins.

unsigned long Channel1;
unsigned long Channel2;
unsigned long lastgood1;
unsigned long lastgood2;
unsigned long Initial1;
unsigned long Initial2;
unsigned long autotimer1;
unsigned long autotimer2;
unsigned long autotimer3;
unsigned long autotimer4;
unsigned long autotimer5;
unsigned long autotimer6;

All those numbers just scream for using arrays.

PaulS:

I thought only 1 channel can be read, because the Arduino only has 1 pin that supports PulseIn.

Which pin would that be? The pulseIn() function times how long it takes the specified digital pin to go LOW then HIGH or HIGH then LOW, based on the direction requested. It works for any digital pin, including the analog pins used as digital pins.

Wait, so I can use the analog pins for I/O? That would solve the pin crisis i have...until I added proximity sensors.

Arrays and I dont get along well. I know they can be handy, but i just have trouble with them.

That said, if I can use the analogs for digital IO, then I'll only have 3 pins free on the whole board. Would it be a good idea to I2C two boards together to give me more options? How does the programming work, does the slave have a prefix to be referenced on the master?

Wait, so I can use the analog pins for I/O?

Yes, you can.

Arrays and I dont get along well. I know they can be handy, but i just have trouble with them.

If you can keep track of whether to use autotimer4 vs. autotimer2 vs. autotimer5, I don't see why you can't keep track of whether to use autotimer[3] vs. autotimer[1] vs. autotimer[4].

Practice using arrays is the only way to get better/proficient.

That said, if I can use the analogs for digital IO, then I'll only have 3 pins free on the whole board.

Having no pins free, if you are still getting everything done, is fine.

Would it be a good idea to I2C two boards together to give me more options?

If you can't handle arrays? Probably not. :)

How does the programming work, does the slave have a prefix to be referenced on the master?

An address, not a prefix.

PaulS:

Arrays and I dont get along well. I know they can be handy, but i just have trouble with them.

If you can keep track of whether to use autotimer4 vs. autotimer2 vs. autotimer5, I don't see why you can't keep track of whether to use autotimer[3] vs. autotimer[1] vs. autotimer[4].

I should have clarified. Linear arrays I'm fine with. When i think of arrays, I think of multidimensionals...those make my brain hurt.

PaulS:

That said, if I can use the analogs for digital IO, then I'll only have 3 pins free on the whole board.

Having no pins free, if you are still getting everything done, is fine.

Would it be a good idea to I2C two boards together to give me more options?

If you can't handle arrays? Probably not. :)

How does the programming work, does the slave have a prefix to be referenced on the master?

An address, not a prefix.

My problem with not having any free pins, is that it hurts future growth. It's better to start with an idea of the big picture, granted I would have to use a second radio, since I've maxxed the RC channels. Chances that I may want to add 10 more sensors down the road is slim, but you never know. I was going to start with just 2 proximity sensors (Front/back), get my feet wet (and let the budget recover), then go from there. Might add some lights that operate automatically from an LDR or similar, add some proximity sensors (Being 18" wide and 2 feet long, not sure only one on each end will be sufficient), probably an inclination sensor to slow the motors when going up or down stairs or other slopes, And if I intend on using this around high water levels alot, probably a sensor on the belly that can detect water (maybe just another ultrasonic sensor?) to tell it to back out of high water. So that's be anywhere from 4-6 inputs and an extra output, so using 2 boards is probably a good idea. Additionally, with 2 boards I2C'd together, could I still use a bluetooth modem?

Is there some example code/syntax that would help me better understand how the I2C interface works between the two? I found this but I'm still not getting the jist of high speed data transport. I understand "logging on" to the comm bus, the data send/data recieve, but I don't understand the while loop necessary when receiving information, and the reference page for Wire.available doesn't help me much?

edit- via another thread, I got these links. I don't have time to fully read at the moment, but they look more helpful.

http://www.gammon.com.au/forum/?id=10896 http://www.arduino.cc/en/Reference/Wire

Keep in mind I would preferably like to just link two Uno's together.

So, if I were to link two Uno's over I2C, and think big picture. The thought is, that the master should contain all the devices neccesary for the driving and autopilot, while the slave should have the peripherals. What this allows, that the boards only talk during the autopilot, in which case I just send the slave a simple signal, which runs the autopilot function on the slave drive for the peripherals.

Now, I know somebody will tell me to go to a Mega. But my question with the mega, is does it have the same PulseIn CPU usage (30% per pin) as the Uno? If so, it makes more sense to me to use more Uno's and split the reading between two processors.

That said, given the layout below, I have 5 ping sensors and 3 PWM Rx inputs. On the ping sensors, the fronts only get polled moving forward, and the backs only get polled moving backwards, and they're only used during autopilot, which means it's not listening for Rx CH1 or 2. Thus,

--RF control Rx CH1 Rx CH2 Belly Ping

--Autopilot Forward-- Left Front Ping Right Front Ping Belly Ping

Backwards-- Left Rear Ping Right Rear Ping Belly Ping

On top of that, I'll only be pinging the sensors on half second intervals on Autopilot. So theoretically, I could have all 3 pulseIns running at once, with the front or back sensors on the 0, 0.5, 1.0, ect time scale, and the belly ping on the 0.25, 0.75, 1.25, 1.75, ect scale. The problem would be on manual RF control, I'd like to ping the belly sensor while driving, although not totally necessary- I should have 7" from the ground to the lowest electronics (which will be shielded from splash), but I'd like to never go through anything deeper than 5". Ideally, I'd be able to use the ping sensor to override the stick's throttle settings when something was in a certain range (say, 3" from sensor).

If I could do that, then I'd be perfectly set with a Mega board.

8 analog pins (lose 6 for comm channel) 12 PWM channels 12 solid state digital channels

Bluetooth Interface Rx CH1 input Rx CH2 input Rx CH3 input Rx CH4 input Rx CH5 input Rx CH6 input Left Motor Drive Left Motor Direction Right Motor Drive Right Motor Direction Stepper Motor Drive Stepper Motor Direction Winch Motor Drive Winch Motor Direction Cargo Box Forward Limit Switch Cargo Box Lower Limit Switch Left Servo Drive Right Servo Drive Camera Servo Drive Lighting System LDR/Light Sensor for Lighting System Longitudinal Tilt Switch Left Front Ping Sensor Right Front Ping Sensor Right Rear Ping Sensor Left Rear Ping Sensor Belly Ping Sensor

I count 28 devices - 1 bluetooth modem (2 pins) - 3 PWM Rx inputs - 3 Solid State Rx inputs - 5 ping sensors (input) - 3 servos (PWM output) - 4 motor drives (PWM output) - 4 directional controls (digital) - 1 LDR/Light Sensor input (digital) - 1 Lighting System Output (digital) - 2 Limit Switch inputs (digital) - 1 Tilt Switch Input (digital)

=

  • 1 Comm Channel (BT)
  • 7 PWM outputs
  • 7 digital inputs
  • 5 digital outputs
  • 8 PWM inputs (ping sensors + 3 Rx channels)

Which would have to be divided like so:

-----Master-

Bluetooth Modem

PWM input- Rx CH1 Rx CH2 Left Front Ping Sensor Right Front Ping Sensor Right Rear Ping Sensor Left Rear Ping Sensor

Digital input- Longitudinal Tilt Switch LDR/Light Sensor Rx CH6

PWM output- Left Motor Speed Right Motor Speed

Digital output- Lighting System Left Motor Direction Right Motor Direction

-----Slave-

PWM input- Rx CH3

Belly Ping Sensor

Digital input- Rx CH4 Rx CH5

Cargo Front Limit Switches Cargo Bottom Limit Switches

PWM output- Left Servo Control Right Servo Control Stepper Motor Speed Winch Motor Speed Camera Servo

Digital output- Stepper Motor Direction Winch Motor Direction


// Pins 3, 5, 6, 9, 10, 11 are PWM. // Pins 2, 4, 7, 8, 12, 13 are Digital. // Pins A0, A1, A2, A3, A4, A5 are analog.

Master.Pin0- Bluetooth Master.Pin1- Bluetooth Master.Pin2- Rx CH1 Master.Pin3- Left motor drive Master.Pin4- Rx CH2 Master.Pin5- Right motor drive Master.Pin6- empty PWM Master.Pin7- Left Motor Direction Master.Pin8- Right Motor Direction Master.Pin9- empty PWM Master.Pin10- empty PWM Master.Pin11- Longitudinal Tilt Switch Master.Pin12- Left Front Ping Sensor Master.Pin13- Right Front Ping Sensor

Master.PinA0- Rx CH6 Master.PinA1- Left Rear Ping Sensor Master.PinA2- right Rear Ping Sensor Master.PinA3- empty Master.PinA4- I2C Master.PinA5- I2C

Slave.Pin0- reserved Rx/Tx Slave.Pin1- reserved Rx/Tx Slave.Pin2- Rx CH5 Slave.Pin3- Camera Servo Slave.Pin4- Rx CH3 Slave.Pin5- Winch Motor Control Slave.Pin6- Winch Motor Direction Slave.Pin7- LDR/Light Sensor Slave.Pin8- Rx CH4 Slave.Pin9- Left Servo Control Slave.Pin10- Right Servo Control Slave.Pin11- Stepper Motor drive Slave.Pin12- Stepper Motor Direction Slave.Pin13- empty

Slave.PinA0- Cargo Box Front Limit Switches Slave.PinA1- Cargo Box Bottom Limit Switches Slave.PinA2- Lighting System Slave.PinA3- empty Slave.PinA4- I2C Slave.PinA5- I2C

Since I’ve strolled off the topic of communication, I moved over to the general forum- http://arduino.cc/forum/index.php/topic,63393.0.html

But my question with the mega, is does it have the same PulseIn CPU usage (30% per pin) as the Uno?

The pulseIn function is the same on the Mega and the UNO. Perhaps a better name for the function would be waitForASignalChangeAndTellMeHowLongItTook() but that's hard to type.

Of course the pulseIn() function ties up the Arduino. That's why it isn't used to read switch presses. It's used to read things that change frequently, and the time required to make that change is of interest. If the signal changes slowly, pulseIn() is a lousy function to use.

Where you got the idea that it uses 30% of the cpu I have no idea. The function uses 100% of the CPU while it is waiting for the signal change.

So theoretically, I could have all 3 pulseIns running at once,

Not on an Arduino. Once CPU == one activity, like a call to pulseIn.

The problem would be on manual RF control, I'd like to ping the belly sensor while driving, although not totally necessary- I should have 7" from the ground to the lowest electronics (which will be shielded from splash), but I'd like to never go through anything deeper than 5". Ideally, I'd be able to use the ping sensor to override the stick's throttle settings when something was in a certain range (say, 3" from sensor).

I'm confused about the type of device you are talking about here. You're driving something with a stick. A stick is usually how an airplane or helicopter is controlled. One does not drive an airplane or helicopter.

What is going to be splashing onto the device that it needs to be shielded from?

Flying something 7 " off the ground is going to be quite challenging.

So, what is this device you are making?