R/C Speedcontroller Attiny85

Hello,

I'm currently building a radio controlled speed-controller that is bidirectional. The brains for this is an attiny85 running at 8MHz (internal) with a supply voltage of 5V. The H-bridge is connected on pin 5 and 6, as they are PWM-capable pins and RC-receiver connected on 7. When it's powered, one of the directions is lit up. When the stick moves in either direction, the speed decreases a bit and go back to previous speed. Could it be due attiny running on internal clock or something else?

Code of attiny:

#define FORWARD 0 // Pin PB0.
#define REVERSE 1 // Pin PB1.
#define SERVOINPUT 2 // Pin PB2.

bool forwardDirection = false;
bool reverseDirection = false;

void setup()
{
  // Set pins of µC to correct state.
  pinMode(FORWARD, OUTPUT);
  pinMode(REVERSE, OUTPUT);
  pinMode(SERVOINPUT, INPUT);
}

void loop()
{
  // Variable for speed.
  byte currentSpeed = 0;
  // Get the length of servo signal from receiver.
  unsigned long pulseWidth = pulseIn(SERVOINPUT, HIGH, 21000);
  // Is stick centered or connector disconnected?
  if (pulseWidth == 1500 || pulseWidth == 0)
  {
    // When centered or connection lost, disable all outputs.
    digitalWrite(FORWARD, LOW);
    digitalWrite(REVERSE, LOW);
    // Set direction to none.
    forwardDirection = false;
    reverseDirection = false;
  }
  // Is stick lower than center?
  if (pulseWidth < 1500)
  {
    // Check if other direction is active.
    if (forwardDirection)
    {
      // Disable output.
      digitalWrite(FORWARD, LOW);
      // Change direction.
      forwardDirection = false;
      reverseDirection = true;
    }
    // Calculate speed of motor.
    currentSpeed = (1500 - pulseWidth) / 2;
    // Activate motor in reverse direction according to speed.
    analogWrite(REVERSE, currentSpeed);
  }
  // Is stick higher than center?
  if (pulseWidth > 1500)
  {
    // Check if other direction is active.
    if (reverseDirection)
    {
      // Disable output.
      digitalWrite(REVERSE, LOW);
      // Change the direction.
      forwardDirection = true;
      reverseDirection = false;
    }
    // Calculate speed of motor.
    currentSpeed = (pulseWidth - 1500) / 2;
    // Activate motor in reverse direction according to speed.
    analogWrite(REVERSE, currentSpeed);
  }
}

Properly used code tags on your first post. Karma++ :smiley:

Now help me better understand your problem.

  • What do you expect the sketch to do?
  • What does it actually do?
  • How do those two differ?

An enum for the states (going forward, stopped, going backward) would allow you to have ONE variable that defined what the thing is doing, not two.

Every time you write to the FORWARD pin, you should also write to the REVERSE pin, so you don't accidentally leave the thing in two gears, or no gear.

Functions are your friends. Make use of them.

@vinceherman: Thx for that, I've quickly read the forum rules and also saw some people complaining about before I signed here :slight_smile: .The sketch is supposed to look for an incoming servo-signal. When it's centered (meaning that stick is centered) or the receiver has either lost connection there should no movement. But when down or up it must to that direction and the further the stick, the faster the motor should rotate. But when you test it, with the stick is centered it goes in reverse direction at a certain speed (at the moment using leds instead of motor as I thought to have one laying around). When the stick is moved down slowly the led brightness goes down a little and going further makes it go back to original brightness. When stick is moved to up from the center the direction doesn't change and brightness stays the same.

@PaulS: Thanks for the tip! I will modify the code with the enum and a function for controlling the pins. I somehow keep forgetting about the enum existing :sweat_smile:

Thx all for the quick response!

You use pulseIn() to get the value from the RC receiver and then do stuff with it. The stuff does not meet your expectations (which you expanded upon for us, thank you).
Do you know the actual values that PulseIn() is returning? I would put in a Serial.print() to see if it is return the value range that you, and your sketch logic, expect.

Well, seems i get mixed results. But when I inspected the signal with a scope, it's shows a valid signal, but the voltage level isn't the best. Voltage level of the signal is only about 2V. So I reversed the pull-down resistor to pull-up to see a difference. Then it sees valid times.

Centered is between 1546 and 1560.

Fully down is 1126 to 1136.

Fully up is around 1966.

But the next measuring always ends in a zero and then a valid measurement (probably by the 1 second delay added in the code).

Smalln:
But the next measuring always ends in a zero and then a valid measurement (probably by the 1 second delay added in the code).

Your values look reasonable.

If you get bad values that far outside the range of expected values, you might consider ignoring them and using the last good value instead.

Well guy's, made a lot of progress. Now the sketch works like should. It respond now to either directions as normal. Now the only thing I need to do is a bit fine-tuning (or perhaps adding a calibration function).

Well a big thanks from me!

Awesome! Do you know what you changed to make it work better?
Do you mind posting the working code here for future generations?

I changed the values for when the stick is centered. Now it has some tolerance in it, plus I added extra code since most transmitters have the capability to go out of the normal range. When this happens the attiny will go full speed. This currently works for my receiver (a Futaba R617FS). It is possible that you might need a logic level shifter to make it work. Here's the code for it:

#define FORWARD 0 // Pin PB0.
#define REVERSE 1 // Pin PB1.
#define SERVOINPUT 2 // Pin PB2.

enum motorDirection
{
  stopped = 0,
  forward = 1,
  reverse = 2
};

byte currentDirection = motorDirection::stopped;

void setup()
{
  // Set pins of µC to correct state.
  pinMode(FORWARD, OUTPUT);
  pinMode(REVERSE, OUTPUT);
  pinMode(SERVOINPUT, INPUT_PULLUP);
}

void loop()
{
  // Get the length of servo signal from receiver.
  unsigned long pulseWidth = pulseIn(SERVOINPUT, HIGH, 21000);
  // Set motor according to pulse timing.
  setMotor(pulseWidth);
  // Add a delay.
  delay(10);
}

void goForward(byte speed)
{
  // Check if other direction is active.
  if (currentDirection == motorDirection::reverse)
  {
    // When other direction is still active, disable direction.
    digitalWrite(REVERSE, LOW);
    // Add a slight delay.
    delay(1);
  }
  // Set direction to forward.
  currentDirection == motorDirection::forward;
  // Activate motor according to speed.
  analogWrite(FORWARD, speed);
}

void goReverse(byte speed)
{
  // Check if other direction is active.
  if (currentDirection == motorDirection::forward)
  {
    // When other direction is still active, disable direction.
    digitalWrite(FORWARD, LOW);
    // Add a slight delay.
    delay(1);
  }
  // Set direction to forward.
  currentDirection == motorDirection::reverse;
  // Activate motor according to speed.
  analogWrite(REVERSE, speed);
}

void setMotor(unsigned long pulseWidth)
{
  byte speed = 0;
  // Is the pulse out of range?
  if (pulseWidth > 2000 || pulseWidth < 1000)
  {
    // When out of normal range, go full speed.
    speed = 255;
  }
  // Is stick centered or connection missed?
  if ((pulseWidth >= 1440 && pulseWidth <= 1560) || pulseWidth == 0)
  {
    // When centered or missing disable outputs.
    digitalWrite(FORWARD, LOW);
    digitalWrite(REVERSE, LOW);
    // Remember state of direction.
    currentDirection = motorDirection::stopped;
  }
  // Is stick pulled down?
  if (pulseWidth < 1440)
  {
    // Calculate speed first.
    speed = (1500 - pulseWidth) / 2;
    // Go in reverse.
    goReverse(speed);
  }
  // Is stick pushed forward?
  if (pulseWidth > 1560)
  {
    // Calculate speed first.
    speed = (pulseWidth - 1500) / 2;
    // Go in forward direction.
    goForward(speed);
  }
}