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?

Martins
  • 43
  • 2
  • 5

2 Answers2

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.

James Waldby - jwpat7
  • 8,920
  • 3
  • 21
  • 33
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);
}
Martins
  • 43
  • 2
  • 5