Simple Mysensors clock
-
It was an idea a year ago to test an idea of simple and smart clock
this clock will need no attention because it will be set and synchronized by MySensors support.The previous version a year ago was running my port to Plain C MySensors 1.3 and able to fit into atmega8
Now it is upgraded to arduino + Mysensors 1.4.1 but for this I had to also upgrade to atmega328p (same as in arduino UNO)
(google translated article is here https://translate.google.com/translate?sl=ru&tl=en&js=y&prev=_t&hl=ru&ie=UTF-8&u=http%3A%2F%2Fradiokot.ru%2Fcircuit%2Fdigital%2Fhome%2F176%2F&edit-text=)Simple - means simple. MCU itself is driving 7 segment display. I'm not using any external RTC clock but I'm using a special asynchronous mode of atmega328 and external 32768 so called clock crystal.
DS18B20 and light sensor are on board.
Display driver contains a software PWM with 0-15 level of the light intensity.
Intensity is adjusted by light.schematics is here http://radiokot.ru/circuit/digital/home/176/01.png
-
Sketch is here
/* p6004_simple_clock.c * * Created: 07.09.2013 17:39:43 * Author: axillent Simple Mysensors.org clock with time synchronization */ //--------------------------------------------------------------------------------------------------- // parameters #define TIMERS_CLOCK_SYNC_HOURS 12 #define TIMERS_TEMPERATURE_SECONDS 30 #define TIMERS_LIGHT_LEVEL_SECONDS 5 #define DISPLAY_CLOCK_SECONDS 10 #define DISPLAY_DATE_SECONDS 2 #define DISPLAY_TEMPERATURE_SECONDS 2 #define DISPLAY_LIGHT_SECONDS 0 #define DISPLAY_MINIMUM_PWM 1 //--------------------------------------------------------------------------------------------------- // hardware definition #include "axlib.h" #define DISP_7SEG_DIGITS 4 #define DISP_7SEG_PWMMAX 16 #define DISP_7SEG_PORT PORTD #define DISP_7SEG_DDR DDRD #define DISP_7SEG_DIG_PORT PORTC #define DISP_7SEG_DIG_DDR DDRC #define DISP_7SEG_DIG_DIG1 PC0 #define DISP_7SEG_DIG_DIG2 PC1 #define DISP_7SEG_DIG_DIG3 PC2 #define DISP_7SEG_DIG_DIG4 PC3 #define SensLight 7 // канал оцифровки освещенности AXLIB_define_io_functions(ledRadio, C, 4) AXLIB_define_io_functions(ledDot, C, 5) #define RF_CE 8 #define RF_CSN 9 // стандартные библиотеки #include <avr/io.h> #include <avr/wdt.h> #include <avr/interrupt.h> #include <util/delay.h> #include <stdlib.h> #include <MySensor.h> #include <SPI.h> // configure ds18b20 #define TEMP_PIN PINB2 #define TEMP_IN PINB #define TEMP_OUT PORTB #define TEMP_DDR DDRB #define MAXSENSORS 1 #include "onewire.h" #include "ds18x20.h" //--------------------------------------------------------------------------------------------------- // functions definition void mcu_init(); void disp_7seg_update(); void disp_7seg_init(); void disp_7seg_show(uint8_t digit, uint8_t value, uint8_t dp); void disp_7seg_setpwm(uint8_t v); uint8_t disp_7seg_getpwm(); void disp_7seg_setdots(uint8_t dots); void displayClock(); void displayTemp(); void displayLight(); void displayDate(); void sync_clock(); void time_convert(unsigned long time, uint8_t* hour, uint8_t* minute, uint8_t* second); AXLIB_TIMER_BLINK(radio, ledRadio_getinv(), ledRadio_clr(), ledRadio_set()) //--------------------------------------------------------------------------------------------------- // the programm itself enum { display_clock=0, display_temp, display_light, display_date }; MySensor gw(RF_CE, RF_CSN); MyMessage msgTemp(0, V_TEMP); MyMessage msgLight(1, V_LIGHT_LEVEL); struct { struct { uint8_t count; int temp; int prev_temp; volatile uint8_t timer; } temperature; struct { volatile uint8_t seconds; volatile uint8_t minutes; volatile uint8_t hours; volatile uint8_t day; volatile uint8_t month; volatile uint16_t sync_timer; } clock; struct { volatile uint8_t light; volatile uint16_t avg; volatile uint8_t cnt; volatile uint8_t timer; volatile uint16_t max_light; volatile uint16_t min_light; uint8_t prev_light; } light; struct { volatile uint8_t what; volatile uint8_t timer; } display; } var; uint8_t gSensorIDs[MAXSENSORS][OW_ROMCODE_SIZE]; // содержит 64бит идентификаторы датчиков температуры в произвоьном порядке (определенном при поиске) // таймер динамической индикации ISR(TIMER1_OVF_vect) { disp_7seg_update(); } // часовой таймер ISR(TIMER2_OVF_vect) { // срабатывает каждую секунду от часового кварца //axnet_rtcsoft_update(); var.clock.seconds++; if(var.clock.seconds == 60) { var.clock.seconds = 0; var.clock.minutes++; if(var.clock.minutes == 60) { var.clock.minutes = 0; var.clock.hours++; if(var.clock.hours == 24) { var.clock.hours = 0; var.clock.sync_timer = 0; } } } // запускаем измерения уровня освещенности ADCSRA |= (1 << ADSC); // мерцание точек if(var.display.what == display_clock) { if(var.clock.seconds % 2) { disp_7seg_setdots(1); } else { disp_7seg_setdots(0); } } if(var.clock.sync_timer) var.clock.sync_timer--; if(var.temperature.timer) var.temperature.timer--; if(var.display.timer) var.display.timer--; if(var.light.timer) var.light.timer--; axlib_timer_blink_radio_update(); } // измерение освещенности завершено ISR(ADC_vect) { var.light.avg += ADCW; if(!--var.light.cnt) { uint16_t light = var.light.avg / 32; if(var.light.min_light > light) var.light.min_light = light; if(var.light.max_light < light) var.light.max_light = light; uint32_t v = light - var.light.min_light; v *= 100; v /= (var.light.max_light - var.light.min_light); var.light.light = 100 - v; var.light.avg = 0; var.light.cnt = 32; } else { ADCSRA |= (1 << ADSC); } } void setup() { // RTC init ---------------------------------------------- // timer2 prescaler 1/128 // timer2 overflow 1Hz (32768 osciilator) // asynhronous timer mode for timer 2 - RTC // TIMSK2 = 0; //_delay_ms(100); ASSR = (1 << AS2); //----------------------------------------------------------- _delay_ms(100); // RTC init continue --------------------------------------- TCNT2 = 0; OCR2A = 0; OCR2B = 0; TCCR2B = (1 << CS22) | (0 << CS21) | (0 << CS20); while(ASSR & ((1 << OCR2AUB) | (1 << OCR2BUB) | (1 << TCR2AUB) | (1 << TCR2BUB))); //_delay_ms(500); // interrupts for timer2 overflow //TIFR2 |= (1 << OCF2A) | (1 << OCF2B) | (1 << TOV2); TIMSK2 = (1 << TOIE2); //------------------------------------------------------------------ // Startup OneWire var.temperature.count = search_sensors(); // Startup and initialize MySensors library. Set callback for incoming messages. // gw.begin(incomingMessage); gw.begin(); // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("Simple Clock", "2.0"); gw.present(0, S_TEMP); gw.present(1, S_LIGHT_LEVEL); Serial.end(); mcu_init(); disp_7seg_init(); ledRadio_set(); axlib_timer_blink_radio_init(); var.display.what = display_clock; var.display.timer = DISPLAY_CLOCK_SECONDS; var.temperature.prev_temp = 9999; var.light.light = 255; var.light.min_light = 1023; var.light.max_light = 0; var.light.prev_light = 9999; var.light.timer = 2; sei(); ADCSRA |= (1 << ADSC); } void loop() { gw.process(); if(!var.clock.sync_timer) { gw.requestTime(receiveTime); axlib_timer_blink_radio_run(3, 1); var.clock.sync_timer = 2; // next update in seconds } switch(var.display.what) { case display_clock: displayClock(); break; case display_date: displayDate(); break; case display_temp: displayTemp(); break; case display_light: displayLight(); break; } // ------------------------------------------------------------------------------------------- // реагирование на освещенность uint16_t pwm = var.light.light * 15; pwm /= 100; if(pwm < DISPLAY_MINIMUM_PWM) pwm = DISPLAY_MINIMUM_PWM; disp_7seg_setpwm(pwm); // ------------------------------------------------------------------------------------------- // переключение отображения if(!var.display.timer) { disp_7seg_setdots(0); switch(var.display.what) { case display_clock: if(DISPLAY_DATE_SECONDS) { // date var.display.what = display_date; var.display.timer = DISPLAY_DATE_SECONDS; } else if(DISPLAY_TEMPERATURE_SECONDS) { // temperature var.display.what = display_temp; var.display.timer = DISPLAY_TEMPERATURE_SECONDS; } else if(DISPLAY_LIGHT_SECONDS) { // light var.display.what = display_light; var.display.timer = DISPLAY_LIGHT_SECONDS; } else { var.display.timer = DISPLAY_CLOCK_SECONDS; // clock } break; case display_date: if(DISPLAY_TEMPERATURE_SECONDS) { // temperature var.display.what = display_temp; var.display.timer = DISPLAY_TEMPERATURE_SECONDS; } else if(DISPLAY_LIGHT_SECONDS) { // light var.display.what = display_light; var.display.timer = DISPLAY_LIGHT_SECONDS; } else { var.display.what = display_clock; var.display.timer = DISPLAY_CLOCK_SECONDS; // clock } break; case display_temp: if(DISPLAY_LIGHT_SECONDS) { // light var.display.what = display_light; var.display.timer = DISPLAY_LIGHT_SECONDS; } else { var.display.what = display_clock; var.display.timer = DISPLAY_CLOCK_SECONDS; // clock } break; case display_light: var.display.what = display_clock; var.display.timer = DISPLAY_CLOCK_SECONDS; break; } } // ------------------------------------------------------------------------------------------- // temperature if(var.temperature.count && !var.temperature.timer) { SPCR &= ~(1 << SPE); int temp = 0; if(DS18X20_start_meas( DS18X20_POWER_EXTERN, &gSensorIDs[0][0] ) == DS18X20_OK) { _delay_ms(1000); if(DS18X20_read_decicelsius(&gSensorIDs[0][0], &temp) == DS18X20_OK ) var.temperature.temp = temp; } SPCR |= (1 << SPE); if(var.temperature.temp != var.temperature.prev_temp) { axlib_timer_blink_radio_run(2, 1); gw.send(msgTemp.set(temp/10.0, 1)); var.temperature.prev_temp = var.temperature.temp; } var.temperature.timer = TIMERS_TEMPERATURE_SECONDS; } // ------------------------------------------------------------------------------------------- // light level if(!var.light.timer) { if(var.light.light != var.light.prev_light) { axlib_timer_blink_radio_run(2, 1); gw.send(msgLight.set(var.light.light)); var.light.prev_light = var.light.light; } var.light.timer = TIMERS_LIGHT_LEVEL_SECONDS; } } void displayClock() { disp_7seg_show(0, var.clock.hours / 10, 0); disp_7seg_show(1, var.clock.hours % 10, 0); disp_7seg_show(2, var.clock.minutes / 10, 0); disp_7seg_show(3, var.clock.minutes % 10, 0); } void displayDate() { disp_7seg_show(0, var.clock.day / 10, 0); disp_7seg_show(1, var.clock.day % 10, 1); disp_7seg_show(2, var.clock.month / 10, 0); disp_7seg_show(3, var.clock.month % 10, 0); } void displayTemp() { disp_7seg_show(0, var.temperature.temp / 100, 0); disp_7seg_show(1, (var.temperature.temp / 10) % 10, 1); disp_7seg_show(2, var.temperature.temp % 10, 0); disp_7seg_show(3, 20, 0); } void displayLight() { disp_7seg_show(0, var.light.light / 1000, 0); disp_7seg_show(1, (var.light.light / 100) % 10, 0); disp_7seg_show(2, (var.light.light / 10) % 10, 0); disp_7seg_show(3, var.light.light % 10, 0); } void mcu_init() { // specific hardware ledRadio_setoutput(); ledDot_setoutput(); // инициализируем таймер для динамической индикации TCCR1A = (0 << WGM11) | (1 << WGM10); TCCR1B = (0 << WGM13) | (1 << WGM12) | (0 << CS12) | (0 << CS11) | (1 << CS10); TIMSK1 = (1 << TOIE1); //TCCR0 = (1 << CS00); //TIMSK |= (1 << TOIE0); // инициализация ADC // AVCC ADMUX = (0 << REFS1) | (1 << REFS0) | SensLight; // interrups ON, prescaler 64 + free running ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0); } //--------------------------------------------------------------------------------------------------- // 7 segment led handling uint8_t buffer_7seg[DISP_7SEG_DIGITS]; uint8_t buffer_7seg_index; uint8_t buffer_7seg_mask; volatile uint8_t disp_7seg_pwm_cnt = 0; volatile uint8_t disp_7seg_pwm_value = 15; volatile uint8_t disp_7seg_dots; static void disp_7seg_setpwm(uint8_t v) { disp_7seg_pwm_value = (v<DISP_7SEG_PWMMAX)?v:DISP_7SEG_PWMMAX-1; } uint8_t disp_7seg_getpwm() { return disp_7seg_pwm_value; } static void disp_7seg_setdots(uint8_t dots) { disp_7seg_dots = dots; } // initialise all ports and variables void disp_7seg_init() { DISP_7SEG_DDR = 0xff; // все биты порта на вывод DISP_7SEG_DIG_DDR |= (1 << DISP_7SEG_DIG_DIG1) | (1 << DISP_7SEG_DIG_DIG2) | (1 << DISP_7SEG_DIG_DIG3) | (1 << DISP_7SEG_DIG_DIG4); buffer_7seg_index = 0; buffer_7seg_mask = (1 << DISP_7SEG_DIG_DIG1); for(uint8_t i=0; i<DISP_7SEG_DIGITS; i++) { buffer_7seg[i] = 0; } } // should be called often to update dynamically leds void disp_7seg_update() { // гасим полностью если pwm=0 if(disp_7seg_pwm_cnt == 0 && disp_7seg_pwm_value) { // сначала сбрасываем все цифры в 1 (инверсия, т.е. отключаем) DISP_7SEG_DIG_PORT |= 0b00001111; // выводим новые значения сегментов DISP_7SEG_PORT = buffer_7seg[buffer_7seg_index]; // включаем нужную цифру DISP_7SEG_DIG_PORT &= ~ buffer_7seg_mask; // готовимся к показу следующей цифры buffer_7seg_index++; if(buffer_7seg_index == DISP_7SEG_DIGITS) { buffer_7seg_index = 0; buffer_7seg_mask = (1 << DISP_7SEG_DIG_DIG1); } else { buffer_7seg_mask = (buffer_7seg_mask << 1); } // точки if(disp_7seg_dots) { ledDot_clr(); } else { ledDot_set(); } } else if(disp_7seg_pwm_cnt > disp_7seg_pwm_value || !disp_7seg_pwm_value) { DISP_7SEG_DIG_PORT |= 0b00001111; ledDot_set(); } disp_7seg_pwm_cnt++; if(disp_7seg_pwm_cnt == DISP_7SEG_PWMMAX) { disp_7seg_pwm_cnt = 0; } } uint8_t disp_7seg_digits[] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, // digits 0-9 0x00, // blank 0x40, // minus 0x77, // A 0x39, // C 0x79, // E 0x71, // F 0x1F, // G 0x76, // H 0x38, // L 0x73, // P 0x63 // градус }; void disp_7seg_show(uint8_t digit, uint8_t value, uint8_t dp) { uint8_t code = 0; if(value < sizeof(disp_7seg_digits)) { // декодируем code = disp_7seg_digits[value]; } if(dp) { code |= (1 << 7); } buffer_7seg[(digit<sizeof(buffer_7seg))?digit:0] = code; } // This is called when a new time value was received void receiveTime(unsigned long time) { uint8_t hour, minute, second; uint8_t month, day; uint16_t year; axlib_timer_blink_radio_run(1, 1); axlib_time_convert(time, &hour, &minute, &second); axlib_date_convert(time, &day, &month, &year); cli(); var.clock.hours = hour; var.clock.minutes = minute; var.clock.seconds = second; var.clock.day = day; var.clock.month = month; sei(); var.clock.sync_timer = 60 * 60 * TIMERS_CLOCK_SYNC_HOURS; // next update in seconds } uint8_t search_sensors(void) { uint8_t i; uint8_t id[OW_ROMCODE_SIZE]; uint8_t diff, nSensors; ow_set_bus(&TEMP_IN,&TEMP_OUT,&TEMP_DDR,TEMP_PIN); ow_reset(); nSensors = 0; diff = OW_SEARCH_FIRST; while ( diff != OW_LAST_DEVICE && nSensors < MAXSENSORS ) { DS18X20_find_sensor( &diff, &id[0] ); for ( i=0; i < OW_ROMCODE_SIZE; i++ ) gSensorIDs[nSensors][i] = id[i]; nSensors++; } return nSensors; }
-
some 3D models are here http://www.thingiverse.com/thing:160569
-
@axillent is unstoppable!