Pages: [1]   Go Down
Author Topic: Simple communication between two Uno's  (Read 667 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Full Member
***
Karma: 1
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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)? 
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 615
Posts: 49417
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12630
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
Full Member
***
Karma: 1
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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:
#include <MOTOR.h> // elechouse.com
#include <SoftwareSerial.h>
Here is the code for the drive Uno:
Code:
// 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("}");
}
« Last Edit: September 05, 2013, 05:10:19 pm by warren631 » Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 615
Posts: 49417
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
  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.
Logged

Offline Offline
Full Member
***
Karma: 1
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?

« Last Edit: September 05, 2013, 05:42:18 pm by warren631 » Logged

Atlanta, USA
Offline Offline
Edison Member
*
Karma: 53
Posts: 1793
AKA: Ray Burne
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Offline Offline
Full Member
***
Karma: 1
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Full Member
***
Karma: 1
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
« Last Edit: September 05, 2013, 07:28:11 pm by warren631 » Logged

Atlanta, USA
Offline Offline
Edison Member
*
Karma: 53
Posts: 1793
AKA: Ray Burne
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
« Last Edit: September 05, 2013, 07:47:21 pm by mrburnette » Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 615
Posts: 49417
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Full Member
***
Karma: 1
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
it keeps other interrupts (timer based, mostly) from firing at the right times.
- that is what I would call 'disabling'. 
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 615
Posts: 49417
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Pages: [1]   Go Up
Jump to: