Getting two Pots to influence one PWM output

Hi.

I'm a complete novice at Arduino, only really used it to change 3d printer configurations - and my project it a little too overwhelming to get started so I'm asking for some help.

I'm building an off-road mobility scooter out of salvaged parts - I have multiple sclerosis and I'd like to take my dog out for walks in the forest near me. The main obstacle to getting this running is getting my DNO-10 motor controller to interface with the hand controls on my scooter. The DNO-10 can use a pot or PWM to set the speed of the motor but the hand controls on a mobility scooter aren't really compatible with this. It's hard to explain how they work in words so I've made a little graphic to kind of show what I mean.

I think that an Arduino would be the perfect way to get the hand controls to work on my motor controller - I just have no idea where to start or what keywords to search for.

Any help with this would be really appreciated. Thanks!

Control of a powered vehicle is a tricky project and one that can result in fatal consequences when things go wrong. I would strongly recommend that you get some more experience before you start on the real thing.

Why not start with a model car so you can get the hang of things?

Start simple and work up, first to emulating you controls and only then think about controlling motors, and don’t even mount them on a vehicle until you have it right.

I have recently bought a scooter of this type for my wife, and there is a bit more to it than just motor speed control. There is also the auto break function, and the “torque control” and the different powers that should be applied when going forward or reversing.

I appreciate the safety concerns and you're certainly not wrong that I should be cautious.

I hope that the following information will put you at ease:

  • I have a battery quick disconnect that's easily accessible.

  • While in the development and troubleshooting stages of this project the the rear axle and motor is disconnected from the rest of the scooter and is secured on my bench top without the wheels in contact with anything. I'm not riding this until I know it's safe.

  • The motor controller I'm using is designed for use with mobility scooter drivetrains and has the E-break and separate forward/reverse torque curves figured out. I believe it's designed for those miniature land rover conversions which are operated with different controls, not these flappy paddle style throttles.

The hardware side of this functions well when using a simple potentiometer for throttle control and the Arduino would just be taking the place of that potentiometer throttle to create an interface that's more usable for me and my condition. The manufacturer states that it's possible to use an Arduino/raspberry pi to take the place of the potentiometer for throttle control. None of this is out of specification, I just need help figuring out how to create one PWM output from the two analogue inputs I have. The motor controller takes care of all the more complicated stuff.

Here's a link to where they show how to control it with PWM: Driving the DNO by Raspberry Pi - 4QD - Electric Motor Control

analogWrite(PWMpin, value);

@philipjms96 If I understand the situation correctly there are 2 pots that control the speed and direction of the scooter. That is certainly the case on my wife's mobility scooter

One pot, let's call it maxSpeed, controls the maximum speed that the scooter can run and the other pot, let's call it direction controls the speed within the maximum speed allowed and the direction of the motor

If I am correct then the Arduino map() function (map() - Arduino Reference) is what you need. Take the output of an analogRead() of the maxSpeed pot, which will be a value between 0 and 1023 and use the map() function on the value output from the direction pot to convert it to the required PWM range. The trick to combining the 2 pot values is to use the maxSpeed value as the upper limit in map() to control the maximum speed in either direction.

A further wrinkle on my wife's scooter is that whatever the max speed is set to is halved when the scooter is in reverse, but I suggest that you get the simpler version working first before considering that, if at all

Please be careful when experimenting and don't connect the scooter motor to the controller until the project has been thoroughly debugged using output to the Serial monitor. When you do connect the scooter I suggest that you run it initially with the drive wheels off the ground

1 Like

@UKHeliBob
quote="UKHeliBob, post:5, topic:896602"]
The trick to combining the 2 pot values is to use the maxSpeed value as the upper limit in map() to control the maximum speed in either direction.
[/quote]

This is the information I was looking for, Thanks!

I've now got the maxSpeed set to limit the upper value of the direction pot, and when I change the value of the maxSpeed pot it scales down the output I'm seeing through the serial monitor all the way to zero which is great and means that portion of the code works.

However now I'm running into an issue dividing the direction pot into separate forward and backwards values. I'm testing this on a breadboard with two 10k pots and looking at the serial monitor.
When the direction pot is swept to the left (forward) the serial is showing a positive number between 0 and 255 for both FwdOut and RevOut (The same value for both) and when it's swept to the right (reverse) it's showing a negative number between 0 and -265 for both Fwd and Rev (The same value for both)

I had hoped that setting the map(DirSensorValue, X, Y, 0, SpeedOut) X and Y values to be the lower and higher vales for each of the forward and backwards sections of the pot would make it disregard any values outside of the ranges I specified however I think that my understanding of how this works is wrong.

How do I make the FwdOut value show a value between 0 and 255 when the direction pot sweeps from a value of 500 to 0, and the RevOut show a value between 0 and 255 when the Pot sweeps from a value of 523-1023? (500 to 523 is the "dead zone" between forward and reverse)

This is what I've written so far:

// These constants won't change. They're used to give names to the pins used:
const int SpeedPot = A0;  // Analog input pin that the speed pot is attached to
const int DirPot = A1; // Analog input pin that the direction pot is attached to
const int analogOutPin = 9; // Analog output pin that the motor controller is attached to

int SpeedSensorValue = 0;        // value read from the speed pot
int SpeedOut = 0;          //Value from maping speedpot output
int DirSensorValue = 0;        // value read from the direction pot
int FwdOut = 0;           // forward value from mapping direction pot
int RevOut = 0;           // reverse value from mapping direction pot
int OutputValue = 0;        // value output to the PWM (analog out)

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600);
}

void loop() {
  Speed();
  Direction();
  Forward();
  Reverse();
  Output();
  cereal();

}

void Speed() {
  // read the speed pot analog in value:
  SpeedSensorValue = analogRead(SpeedPot);
  // map it to the range of the analog out:
  SpeedOut = map(SpeedSensorValue, 0, 1023, 0, 255);
}

void Direction() {
  // read the direction pot analog in value:
  DirSensorValue = analogRead(DirPot);
}

void Forward() {
  // map expected values from pressing the forward lever to the range of the maximum speed:
  FwdOut = map(DirSensorValue, 523, 1023, 0, SpeedOut);
}

void Reverse() {
  // map expected values from pressing the reverse lever to the range of the maximum speed:
  RevOut = map(DirSensorValue, 0, 500, 0, SpeedOut);
}

void Output() {
  OutputValue = min(FwdOut, RevOut);
  // change the analog out value:
  analogWrite(analogOutPin, OutputValue);
}
void cereal () {
  // print the results to the Serial Monitor:
  Serial.print("\t output = ");
  Serial.println(OutputValue);
  Serial.print("\t fwd = ");
  Serial.println(FwdOut);
  Serial.print("\t rev = ");
  Serial.println(FwdOut);
  Serial.print("\t Direction = ");
  Serial.println(DirSensorValue);

  // wait 1 second before the next loop for the analog-to-digital
  // converter to settle after the last reading:
  delay(1000);
}

What I would do:

Take a pencil and paper and draw some X-Y plots with pot setting on the X axis, control outputs on the Y axis. It will help derive the math that you need to program.

I often joke that I am fine with math, unless there are dollar signs on the figures. Try to think of this as a pure math problem, rather than a programming problem.

The mapping would be something like:
Forward = abs( in/2 - deadzone)

You can figure out the reverse

1 Like

X/Y Axis! That's it! What I'm trying to make is a single axis (forward and backwards) joystick that's turned over on it's side. What I'm looking for is has likely already been solved but I just needed the right keyword to search for it, which I think is "joystick".

More people will jump in if you go back and format your code correctly for the forum...

sorry, still learning the forum. I think I've formatted it correctly now.

Thanks to everyone for the help!

I've taken peoples suggestions and managed to piece together this bit of code which, as long as the serial monitor is correct, should do exactly what I need.

// These constants won't change. They're used to give names to the pins used:
const int SpeedPot = A0;  // Analog input pin that the speed pot is attached to
const int DirPot = A1; // Analog input pin that the direction pot is attached to
const int analogOutPin = 9; // Analog output pin that the motor controller is attached to
const int Relay_Pin = 52; //Relay pin for reverse control

int SpeedSensorValue = 0;        // value read from the speed pot
int SpeedOut = 0;          //Value from mapping speedpot output
int DirSensorValue = 0;        // value read from the direction pot
int OutputValue = 0;        // value output to the PWM (analog out)

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600);
  pinMode(Relay_Pin, OUTPUT);
}

void loop() {
  Speed();
  Direction();
  Output();
  cereal();

}

void Speed() {
  // read the speed pot analog in value:
  SpeedSensorValue = analogRead(SpeedPot);
  // map it to the range of the analog out:
  SpeedOut = map(SpeedSensorValue, 0, 1023, 0, 255);
}

void Direction() {
  // read the direction pot analog in value:
  DirSensorValue = analogRead(DirPot);
}

void Output() {
   // Y-axis used for forward and backward control
  if (DirSensorValue < 470) {
    // trigger relay to tell motor controller to reverse motor
    digitalWrite(Relay_Pin, HIGH);
    // Convert the declining Y-axis readings for going backward from 470 to 0 into 0 to 255 value for the PWM signal for increasing the motor speed
    OutputValue = map(DirSensorValue, 470, 0, 0, SpeedOut);
  }
  else if (DirSensorValue > 550) {
    // Set Motor A forward
    digitalWrite(Relay_Pin, LOW);
    // Convert the increasing Y-axis readings for going forward from 550 to 1023 into 0 to 255 value for the PWM signal for increasing the motor speed
    OutputValue = map(DirSensorValue, 550, 1023, 0, SpeedOut);
  }
    // If joystick stays in middle the motors are not moving
  else {
    OutputValue = 0;
    digitalWrite(Relay_Pin, LOW);
  }
  
  
}
void cereal () {
  // print the results to the Serial Monitor:
  Serial.print("\t output = ");
  Serial.println(OutputValue);


  // wait 1 second before the next loop for the analog-to-digital
  // converter to settle after the last reading:
  delay(1000);
}

I am glad you got it working, at least this far. The practical testing could be interesting and a physical switch that cuts the power rather than relying on software would be a good idea

A couple of comments

  • The code that you posted does not compile because you missed the end of it when copy/pasting it here. If you use the "Copy for forum" option in the IDE with nothing selected then you get the whole sketch, and code tags with no extra effort as a bonus

  • a bugbear of mine is the use of the most appropriate variable type for the purpose for which they are being used. Do your pin number really need to be declared as int rather than byte ? The same goes for some of the other variables. The pot values need to be int or unsigned int but not the final output value and the DirOut variable is not used in the sketch. As the sketch is so small then saving a few bytes here and there is not necessary but it is good practice to do so when you can.

1 Like

I'll absolutely have a physical switch to cut power and it'll be key operated to prevent joyrides lol

I should have fixed the compiling issue now. I didn't know about the copy for forum option, I'll definitely use that next time!

To be honest with you I didn't know the pin numbers could be declared in other ways, this is my first time creating Arduino code. I'll have a look into variable types and try and do better in the future.

When testing, the "kill" switch should be a big red button always in easy reach in case of any havoc when the motor runs amok

The time for a key switch is when you put the scooter together in its final form