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?

asked Oct 3, 2016 at 17:11
6
  • Did you try with && instead of &? The meanings are very very different. Commented 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... Commented 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 Commented 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? Commented 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] Commented 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.

answered Oct 3, 2016 at 18:55
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);
}
answered Oct 4, 2016 at 17:36

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.