Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. My Project
  3. Simple Mysensors clock

Simple Mysensors clock

Scheduled Pinned Locked Moved My Project
clock
4 Posts 2 Posters 6.6k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    axillent
    Mod
    wrote on last edited by axillent
    #1

    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

    sense and drive

    1 Reply Last reply
    5
    • A Offline
      A Offline
      axillent
      Mod
      wrote on last edited by
      #2

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

      sense and drive

      1 Reply Last reply
      2
      • A Offline
        A Offline
        axillent
        Mod
        wrote on last edited by
        #3

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

        sense and drive

        R 1 Reply Last reply
        0
        • A axillent

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

          R Offline
          R Offline
          RJ_Make
          Hero Member
          wrote on last edited by
          #4

          @axillent is unstoppable!

          RJ_Make

          1 Reply Last reply
          0

          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
          Reply
          • Reply as topic
          Log in to reply
          • Oldest to Newest
          • Newest to Oldest
          • Most Votes


          12

          Online

          11.9k

          Users

          11.2k

          Topics

          113.4k

          Posts


          Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
          • Login

          • Don't have an account? Register

          • Login or register to search.
          • First post
            Last post
          0
          • MySensors
          • OpenHardware.io
          • Categories
          • Recent
          • Tags
          • Popular