Начало было описано тут.
Напомню, на тот момент было реализовано:
Чтобы адекватно отслеживать информацию по активности датчика введем новую структуру:
Сразу же эту структуру заполним актуальными данными:
Теперь необходимо немного модифицировать функцию listenRF24() таким образом, чтобы в ней заполнялись данные вышеприведенной структуры:
Таким образом, после этих подготовительных шагов можно приступать к "визуальным эффектам".
В общем-то, функция осталась без изменений, поменялись только конкретные пороговые значения, с которыми идет сравнение.
Собственно, тут все достаточно очевидно. Есть день (первый параметр в структуре), есть месяц (второй параметр в структуре) и собственно сообщение (третий параметр).
Вы самостоятельно можете дополнить эту структуру собственными памятными датами (дни рождения, годовщины и т.п.)
Теперь при совпадении дня и месяца из структуры и текущего дня и месяца - можно получить актуальное сообщение.
- C помощью Shield MaTrix (SM) выводим следующую информацию:
- текущее время,
- день недели,
- дата,
- температура дома,
- температура "за бортом",
- сообщение о необходимости заменить батарейку (когда это действительно требуется).
- С помощью Sensor Node (SN) получаем и передаем информацию:
- о домашней температуре,
- о температуре "за бортом",
- уровне заряда батарейки модуля.
После нескольких полноценных дней эксплуатации выяснилось, что есть вещи, которые не слишком удобны:
- При разрядке батареи питания одного из сенсоров (попалась "севшая" батарейка среди новых):
- слишком много времени всего цикла отображения занимает назойливая надпись с требованием замены батарейки,
- отображается неверная информация о текущей температуре (SM все время показывает последние полученные данные от датчика).
- Поскольку модуль хотелось использовать как "продвинутые" часы - необходимо увеличить интервал отображения собственно текущего времени.
- Предусмотреть краткое представление даты и дня недели (опять же для более оперативного получения информации).
Опять же (помимо исправлений "недочетов"), всегда "хочется большего", поэтому добавим:
- Индикатор "уровня сигнала" от беспроводных датчиков (показатель скорее качественный и будет анализировать период получения данных от SN).
- Добавим датчик давления и отобразим полученную информацию.
- Построим систему поздравлений (или напоминаний о памятных событиях).
- Более точно будем отображать информацию об уровне заряда элемента питания беспроводного датчика.
- Задействуем кнопку на SM для переключения формата дня недели и даты между "полным" (Понедельник 23 декабря 2013 года) и "кратким" (Пн 23.12.13) представлениями.
Итак, "Поехали!" (с).
Чтобы адекватно отслеживать информацию по активности датчика введем новую структуру:
typedef struct {Собственно, комментарии описывают, для чего нужен тот или иной параметр.
int SensorID;
uint32_t Last; // последнее время коннекта unixtime
int Period; // периодичность отправки данных датчиком (штатная) в секундах
int Batt; // сколько раз сообщили о разряженной батарейке
int Signal; // сколько раз пропустили сообщение
}
dSensorStruct;
Сразу же эту структуру заполним актуальными данными:
dSensorStruct dSensor[2] = {Обратите внимание, что для датчиков мы указываем периодичность отправки данных в 250 секунд (хотя на самом деле отправляем данные один раз в 240 секунд (раз в 4 минуты)) - сделано это специально, чтобы гарантированно отслеживать период отправки.
200, 0, 250, 0, 0,
300, 0, 250, 0, 0,
};
Теперь необходимо немного модифицировать функцию listenRF24() таким образом, чтобы в ней заполнялись данные вышеприведенной структуры:
void listenRF24() {Правки в функции выделены полужирным шрифтом.
waitRF24 = false;
if ( radio.available() )
{
bool done = false;
while (!done)
{
done = radio.read( &sensor, sizeof(sensor) );
// коррекция времени (только если уже получили данные о дате и времени)
if(sensor.SensorID==900 && sensor.ParamID==2 && dataReady) {
syncH=int(sensor.ParamValue/100);
syncM=int(sensor.ParamValue-syncH*100);
syncS=int((sensor.ParamValue-syncH*100-syncM+0.002)*100);
RTC.adjust(DateTime(syncYear, syncMonth, syncDay, syncH, syncM, syncS));
//tone(45, 2000, 200);
dataReady = false;
}
if(sensor.SensorID==900 && sensor.ParamID==1) {
syncYear=int(sensor.ParamValue/100);
syncMonth=int(sensor.ParamValue-syncYear*100);
syncDay=int((sensor.ParamValue-syncYear*100-syncMonth+0.002)*100);
dataReady = true;
}
now = RTC.now();
uTime=now.unixtime();
// датчик 1 (домашний)
if(sensor.SensorID==200) {
dSensor[0].Last = uTime; // обновим время коннекта
dSensor[0].Signal = 0; // поскольку "услышали" датчик, сбросим счетчик "пропущенных пакетов"
if(sensor.ParamID==1) t1in=sensor.ParamValue;
if(sensor.ParamID==2) vcc1=sensor.ParamValue;
if(sensor.ParamID==3) b1=sensor.ParamValue;
if(sensor.ParamID==4) bar=sensor.ParamValue;
if(sensor.ParamID==5) t1bar=sensor.ParamValue;
}
// датчик 2 (домашний & уличный)
if(sensor.SensorID==300) {
dSensor[1].Last = uTime; // обновим время коннекта
dSensor[1].Signal = 0; // поскольку "услышали" датчик, сбросим счетчик "пропущенных пакетов"
if(sensor.ParamID==1) t2in=sensor.ParamValue;
if(sensor.ParamID==2) vcc2=sensor.ParamValue;
if(sensor.ParamID==3) b2=sensor.ParamValue;
if(sensor.ParamID==4) t2out=sensor.ParamValue;
}
}
}
}
Таким образом, после этих подготовительных шагов можно приступать к "визуальным эффектам".
Уровень сигнала
К сожалению, в явном виде уровень сигнала получить от радиомодуля нельзя (или я просто не нашел, как это сделать). Но это совершенно не пугает - придумаем альтернативный вариант оценки качества приема.
Будем оценивать информацию о времени между полученными посылками данных от беспроводных датчиков.
При этом, если интервал получения посылок меньше, чем указано в структуре (параметр Period), то будем считать, что уровень приема - 100%, если же получаем данные чаще, чем 2*Perod - 75%, 4*Period - 50%, 8*Period - 25%. Если же данные получаем и того реже - отображаем 0% и увеличиваем счетчик "пропущенных пакетов".
Реализуем это с помощью функции:
// возвращает символ уровня приема в зависимости от идентификатора датчика
char signalSymbol(int sensor_id) {
char Sym;
now = RTC.now();
uTime=now.unixtime();
uint32_t tDelay = uTime-dSensor[sensor_id].Last;
if(tDelay < dSensor[sensor_id].Period) {
Sym='\xB5'; // 100%
}
else if (tDelay < 2*dSensor[sensor_id].Period) {
Sym='\xB6'; // 75%
}
else if (tDelay < 4*dSensor[sensor_id].Period) {
Sym='\xB7'; // 50%
}
else if (tDelay < 8*dSensor[sensor_id].Period) {
Sym='\xB8'; // 25%
}
else {
Sym='\xB9'; // 0%
// увеличим счетчик "пропущенных пакетов"
dSensor[sensor_id].Signal++;
}
return Sym;
}
Уровень заряда элемента питания
В ходе стресс-тестов SN удалось получить полную кривую разряда батареи, поэтому можно подправить функцию, которая возвращает символ уровня заряда батареи в зависимости от напряжения питания для более корректного отображения статуса батарейки:
// возвращает символ батарейки (уровень заряда) в зависимости от напряжения питания
char battSymbol(float vcc) {
char Sym;
if(vcc > 2.85) {
Sym='\xB0'; // 100%
}
else if (vcc > 2.80) {
Sym='\xB1'; // 75%
}
else if (vcc > 2.75) {
Sym='\xB2'; // 50%
}
else if (vcc > 2.70) {
Sym='\xB3'; // 25%
}
else {
Sym='\xB4'; // 0%
}
return Sym;
}
Переключение форматов даты
Задействуем встроенную кнопку на SM (на фото сверху).
Кнопка уже имеет внешний подтягивающий резистор, поэтому просто включим соответствующий цифровой пин в режим "вход":
// кнопка
pinMode(BUTTON, INPUT);
Для определения режимов, введем бинарную переменную mode (1 - короткие даты, 0 - длинные).
Поскольку полный цикл индикации всех параметров на SM достаточно большой (больше минуты), хочется как-то понимать, что режим отображения даты поменялся после нажатия кнопки.
Задействуем для этого встроенный зуммер: если переключаемся в режим "полного" представления данных - выдадим "длинный" звуковой сигнал, если же переключились в режим "краткого" представления - "короткий" звуковой сигнал.
Чтобы отслеживать состояние кнопки, добавим соответствующий блок в функцию code():
void code(){
// автоматическая регулировка яркости в зависимости от освещенности
brightLcur = analogRead(LightSENS);
if(brightLcur > brightLmax) {
brightLmax = brightLcur;
}
brightL = map(brightLcur, 0, brightLmax, 20, 255);
mymatrix.brightness(brightL);
// обработка ИК-команд
if (irrecv.decode(&results)) {
storeCode(&results);
irrecv.resume();
}
// радио
if (waitRF24) {
listenRF24();
}
// кнопка
if ((millis()>lastButton) && (digitalRead(BUTTON) == LOW)) {
mode = !mode;
if (mode) {
tone(45, 2000, 100); // короткий сигнал
}
else {
tone(45, 2000, 250); // длинный сигнал
}
lastButton = millis()+500;
}
}
В данном случае никак не отрабатывается "дребезг" кнопки, но введена задержка на опрос кнопки не чаще, чем один раз в 0.5 сек.
Поздравления
Чтобы подготовить наш скетч к выдаче поздравительных сообщений необходимо создать структуру:
// структура для поздравлений
typedef struct {
int Day; // день
int Month; // месяц
char Message[256]; // поздравление
}
GreatingsStruct;
и наполнить ее данными:
#define NumMessages 8
GreatingsStruct gMessage[NumMessages] = {
1, 1, "C Ho\x97\xAB\xA1"" \x81""o\x99""o\xA1""!", // C Новым годом!
7, 1, "C Po\x9B\x99""ec\xA4\x97""o\xA1""!", // С Рождеством!
23, 2, "C 23 \xA5""e\x97""pa\xA0\xAF""!", // С 23 февраля!
8, 3, "C 8 \xA1""ap\xA4""a""!", // С 8 марта!
1, 4, "C \x99\xA2""e\xA1"" \x82""ypa\x9F""a!", // С днем Дурака!
1, 5, "C \x99\xA2""e\xA1"" \x97""ec\xA2\xAB"" \x9D"" \xA4""py\x99""a!", // С днем весны и труда!
9, 5, "C \x99\xA2""e\xA1"" \x89""o\x96""e\x99\xAB""!", // С днем Победы!
31, 12, "C Hac\xA4""y\xA3""a\xAE\xA9\x9D\xA1""!", // С Наступающим!
};
Вы самостоятельно можете дополнить эту структуру собственными памятными датами (дни рождения, годовщины и т.п.)
Теперь при совпадении дня и месяца из структуры и текущего дня и месяца - можно получить актуальное сообщение.
Атмосферное давление
У меня не было готового датчика давления, но был электронный компонент BMP085, поэтому я просто развел маленькую платку и сделал датчик для подключения к SN через разъем i2c:
Вы можете поступить так же (изготовить датчик самостоятельно) или приобрести уже готовый датчик.
При выборе датчика обратите внимание на следующую вещь: SN работает от элемента питания с напряжением 3В, а большая часть готовых датчиков рассчитана на работу от 5В (хотя сам сенсор BMP085 работает как раз от 3В и для его питания на плату датчика ставится линейный стабилизатор питания, который понижает "входные" 5В до необходимых 3В).
Наличие стабилизатора не является чем-то особенным, но как правило его эффективнсть крайне мала (т.е. будет неэффективно расходоваться батарейка модуля SN и ее придется чаще менять).
Если же датчик без линейного стабилизатора найти не удастся, то такой датчик можно подключить непосредственно к Shield MaTrix, который тоже имеет i2c-разъем и подключен к источнику питания (а не батарейке, как SN). Естественно, функции чтения данных с датчика нужно будет перенести в скетч SM.
Для этого датчика пришлось чуть-чуть поправить код (добавил пару функций для чтения данных с датчика давления) и расширил структуру с передаваемыми SN данными:
Parameter MySensors[NumSensors+1] = { // описание датчиков (и первичная инициализация)
NumSensors, "SN1 (in)", // в поле "комментарий" указываем пояснительную информацию о датчике и количество сенсоров
0, "TempIN, C", // температура со встроенного датчика
0, "VCC, V", // напряжение питания (по внутренним данным МК)
0, "BATT", // статус того, что батарейка в порядке (0 - батарейка "мертвая", 1 - "живая")
0, "Pressure", // давление (мм.рт.ст.)
0, "TempBAR, C" // температура с барометра
};
Message sensor;
Отображение данных
Теперь все подготовительные шаги сделаны, данные готовы - можно править ту часть скетча, где организован вывод соответствующих параметров.
Это реализовано в основном цикле скетча (функция loop()) - через конструкцию switch - case.
Поскольку эта функция достаточно объемная, в данной записи мы ее приводить и подробно разбирать не будем - смотрите полный код скетча, внутри него достаточно много комментариев, которые помогут разобраться с соответствующими "ветками" переключателя.
Заключение
Таким образом, мы поправили все недочеты предыдущего опыта и добавили новых полезных и приятных функций нашему модулю.
И очевидно, что не стоит останавливаться на достигнутом - далеко не все возможности Shield MaTrix и Sensor Node исчерпаны.
Дерзайте!
Примечание: для корректного отображения "уровня приема" необходимо использовать актуальную версию библиотеки MaTrix.
Архив со всеми необходимыми скетчами находится по ссылке.
Ранее не пользовался USBTinyISP. очевидно поэтому испытываю проблемы с программированием SensorNode. Все установил, в Arduino IDE настроил- выбрал SensorNode, программатор, но при попытке записи скетча avrdude: stk500_getsync(): not in sync: resp=0x00
ОтветитьУдалитьчто посоветуете?