Flash LED's x number of times then off for 4 seconds and repeat.

Hi
My first post.
Chip: ATTiny85.

I’m trying to create a status LED that flashes to indicate a specific function.
This I think is where my problem lies at the moment. blinkLED1()
Anyone that can check my code and help with it please?
I started with an ClickButton example and various other bits found on the forums.

Thanks in advance.
Link to the library Google Code Archive - Long-term storage for Google Code Project Hosting.

/* ClickButton to control two LED's

  Changes on time of LED according to different clicks on one button.
  
  Short clicks:

    Single click - Toggle LED1 on for 30 seconds. LED2 flashes 1 times , delays 4 seconds and repeat.
    Double click - Toggle LED1 on for 45 seconds. LED2 flashes 2 times , delays 4 seconds and repeat.
    Triple click - Toggle LED1 on for 60 seconds. LED2 flashes 3 times , delays 4 seconds and repeat
    
  Long click (hold button for three seconds on click):
    
    Single-long click - Turn off LED1  (LED2 flashes 250ms 5 times, OFF for 4 seconds)

  The circuit:
  - LED1 and 2 attached from pin 6 and 7 to resistors (say 220-ish ohms), other side of resistor to GND (ground).
  - pushbutton attached from pin 4 to GND.
  No pullup resistor needed, using the Arduino's (Atmega's) internal pullup resistor in this example.
LDR connected to pin 2 to measure light level. If too light then LED2 will be off and LED1 will be in a standby flashing state.

  Based on the Arduino Debounce example.

  2010, 2013 raron
 
 GNU GPLv3 license
*/

#include "ClickButton.h"

int sensorPin = 2;
const int ledPin1 = 6;
const int ledPin2 = 7;
int led1State = 0;
int led2State = 0;

// Variables
int lightState = 0;
int lowThreshold = 500;
int highTreshold = 600;
int sensorSW = LOW;


// the Button
ClickButton button1(4, LOW, CLICKBTN_PULLUP);

  
int ButtonFunction = 0;
unsigned long ledCurrentTime = 0;
unsigned long ledPreviousTime = 0;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;

void setup()
{
  pinMode(ledPin1,OUTPUT);
  pinMode(ledPin2,OUTPUT);
 
  // Setup button timers (all in milliseconds / ms)

  button1.debounceTime   = 20;   // Debounce timer in ms
  button1.multiclickTime = 250;  // Time limit for multi clicks
  button1.longClickTime  = 3000; // time until "held-down long click" registers.

}


void loop()
{
  // Read the sensor pin
  int sensorValue = analogRead(sensorPin);

   // If light level is low is detected, switch light on
  if (sensorValue < lowThreshold){
    sensorSW = HIGH;
  }
  
  // If light level goes up again, switch the lights off
  if (sensorValue > highTreshold){ 
    sensorSW = LOW;
  }

    // Update button state
  button1.Update();
  
  // Save click codes in ButtonFunction, as click codes are reset at next Update()
  if (button1.clicks != 0) ButtonFunction = button1.clicks;
  

  // Single click - Call function.
  if(button1.clicks == 1) OneClick();

  // Double click - Call function.
  if(ButtonFunction == 2) TwoClick();

  // Tripple click - Call function.
  if(ButtonFunction == 3) ThreeClick();

  // Long click - Call function.
  if(ButtonFunction == -1) LongClick();

}

// blink the LED at a pre-defined interval and number
void blinkLED1(long ledBlinkInterval, int flashes) {
  unsigned long ledCurrentTime = millis();
  unsigned long ledOffDelay = millis();
  for(int i = 0; i < flashes; i++)
  if(ledCurrentTime - ledPreviousTime > ledBlinkInterval) {
    ledPreviousTime = ledCurrentTime;   
    if (led1State == LOW) {
      led1State = HIGH;
    }
    else {
      led1State = LOW;
    }
    digitalWrite(ledPin1, led1State);
        if(millis() - ledOffDelay > 4000) {    
      led1State = LOW;
    }
  }
}

void led2function(unsigned long timer) { 
digitalWrite(ledPin2, HIGH); 
  if((sensorSW == HIGH) && (currentMillis - previousMillis >= timer))  
{
  led2State = HIGH;
    }
    else {
      
  led2State = LOW;  // Turn it off   
}
}
void OneClick() { // 30 seconds
  blinkLED1(500,1);
  led2function(30000);
}
void TwoClick() { // 45 seconds
  blinkLED1(500,2);
  led2function(45000);
}
void ThreeClick() { // 60 seconds
  blinkLED1(500,3);
  led2function(60000);
}
void LongClick() { //Clears the timer and switches off LED2
  blinkLED1(250,5);
  digitalWrite(ledPin2, LOW);
}

You can't use millis() and for loops like that. If you use a for loop to control how many times the LED blinks, you must use delay() to ensure that the function doesn't return until it has blinked the LED the correct number of times.

If you want non-blocking blinking, then you can NOT assume that all the blinking will happen in one call to the function.

Thanks for the reply.

I understand that, but the LED1 will flash 1 then off for 4 seconds whilst LED2 is on for 30seconds.
At the end of the 30 seconds, LED2 will go off and LED1 will flash fast (250ms) 5 times and be off for 3 seconds then loop back and do the same. (It indicates that the timer isn't running.)

How can I implement this as I can't use delay() to do what I need, this is from everything I have read in the forums. "DON'T USE DELAY" so how else to do what I need.

Does the rest of my code look OK? Compiles at least OK.
I am testing on an Uno but it will be loaded onto an ATTiny85 when it works.

How can I implement this as I can't use delay() to do what I need

Start by making a list of steps that YOU could teach your grandmother to perform. When you understand that for loops are not possible, and start to think about how a for loop actually works, and realize that you can perform the task without a for loop, then the coding becomes relatively easy.

One thing you need to decide is whether to call the function on every pass through loop, and let it decide if there is more blinking to do, or if you want to call the function only if there is more blinking to do. Robin2 has a good tutorial on doing multiple things at once. It's worth searching for.

I tried doing this without “for” before, but this is where I thought I made a breakthrough as “for” can be used to count the times a thing happens. I was impressed with what it could do and was hoping this would work, but alas it seems back to the drawing board.

I’ve been at this for some time and my previous iteration is a bit longer but seems to work, although not how I want it to…

I’ll attach it here below.

Original code.

/* ClickButton to control two LED's

  Changes on time of LED according to different clicks on one button.
  
  Short clicks:

    Single click - Toggle LED2 on for 30 seconds. LED1 flashes ON-OFF every 500ms.
    Double click - Toggle LED2 on for 20 minutes. LED1 flashes ON-OFF every 1000ms.
    Triple click - Toggle LED2 on for 30 minutes. LED1 flashes ON-OFF every 2000ms.
    
  Long click (hold button for three seconds on click):
    
    Single-long click - Turn off LED1  (LED1 flashes ON-OFF every 250ms.)

  The circuit:
  - LED1 and 2 attached from pin 6 and 7 to resistors (say 220-ish ohms), other side of resistor to GND (ground).
  - pushbutton attached from pin 4 to GND.
  - LDR connected to pin 2 to measure light level. If too light then LED2 will be off and LED1 will be in a standby flashing state. 
  No pullup resistor needed, using the Arduino's (Atmega's) internal pullup resistor in this example.

  Based on the Arduino Debounce example.

  2010, 2013 raron
 
 GNU GPLv3 license
*/

#include "ClickButton.h"

int sensorPin = 2;
const int ledPin1 = 6;
const int ledPin2 = 7;
int led1State = 0;
int led2State = 0;

// Variables
int lightState = 0;
int lowThreshold = 500;
int highTreshold = 600;
int sensorSW = LOW;


// the Button
ClickButton button1(4, LOW, CLICKBTN_PULLUP);

//  
int ButtonFunction = 0;
unsigned long ledBlinkInterval1 = 250;
unsigned long ledBlinkInterval2 = 500;
unsigned long ledBlinkInterval3 = 1000;
unsigned long ledBlinkInterval4 = 2000;
unsigned long ledCurrentTime = 0;
unsigned long ledPreviousTime = 0;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
 
unsigned long timer1 = 30000;
unsigned long timer2 = 45000;
unsigned long timer3 = 60000;


void setup()
{
  pinMode(ledPin1,OUTPUT);
  pinMode(ledPin2,OUTPUT);
 
  // Setup button timers (all in milliseconds / ms)
  // (These are default if not set, but changeable for convenience)
  button1.debounceTime   = 20;   // Debounce timer in ms
  button1.multiclickTime = 250;  // Time limit for multi clicks
  button1.longClickTime  = 3000; // time until "held-down clicks" register

}


void loop()
{
  // Read the sensor pin
  int sensorValue = analogRead(sensorPin);

   // If light level is low is detected, switch light on
  if (sensorValue < lowThreshold){
    sensorSW = HIGH;
  }
  
  // If light level goes up again, switch the lights off
  if (sensorValue > highTreshold){ 
    sensorSW = LOW;
  }

    // Update button state
  button1.Update();
  
  // Save click codes in ButtonFunction, as click codes are reset at next Update()
  if (button1.clicks != 0) ButtonFunction = button1.clicks;
  

  // Single click - Call function.
  if(button1.clicks == 1) Option1();

  // Double click - Call function.
  if(ButtonFunction == 2) Option2();

  // Tripple click - Call function.
  if(ButtonFunction == 3) Option3();

  // Long click - Call function.
  if(ButtonFunction == -1) Standby();

 
}

// blink the LED at a pre-defined interval
void blinkLED1() {
  unsigned long ledCurrentTime = millis();
 
  if(ledCurrentTime - ledPreviousTime > ledBlinkInterval1) {
    ledPreviousTime = ledCurrentTime;   
    if (led1State == LOW) {
      led1State = HIGH;
    }
    else {
      led1State = LOW;
    }
    digitalWrite(ledPin1, led1State);
  }
}

// blink the LED at a pre-defined interval
void blinkLED2() {
  unsigned long ledCurrentTime = millis();
 
  if(ledCurrentTime - ledPreviousTime > ledBlinkInterval2) {
    ledPreviousTime = ledCurrentTime;   
    if (led1State == LOW) {
      led1State = HIGH;
    }
    else {
      led1State = LOW;
    }
    digitalWrite(ledPin1, led1State);
 }
} 

// blink the LED at a pre-defined interval
void blinkLED3() {
  unsigned long ledCurrentTime = millis();
 
  if(ledCurrentTime - ledPreviousTime > ledBlinkInterval3) {
    ledPreviousTime = ledCurrentTime;   
    if (led1State == LOW) {
      led1State = HIGH;
    }
    else {
      led1State = LOW;
    }
    digitalWrite(ledPin1, led1State);
  }
}

// blink the LED at a pre-defined interval
void blinkLED4() {
  unsigned long ledCurrentTime = millis();
 
  if(ledCurrentTime - ledPreviousTime > ledBlinkInterval4) {
    ledPreviousTime = ledCurrentTime;   
    if (led1State == LOW) {
      led1State = HIGH;
    }
    else {
      led1State = LOW;
    }
    digitalWrite(ledPin1, led1State);
  }
}
void Option1() { // 30 minute timer
  blinkLED1();
  digitalWrite(ledPin2, HIGH); 
  if((sensorSW == HIGH) && (currentMillis - previousMillis >= timer1))  
{
  led2State = HIGH;
    }
    else {
      
  led2State = LOW;  // Turn it off   
}
}
void Option2() { // 45 second timer
  blinkLED2();
  digitalWrite(ledPin2, HIGH); 
  if((sensorSW == HIGH) && (currentMillis - previousMillis >= timer2))  
{
  led2State = HIGH;
    }
    else {
      
  led2State = LOW;  // Turn it off   
}
}
void Option3() { // 60 second timer
  blinkLED3();
  digitalWrite(ledPin2, HIGH); 
  if((sensorSW == HIGH) && (currentMillis - previousMillis >= timer3))  
{
  led2State = HIGH;
    }
    else {
      
  led2State = LOW;  // Turn it off   
}
}
void Standby() { //Clears the timer and switches off LED2

  blinkLED4();
  digitalWrite(ledPin2, LOW);
}

Something I noticed is that the SeveralThingsAtTheSameTime thread that was mentioned above is rather confusing. There is some useless info in there, but I’m still working through the sketch to see what I can learn from it.

It doesn’t actually help me as far as I can see to do what I need as in the first post.
Further my LED2 timing in the final code will have longer times such as 10 minutes, 20 minutes and 30 minutes.
I have no problem if the timing isn’t 100% accurate. A couple seconds here or there are not important. The LED1 flashing is just to indicate which “Mode” it is in.
Flashing must just be distinguishable to be identifiable.
Thanks

I had a look at FSM and it seems to me that I will have to re-write my code which is fine, as I'm starting to understand how FSM works.
I started this project from the wrong end and tried to adapt what I had to do what I wanted it to, but since it can't be done that way I will re-write.

This link has helped allot.

I am still far from finished but will post what I have when I'm further along.
FSM to do more than one thing at a time is what I need to do.

Any help will be appreciated even though I will continue working this out.

Thanks.

Right I’'ve made some changes that I think will do what I need.

/*
||
|| The idea is still the same as the original, however I've used FSM.
/**

*/
//http://www.arduino.cc/playground/uploads/Code/FSM_1-6.zip
#include <FiniteStateMachine.h>
//http://www.arduino.cc/playground/uploads/Code/Button.zip
#include <Button.h>


/** program config and constants */
//WIRE THIS DIRECTLY FROM GROUND TO PIN
const byte BUTTON_PIN = 2; 
//how many states are we cycling through?
const byte NUMBER_OF_SELECATBLE_STATES = 4; 
int led1State = LOW;
int led2State = LOW;
const int ledPin1 = 6;
const int ledPin2 = 7;
int sensorPin = 2;
int lowThreshold = 500;
int highTreshold = 600;
char sensorSW = LOW;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;

/** flash config */
//const int FLASH_ITERATIONS = 0;
//const int FLASH_INTERVAL = 0;

/** this is the definitions of the states that our program uses */
State stdby = State(standby);  //no operation
State min10 = State(minute10);  //this state sets LED2 on for 10 minutes
State min20 = State(minute20);  //this state sets LED2 on for 20 minutes
State min30 = State(minute30); //this state sets LED2 on for 30 minutes


/** the state machine controls which of the states get attention and execution time */
FSM stateMachine = FSM(stdby); //initialize state machine, start in state: standby

/** this is the control interface, and is used to change the state of the program */
Button button = Button(BUTTON_PIN,PULLUP); //initialize the button (wire between BUTTON_PIN and ground)

void setup(){ 
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  }

void loop(){
    int sensorValue = analogRead(sensorPin);   // Read the sensor pin
  // If light level is low is detected, switch light on
  if (sensorValue < lowThreshold) {
    sensorSW = HIGH;
  }

  // If light level goes up again, switch the lights off
  if (sensorValue > highTreshold) {
    sensorSW = LOW;
  }
  //counter variable, holds number of button presses
  static byte buttonPresses = 0; //only accessible from this function, value is kept between iterations
  
  if (button.uniquePress()){
    //increment buttonPresses and constrain it to [0, NUMBER_OF_SELECATBLE_STATES-1]
    buttonPresses = ++buttonPresses % NUMBER_OF_SELECATBLE_STATES; 
	/*
	  manipulate the state machine by external input and control
	*/
	//CONTROL THE STATE
    switch (buttonPresses){
      case 0: stateMachine.transitionTo(stdby); break;
      case 1: stateMachine.transitionTo(min10); break; //first press
      case 2: stateMachine.transitionTo(min20); break; //second press
      case 3: stateMachine.transitionTo(min30); break; //third press
          }
  }
  //THIS LINE IS CRITICAL
  //do not remove the stateMachine.update() call, it is what makes this program 'tick'
  stateMachine.update();
}

/*
  ALL the functions below are helper functions for the states of the program
*/
///[noop state:update] the state machine is in a state that does nothing
//void noopUpdate() {
  //this function gets called as long as the user have not pressed any buttons after startup
//}

void minute10() {
 led2function(600000);
 flashUpdate(1,1000);
}

void minute20() {
 led2function(1200000);
 flashUpdate(2,1000);
}

void minute30() {
 led2function(1800000);
 flashUpdate(3,1000);
}

void standby() {
 led2State = LOW;
 flashUpdate(1,2000);
}
///[flash state:update] this state blocks loop until done and does an immediateTransition to circle state
void flashUpdate(const int FLASH_INTERVAL, const int FLASH_ITERATIONS) {
  for (int times=0; times<FLASH_ITERATIONS; times++) {
    change(LOW);
	delay(FLASH_INTERVAL/2);
    change(HIGH);
	delay(FLASH_INTERVAL/2);
  }
}

///changing the LED state
void change(boolean state) {

}

///the LED2 output control
void led2function(unsigned long timer) {
  digitalWrite(ledPin2, HIGH);
  if ((sensorSW == HIGH) && (currentMillis - previousMillis >= timer))
  { led2State = HIGH;
  }
  else {
    led2State = LOW;  // Turn it off
   }
   stateMachine.immediateTransitionTo(stdby);
}

I also just realised I’m not finished with my code. I’ve been busy with the hardware design.

Treat each LED as a 'task' in itself... (not a state)... because they operate indepently of the overall state until they finish their blinking task.

So the number of 'blinks' is managed independently of the millis() timers

Yes, the gap & duration of blinks is millis() based, but what you want requires some tricky
flow planning to make sure they occur when you want them to - and not otherwise.

Also in this, think about what will happen when you push another button while a blink sequence is already happening... stop the current mode of operation and start the new, replace/override the current with the new, or something else...

The 'modes' of operation are loosely aligned with the states, but may have to wait for a 'running' state to complete before stepping to the new mode/state - that's for you to decide and code in.

Have a look at Automaton. It’s an FSM based framework with standard bundled state machines for blinking leds, handling button presses and reading analog pins all multitasking & reactive.

I bet you could do it in half the number of code lines :wink:

I can’t quite follow what you’re trying to achieve or I’d write it for you.

Tinkerspy. Thank you for your reply.

Let me explain in detail what I need from this code.

Main parts:
ATTiny85
LDR = INPUT1
LED1.1 status DUAL LED = OUTPUT0
LED1.2 status DUAL LED = OUTPUT1
LED2 = OUTPUT2
Button INPUT2 LOW signal pulse.

As I have few IO requirements I decided to use the ATTiny85.

An LDR INPUT1 will be an overriding input in the code.
Unless it's dark enough, it won't matter what the INPUT2 does. Ideally just an LED indication that an input was received from the INPUT2 and remain in standby. I think a dual color or RGB LED will do the trick.

When the first LOW INPUT2 is received a counter will begin as well as a timer.
Number of LOW inputs counted within 4 seconds will set the LED2 OUTPUT2 timer.

On 1 LOW signal INPUT2, the OUTPUT2 timer must start counting down to a defined interval x1 with the status LED indicating which timer is running.
On 2 LOW signal INPUT2 the OUTPUT2 timer .... x2
Same for 3 LOW signal INPUT2 the OUTPUT2 timer... x3

At the end of the timer x1-3 the LED2 OUTPUT2 must change to LOW shutting it down.

I was originally considering using a long LOW to reset to standby mode and clear the counters and timer for LED2, also the status LED indicating it is in standby.
I think this will be better for controlling the device.
4 LOW INPUT2 signals within 4 seconds will still only register as 3 LOW. So timer will run for x3 and then revert to standby.

Standby mode will have LED2 off but DUAL LED1 will indicate the current mode.

Any INPUT2 will set a new value timer and begin from the time the input was received.

I think this is a somewhat more basic explanation below.

None of the below will happen unless it's dark enough. (By sampling the light level every 10 minutes and having light and dark thresholds.)
(Button presses must happen within 4 seconds or the counter is reset to 0.)
I press the button 1 times and the light stays on for 10 minutes from being pressed.
I press the button 2 times and the light stays on for 20 minutes from being pressed.
I press the button 3 times and the light stays on for 30 minutes from being pressed.
I press and hold the button for 2 seconds and the light goes off immediately.

The status LED must indicate which timer or mode is selected, or a different flash pattern will have to be used. I only have 2 outputs I can use for status, so I'm limited on what can be done with them.
Two LED's or a DUAL color LED.

My pinout on this project is:
Pin 1 = Reset button
Pin 2 = INPUT1 LDR
Pin 3 = INPUT2 Button LOW with PULLUP
Pin 4 = GND
Pin 5 = OUTPUT0 Status LED2
Pin 6 = OUTPUT1 Status LED1
Pin 7 = OUTPUT2 LED2
Pin 8 = VCC

I am getting the flow diagram thing clearer myself as I'm explaining, which is giving me some ideas, but still confusing.

I remember playing The Incredible Machine in it's day and I think I still have a copy somewhere.
It sounds pretty simple but I can't get my head around it.

Well, it’s a challenge, I guess. See if I can do this with standard Automaton components
Main problem is that the Automaton ‘button machine’ has no provision for multi-click counting.
It does do long presses out of the box, but I’ll try to stick to your specs.

Let’s approach this one in steps.

“None of the below will happen unless it’s dark enough. (By sampling the light level every 10 minutes and having light and dark thresholds.)”

I create a button, and an ‘analog’ machine for the light sensor. (note that I used my own pin setup for my test board)
The button is configured to call a callback when pressed, the callback first checks if ‘it is dark enough’ by comparing
the lightSensor with the preset constant.

From now on the button will do nothing unless it’s dark enough.

#include <Automaton.h>

Atm_button button;
Atm_analog light_sensor;
Appliance app;

const int lightThreshold = 500;

void callback( int idx, int v, int up ) {
  if ( light_sensor.state() < lightThreshold && v > 0 ) {
    Serial.println( "Press detected" );
  }
}

void setup() {
  Serial.begin( 9600 );
  app.component( 
    button.begin( 2 ) 
      .onPress( callback )
  );
  app.component( light_sensor.begin( A0 ) );
}

void loop() {
  app.run();
}

Now I expand the previous sketch with a counter to represent the number of button presses and a timer to reset
the counter 4 seconds after the last button press.

#include <Automaton.h>

Atm_button button;
Atm_analog lightSensor;
Atm_timer timer;
Appliance app;

const int lightThreshold = 500;
const int resetCounterDelay = 4000;
int pressCounter = 0;

void callback( int idx, int v, int up ) {
  if ( lightSensor.state() < lightThreshold && v > 0 ) {
    pressCounter++;
    Serial.print( "Press detected, pressCounter=" );
    Serial.println( pressCounter );
    timer.trigger( timer.EVT_START );
  }
}

void setup() {
  Serial.begin( 9600 );
  app.component( 
    button.begin( 2 ) 
      .onPress( callback )
  );
  app.component( lightSensor.begin( A0 ) );
  app.component( 
    timer.begin( resetCounterDelay )
      .onTimer( []( int idx, int v, int up ) {
        pressCounter = 0;
      })
  );
}

void loop() {
  app.run();
}

Be sure to test the sketch and watch the Serial monitor, we are now counting presses and
resetting. (wait, you’re not developing this directly on the ATTiny right? Develop on
something like the UNO, move to the tiny when everything is working)

I added the Atm_led machine that controls the light and put the desired delays in an
initialized array.

#include <Automaton.h>

Atm_button button;
Atm_analog lightSensor;
Atm_timer timer;
Atm_led light;
Appliance app;

const int lightThreshold = 500;
const int resetCounterDelay = 4000;
const int lightPeriod[] = { 0, 10000, 20000, 30000 };

int pressCounter = 0;

void callback( int idx, int v, int up ) {
  if ( lightSensor.state() < lightThreshold && v > 0 ) {
    if ( v == 2 ) { // Long press turns everything off
       pressCounter = 0;
       statusLed.trigger( Atm_led::EVT_OFF );   
       light.trigger( Atm_led::EVT_OFF );   
    } else { // Short presss increments counter
      pressCounter++; 
      Serial.print( "Press detected, pressCounter=" );
      Serial.println( pressCounter );
      timer.trigger( timer.EVT_START );
      if ( pressCounter <= 3 ) {
        light.blink( lightPeriod[pressCounter], 0, 1 ).trigger( Atm_led::EVT_BLINK );
        Serial.print( "Light on for: " );
        Serial.println( lightPeriod[pressCounter] );
      }
    }
  }
}

void setup() {
  Serial.begin( 9600 );
  app.component( // The button
    button.begin( 2 ) 
      .longPress( 2, 2000 )
      .onPress( callback )
  );
  app.component( lightSensor.begin( A0 ) ); // The light sensor
  app.component( 
    timer.begin( resetCounterDelay ) // The timer for resetting the counter
      .onTimer( []( int idx, int v, int up ) {
        pressCounter = 0;
      })
  );
  app.component( light.begin( 5) ); // The light  
}

void loop() {
  app.run();
}

Now I add the status led which will blink faster when the selected period increases.
I also tidy up tings by removing the Serial output and using constants where needed.

Full working code below:

#include <Automaton.h>

Atm_button button;
Atm_analog lightSensor;
Atm_timer timer;
Atm_led light, status;
Appliance app;

const int pinLightSensor = A0;
const int pinLight = 4;
const int pinStatus = 13;
const int pinButton = 2;

const int lightThreshold = 500;
const int resetCounterDelay = 4000;
const int longPressDelay = 2000;

const int lightPeriod[] = { 0, 10000, 20000, 30000 };
const int statusPeriod[] = { 0, 400, 200, 100 };

int pressCounter = 0;

void callback( int idx, int v, int up ) {
  if ( lightSensor.state() < lightThreshold && v > 0 ) {
    if ( v == 2 ) { // Long press turns everything off
       pressCounter = 0;
       light.trigger( Atm_led::EVT_OFF );   
       status.trigger( Atm_led::EVT_OFF );   
    } else { // Short presss increments counter
      pressCounter++; 
      timer.trigger( timer.EVT_START );
      if ( pressCounter <= 3 ) {
        light.blink( lightPeriod[pressCounter], 0, 1 ).trigger( Atm_led::EVT_BLINK );
        status.blink( statusPeriod[pressCounter], statusPeriod[pressCounter] ).trigger( Atm_led::EVT_BLINK );
      }
    }
  }
}

void setup() {
  app.component( // The button
    button.begin( pinButton ) 
      .longPress( 2, longPressDelay )
      .onPress( callback )
  );
  app.component( lightSensor.begin( pinLightSensor ) ); // The light sensor
  app.component( 
    timer.begin( resetCounterDelay ) // The timer for resetting the counter
      .onTimer( []( int idx, int v, int up ) {
        pressCounter = 0;
      })
  );
  app.component(  // The light
    light.begin( pinLight ) 
      .onFinish( status, Atm_led::EVT_OFF ) // Resets the status led too
  );  
  app.component( status.begin( pinStatus ) ); // The status led  
}

void loop() {
  app.run();
}

Note that this is event driven programming. Most of the logic is performed in the callback
routine that is called from the button machine. Status and light are both led machines, one
blinks once very slowly, the other fast. I’ve used different intervals than you specified for
testing purposes. Fixing that and adapting the sketch to the ATTiny is left to you as an
exercise.

PS: As predicted it could be done in less than half the lines of code :wink:

Thanks
I was using ClickButton library for the click count and long click.

I'm testing on Uno and will move to Tiny when done.
I am getting errors when compiling the code.
I have downloaded and installed your Automaton Library

I doubt I'll be shortening the code as I'm not very good at coding.
Your code is already so much shorter than anything I could write.

The errors I get...
Arduino: 1.6.5 (Windows 7), Board: "Arduino/Genuino Uno"
But upgrading to the latest IDE and library has resolved the errors.

Hi Shaun,

Sorry, I forgot to mention you need the github master version. It's much further advanced than the one in the library manager.

Rgdz,
Tinkerspy

Hi
I did download the master file and installed it this morning.
I'll do so again unless I have the wrong link.

Thanks

I think you’ll need a newer version of the Arduino IDE.
You should be OK from version 1.6.6 which enables the C11 extensions by default.

I use 1.6.9 and 1.6.6 on an older system and they work fine.

I read somewhere that the later versions disable some of the clone boards. Do you know if this is true?

That sounds highly unlikely to me. Arduino is open source, anyone can make a board that looks - to any software - exactly like an Arduino. Only thing is some vendors put the Arduino brandname on it. That is not allowed, but it is not enforced through software.

Hi
Installed latest version and compiles fine.
A question though, it uses quite a bit of my tiny85’s small available storage and dynamic memory.

Sketch uses 6,272 bytes (76%) of program storage space. Maximum is 8,192 bytes.
Global variables use 368 bytes (71%) of dynamic memory, leaving 144 bytes for local variables. Maximum is 512 bytes.

Seems like the library takes a lot of space.

You said it would be possible to do this in less lines of code.
I’ve excluded unused parts from the Automaton.h library include statements to see if it improves the memory handling.

//#include <Atm_fan.hpp>
//#include <Atm_step.hpp>
//#include <Atm_player.hpp>
//#include <Atm_command.hpp>
//#include <Atm_comparator.hpp>
//#include <Atm_controller.hpp>
//#include <Atm_digital.hpp>
//#include <Atm_encoder.hpp>
//#include <Atm_fade.hpp>
//#include <Atm_bit.hpp>

But this does not change the memory available.

Thank you for your help.

Shaun99:
I read somewhere that the later versions disable some of the clone boards. Do you know if this is true?

It's true of some variants, like the Pro Micro. 1.6.4 was the last version that didn't have third party board support that wasn't broken AFAIK. It didn't work for me in 1.6.8 and I'm not willing to play around. So I don't upgrade.

Shaun99:
Hi
Installed latest version and compiles fine.

Great news!

A question though, it uses quite a bit of my tiny85’s small available storage and dynamic memory.

Sketch uses 6,272 bytes (76%) of program storage space. Maximum is 8,192 bytes.
Global variables use 368 bytes (71%) of dynamic memory, leaving 144 bytes for local variables. Maximum is 512 bytes.

Seems like the library takes a lot of space.

As long as it fits it’s OK, obviously there are limits as to what an ATTiny is capable of. Every library takes up program space and there is an overhead per state machine to store the state information. You’re running 5 simultaneous multitasking machines on a tiny chip. I think that’s pretty impressive!

The library itself is fairly efficient (that was one of the design goals) but often it is better to write (portions) of your app as a dedicated state machine instead of using the bundled machines. In using the bundled machines to solve your problem I chose the quick&fast way. (my time is limited)

If I wanted to optimize the code within Automaton I would do the following:

  • Drop the Atm_analog machine (lightSensor), there’s really no need for that, an analogRead() would do just fine. (perhaps I should’ve done that in the first place)
  • Create a new state machine that combines the functionality of the timer machine and the two led state machines.

That way you have only 2 machines running (button & the custom machine) and the memory usage would drop considerably. But writing a new machine is more work that just linking together existing ones. You’re welcome to do it yourself but I’ll pass on this one. You’re lucky to find someone prepared to write the sketch for you in the first place. :wink:

I do this for three reasons.

  • To test my framework against real world examples
  • And to show people how powerful it is
  • To help you

You said it would be possible to do this in less lines of code.

As in lines you have to write yourself and I showed you that’s true.
There are always trade offs in writing code.

Automaton makes it possible to quickly write stable multitasking applications, the drawback is less memory efficiency. If memory and speed efficiency is of the highest importance you might even need to switch to assembly language but your development time will explode and you code is harder to asses.

In many projects development time and maintainability are more important than raw speed and memory efficiency. Maybe your project is different but it didn’t seem so from your description.

I’ve excluded unused parts from the Automaton.h library include statements to see if it improves the memory handling.

//#include <Atm_fan.hpp>
//#include <Atm_step.hpp>
//#include <Atm_player.hpp>
//#include <Atm_command.hpp>
//#include <Atm_comparator.hpp>
//#include <Atm_controller.hpp>
//#include <Atm_digital.hpp>
//#include <Atm_encoder.hpp>
//#include <Atm_fade.hpp>
//#include <Atm_bit.hpp>

But this does not change the memory available.

No, the compiler already does that for you and in most cases better than you yourself could.

Does the functionality of the sketch match your requirements?

And, do you think you could understand the code?

Rgdz, Tinkerspy