Simple Mysensors clock


  • Mod

    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

    IMG_2150.JPG
    IMG_2151.JPG
    IMG_2152.JPG


  • Mod

    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;
    }

  • Mod

    some 3D models are here http://www.thingiverse.com/thing:160569


  • Hero Member

    @axillent is unstoppable!


Log in to reply
 

Suggested Topics

0
Online

11.4k
Users

11.1k
Topics

112.7k
Posts