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