Информационная панель с часами и уличной температурой интегрированая с MySensors

Нет ответов
axill
Offline
Зарегистрирован: 05.09.2011

Про MySensors.org есть отдельная ветка http://arduino.ru/forum/proekty/umnyi-dom-s-tsentralnym-kontrollerom-ot-...

Про конструкцию панели я писал год назад статью http://radiokot.ru/circuit/digital/home/194/

Сейчас создаю отдельный пост по причине того, что сейчас этот проект стал чисто ардуиновский. Я настроил ардуину на базе atmega128 по инструкции для этой платы http://www.chip45.com/products/crumbuino-128_arduino_compatible_atmega12...

Скетч переписал под ардуино. Использовал VisualMicro, но он будет компилироваться и в обычной IDE. Здесь полный архив http://forum.mysensors.org/uploads/upload-ce934f98-d171-4227-b6c9-4e1766...

Ссылка на пост на сайте MySensors http://forum.mysensors.org/topic/957/red-green-matrix-information-panel-...

http://www.youtube.com/watch?v=YOuO6zLDz6U

Здесь скетч

/* -------------------------------------------------------------------------------------
 * user configuration
 *-------------------------------------------------------------------------------------*/

//#define F_CPU	16000000UL

#define TIMERS_BMP085_UPDATE_SEC				10
#define TIMERS_BATTERY_UPDATE_MIN				120

#define TIMERS_VERA_PRESSURE_UPDATE_MINUTE		5
#define TIMERS_VERA_TEMPERATURE_UPDATE_MINUTE	2
#define TIMERS_VERA_BATTERY_UPDATE_MINUTE		120
#define TIMERS_VERA_LIGHT_UPDATE_SECONDS		240
#define TIMERS_VERA_CLOCK_UPDATE_HOUR			12
#define TIMERS_VERA_STREET_TEMPERATURE_MINUTE	60				// minutes, time for street temperature recieved from vera to be timed out

#define TIMERS_VERA_FAILED_RADIO_MIN			5

#define rus_str_startup		"RG Matrix Clock v1.0"
#define rus_str_v_komnate	"Local"
#define rus_str_v_kvartire	"Inside"
#define rus_str_na_ulitce	"Outside"
#define rus_str_davlenie	"Preassure"

/* -------------------------------------------------------------------------------------
 * hardware configuration
 *-------------------------------------------------------------------------------------*/
#define LED_PORT	PORTC
#define LED_DATA	3
#define LED_WR		2
#define LED_CLK		1
#define LED_CS		0

#define BUZZER_PORT_OUT	PORTE
#define	BUZZER_PORT_DDR	DDRE
#define BUZZER_PORT_IN	PINE
#define BUZZER_PORT_PIN	(1 << PE2)

typedef enum {
	ADC_LIGHT=3,
	ADC_BATTERY=2,
	ADC_VREF=30
} enum_adc;

//#define RF24_NOUART
//#define RF24_CE_PORT   PORTB
//#define RF24_CE_DDR    DDRB
//#define RF24_CE_PIN    PB4
//#define RF24_CSN_PORT  PORTB
//#define RF24_CSN_DDR   DDRB
//#define RF24_CSN_PIN   PB5
//#define MAX_PAYLOAD_SIZE  32
#define RF_CE	12
#define RF_CSN	13

//#define BMP085_EOC_PORT	PINC
#define BMP085_EOC_PIN	(1 << PINC0)

/* -------------------------------------------------------------------------------------
 * includes
 *-------------------------------------------------------------------------------------*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
//#include <util/delay.h>

#include <stdio.h>

#include <MySensor.h>
#include <SPI.h>

#include "i2c.h"
#include "bmp085.h"
#include "ds3231.h"
#include "ht1632c_c.h"
#include "axlib.h"
#include "incMsg.h"

//#include "uart_printf.h"

/* -------------------------------------------------------------------------------------
 * globals
 *-------------------------------------------------------------------------------------*/
#define STREET_TEMPERATURE_NONE		-2500		// value used to indicate that street temperature was not set or timed outed

enum {
	vera_sens_temp=0,
	vera_sens_pressure,
	vera_sens_light
};
typedef enum {
	display_startup=0,
	display_clock,
	display_clock_date,
	display_temperature_pressure,
	display_failed_radio
} enum_display;

volatile struct {
	uint8_t		second;
	uint8_t		display_sec;
	uint8_t		display_type_sec;
	uint8_t		measure_bmp085_sec;
	uint16_t	measure_battery_sec;
	uint8_t		vera_preassure_min;
	uint8_t		vera_temperature_min;
	uint8_t		vera_battery_min;
	uint16_t	vera_light_sec;
	uint32_t	vera_clock_sec;
	uint16_t	vera_failed_radio_min;
	uint8_t		vera_street_temperature;
	uint8_t		vera_apartment_temperature;
} timers;

enum {
	radio_ok=0,
	radio_failed
};
struct {
	int16_t		temperature;
	uint32_t	pressureMM;
	uint8_t		light;
	uint16_t	h_light_min;
	uint16_t	h_light_max;
	uint8_t		battery;
	uint16_t	vref;
	volatile	int16_t		street_temperature;
	volatile	int16_t		apartment_temperature;
	uint8_t		radio_status;
	char		buff[20];
	enum_display		cur_display;
	bmp085_calibration	calibration;
	struct {
		uint16_t	s_pressureMM;
		int16_t		temperature;
		uint8_t		battery;
		uint8_t		light;
	} vera_last;
} var;

MySensor gw(RF_CE, RF_CSN);

/* -------------------------------------------------------------------------------------
 * function definition
 *-------------------------------------------------------------------------------------*/
void buzzerInit() { BUZZER_PORT_DDR |= BUZZER_PORT_PIN; }
uint8_t buzzerCheck() { return BUZZER_PORT_IN & BUZZER_PORT_PIN; }
void buzzerOn() { BUZZER_PORT_OUT |= BUZZER_PORT_PIN; }
void buzzerOff() { BUZZER_PORT_OUT &= ~ BUZZER_PORT_PIN; }
AXLIB_TIMER_BLINK(buzzer, buzzerCheck(), buzzerOn(), buzzerOff())

void init_mcu();
void display_sec(uint8_t what);
uint8_t adc(uint8_t what);

// 20 times each second
ISR(TIMER1_COMPA_vect) {
	axlib_timer_blink_buzzer_update();
}
// once a second
ISR(TIMER3_COMPA_vect) {
	timers.second++;
	if(timers.second == 60) {
		timers.second = 0;

		// timers with minutes counting
		if(timers.vera_preassure_min) timers.vera_preassure_min--;
		if(timers.vera_temperature_min) timers.vera_temperature_min--;
		if(timers.vera_battery_min) timers.vera_battery_min--;
		if(timers.vera_failed_radio_min && timers.vera_failed_radio_min != 1) timers.vera_failed_radio_min--;
		if(timers.vera_street_temperature) {
			if(!--timers.vera_street_temperature) var.street_temperature = STREET_TEMPERATURE_NONE;
		}
		if(timers.vera_apartment_temperature) {
			if(!--timers.vera_apartment_temperature) var.apartment_temperature = STREET_TEMPERATURE_NONE;
		}
	}

	// timers with seconds counting
	if(timers.measure_bmp085_sec) timers.measure_bmp085_sec--;
	if(timers.display_sec) timers.display_sec--;
	if(timers.display_type_sec) timers.display_type_sec--;
	if(timers.vera_clock_sec) timers.vera_clock_sec--;
	if(timers.vera_light_sec) timers.vera_light_sec--;

	// measurement of RTC battery level
	if(timers.measure_battery_sec) --timers.measure_battery_sec;
}

ISR(TIMER2_OVF_vect) {
}

// adc
#define ADC_COUNT_AVG	16
#define ADC_BG			30
#define BATTERY_MIN		250	// V*100, 0%
#define BATTERY_MAX		300	// V*100, 100%

volatile uint16_t adc_count;
volatile uint16_t adc_sum;

uint8_t adc(uint8_t what) {

	adc_count = ADC_COUNT_AVG;
	adc_sum = 0;
	ADMUX = (0 << REFS1) | (1 << REFS0) | what;
	ADCSRA |= (1 << ADSC);

	return 1;
}

ISR(ADC_vect) {

	adc_sum += ADCW;
	if(!--adc_count) {
		// finished
		uint8_t channel = ADMUX & 0x1F;
		switch(channel) {
			case ADC_LIGHT:
			{
				// should be between 0% and 100%
				// 0% is most dark
				if(var.h_light_max < adc_sum) var.h_light_max = adc_sum;
				if(var.h_light_min > adc_sum) var.h_light_min = adc_sum;

				uint32_t v = adc_sum - var.h_light_min;
				v *= 100;
				v /= (var.h_light_max - var.h_light_min);
				var.light = 100 - v;
				break;
			}
			case ADC_BATTERY:
			{
				uint32_t v = adc_sum;
				v *= var.vref;
				v /= 1023;
				v /= ADC_COUNT_AVG;
				// translate voltage to %
				//if(v >= BATTERY_MAX) var.battery = 100;
				//else if(v <= BATTERY_MIN) var.battery = 0;
				//else {
					//v -= BATTERY_MIN;
					//v *= 100;
					//v /= (BATTERY_MAX - BATTERY_MIN);
				//}
				//var.battery = v;
				var.battery = axlib_convert_volt2percent_lithium_notrechargeable(v);
				timers.measure_battery_sec = TIMERS_BATTERY_UPDATE_MIN * 60;
				break;
			}
			case ADC_BG:
			{
				uint32_t v = 131;	// should be 123 (1.23V) but this one is corrected
				v *= 1023;
				v /= adc_sum;
				v *= 16;
				var.vref = v;
				break;
			}
		}
		// find what next to measure
		if(!timers.measure_battery_sec) channel = ADC_BATTERY;
		else {
			switch(channel) {
				case ADC_LIGHT:
					channel = ADC_VREF;
					break;
				case ADC_VREF:
					channel = ADC_LIGHT;
					break;
				case ADC_BATTERY:
					channel = ADC_LIGHT;
					break;
			}
		}
		adc(channel);
	} else {
		// next round
		ADCSRA |= (1 << ADSC);
	}
}

/* -------------------------------------------------------------------------------------
 * main functions
 *-------------------------------------------------------------------------------------*/
	
void setup()
{
	// variables init
	timers.second = 0;
	timers.measure_battery_sec = 0;
	timers.measure_bmp085_sec = 0;
	timers.vera_preassure_min = 0;
	timers.vera_clock_sec = 10;
	timers.vera_light_sec = 0;
	timers.vera_battery_min = 0;
	timers.display_sec = 0;
	timers.vera_failed_radio_min = 0;
	timers.vera_street_temperature = 0;
	timers.vera_apartment_temperature = 0;

	var.vref = 500;
	var.radio_status = radio_ok;
	var.street_temperature = STREET_TEMPERATURE_NONE;
	var.apartment_temperature = STREET_TEMPERATURE_NONE;

	init_mcu();

	buzzerInit();
	axlib_timer_blink_buzzer_init();

	// init uart printing
	Serial.begin(115200);
	Serial1.begin(9600);
	//uart_debug_init(UART_PRINTF_BAUDRATE(115200, F_CPU));
	//uart_debugf_1_P(PSTR("\n------------------------------\nRG Matrix clock started v1.0\n"));
	gw.debugPrint(PSTR("\n------------------------------\nRG Matrix clock started v1.0\n"));

	// init bmp085
	i2cInit();

	bmp085_readCalibration(&var.calibration);
	gw.debugPrint(PSTR("\n1. BMP085 calibration data:\n"));
	gw.debugPrint(PSTR("- ac1 = %i\n"), var.calibration.ac1);
	gw.debugPrint(PSTR("- ac2 = %i\n"), var.calibration.ac2);
	gw.debugPrint(PSTR("- ac3 = %i\n"), var.calibration.ac3);
	gw.debugPrint(PSTR("- ac4 = %i\n"), var.calibration.ac4);
	gw.debugPrint(PSTR("- ac5 = %i\n"), var.calibration.ac5);
	gw.debugPrint(PSTR("- ac6 = %i\n"), var.calibration.ac6);
	gw.debugPrint(PSTR("- b1 = %i\n"), var.calibration.b1);
	gw.debugPrint(PSTR("- b2 = %i\n"), var.calibration.b2);
	gw.debugPrint(PSTR("- mb = %i\n"), var.calibration.mb);
	gw.debugPrint(PSTR("- mc = %i\n"), var.calibration.mc);
	gw.debugPrint(PSTR("- md = %i\n\n"), var.calibration.md);
	// set 24 hour format of RTC
	DS3231Set24(1);
	gw.debugPrint(PSTR("2. DS3231 init done\n"));

	// init rg matrix
	//uart_debugf_1_P(PSTR("3. LED init\n"));
	gw.debugPrint(PSTR("3. LED init\n"));
	ht1632c_init(&LED_PORT, LED_DATA, LED_WR, LED_CLK, LED_CS, HT1632_COLOR_GEOM_32x16, 1);
	ht1632c_pwm(15);

	// vera init
	//uart_debugf_1_P(PSTR("4. Vera inititalization started\n"));
	Serial.println("4. Vera initialization started\n");
	gw.begin(incomingMessage);
	gw.sendSketchInfo("RG Matrix clock", "2.0");

	gw.present(vera_sens_temp, S_TEMP);
	gw.present(vera_sens_pressure, S_BARO);
	gw.present(vera_sens_light, S_LIGHT_LEVEL);
	
	//VeraSensor_sendSensorPresentation(vera_sens_temp, S_TEMP);
	//VeraSensor_sendSensorPresentation(vera_sens_pressure, S_BARO);
	//VeraSensor_sendSensorPresentation(vera_sens_light, S_LIGHT_LEVEL);
	//uart_debugf_2_P(PSTR("- radioId = %i\n"), gw.getNodeId());

	axlib_timer_blink_buzzer_run(1, 1);

	//wdt_enable(WDTO_2S);
	//uart_debugf_1_P(PSTR("5. WDT init done\n\n"));

	//uart_debugf_1_P(PSTR("------------------------------\nMain loop initiated\n\n"));
	gw.debugPrint(PSTR("------------------------------\nMain loop initiated\n"));
	sei();

	var.cur_display = display_startup;
	timers.display_type_sec = 0;

	adc(ADC_VREF);
}

void loop()
{
		gw.process();
	
		// request clock synchronization if needed
		if(!timers.vera_clock_sec) {
			//uart_debugf_1_P(PSTR("- requested clock sync from vera\n"));
			gw.debugPrint(PSTR("- requested clock sync from vera\n"));
			gw.requestTime(receiveTime);
			timers.vera_clock_sec = 30;
			if(!timers.vera_failed_radio_min) timers.vera_failed_radio_min = TIMERS_VERA_FAILED_RADIO_MIN;
		}

		// it is a time to update measurement on bmp085
		if(!timers.measure_bmp085_sec) {
			int32_t pressure;

			bmp085_get(&var.calibration, BMP085_OSS_ULTRAHIGHRESOLUTION, &var.temperature, &pressure);
			var.pressureMM = bmp085_convertPAtoMM(pressure);
			gw.debugPrint(PSTR("- updated temperature %i.%i, pressure %li.%li mm\n"),
			var.temperature/10, var.temperature % 10, var.pressureMM/1000, var.pressureMM%1000);

			// check if update on pressure to vera is needed
			if(!timers.vera_preassure_min) {
				// update vera on pressure only that often
				// use for vera only one digit after dot, rounded
				uint16_t s_pressureMM = (var.pressureMM + 50) / 100;
				if(var.vera_last.s_pressureMM != s_pressureMM) {
					// do an update to vera
					sprintf_P(var.buff, PSTR("%i.%d"), s_pressureMM / 10, (int)(s_pressureMM % 10));
					gw.debugPrint(PSTR("- sending update to Vera pressureMM = %s\n"), var.buff);

					MyMessage pressureMsg(vera_sens_pressure, V_PRESSURE);
					MyMessage forecastMsg(vera_sens_pressure, V_FORECAST);
					gw.send(pressureMsg.set(var.buff));
					gw.send(forecastMsg.set("unknown"));
					//VeraSensor_sendVariableString(vera_sens_pressure, V_PRESSURE, buff);

					var.vera_last.s_pressureMM = s_pressureMM;
					timers.vera_preassure_min = TIMERS_VERA_PRESSURE_UPDATE_MINUTE;
				}
			}
			// check if update on temperature to vera is needed
			if(!timers.vera_temperature_min || abs(var.vera_last.temperature-var.temperature) > 20) {
				// update vera on temperature only that often
				if(var.vera_last.temperature != var.temperature) {
					// do an update to vera
					sprintf_P(var.buff, PSTR("%i.%i"), var.temperature / 10, var.temperature % 10);
					gw.debugPrint(PSTR("- sending update to Vera temperature = %s\n"), var.buff);

					MyMessage tempMsg(vera_sens_temp, V_TEMP);
					gw.send(tempMsg.set(var.buff));
					//VeraSensor_sendVariableString(vera_sens_temp, V_TEMP, buff);

					var.vera_last.temperature = var.temperature;
					timers.vera_temperature_min = TIMERS_VERA_TEMPERATURE_UPDATE_MINUTE;
				}
			}

			timers.measure_bmp085_sec = TIMERS_BMP085_UPDATE_SEC;
		}

		// update vera on battery
		if(!timers.vera_battery_min) {
			if(var.vera_last.battery != var.battery) {
				gw.debugPrint(PSTR("- sending update to Vera battery = %d%%\n"), var.battery);
				gw.sendBatteryLevel(var.battery);
				var.vera_last.battery = var.battery;
				timers.vera_battery_min = TIMERS_VERA_BATTERY_UPDATE_MINUTE;
			}
		}
		
		// update vera on light
		if(!timers.vera_light_sec || abs(var.vera_last.light - var.light) > 10) {
			if(var.vera_last.light != var.light) {
				gw.debugPrint(PSTR("- sending update to Vera light = %d%%\n"), var.light);
				
				MyMessage msg(vera_sens_light, V_LIGHT_LEVEL);
				gw.send(msg.set(var.light));
				
				var.vera_last.light = var.light;
				timers.vera_light_sec = TIMERS_VERA_LIGHT_UPDATE_SECONDS;
			}
		}

		if(timers.vera_failed_radio_min == 1) {
			timers.vera_failed_radio_min = TIMERS_VERA_FAILED_RADIO_MIN;
			var.cur_display = display_failed_radio;
			var.radio_status = radio_failed;
		}

		if(!timers.display_sec) {
			display_sec(var.cur_display);
			timers.display_sec = 1;
		}

		if(!timers.display_type_sec) {
			switch(var.cur_display) {
				case display_startup:
				case display_failed_radio:
				case display_temperature_pressure:
					var.cur_display = display_clock;
					timers.display_type_sec = 10;
					//ht1632c_clear();
					break;
				case display_clock:
					var.cur_display = display_clock_date;
					timers.display_type_sec = 6;
					//ht1632c_clear();
					break;
				case display_clock_date:
					var.cur_display = display_temperature_pressure;
					timers.display_type_sec = 6;
					//ht1632c_clear();
					break;
			}
		}

		wdt_reset();
}

/* -------------------------------------------------------------------------------------
 * internal function
 *-------------------------------------------------------------------------------------*/
// 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_time_convert(time, &hour, &minute, &second);
	axlib_date_convert(time, &day, &month, &year);

	gw.debugPrint(PSTR("- new time recieved %02d:%02d.%02d\n"), hour, minute, second);
	gw.debugPrint(PSTR("- new date recieved %02d.%02d.%04d\n"), day, month, year);

	DS3231SetFullTime24(hour, minute, second);
	DS3231SetFullDate(year, month, day, 1);

	timers.vera_clock_sec = TIMERS_VERA_CLOCK_UPDATE_HOUR * 3600UL;
}

void setStreetTemp(int t) {
	// getting street temperature
	var.street_temperature = t;
	gw.debugPrint(PSTR("- street temperature received %d\n"), var.street_temperature);
	// stupid validity check
	if(var.street_temperature > -600 && var.street_temperature < 1000) timers.vera_street_temperature = TIMERS_VERA_STREET_TEMPERATURE_MINUTE;
	else var.street_temperature = STREET_TEMPERATURE_NONE;
}
void setRoomTemp(int t) {
	// getting apartment temperature
	var.apartment_temperature = t;
	gw.debugPrint(PSTR("- apartment temperature received %d\n"), var.apartment_temperature);
	// stupid validity check
	if(var.apartment_temperature > -600 && var.apartment_temperature < 1000) timers.vera_apartment_temperature = TIMERS_VERA_STREET_TEMPERATURE_MINUTE;
	else var.apartment_temperature = STREET_TEMPERATURE_NONE;
}
void setTextToShow(char* str) {
	// text message received
	ht1632c_setfont(FONT_8x13BK);
	ht1632c_animate_hscrolltext(1, str, HT1632_COLOR_GREEN, 20, 1, HT1632_DIR_LEFT, 0, HT1632_COLOR_BLACK, 8);
}
void setSoundID(int id) {
	Serial1.println(id);
}
void clearRadioStatus() {
	timers.vera_failed_radio_min = 0;
	var.radio_status = radio_ok;

	ht1632c_animate_rect_fillfrominside(10, 5, HT1632_COLOR_RED, 5);
	ht1632c_animate_rect_fillfrominside(17, 10, HT1632_COLOR_GREEN, 5);
	ht1632c_animate_rect_fillfrominside(15, 7, HT1632_COLOR_BLACK, 5);

	for(uint8_t i=0; i<3; i++) {
		ht1632c_setfont(FONT_5x7W);
		ht1632c_rect(8, 2, 23, 13, HT1632_COLOR_ORANGE);
		ht1632c_putstr(10, 4, "IN", HT1632_COLOR_RED, 0, HT1632_COLOR_BLACK);
		ht1632c_sendframe();
		wdt_reset();
		_delay_ms(100);
		ht1632c_clear();
		ht1632c_sendframe();
		wdt_reset();
		_delay_ms(100);
		wdt_reset();
	}

}

void display_temperature(int16_t temperature, uint8_t color, uint8_t bg_color)
{
	uint8_t	neg  = (temperature<0)?1:0;

	if(neg) temperature = -temperature;

	uint8_t dig1 = temperature / 100;
	uint8_t dig2 = (temperature / 10) % 10;
	uint8_t dig3 = temperature % 10;
	uint8_t x = (dig1)?0:7;

	if(!dig3) x += 4;
	ht1632c_setfont(FONT_8x13BK);

	if(neg) {
		temperature = - temperature;
		ht1632c_rect(x, 7, x+2, 8, color);
		x += (dig1)?3:4;
	} else {
		x += 1;
	}

	ht1632c_setfont(FONT_8x13BK);
	if(dig1) {
		// ГЙЖТБ ДЕУСФЛПЧ, ОПМШ РТПРХУЛБЕН
		ht1632c_putchar(x, 2, dig1 + '0', color, 0, bg_color);
		x += 8;
	}
	// ГЙЖТБ ЕДЙОЙГ, ОПМШ РПЛБЪЩЧБЕН
	ht1632c_putchar(x, 2, dig2 + '0', color, 0, bg_color);
	x += 8;

	if(dig3) {
		// ДЕУСФЙЮОБС ФПЮЛБ: ПФПВТБЦБЕН ЕУМЙ ЪОБЮБЪБС ГЙЖТБ РПУМЕ ФПЮЛЙ
		ht1632c_rect(x, 11, x+1, 12, color);
		x += 3;
		ht1632c_putchar(x, 2, dig3 + '0', color, 0, bg_color);
		x += 8;
	}

	// ЪОБЛ ЗТБДХУПЧ
	ht1632c_line(x-1, 0, x-1, 2, color);
	ht1632c_line(x+1, 0, x+1, 2, color);
	ht1632c_line(x-1, 0, x+1, 0, color);
	ht1632c_line(x-1, 2, x+1, 2, color);
	x += 3;

	if(x < 31-8) {
		ht1632c_putchar(x, 2, 'C', color, 0, bg_color);
	}

	ht1632c_sendframe();

/*	if(var.street_temperature > 0 || (var.street_temperature > -99)) {
		// display with dot if we have space
		sprintf_P(buff, PSTR("%d.%d`"), var.street_temperature/10, abs(var.street_temperature)%10);
		ht1632c_setfont(FONT_6x13);
		ht1632c_putstr(1, 1, buff, HT1632_COLOR_RED, 0, HT1632_COLOR_BLACK);
	} else {
		// duisplay without dot if space is limited
		sprintf_P(buff, PSTR("%d`"), var.street_temperature/10);
		ht1632c_setfont(FONT_8x13BK);
		ht1632c_putstr(4, 2, buff, HT1632_COLOR_RED, 0, HT1632_COLOR_BLACK);
	}
	ht1632c_sendframe();
	*/
}

void display_pressure(uint16_t pressure, uint8_t color, uint8_t bg_color) {
	uint8_t dig1 = pressure / 100;
	uint8_t dig2 = (pressure / 10 ) % 10;
	uint8_t dig3 = pressure % 10;

	ht1632c_setfont(FONT_8x13BK);
	ht1632c_putchar(0, 2, dig1+'0', color, 0, bg_color);
	ht1632c_putchar(8, 2, dig2+'0', color, 0, bg_color);
	ht1632c_putchar(16, 2, dig3+'0', color, 0, bg_color);
	ht1632c_setfont(FONT_4x6);
	ht1632c_putchar(24, 8, 'm', color, 0, bg_color);
	ht1632c_putchar(28, 8, 'm', color, 0, bg_color);
	ht1632c_sendframe();
}

void display_clockf(int8_t x, int8_t y, uint8_t color, uint8_t bg_color) {
	uint8_t a, b, c;

	ht1632c_setfont(FONT_6x13B);
	DS3231GetFullTime24(&a, &b, &c);

	x += ht1632c_putchar(x, y, '0'+a/10, HT1632_COLOR_ORANGE, 0, HT1632_COLOR_BLACK);
	x += ht1632c_putchar(x, y, '0'+a%10, HT1632_COLOR_ORANGE, 0, HT1632_COLOR_BLACK);

	if(c%2) {
		ht1632c_rect(x, y+3, x+1, y+3+2, HT1632_COLOR_ORANGE);
		ht1632c_rect(x, y+7, x+1, y+7+2, HT1632_COLOR_ORANGE);
	}
	x += 6;

	x += ht1632c_putchar(x-3, y, '0'+b/10, HT1632_COLOR_ORANGE, 0, HT1632_COLOR_BLACK);
	ht1632c_putchar(x-3, y, '0'+b%10, HT1632_COLOR_ORANGE, 0, HT1632_COLOR_BLACK);

	ht1632c_sendframe();
}

void display_sec(uint8_t what) {
	char buff[64];

	ht1632c_clear();
	switch(what) {
		case display_clock:
			display_clockf(1, 1, HT1632_COLOR_ORANGE, HT1632_COLOR_BLACK);
			break;
		case display_clock_date:
		{
			uint8_t a, b, c, d;

			ht1632c_setfont(FONT_5x7W);
			DS3231GetFullTime24(&a, &b, &c);
			sprintf(buff, "%02d%c%02d", a, (c%2)?':':' ', b);
			ht1632c_putstr(1, 1, buff, HT1632_COLOR_ORANGE, 0, HT1632_COLOR_BLACK);

			DS3231GetFullDate(&a, &b, &c, &d);
			sprintf(buff, "%02d.%02d", c, b);
			ht1632c_putstr(1, 9, buff, HT1632_COLOR_ORANGE, 0, HT1632_COLOR_BLACK);
			break;
		}
		case display_startup:
		{
			ht1632c_animate_rect_fillfrominside(15, 7, HT1632_COLOR_GREEN, 10);
			//_delay_ms(500);
			ht1632c_animate_rect_fillfrominside(15, 7, HT1632_COLOR_RED, 10);
			//_delay_ms(500);
			ht1632c_animate_rect_fillfrominside(15, 7, HT1632_COLOR_ORANGE, 10);
			//_delay_ms(500);
			ht1632c_animate_rect_fillfrominside(15, 7, HT1632_COLOR_BLACK, 10);

			ht1632c_setfont(FONT_8x13BK);
			ht1632c_animate_hscrolltext(1, rus_str_startup, HT1632_COLOR_ORANGE, 20, 1, HT1632_DIR_LEFT, 0, HT1632_COLOR_BLACK, 16);
			break;
		}
		case display_temperature_pressure:
			// display street temperature
			if(var.street_temperature != STREET_TEMPERATURE_NONE) {
				ht1632c_setfont(FONT_8x13BK);
				sprintf_P(buff, PSTR(rus_str_na_ulitce));
				ht1632c_animate_hscrolltext(1, buff, HT1632_COLOR_RED, 5, 1, HT1632_DIR_LEFT, 0, HT1632_COLOR_BLACK, 16);
/*				if(var.street_temperature > 0 || (var.street_temperature > -99)) {
					// display with dot if we have space
					sprintf_P(buff, PSTR("%d.%d`"), var.street_temperature/10, abs(var.street_temperature)%10);
					ht1632c_setfont(FONT_6x13);
					ht1632c_putstr(1, 1, buff, HT1632_COLOR_RED, 0, HT1632_COLOR_BLACK);
				} else {
					// duisplay without dot if space is limited
					sprintf_P(buff, PSTR("%d`"), var.street_temperature/10);
					ht1632c_setfont(FONT_8x13BK);
					ht1632c_putstr(4, 2, buff, HT1632_COLOR_RED, 0, HT1632_COLOR_BLACK);
				}
				ht1632c_sendframe();*/
				display_temperature(var.street_temperature, HT1632_COLOR_RED, HT1632_COLOR_BLACK);
				ht1632c_delay_ms(3000);
				ht1632c_clear();
			}

			// display room temperature
			{
				const void* str;
				int16_t temp;

				if(var.apartment_temperature != STREET_TEMPERATURE_NONE) {
					str = PSTR(rus_str_v_kvartire);
					temp = var.apartment_temperature;
				} else {
					str = PSTR(rus_str_v_komnate);
					temp = var.temperature;
				}
				ht1632c_setfont(FONT_8x13BK);
				sprintf_P(buff, (const char*)str);
				ht1632c_animate_hscrolltext(1, buff, HT1632_COLOR_ORANGE, 5, 1, HT1632_DIR_LEFT, 0, HT1632_COLOR_BLACK, 16);
//				sprintf_P(buff, PSTR("%d.%d`"), temp/10, temp%10);
				display_temperature(temp, HT1632_COLOR_ORANGE, HT1632_COLOR_BLACK);
				ht1632c_delay_ms(3000);
				ht1632c_clear();
			}
//			ht1632c_setfont(FONT_6x13);
			//ht1632c_putstr(1, 1, buff, HT1632_COLOR_ORANGE, 0, HT1632_COLOR_BLACK);
			//ht1632c_sendframe();

			// display atmosphere preasure
			ht1632c_setfont(FONT_8x13BK);
			sprintf_P(buff, PSTR(rus_str_davlenie));
			ht1632c_animate_hscrolltext(1, buff, HT1632_COLOR_GREEN, 5, 1, HT1632_DIR_LEFT, 0, HT1632_COLOR_BLACK, 16);
//			sprintf(buff, "%dmm", (int)((var.pressureMM+500)/1000));
//			ht1632c_setfont(FONT_6x13);
//			ht1632c_putstr(1, 1, buff, HT1632_COLOR_GREEN, 0, HT1632_COLOR_BLACK);
//			ht1632c_sendframe();
			display_pressure(((var.pressureMM+500)/1000), HT1632_COLOR_GREEN, HT1632_COLOR_BLACK);
			ht1632c_delay_ms(3000);
			break;
		case display_failed_radio:
			ht1632c_setfont(FONT_8x13BK);
			//axlib_timer_blink_buzzer_run(3, 1);
			ht1632c_animate_hscrolltext(1, "Тег йбиум й Vera", HT1632_COLOR_RED, 50, 2, HT1632_DIR_LEFT, 0, HT1632_COLOR_BLACK, 16);
			break;
	}
	// radio status
	//ht1632c_rect(30, 0, 31, 1, (var.radio_status==radio_ok)?HT1632_COLOR_GREEN:HT1632_COLOR_RED);
	ht1632c_plot(31, 0, (var.radio_status==radio_ok)?HT1632_COLOR_GREEN:HT1632_COLOR_RED);

	ht1632c_sendframe();
}


void init_mcu() {

	// timer 1 - 20 times per second
	// CTC mode
//	TCCR1A = (0 << WGM11) | (0 << WGM10);
	// CTC mode + /64
//	TCCR1B = (0 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (1 << CS10);
//	OCR1A = 9375;

	// timer2
	// clock source - T2
	TCCR2 = (1 << CS22) | (1 << CS21) | (1 << CS20);

	// timer 3 - once a second
	// CTC mode
	TCCR3A = (0 << WGM31) | (0 << WGM30);
	// /256 prescaler + CTC mode
	TCCR3B = (0 << WGM33) | (1 << WGM32) | (1 << CS32) | (0 << CS31) | (0 << CS30);
	OCR3A = F_CPU / 256;

	TIMSK |= (0 << TOIE2);// | (1 << OCIE1A);
	ETIMSK = (1 << OCIE3A);

	// adc
	ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
}