1

Im having problems with Attiny85 programming. I am making something similar like this project. But i added function that every day on some time it turns on output - something like alarm clock. The real struggle is to make it active for longer time as one minute.. If current time is equals alarm time wake_HOUR==hour()& wake_MINUTE==minute() then it will run only for a minute so I added changeable field for the time how long output should be active - for example 5 minutes despite 1 minute. I tried to make timer with millis() and after those 5 minutes reset timer but this is where i stuck, because compiling for attiny it shows error, but if i use same code sample just for arduino then its working.. Code below:

#include <avr/sleep.h>
#include <TinyWireM.h>
#include <EEPROM.h>
#include "ssd1306.h"
#include "WDT_Time.h"

#define TIMEOUT 6000            //display timeout
#define UNUSEDPINA 1            //RESET
#define ALARM 4                 //ALARM
#define BUTTONPIN  3            //GND
unsigned int wake_HOUR = 0;     //Hours when it should wake up
unsigned int wake_MINUTE = 0;   //Minutes when it should wake up
unsigned int wake_SECOND = 0;   //Seconds when it should wake up - Not using
unsigned long ALARM_TIME;       //How long alarm should be active- changable in display
unsigned long new_millis = 0;   //Timer
unsigned long interval;         //How long alarm should be active
extern volatile unsigned long timer0_millis;
unsigned int ACTIVE_ALARM = 0;  //alarm activated?

#define SET_UP_BUTTON_THRESHOLD 100   //100k
#define UP_DOWN_BUTTON_THRESHOLD 600  //7k
#define PRESSED_BUTTON_THRESHOLD 1000 //NO resistance


// enum
typedef enum {
normal, sleeping
}  run_status_t;

typedef enum {
time_mode, debug_mode, alarm_mode
}  display_mode_t;

// button field constant
#define NO_FIELD 0
#define YEAR_FIELD 1
#define MONTH_FIELD 2
#define DAY_FIELD 3
#define HOUR_FIELD 4
#define MINUTE_FIELD 5
#define SECOND_FIELD 6
#define ALARM_HOUR_FIELD 7      //Alarm Hour
#define ALARM_MINUTE_FIELD 8    //Alarm Minute
#define ALARM_FIELD 9           //How long should alarm be active - minutes
#define SET_ALARM_FIELD 10      //Activate alarm
#define FIELD_COUNT 10
//#define ALARM_SECOND_FIELD 9

// variables
SSD1306 oled;
static uint32_t display_timeout = 0;
static run_status_t run_status = normal;
static display_mode_t display_mode = time_mode;
static display_mode_t last_display_mode = time_mode;
static bool time_changed = false;
static bool alarm_changed = false;
static uint8_t selected_field = NO_FIELD;


uint8_t wake_SET = 0;
//const char wake_BACK[15] = "GO BACK";

void setup() {
  // setup input pins, also pullup unused pin for power saving purpose
  pinMode(UNUSEDPINA, INPUT_PULLUP);
  pinMode(BUTTONPIN, INPUT_PULLUP);
  pinMode(ALARM, OUTPUT);
  digitalWrite(ALARM, LOW);
  interval = ALARM_TIME*60000;

  // init time
  init_time();

  // init I2C and OLED
  TinyWireM.begin();
  oled.begin();
  oled.fill(0x00); // clear in black

  // init display timeout
  set_display_timeout();
}

void loop() {
  // detect and handle button input
  check_button();
  unsigned long currenTime = millis();
  //unsigned long new_value;
  //setMillis(new_value);

  if (run_status == sleeping) {
    // return to sleep mode after WDT interrupt
    system_sleep();
  } else { // not sleeping
    if (millis() > display_timeout) { // check display timeout
      enter_sleep();
    } else { // normal flow
      readRawVcc();
      readRawTemp();
      draw_oled();
    } // normal flow
  } // not sleeping
  if (ACTIVE_ALARM==1)
  {
    if (wake_HOUR==hour()& wake_MINUTE==minute())
    {
    digitalWrite(ALARM, HIGH);
    new_millis = millis();
    }
    else if (currenTime -new_millis <= interval)
    {
    new_millis = millis();
    digitalWrite(ALARM, HIGH);
    }
    else if (currenTime -new_millis >= interval)
    {
    new_millis = 0;
    digitalWrite(ALARM, LOW);
    }}
  else if (ACTIVE_ALARM==0)
  {
    new_millis = 0;
    digitalWrite(ALARM, LOW);
  }
}
//void setMillis(unsigned long new_millis){
//  uint8_t oldSREG = SREG;
//  cli();
//  timer0_millis = new_millis;
//  SREG = oldSREG;
//}

void enter_sleep() {
  oled.fill(0x00); // clear screen to avoid show old time when wake up
  oled.off();
  delay(2); // wait oled stable

  run_status = sleeping;
}

void wake_up() {
  run_status = normal;

  delay(2); // wait oled stable
  oled.on();

  // update display timeout
  set_display_timeout();
}

void set_display_timeout() {
  display_timeout = millis() + TIMEOUT;
}

/*
 * UI related
 */

void draw_oled() {
  if (display_mode != last_display_mode) {
    oled.fill(0x00);
    last_display_mode = display_mode;
  }
  oled.set_font_size(1);
  if (display_mode == time_mode) {
    // 1st row: print info
    oled.set_pos(5, 0);
    oled.print("TIME");
    oled.set_pos(80, 0);
    oled.print("AcALARM=");
    oled.print(digitalRead(ACTIVE_ALARM));
    oled.set_pos(80, 1);
    oled.print("A_TIME=");
    oled.print(digitalRead(ALARM_TIME));
    oled.set_pos(80, 2);
    oled.print("ALARM=");
    oled.print(digitalRead(ALARM));

    // 2nd row: print date
    print_digit(7, 1, year(), (selected_field == YEAR_FIELD));
    oled.write('-');
    print_digit(7 + (5 * FONT_WIDTH), 1, month(), (selected_field == MONTH_FIELD));
    oled.write('-');
    print_digit(7 + (8 * FONT_WIDTH), 1, day(), (selected_field == DAY_FIELD));

    // 3rd-4th rows: print time
    oled.set_pos(30, 4);
    oled.print("SET ALARM");
    oled.set_pos(3, 5);
    oled.print("HH:MM-->ALARM MIN");
    oled.set_pos(95, 5);
    oled.print("ON/OFF");
    oled.set_font_size(2);
    print_digit(0, 2, hour(), (selected_field == HOUR_FIELD));
    oled.draw_pattern(2 * FONT_2X_WIDTH + 1, 2, 2, 2, 0b00011000);
    print_digit(2 * FONT_2X_WIDTH + 5, 2, minute(), (selected_field == MINUTE_FIELD));
    oled.draw_pattern(4 * FONT_2X_WIDTH + 6, 2, 2, 2, 0b00011000);
    print_digit(4 * FONT_2X_WIDTH + 2 * FONT_WIDTH, 2, second(), (selected_field == SECOND_FIELD));
    print_digit(0, 6, wake_HOUR, (selected_field == ALARM_HOUR_FIELD));
    oled.draw_pattern(2 * FONT_2X_WIDTH + 1, 6, 2, 2, 0b00011000);
    print_digit(2 * FONT_2X_WIDTH + 5, 6, wake_MINUTE, (selected_field == ALARM_MINUTE_FIELD));
    print_digit(6 * FONT_2X_WIDTH + 2 * FONT_WIDTH, 6, ALARM_TIME, (selected_field == ALARM_FIELD));
    print_digit(80, 6, ACTIVE_ALARM, (selected_field == SET_ALARM_FIELD));
  } 
  else if (display_mode == debug_mode) { // debug_mode
    print_debug_value(2, 'I', get_wdt_interrupt_count());
    print_debug_value(3, 'M', get_wdt_microsecond_per_interrupt());
    print_debug_value(4, 'V', getVcc());
    print_debug_value(5, 'T', getRawTemp());
  }
}

void print_digit(uint8_t col, uint8_t page, int value, bool invert_color) {
  oled.set_pos(col, page);
  if (invert_color) oled.set_invert_color(true);
  if (value < 10) oled.write('0');
  oled.print(value);
  if (invert_color) oled.set_invert_color(false);
}

void print_debug_value(uint8_t page, char initial, uint32_t value) {
  oled.set_pos(0, page);
  oled.write(initial);
  oled.set_pos(14, page);
  oled.print(value);
}

// PIN CHANGE interrupt event function
ISR(PCINT0_vect) {
  set_display_timeout(); // extent display timeout while user input
}

void check_button() {
  int buttonValue = analogRead(BUTTONPIN);

  if (buttonValue < PRESSED_BUTTON_THRESHOLD) { // button down
    set_display_timeout(); // extent display timeout while user input

    if (run_status == sleeping) {
      // wake_up if button pressed while sleeping
      wake_up();
    } else { // not sleeping
      if (buttonValue > UP_DOWN_BUTTON_THRESHOLD) { // down button
        handle_adjust_button_pressed(-1);
      } else if (buttonValue > SET_UP_BUTTON_THRESHOLD) { // up button
        handle_adjust_button_pressed(1);
      } else { // set button
        handle_set_button_pressed();
      }
    } // not sleeping
  } // button down
}

void handle_set_button_pressed() {
  display_mode = time_mode; // always switch to time display mode while set button pressed

  selected_field++;
  if (selected_field > FIELD_COUNT) { // finish time adjustment
    selected_field = NO_FIELD;
    if (time_changed) {
      wdt_auto_tune();
      time_changed = false;
    } //time changed
  } // finish time adjustment
}


void handle_adjust_button_pressed(long value) {
  if (selected_field == NO_FIELD) {
    // toggle display_mode if no field selected
    display_mode = (display_mode == time_mode) ? debug_mode : time_mode;} 
  else {
    long adjust_value;
    if (selected_field == YEAR_FIELD) {
      // TODO: handle leap year and reverse value
      adjust_value = value * SECS_PER_DAY * (leapYear(CalendarYrToTm(year())) ? 366 : 365);
    } 
    else if (selected_field == MONTH_FIELD) {
      // TODO: handle leap year and reverse value
      adjust_value = value * SECS_PER_DAY * getMonthDays(CalendarYrToTm(year()), month());
    } 
    else if (selected_field == DAY_FIELD) {
      // TODO: handle leap year and reverse value
      adjust_value = value * SECS_PER_DAY;
    } 
    else if (selected_field == HOUR_FIELD) {
      adjust_value = value * SECS_PER_HOUR;
    } 
    else if (selected_field == MINUTE_FIELD) {
      adjust_value = value * SECS_PER_MIN;
    } 
    else if (selected_field == SECOND_FIELD) {
      adjust_value = value;
    }
    else if (selected_field == ALARM_HOUR_FIELD) 
    {
      if (wake_HOUR >= 23)
      {
      wake_HOUR=0;
      }
      else if (analogRead(BUTTONPIN) > UP_DOWN_BUTTON_THRESHOLD) 
      { // down button
       wake_HOUR--;
      } 
      else if (analogRead(BUTTONPIN) > SET_UP_BUTTON_THRESHOLD) 
      { // up button
       wake_HOUR++;
       }
        } 
    else if (selected_field == ALARM_MINUTE_FIELD) {
      if (wake_MINUTE >= 59)
      {
      wake_MINUTE=0;
      }
      else if (analogRead(BUTTONPIN) > UP_DOWN_BUTTON_THRESHOLD) 
      { // down button
       wake_MINUTE--;
      } 
      else if (analogRead(BUTTONPIN) > SET_UP_BUTTON_THRESHOLD) 
      { // up button
       wake_MINUTE++;
       }
        }
      else if (selected_field == ALARM_FIELD) {
      if (ALARM_TIME >= 59) //MAX 1h
      {
      ALARM_TIME=0;
      }
      else if (analogRead(BUTTONPIN) > UP_DOWN_BUTTON_THRESHOLD) 
      { // down button
       ALARM_TIME--;
      } 
      else if (analogRead(BUTTONPIN) > SET_UP_BUTTON_THRESHOLD) 
      { // up button
       ALARM_TIME++;
       }
        }
      else if (selected_field == SET_ALARM_FIELD) {
      if (ACTIVE_ALARM >= 1)
      {
      ACTIVE_ALARM=0;
      }
      else if (analogRead(BUTTONPIN) > UP_DOWN_BUTTON_THRESHOLD) 
      { // down button
       ACTIVE_ALARM--;
      } 
      else if (analogRead(BUTTONPIN) > SET_UP_BUTTON_THRESHOLD) 
      { // up button
       ACTIVE_ALARM++;
       }
        }

    adjustTime(adjust_value);
    time_changed = true;
  }
}

I would like to reset timer then when wake_hours&minutes and changeable alarm activation time interval = ALARM_TIME*60000 have passed.

      if (ACTIVE_ALARM==1)
  {
    if (wake_HOUR==hour()& wake_MINUTE==minute())
    {
    digitalWrite(ALARM, HIGH);
    new_millis = millis();
    }
    else if (currenTime -new_millis <= interval)
    {
    new_millis = millis();
    digitalWrite(ALARM, HIGH);
    }
    else if (currenTime -new_millis >= interval)
    {
    new_millis = 0;
    digitalWrite(ALARM, LOW);
    }}
  else if (ACTIVE_ALARM==0)
  {
    new_millis = 0;
    digitalWrite(ALARM, LOW);
  }

Any ideas what kind a timer/delay i could use to make my code work?

6
  • Did you try with && instead of &? The meanings are very very different.
    – Majenko
    Oct 3, 2016 at 17:14
  • You have an awful lot of code in there that has nothing to do with what you're asking... Oct 3, 2016 at 17:14
  • Its all okey with wake_HOUR==hour()& wake_MINUTE==minute() part. Because in next line i declare that if the interval havent reached then output still is ON
    – Martins
    Oct 3, 2016 at 17:16
  • Then i will change my question - Is there any proper way how to make timer that will work in my situation?
    – Martins
    Oct 3, 2016 at 17:18
  • @IgnacioVazquez-Abrams, in the case of boolean predicates (like wake_HOUR==hour() and wake_MINUTE==minute() it's the same... :) ... [both & and && have lower precedence than == which allows rookie mistake to turn out ok] Oct 3, 2016 at 18:39

2 Answers 2

2

As noted in the comment of IgnacioVazquez-Abrams, in the instances at hand you should be using the && (logical AND) relational operator, rather than the & (bitwise AND) arithmetic operator.

That aside, you should separate things out so that instead of an ungainly if - else series you have some independent cases. For example:

if (wake_HOUR==hour() && wake_MINUTE==minute()) {
   digitalWrite(ALARM, HIGH);
   alarmMillis = millis();
}
if (millis()-alarmMillis > interval) {
   digitalWrite(ALARM, LOW);
}

This will do the job, as follows:

• During the match-minute, ALARM will be driven high over and over, and new_millis will update to the current millis() value over and over. Which is OK.

• After the match-minute but before the end of the timed interval, ALARM will remain high and new_millis will remain fixed with the time of the end of the match-minute. OK, that's as desired.

• After the end of the timed interval, ie, when millis()-new_millis > interval, ALARM will be driven low over and over. Which is OK.

Note, I don't see a declaration of currenTime or anywhere it is updated. Hence, it does not represent current time and should not be used as such.

Note, interval should be declared as something like a const unsigned long int, with a value that is a minute shorter than the total length of interval that you want. (new_millis represents the end of the first minute of the interval.)

Note, new_millis should not be zeroed at the end of the interval. If the original code were magically made to mostly work, because of new_millis being zeroed it still would have a bug. During the first few minutes after each millis() overflow, a false alarm would sound. millis() overflows are about 49.71 days apart.

0

So i fixed my problem. I didnt use timer what i wanted in the beginning but as the code it self's running clock i used current clock as to activate output and then based on clock to turn off output. Not always the hardest way is better. Basically this is what i needed to change:

if (ACTIVE_ALARM==1)
{
if (start_wake_HOUR==hour()&& start_wake_MINUTE==minute())
{
digitalWrite(ALARM, HIGH);
}
if (end_wake_HOUR==hour()&& end_wake_MINUTE==minute())
{
digitalWrite(ALARM, LOW);
}
}
else if (ACTIVE_ALARM==0)
{
digitalWrite(ALARM, LOW);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.