Go Down

Topic: Help with varying times on Blink Without Delay (Read 197 times) previous topic - next topic

handymanscotty

Jan 24, 2021, 12:20 am Last Edit: Jan 24, 2021, 12:22 am by handymanscotty
Hi All,

It's my first time posting, but I've been reading the forum for a few weeks.  It's very clear that the quality of advice is dependent on the quality of the information given, so I will try to be thorough and stick to the guidelines.


The Build:

I'm taking Mark Rober's online creative engineering course, and I am on the electrical engineering build. I have to make something "art" related (a piece of art, a machine that makes art, a machine that buys art on ebay, something like that).  I came up with an idea I am calling the HoloGIF.  It is a machine that displays a holographic 2D animation, 5 frames long.  There are 5 acrylic slides, each one positioned above a row of LED's that light in sequence.  Each slide has 1 frame of an animation etched into it that lights up when the LED's below are on. Here is a link to a video of the prototype: https://www.instagram.com/p/CKXTvzkAtBB/?utm_source=ig_web_copy_link


The Hardware:

I'm running an Elegoo Nano v3, which I think is the equivalent of an Arduino Nano.

Digital Pins 2-6 are each controlling a bank of 8 LED's through a transistor.

The LED's are powered from the 5V pin and each has its own resistor.

A momentary switch is connected to digital pin 12 with a pullup resistor (I did my homework!)


The Code:

Since I want the slides to be interchangeable, and some animations look good at different speeds, and some will be reciprocal (like a bouncing ball) and some cyclical (like a wolf running), I want to be able to click a button to cycle through different light programs.


The Problem:

I can't figure out how to make any blink-without-delay codes to work with variable timing.  I'm really over my head.  I can make a basic code where two LED's blink at different intervals without DELAY, but I can't figure out how to make it work with varying times and with multiple "programs".  Is the solution some type of blink array?  I dunno. I'm sleepy. Thanks for your time and advice!





Code: [Select]
//disclosure - some of this code is mine, most is copied and pasted from tutorials

const int  buttonPin = 12; //momentary switch for program selection
const int ledPin1 = 2; //I know there is a way to do ID all the LED's in a
const int ledPin2 = 3; //single line of code, but I got lazy and did'nt look it up
const int ledPin3 = 4;
const int ledPin4 = 5;
const int ledPin5 = 6;

int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(ledPin4, OUTPUT);
  pinMode(ledPin5, OUTPUT);
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes: ");
      Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;

  if (buttonPushCounter == 0)
  {
    digitalWrite(ledPin1, HIGH); //this is a code for my "bouncing ball" animation
    digitalWrite(ledPin2, LOW);  //it looks best when it speeds up near the bounce
    digitalWrite(ledPin3, LOW);  //and slows down at the apex.  thats why the
    digitalWrite(ledPin4, LOW);  //changing values
    digitalWrite(ledPin5, LOW);  //it lights LED's in the order 1,2,3,4,5,4,3,2,
    delay(180);
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, HIGH);
    delay(130);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, HIGH);
    delay(110);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, HIGH);
    delay(100);
    digitalWrite(ledPin4, LOW);
    digitalWrite(ledPin5, HIGH);
    delay(90);
    digitalWrite(ledPin5, LOW);
    digitalWrite(ledPin4, HIGH);
    delay(100);
    digitalWrite(ledPin4, LOW);
    digitalWrite(ledPin3, HIGH);
    delay(110);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin2, HIGH);
    delay(130);
  }
  else if (buttonPushCounter == 1)
  {
    digitalWrite(ledPin1, HIGH);  //this is the "cyclical animation" I use is for
    digitalWrite(ledPin2, LOW);   //the running wolf animation.
    digitalWrite(ledPin3, LOW);   //it lights the LED's in the order 1,2,3,4,5,
    digitalWrite(ledPin4, LOW);
    digitalWrite(ledPin5, LOW);
    delay(150);
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, HIGH);
    delay(150);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, HIGH);
    delay(150);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, HIGH);
    delay(150);
    digitalWrite(ledPin4, LOW);
    digitalWrite(ledPin5, HIGH);
    delay(150);
  }
  else (buttonPushCounter) = 0;  //this resets the push button count

}




alto777

#1
Jan 24, 2021, 12:34 am Last Edit: Jan 24, 2021, 12:35 am by alto777
Right now does the code you show work to switch between the "bounce" and the "cyclical" animations?

It looks like that should working, just want to be sure you've gotten that far.

BTW looks cool in the insta!

a7

Deva_Rishi

Quote
The LED's are powered from the 5V pin and each has its own resistor.
Which 5v, usb power ? ah it's probably fine, there is a limit to what a USB hub should provide (500mA) though many times more is available.
Quote
I can make a basic code where two LED's blink at different intervals without DELAY,
Well that is the code we want to see as well.
Quote
const int ledPin1 = 2; //I know there is a way to do ID all the LED's in a
const int ledPin2 = 3; //single line of code, but I got lazy and did'nt look it up
well i am confident in saying that you are going to need that method for the ultimate goal.
Quote
Since I want the slides to be interchangeable, and some animations look good at different speeds, and some will be reciprocal (like a bouncing ball) and some cyclical (like a wolf running),
I suspect that the length of the sequence may vary.
As a data-structure basically you want a series of a variable that sets which leds are turned on or off (since you use 6 leds, a byte would suffice) and a variable that determines the waiting time in between, and this really only depends on the resolution (you could use units of 10ms and probably get away with a byte as well)  since memory may be an issue you should probably use progmem to store the data in flash and just create a null terminated array.
To 'Correct' you have to be Correct. (and not be condescending..)

wildbill

You could do it with two array of structs. In the struct, define four fields: Pin, desired state (high or low), milliseconds and a boolean to indicate that the action is done.

So each struct tells you that at a certain time into the sequence you digitalWrite the pin to the desired state and lets you record that you did. The array contains all the actions for the sequence.

So for your first sequence, at time zero from start, you set five pins. At time 180 you do another two. At time 310 (180+130) you do another two etc. etc. Everything you need to do is in the data.

You have one array for each sequence and you pass it to a function that implements it when asked. The function will use blink without delay timing (i.e. millis) to see if anything needs doing, do it, mark it done and return so that the button will be responsive.

If you wanted to be more modern, change the struct into a class instead of a and have the function that does the work in the class.

drmpf

The struct idea is a good one.  
See my tutorial on How to Write Timers and Delays in Arduino for a Led Sequencing example and the mods to handle sequential switching of output pins.  It just uses two arrays instead of a struct :-(

jimLee

You want to work on this? Or would you like a "here you go this'll do it" kinda' thing?

-jim lee
PNW Ardiuno & Maker club
1012 9Th Street, Anacortes, WA 98221 (Around the back of building)
GITHUB -> https://github.com/leftCoast

Deva_Rishi

#6
Jan 24, 2021, 09:54 am Last Edit: Jan 24, 2021, 09:55 am by Deva_Rishi
Quote
In the struct, define four fields: Pin, desired state (high or low), milliseconds and a boolean to indicate that the action is done.
I was thinking more along the lines of 6 bits indicating the state of 6 pins and a time interval either before you go to the new state or after. If intervals could range from 1270ms to 10ms you could even use just an array of byte, where 1 bit signifies it to be a pin state or an interval. Considering this i think it can be done on an ATtiny13 and a bitshifter !
To 'Correct' you have to be Correct. (and not be condescending..)

alto777

I can't help suggesting that version two should use smart LEDs. One pin for all the LEDs and access to color and intensity for effects…

It may be that a strip with exactly the physical spacing exists that could be cut, there are also strips of 8. Or an 8x8 matrix.

a7


handymanscotty

Ok wow!  thank you all for the pointers.  I've spend the last six hours falling down the rabbit hole of tutorials and C++ for dummies.  So much new info to take in!

So I am now trying to learn how to create a struct and make light sequences under that struct.  just for the sake of asking questions, I wrote my best guess at how it should look, but only included 1 sequence for now.

I hope it's cool that I wrote my questions in the code... I had a lot and it just seemed more expedient.  Here's where I'm at:

Code: [Select]
/*
  this is a partial sketch just trying to figure out how struct works
  and to get enough code down that I can ask questions about it
*/

const int buttonPin = 12; //momentary switch for program selection
const int ledPin[] = {2, 3, 4, 5, 6};  //labelling my led pins with an arra

int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

struct lightprofile { //naming the struct
  byte Pinnumber;  //identifying the variable
  byte LEDstate;   //high/low
  int milliseconds; //or should this be an unsigned long?
  //do i have to define Pinnumber, LEDstate, and milliseconds somewhere?  eg Milliseconds=millis;
  //a boolean to indicate the action is done goes here?
};
//or does the boolean go here?
bool LEDstate = false; //am i even close? my brain is melting, lol

struct lightprofile bounce =
{1, 1, 0} //led 1 turns on at 0ms
{1, 0, 180} //led 1 turns off at 180ms
{2, 1, 180} //led 2 turns on at 180ms
{2, 0, 310} //led 2 turns off at 310ms
{3, 1, 310} //led 3 turns on at 310 ms
{3, 0, 420} //etc
{4, 1, 420}
{4, 0, 520}
{5, 1, 520}
{5, 0, 610}
{4, 1, 610}
{4, 0, 720}
{3, 1, 720}
{3, 0, 830}
{2, 1, 830}
{2, 0, 960}
//then it should repeat.. but how if time keeps on ticking?
// would it be something in the loop like if milliseconds=960; millis=0?


void setup() {

}  //this is where the pin modes are set and serial begins

void loop() {

}  //this is where the light profiles will be activated depending on how many times the button is pressed.




Right now does the code you show work to switch between the "bounce" and the "cyclical" animations?
-yes it does, but I have to hold the button down until the delays finish and the program loops


Which 5v, usb power ? ah it's probably fine,
-I figured it would be ok, since only 8 leds are lit at a time, but yeah if all 40 were lit, it would definitely be an issue


As a data-structure basically you want a series of a variable that sets which leds are turned on or off (since you use 6 leds, a byte would suffice) and a variable that determines the waiting time in between, and this really only depends on the resolution (you could use units of 10ms and probably get away with a byte as well)  since memory may be an issue you should probably use progmem to store the data in flash and just create a null terminated array.
- I started looking into progmem, and it looks very promising.  I will continue my research into it tomorrow once my brain starts working again

You want to work on this? Or would you like a "here you go this'll do it" kinda' thing?

-jim lee
- I really do want to figure this out, but I'm starting to think I need a little more of the fundamentals before i can tackle this... I'm committed, though.
Thanks all!

UKHeliBob

Your struct is declared wrongly and in any case, you could do what you want with just a 2 dimensional array



Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

wildbill

Your code doesn't compile of course, but you're on the right track.

Your bounce variable needs to be an array. The structs that make up the data in it need commas between them too.

You can't set millis back to zero. What you can do though is store the time (millis) when the sequence started. Then all the times you have defined in the bounce array are times since the start and you can run it multiple times by setting a new start time.

I think it's easiest to put a boolean in the struct so you can mark the action done. It does mean that you have to clear them all if you want to run the sequence again though.

UKHeliBob

#11
Jan 24, 2021, 11:15 pm Last Edit: Jan 24, 2021, 11:15 pm by UKHeliBob
An example of an array of structs
Code: [Select]

struct dataLayout //a struct definition
{
  byte ledNumber;
  byte state;
  unsigned long stateStart;
};

dataLayout ledData[4];  //an array of structs of type dataLayout

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  ledData[0] = {1, HIGH, 0};  //fill the array with data
  ledData[1] = {1, LOW, 500};
  ledData[2] = {2, HIGH, 500};
  ledData[3] = {2, LOW, 1000};
  //
  for (int x = 0; x < 4; x++)
  {
    Serial.print("LED number : ");  //use the data in the array
    Serial.print(ledData[x].ledNumber);
    Serial.print("\tstate : ");
    Serial.print(ledData[x].state);
    Serial.print("\tstate start time : ");
    Serial.println(ledData[x].stateStart);
  }
}

void loop()
{
}
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

Deva_Rishi

In the end i think my struct would be only 2 bytes (so technically a simple 2 dimensional array would work, but since in the end you want to create a sequence and type in the values the easiest would be to just make it 1 dimension. where alternating you have the state of the leds (which you could write in binary for the sake of overview) and an interval which can be in a unit of 10ms
Code: [Select]
const uint8_t sequence[] = {18, 0b100000, 13, 0b010000, 11, 0b001000, 10, 0b000100, 90, 0b000010, 0x0 } // the alternate numbering systems are just for the sake of overview
this is woithput progmem but in a way that hardly matters, what to keep in mind is that though the interval is the first value, you read it multiply by 10, and that is the interval you wait after you set the leds (the 2nd value turns led 6 on and all other off ) to their new state before you read the next to values. If the interval value is zero (null) you return the counter to zero and read the interval again. as i said i am confident i can do it it for several different sequences (a total of .5 kb of sequences should be really quite a lot, that is 255 different states + interval) on an ATtiny13 using progmem.
Quote
I can't help suggesting that version two should use smart LEDs.
even that can be done on an ATtiny13, and then we wouldn't even need the bitshifter, but some flash memory will be required to create the signal.
Of course you don't have to do it on an ATtiny, but the structure i suggest is the most efficient and will work. You could create sequences as a dimension, but the size will not be the same, which is wasteful.
Anyway the main point, you need 1 byte to to set the state of the leds (you have 8 on / off switches in that) and probably only 1 byte for interval.
To 'Correct' you have to be Correct. (and not be condescending..)

alto777

@Diva_Rishi: Yes ATtiny13 would be an engineering triumph, but

Why not get the thing working with the board we have in front of us, which is directly and simply supported by the Arduino IDE, and leave the effort it would take to put this on a bare chip for another day?

And using the LEDs and driver that already functions.

The ATtiny using multiple dumb LEDs would also involve fussing with additional hardware.

The OP might enjoy learning to walk before undertaking a marathon.

Just sayin'.

a7

Deva_Rishi

Quote
Why not get the thing working with the board we have in front of us,
The same method will work on an UNO, the advantage of not creating a struct (which was my initial suggestion) is that you won't need to separate the data with all those braces. My main point is that rather than switching 'a' LED on or off, you just set all of them to a specific state .. using just 1 byte. That 1 byte is actually enough as an interval (the OP was going from a relative to the zero point in time, which i think i wrong) . You could consider using a 'type-bit' like midi does since actually 7 bits suffice for both values, but we are not transmitting and receiving anything, so there is no real chance of data loss. The only slightly strange thing is that the interval is put before the leds-state, but that is just to be able to null terminate it (an interval is always bigger than '0' but all leds could be turned off.)
To 'Correct' you have to be Correct. (and not be condescending..)

Go Up