Setting a fixed on/high-time for digital output

Hi folks,

Apologies for the probable n00bishness of this question, but is it possible to code a monostable for a digital output, i.e. output goes low after x amount of time, regardless of whether the output control's still on/high or not?

I've used a simple delay to turn a solenoid off after 10ms, after being activated by a MIDI-in key, so even if the key is held it still goes off after 10ms, but of course this also stops the program for 10ms, throwing the timing off if there are multiple key presses.

I've looked at millis() to see if I can use that to set an off time to 10s, but I haven't been able to get it to work due to a severe case of n00bism. I've based the MIDI stuff on Kuki's excellent code, btw.

If it's not possible to code something without monkeying with the timing, perhaps it's possible to read an analog input from a pot, set to control this gate time at all?

Any help would be magnificent, thanks.

I am not sure I understand the problem, but if all you want is something to trigger after 10 ms without blocking your application then millis() is the easiest approach.
When the Midi In event occurs read out the millis() value and store it in a (global) variable. Then keep doing your normal processing and every once in a while do a check to see if the current time, i.e. millis(), is larger than the saved time + 10. In pseudo code:

void loop() {
static long timeOut;
const unsigned int DURATION=10;
if (midiEvent) timeOut=millis()+ DURATION;
doSomeStuff();
if (millis()>timeOut) turnItOff();
doSomeOtherStuff();
if (millis()>timeOut) turnItOff();
doEvenMoreStuff();
}

Thanks Cross -- yep, you've got it. I'll have another look, but I'm not very proficient with coding, and millis() seems a bit more complex than just using delay. It'd be nice to be able to do this in software, as otherwise I'll need a monostable for every solenoid I intend to drive (in this case a chromatic octave, 12 solenoids -- all the spare digi pins I think).

There is a lot of trickery going on with millis() behind the scenes, but from the development side of things it's really easy to use since it's just an automatically increasing 32-bit counter.

There is also the option of starting a timer on the arduino that will then call your own function once time is up, however that requires some more advanced programming.

By the way you can use the analog pins just like regular digital pins, giving you 6 (?) more outputs. Don't forget the arduino current limitations when driving multiple solenoids.

Thanks Cross -- I shall persevere with millis() and try and get something going. As I'm using 12 solenoids, I'll have to address each solenoid's timing separately to have the correct on-time, which might bulk up the coding a bit. Yep -- currently driving the solenoids via a transistor and a 12v wall wart. Shouldn't have more than 3 or 4 solenoids running concurrently anyway. :slight_smile:

Brilliant -- got it working, I think! Thanks again. :slight_smile:

Okay, I'm trying to expand this to address 13 outputs, and for that I need to do something similar to dynamically renaming a variable to update millis timings for different pics.

I've probably described that horrendously, so perhaps looking at my mishmash of code will help:

void playNote(byte note, byte velocity) {
  int value = LOW;
  if (velocity > 10) {
    value = HIGH;
  }else{
    value = LOW;
  }
  if (note >= 36 && note < 44) {
    byte myPin = note - 34; // work out which pin to send note to
    digitalWrite(myPin, value);
    previousMillisX = millis(); // reset the note-on time for this solenoid pin
  }
}

It's the 'previousMillisX' part at the end I'd love some help with -- is there any way I can rename 'previousMillis' dynamically to append the pin number denoted by 'myPin' (in place of the X -- used here just to show where the number would go), so that the millis time can be stored for the correct pin as listed at the top?

I'm fairly certain there'll be a better way to achieve this than what I'm attempting, but I'm too green to be able to figure out how. Perhaps I should set up an array for the previousMillis values?

I believe you definitely want to use arrays: http://arduino.cc/en/Reference/Array and http://www.arduino.cc/en/Tutorial/KnightRider

Andrew

The above example is a good one for learning how to use arrays. You might not want to go for the extra challenge, but I'd use an array of class objects.

class Pulser
{
    int p;
    long l;
    long w;
public:
    Pulser(int pin, long wait = 10)
    {
        p = pin;
        w = wait;
        l = -1;
    }
    void pulse()
    {
        digitalWrite(p, HIGH);
        l = millis();
    }
    void loop()
    {
        if (l >= 0 && millis() >= l + w)
        {
            l = -1;
            digitalWrite(p, LOW);
        }
    }
};

This is a class I have called Pulser. You can make many "objects" which are independent of each other, but have the same functions available for them. In this case, it needs a mandatory pin number, and can optionally take an auto-off wait parameter, 10 milliseconds if not given.

#define countof(a)  (sizeof(a)/sizeof(*(a)))

Pulser outs[3] =
{
    Pulser(2), // support pin 2
    Pulser(5), // support pin 5
    Pulser(7, 100), // pin 7 auto-offs after a longer pulse
};

Then you just need to use them.

void loop()
{
    // decide if any outs need to be pulsed HIGH
    if (buttonA())
        outs[0].pulse();
    if (buttonB())
        outs[1].pulse();
    if (buttonEscape())
        outs[2].pulse();

    // update all pulse auto-off status
    for (i = 0; i < countof(outs); i++)
        outs[i].loop();
}

Thanks very much chaps (gosh Ed -- you've made my head spin! Will take a while for that to sink in, thanks).

It's not pretty, but everything seems to work nicely with multiple notes without slowing or queuing (had to switch the status LED to use millis too -- forgot about that). Also moved things along pins-wise, to get a full octave (+ high note) of pins, so 13 output pins plus an LED pin. Will have to tidy further though.