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
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login