Go Down

Topic: Simple communication between two Uno's (Read 817 times) previous topic - next topic

warren631

One Uno determines robot speeds of each wheel.  Second Uno controls the wheel motors.  I had to split this up because of so many conflicts between library timers and interrupts.  Now I find I can't use SoftwareSerial to transmit wheel speeds between the two Uno's because of a conflict between SoftwareSerial and the motor driver library (SoftwareSerial shuts down interupts that the motor driver library uses to adjust PWM outputs).

Question is: what other ways can I communicate the two wheel speeds from one Uno to another?  I don't want to use regular Serial pins 0 and 1 because I want to use that for monitoring and programming.  Can I use analogWrite to send a value from one Uno and then use analogRead to read the value in the other Uno (perhaps with a cap to smooth out the PWM ripple?).

What other (simple) methods do you suggest to send two integers from one Uno to another (that don't use timers or interrupts and will not conflict with my motor driver)? 

PaulS

Quote
Question is: what other ways can I communicate the two wheel speeds from one Uno to another?

SPI, I2C, AltSoftSerial, bit-banging your own protocol.

PeterH


many conflicts between library timers and interrupts


Are you sure the library timers are all required and the conflicts can't be resolved? Having multiple Arduinos collaborate is going to make your solution much more complicated.
I only provide help via the forum - please do not contact me for private consultancy.

warren631

#3
Sep 06, 2013, 12:01 am Last Edit: Sep 06, 2013, 12:10 am by warren631 Reason: 1
Do SPI, I2C or AltSoftSerial use timers or interrupts?  Seems like every time I want to add something, such as NewPing or a Servo or IRremote, something else no longer works because of timer or interrupt conflicts.  I have managed to get the following to work together in the first Uno:
Code: [Select]
#include <SoftwareSerial.h>
#include <NewPing.h>
#include <Servo.h>
#include <IRremote.h>
#include <PID_v1.h>
#include <Wire.h>
#include <HMC5883L.h>

But the following do not work together in the second Uno:
Code: [Select]
#include <MOTOR.h> // elechouse.com
#include <SoftwareSerial.h>

Here is the code for the drive Uno:
Code: [Select]
// Uno drive control**************************************************************************

#include <SoftwareSerial.h>
#include <PID_v1.h>
#include <MOTOR.h>

int xx = 0;
int tachL = 512;
int tachR = 512;
int tachLpin = A2;
int tachRpin = A3;

/*
int IN_A1 = 3;     // input RPWM (forward A motor)
int IN_A2 = 11;    // input LPWM (reverse A motor)
int IN_B1 = 9;     // input RPWM (forward B motor)
int IN_B2 = 10;    // input LPWM (reverse B motor)
*/
int potPin = A0;   // pin for the adjustment potentiometer

double KpL =   1.00;                               // PID P Gain
double KiL =   0.00;                               // PID I Gain
double KdL =   0.00;                               // PID D gain
double KpR =   1.00;                               // PID P Gain
double KiR =   0.00;                               // PID I gain
double KdR =   0.00;                               // PID D gain


SoftwareSerial mySerial(4, 5); // RX, TX

//const int ledPin =  13;      // the number of the LED pin

//Define Variables we'll be connecting to (append L=left wheel, R=right wheel)
double act_speedL = 0;
double pid_speedL = 0;
double new_speedL = 0;
double old_speedL = 0;
double errorL = 0;
double act_speedR = 0;
double pid_speedR = 0;
double new_speedR = 0;
double old_speedR = 0;
double errorR= 0;;
double set_speedL = 0;
double set_speedR = 0;
//double motorAdjustment = 0;

//Specify the PID links and initial tuning parameters
//PID myPID(&Input, &Output, &Setpoint,kP,kI,kD, DIRECT);
PID myPIDL(&act_speedL, &pid_speedL, &set_speedL,KpL,KiL,KdL, DIRECT);
PID myPIDR(&act_speedR, &pid_speedR, &set_speedR,KpR,KiR,KdR, DIRECT);

void setup()
{
  // put your setup code here, to run once:
  /** motor driver initialize */
  motor.begin();

  Serial.begin(57600);
  Serial.flush();
  mySerial.begin(57600);
  mySerial.flush();
/*   
    pinMode(IN_A1, OUTPUT);
    pinMode(IN_A2, OUTPUT);
    pinMode(IN_B1, OUTPUT);
    pinMode(IN_B2, OUTPUT);
        //motors stop
    digitalWrite(IN_A1, HIGH);
    digitalWrite(IN_A2, HIGH);
    digitalWrite(IN_B1, HIGH);
    digitalWrite(IN_B2, HIGH);
*/
  // initialize the LED pin as an output:
//  pinMode(ledPin, OUTPUT);     

/*
// increase frequency of PWM on pins 11 & 12 (TCCR1B) and pins 3 & 5 (TCCR2B)
int prescalerVal = 0x07; // create a variable called prescalerVal and set it equal to the binary number "00000111"
TCCR1B &= ~prescalerVal; //AND the value in TCCR1B with binary number "11111000"
TCCR2B &= ~prescalerVal; //AND the value in TCCR2B with binary number "11111000"

prescalerVal = 0x01; //set prescalerVal equal to binary number "00000010"
TCCR1B |= prescalerVal; //OR the value in TCCR1B with binary number "00000001"
TCCR2B |= prescalerVal; //OR the value in TCCR2B with binary number "00000001"
*/

  new_speedL = 0;
  new_speedR = 0;

  //turn the PIDs on
  myPIDL.SetOutputLimits(-250, 250);
  myPIDR.SetOutputLimits(-250, 250);
  myPIDL.SetSampleTime(100);
  myPIDR.SetSampleTime(100);
  myPIDL.SetMode(AUTOMATIC);
  myPIDR.SetMode(AUTOMATIC);
}

void loop()
{
// put your main code here, to run repeatedly:
delay(1);
  xx++; if (xx > 40000) xx=0;  // do prints every xx cycles

  if (mySerial.available()) {   //read the two speeds

    int x = mySerial.parseInt();
    int y = mySerial.parseInt();

    if (mySerial.read() == '\n') {
       new_speedL = float(x);
       new_speedR = float(y);

      Serial.print("set_speedL = ");
      Serial.print(set_speedL);
      Serial.print(", set_speedR = ");
      Serial.println(set_speedR);
    }
    mySerial.flush();

//    motorAdjustment = analogRead(potPin);   
//      Serial.print("motorAdjustment = ");
//      Serial.println(motorAdjustment);
//    if(motorAdjustment < 509){
// new_speedR*=(1+(motorAdjustment/509));
// }else{
// new_speedL*=(1-(motorAdjustment/509));
// }
   }
    //control motors here:

//for testing???????????????????
//if (xx > 10000) new_speedL = -60; else new_speedL = 60;
//if (xx > 10000) new_speedR = -60; else new_speedR = 60;

//     sendPlotData("new_speedL", new_speedL);
//     sendPlotData("new_speedR", new_speedR);

if (new_speedL == -1) new_speedL = 0;
if (new_speedR == -1) new_speedR = 0;

//do ramp
if (new_speedR > old_speedR) set_speedR = set_speedR + 0.2;
if (new_speedR < old_speedR) set_speedR = set_speedR - 0.2;
old_speedR = set_speedR;
if (new_speedL > old_speedL) set_speedL = set_speedL + 0.2;
if (new_speedL < old_speedL) set_speedL = set_speedL - 0.2;
old_speedL = set_speedL;

       set_speedL = 15;
       set_speedR = 15;

//determin actual wheel speeds from tachs
  tachL = analogRead(tachLpin);
  tachR = analogRead(tachRpin);
 
//scale actual speeds
  act_speedL = ((tachL - 505) / 3.944);
  act_speedR = ((tachR - 513) / 3.944);
   
//do speed pid calculations:
//  errorL = set_speedL - act_speedL;
//  pid_speedL = errorL*KpL;
//  errorR = set_speedR - act_speedR;
//  pid_speedR = errorR*KpR;
 

//  int spdL = (int(set_speedL)+int(pid_speedL));
//  int spdR = (int(set_speedR)+int(pid_speedR));
//  int spdL = int(set_speedL);
//  int spdR = int(set_speedR);

//  spdL = updatePid(spdL, set_speedL, act_speedL);
//  spdR = updatePid(spdR, set_speedR, act_speedR);

//do pid calculation:
  myPIDL.Compute();
  myPIDR.Compute();

  pid_speedL = abs(pid_speedL);
  pid_speedR = abs(pid_speedR);
//  spdLalpha = 0.02;
//  spdRalpha = 0.02;
 
//  expspdL = (spdLalpha * spdL) + ((1-spdLalpha) * expspdL);   
//  expspdR = (spdRalpha * spdR) + ((1-spdRalpha) * expspdR);   
 
  if(set_speedL > 0){                 // positive = forward
            motor.set(A, pid_speedL, FOR);     // channel A FOR rotation
}else{                              // negative = reverse
            motor.set(A, pid_speedL, REV);     // channel A (LEFT) FORFOR rotation
}

if(set_speedR > 0 ){                       
            motor.set(B, pid_speedR, FOR);     // channel B (RIGHT) REVREV rotation
}else{
            motor.set(B, pid_speedR, REV);     // channel B FOR rotation
}

  //*******Plot data for MegunoLink******************************************************   
//  if ((xx % 50) == 0) {
//    Serial.print(".");
//    sendPlotData("expwireDistance", expwireDistance);
//    sendPlotData("Direction", Direction);
//    sendPlotData("headingDegrees", headingDegrees);
//    sendPlotData("expSonarDist", expSonarDist);
//     sendPlotData("tachL", tachL);
//     sendPlotData("expspdL", expspdL);
//     sendPlotData("set_speedL", set_speedL);
//     sendPlotData("act_speedL", act_speedL);
//     sendPlotData("pid_speedL", pid_speedL);
//     sendPlotData("Speed", SpeedR);
//     sendPlotData("tachR", tachR);
//     sendPlotData("expspdR", expspdR);
     sendPlotData("set_speedR", set_speedR);
     sendPlotData("act_speedR", act_speedR);
     sendPlotData("pid_speedR", pid_speedR);
//   }
   
}

//*********format data for MegunoLink *****************************************************
void sendPlotData(String seriesName, float data)
{
  Serial.print("{");  // Meguno print format string
  Serial.print(seriesName);
  Serial.print(",T,");
  Serial.print(data);
  Serial.println("}");
}

PaulS

Code: [Select]
  Serial.begin(57600);
  Serial.flush();
  mySerial.begin(57600);
  mySerial.flush();

What version of the IDE are you using? Why are you using flush() If you can't explain, stop doing it.

warren631

#5
Sep 06, 2013, 12:38 am Last Edit: Sep 06, 2013, 12:42 am by warren631 Reason: 1
I got rid of the flushes a while ago, but no solution to my problem.
PS: I only have pins 4 and 5 spare available for communication.  That is why I chose SoftwareSerial.
Again: Which communication library's that don't use timers or Interrupts?


mrburnette

Bill Porter has a library solution, but I have not dug through the code to determine interrupt use.

http://www.billporter.info/2011/05/30/easytransfer-arduino-library/

You could just write your own routine and bit-shift the bytes out one pin... With two pins, you could keep the protocol simple and just back/nak handshake.


Ray

warren631

I really think there should be some kind if reference provided with each library listing what timers or interrupts each use (or, as in the case of SoftwareSerial, what timers or interrupts they disable).  This would save a lot of experimenting and headaches trying to figure out why something does not work.  And also what pins can be used for each library.  I know library's are free, so I guess I shouldn't complain.

I finally figured out how to use SoftwareSerial on my first Uno by only enabling it at the start of transmission and then immediately disabling it.  I was quite proud of my achievement.  Then I find I can't use SoftwareSerial on my second Uno to receive the data from the first Uno.  Whoever would publish a library that disables all interrupts so no other library can be used with it.  Its like the old HP 1980's printer driver that took over the PC so you couldn't run any other programs while it was loaded.

warren631

#8
Sep 06, 2013, 01:56 am Last Edit: Sep 06, 2013, 02:28 am by warren631 Reason: 1
Quote
You could just write your own routine and bit-shift the bytes out one pin... With two pins, you could keep the protocol simple and just back/nak handshake.

- that's easy for you to say.

I think I will use AnalogWrite on two PWM pins of the first Uno connected to two pins of the second Uno and use PulseIn on these two pins with some convertion factor to convert from microseconds to speed value.  Its simple.  Does that sound feasible?

mrburnette

#9
Sep 06, 2013, 02:37 am Last Edit: Sep 06, 2013, 02:47 am by mrburnette Reason: 1
I took a look at Bill Porter SoftSerial code and he derives his class from SoftwareSerial, so he is still using interrupts.

Quote
Again: Which communication library's that don't use timers or Interrupts?


Off the top of my head, I cannot think of any.  "Communications" between the uC and another port is just a natural use of an interrupt driven metaphor.

How much data two integers needs to be sent and how often? You could simply put a non-blocking function call into the loop() and bit-bang to/from the other uC... Matching bytes to identical variables in the two processors.

Ray


Edited as shown

There is some faux-SPI bit-banging code here:
http://forum.arduino.cc/index.php/topic,134941.0.html

This should get you going.

PaulS

Quote
or, as in the case of SoftwareSerial, what timers or interrupts they disable

SoftwareSerial doesn't disable any timers or interrupts. The problem with SoftwareSerial is that it takes a relatively long time to do what the hardware should be doing. The result is that it keeps other interrupts (timer based, mostly) from firing at the right times.

When you find yourself in this situation, it's really time to look at the hardware you are using, and determine if, just maybe, you are pushing it beyond its limits.

You probably should be using a Mega, with 4 hardware serial ports. That would give you more timers, more external interrupts, and, most importantly, allow you to stop using SoftwareSerial and whining about what it does to the rest of your program.

warren631

Quote
it keeps other interrupts (timer based, mostly) from firing at the right times.

- that is what I would call 'disabling'. 

PaulS

Quote
that is what I would call 'disabling'. 

Disabled would be when they didn't fire at all. Not firing at the proper time is a different issue. While the affect may be the same (non working code), the causes and solutions are quite difficult.

Being a pedant is sometimes important.

Go Up