Multichannel linear actuator control - no arduino experience

lyndon - your code is great.

I had to make some modification since the switch pull-up had a default to high when open. It’s essentially the same, and it works!

const int NUMBER_OF_CHANNELS = 4;
const int _inputs[] = {1, 2, 3, 4};
const int _forward[] = {5, 6, 7, 8};
const int _reverse[] = {9, 10, 11, 12};
const int _forwardTime[] = {1000, 1000, 1000, 1000};
const int _reverseTime[] = {1500, 1500, 1500, 1500};
int _inMotion[NUMBER_OF_CHANNELS];
bool _lastState[NUMBER_OF_CHANNELS];

void setup() 
{
  for (int i=0; i < NUMBER_OF_CHANNELS; i++)
  {
    pinMode(_inputs[i], INPUT_PULLUP);
    pinMode (_forward[i], OUTPUT);
    pinMode (_reverse[i], OUTPUT);
    digitalWrite(_forward[i], LOW);
    digitalWrite(_reverse[i], LOW);
    _lastState[i] = !(digitalRead(_inputs[i]));
  }
}

void loop() 
{
  bool currentState = LOW;
  delay(1);
  for (int i=0; i < NUMBER_OF_CHANNELS; i++)
  {
    currentState = !(digitalRead(_inputs[i]));
    if (currentState != _lastState[i]) 
    {
      if (currentState == HIGH)           // Rising Edge
      {
        digitalWrite(_reverse[i], LOW);
        digitalWrite(_forward[i], HIGH);  // Go Forward
        _inMotion[i] = _forwardTime[i];   // Set forward time
      }
      else                                // Falling Edge
      {
        digitalWrite(_forward[i], LOW); 
        digitalWrite(_reverse[i], HIGH);  // Go Reverse
        _inMotion[i] = _reverseTime[i];   // Set reverse time
      }
    }
    _lastState[i] = currentState;         // Update previous state
    if (_inMotion[i] > 0 )                // Now update motions
    {
      if ( --_inMotion[i] == 0 )          // Decrement motion timer
      {     
        digitalWrite(_forward[i], LOW);   // Timed out. Stop
        digitalWrite(_reverse[i], LOW);
      }
    } 
  }   
}

[<LINK_TEXT text=“http://i1062.photobucket.com/albums/t49 … 68yesz.jpg”>http://i1062.photobucket.com/albums/t496/karimwassef/Mobile%20Uploads/263D8946-4EB4-4F91-91E3-1DE84D562AF1_zpstv68yesz.jpg</LINK_TEXT>](Photo Storage)

Great to hear it’s working out for you. Happy to help!

So Uno has 13 digital input/output pins and 5 analog input pins, right?

So if I wanted to control more outputs, I’d first move pins 1-4 from digital to analog, right?

If I wanted to expand further, get another Uno?

Also, the analog pins - can I directly connect an external 5V signal (from another controller) to trigger on?

The analog input pins can be configured as either digital or analog input, so yes, you can move the digital inputs there.

There are alternate techniques that can be used to extend either inputs or outputs, but at that point you’re getting into adding external chips and writing code to support them. Considering that you can find ProMini clones on eBay for $3, I’d be hard pressed to say that it’s worthwhile if you’re just cloning existing behavior.

So a ProMini can do the same as Uno? So if I wanted 16 channels, just go for 4 x ProMinis?

Yes

Thanks. I’ll get the 5V 16MHz Pro Mini and the FTDI USB board to connect it to my PC for programming.

My old controller has power outlets that I’ll use with 4 DC adapters at 5V to drive the analog inputs and I’ll run the digital outputs to my H box relay to power the actuators.

The controller has digital outputs but they’re limited to 10mA, so that forced me to use optical isolators before the relays.

I believe I can drive the relays directly from the Arduino, right?

Another noobie question… Leaving the breadboard and wiring up the pro mini to my 8 relay board, I was just going to solder the wire directly to the mini. Is that the preferred method?

Do most people use a pin array and socket board (in case the mini malfunctions)? Does that even exist?

jremington - thanks for the real time ‘state’ programming link! I got it and I added it to lyndon’s code.

lyndon - this works in real time testing. what do you think? any bugs I didn’t get?

const int NUMBER_OF_CHANNELS = 4;
const int _inputs[] = {1, 2, 3, 4};
const int _forward[] = {5, 6, 7, 8};
const int _reverse[] = {9, 10, 11, 12};
const long _forwardTime[] = {10000, 10000, 10000, 10000};
const long _reverseTime[] = {15000, 15000, 15000, 15000};
long _inMotion[NUMBER_OF_CHANNELS];
byte _lastState[NUMBER_OF_CHANNELS];

void setup() 
{
  for (int i=0; i < NUMBER_OF_CHANNELS; i++)
  { pinMode(_inputs[i], INPUT_PULLUP); 
    pinMode (_forward[i], OUTPUT);   pinMode (_reverse[i], OUTPUT);
    digitalWrite(_forward[i], LOW);  digitalWrite(_reverse[i], LOW);
    _lastState[i] = !(digitalRead(_inputs[i])); }
}

void loop() 
{
  byte currentState = LOW;
  for (int i=0; i < NUMBER_OF_CHANNELS; i++)                              // Repeat for all channels
  { currentState = !(digitalRead(_inputs[i]));                            // Read new State
    if (currentState != _lastState[i])                                    // Detect an Edge?
    { if (currentState == HIGH)                                           // Rising Edge?
      {digitalWrite(_forward[i], HIGH); digitalWrite(_reverse[i], LOW);   // Go Forward
        _inMotion[i] = millis()+_forwardTime[i];}                         // Set forward timer
      else                                                                // Falling Edge?
      {digitalWrite(_forward[i], LOW); digitalWrite(_reverse[i], HIGH);   // Go Reverse
        _inMotion[i] = millis()+_reverseTime[i];}}                        // Set reverse timer
    if (millis()>=_inMotion[i])                                           // Timed out?
    {digitalWrite(_forward[i], LOW); digitalWrite(_reverse[i], LOW);}     // STOP!    
    _lastState[i] = currentState;                                         // Update previous state
  }   
}

I also needed to have an alternating timer to run a DC motor for 15 seconds every 5 minutes. Reused the same method, but I’m all out of ports.

const int NUMBER_OF_CHANNELS = 4;
const int _inputs[] = {1, 2, 3, 4};
const int _forward[] = {5, 6, 7, 8};
const int _reverse[] = {9, 10, 11, 12};
const int _motorPin = 13;
const long _forwardTime[] = {10000, 10000, 10000, 10000};
const long _reverseTime[] = {15000, 15000, 15000, 15000};
long _inMotion[NUMBER_OF_CHANNELS];
byte _lastState[NUMBER_OF_CHANNELS];

const long _motorTimer[] = {15000, 285000};
long _motorMotion;
byte _motorState;

void setup() 
{
  for (int i=0; i < NUMBER_OF_CHANNELS; i++)
  { pinMode(_inputs[i], INPUT_PULLUP); 
    pinMode (_forward[i], OUTPUT);   pinMode (_reverse[i], OUTPUT); pinMode (_motorPin, OUTPUT);
    digitalWrite(_forward[i], LOW);  digitalWrite(_reverse[i], LOW);
    _lastState[i] = !(digitalRead(_inputs[i])); 
  _motorState = HIGH; 
  _motorMotion = _motorTimer[0]+millis();
  digitalWrite(_motorPin, _motorState);
  }
}

void loop() 
{
  byte currentState = LOW;
  int nexttimer;
  for (int i=0; i < NUMBER_OF_CHANNELS; i++)                              // Repeat for all channels
  { currentState = !(digitalRead(_inputs[i]));                            // Read new State
    if (currentState != _lastState[i])                                    // Detect an Edge?
    { if (currentState == HIGH)                                           // Rising Edge?
      {digitalWrite(_forward[i], HIGH); digitalWrite(_reverse[i], LOW);   // Go Forward
        _inMotion[i] = millis()+_forwardTime[i];}                         // Set forward timer
      else                                                                // Falling Edge?
      {digitalWrite(_forward[i], LOW); digitalWrite(_reverse[i], HIGH);   // Go Reverse
        _inMotion[i] = millis()+_reverseTime[i];}}                        // Set reverse timer
    if (millis()>=_inMotion[i])                                           // Timed out?
    {digitalWrite(_forward[i], LOW); digitalWrite(_reverse[i], LOW);}     // STOP!    
    _lastState[i] = currentState;                                         // Update previous state
  }   
    if (millis()>=_motorMotion)
    {
     if (_motorState==HIGH){nexttimer=1;}else{nexttimer=0;}
     _motorMotion = millis()+_motorTimer[nexttimer];
     _motorState = !_motorState;
     digitalWrite(_motorPin, _motorState);
    }
}

Am I correct in not using pin 0 ?

Just move to another module?

If it does what you want, go for it. Doing code review is too much like real work :wink:

You can solder wires directly to the arduino. However, it does make it harder to change things later. I prefer to have a connector at one end or the other, or both!

I’m trying to use pin 0 as a digital output but it’s constantly high. Even without communication and running off an external power supply only.

Pin 1 is a also a communication pin, but it seems to be programmable?

What am I missing?

According to this diagram it should work. No idea why it wouldn’t

http://marcusjenkins.com/wp-content/upl … INO_V2.png7

Ah. I forgot to define it as an output. DOH.

Ok. When I first plug it in, it come on immediately and then resets. Is there any way to avoid it coming on HIGH when the arduino is first plugged in.

Thanks for everyone’s help.

Now that I got it working, I’m starting to explore more sophisticated potential…

My external controller can generate a signal between 0-10V. I’ve been using it at 50% to generate a step 5V/0V on/off signal.

the time delay (10ms or 15ms) was to allow enough time for the linear actuator to be completely open or completely closed. But now I’m thinking of an option to control flow based on the actuator position. It has a 2" stroke.

So… My thinking is to take the analog output from my external controller and use a resistor divisor to get it to 5V max (compared to the 10V from my external). The external controller has a 10mA max limit per channel, so at 10V, the impedance has to be higher than 1K. The Arduino analog input channels have a 100M impedance, so it only draws 0.05mA at 5V (max). So I can use a couple of 1M resistors and meet both requirements.

Now, I should have an analog read between 0 and 1023. Here is where it gets tricky. It takes 10sec to completely open (2" stroke pull) and a little more (I use 15ms for safety but it’s not that much) to close.

I could keep a variable that counts forward time and reverse time to get a capture of the actuator piston position. Then the analog signal can become a reference for the feedback loop so it reduces the error to zero and meets the new piston position requirement…

My concerns…

First, the actuators are not stepper motors, they’re not super accurate and the stroke speed can vary over time. I can calibrate up front, but I don’t know when it’ll need adjusting again.

Second, the accumulation of error over time could mean that my virtual position variable in the Arduino and the actual piston position will drift over time.

If I use an optical distance sensor to determine piston position (I don’t know how to do that yet), I’ll quickly run out of channels. I need 4 signal channels for the position setting and 4 to measure the piston position. This is possible with a Mega but I don’t know much about this path yet.

I found that there are linear actuators with potentiometer feedback. Anyone know how to wire it up?

To the adc I presume…