Самодельные часы с крупными символами, которые никогда не придётся настраивать.

Опубликовал | 14.12.2017
В этом посте я расскажу о самодельных часах, которые не потребуют от пользователя вообще никаких настроек. Достаточно подать им питание, и они сами будут заботиться об отображении точного времени. Им не нужен ни интернет, на Wi-Fi, ни подключение по витой паре, ни настройки в менюшках, ни подключение к смарт-домам / смартфонам. Вообще ничего. Просто «включил, поставил на полку и забыл». Идеальные часы? Конечно нет. Но, возможно, кому-то нижеследующий текст будет интересен.

Скажу сразу. Я, в общем, не планировал писать этот обзор.

Частью из-за того, что тема DIY-часов тут периодически поднимается и без меня, частью из-за того, что товара, из-за которого и стоит писать этот обзор, — а именно, самих индикаторов, на Али уже нет. Более того, даже магазин этот удалён.
Так бы и осталось это без внимания, если бы я в одном из комментариев не обмолвился о своей самоделке. В ответ я получил просьбы посетителей (и в комментариях и в личной переписке) рассказать об этой конструкции. Посему решился на написание этого текста. Меня извиняет лишь то, что
а) часы действительно имеют несколько функциональных «изюминок», отсутствующих у других,
б) что на этом ресурсе чаще обозреваются готовые наборы, а вот именно самоделок, с публикацией исходного кода, не так и много,
в) и что управляющий код содержит немало полезного функционала, который может кому-то пригодиться в своих будущих конструкциях (например алгоритм пофазового опроса DS18B20 с порционным питанием, честный расчёт CRC термодатчика, корректную обработку скретчпада датчика на отрицательных температурах, работу с символьным потоком GPS и синхронизацию времени с коррекцией под часовые зоны).

О часах вцелом.
• Часы собраны на популярном контроллере Arduino. Алгоритмические задачи, реализуемые в данных часах, как раз подходят под вычислительные мощности ардуины.
• В качестве источника точного времени для синхронизации часы используют сигналы со спутников GPS.
• Отсутствии сигналов GPS не является препятствием к работе, поскольку часы имеют кнопки ручной установки времени и даже в самом худшем случае они будут просто классическими электронными часами с ручной установкой часов и минут.
• Часы периодически сохраняют время в собственной энергонезависимой памяти, поэтому даже после сбоев питания они начинают отсчёт с последней сохранённой минуты. Да, такое время после сбоя будет неточным, но это явно лучше, чем начинать отсчёт с 00:00. По мере нахождения сигнала GPS, время, без вмешательства, синхронизируется.
• Часы способны работать в любой часовой зоне.
• Часы умеют опрашивать один датчик температуры DS18B20 и периодически отображать температуру.
• Отсутствие или случайный выход из строя датчика температуры не является препятствием к работе, часы просто не будут отображать экран с температурой.
• В часах отсутствует динамическая индикация. Часы не мерцают при отображении информации.
• В часах предусмотрен автоматический режим пониженной яркости для условий тёмной и ночной обстановки. Яркость изменяется аналоговым способом, что избавляет от мерцания индикаторов и в этом случае.
• По возможности, в конструкции я намеренно применял готовые унифицированные модули, что способствует более простому повторению и требует меньшей радиолюбительской подготовки от повторяющих.

Это не первые мои часы. Наверное каждый, кто научился держать паяльник, рано или поздно к этому приходит. Первые мои часы, тогда ещё с отцом, мы собрали в начале 80-х. На серии К155 и с К514ИД1 + АЛС304 на выходе. Потом было много часов «Старт 7176», сегодня уже не скажу, сколько их было собрано, — и себе, и другим. На КР145ИК1901 и с ИВЛ1-7/5, кто помнит. С различными модификациями, коими тогда изобиловал журнал «Радио». В самом начале двухтысячных я открыл для себя микроконтроллеры Атмел, и в ту же пору сделал часы на 2313 (тогда это было не ATtiny, а AT90S) и кингбрайтовских дюймовых семисегментниках, кои вполне живы до сих пор. Описываемые тут часы являются, пожалуй, логичным продолжением этой цепочки. Ещё с самых первых часов я мечтал создать устройство, чтобы ему не надо было устанавливать каждый раз время, и чтобы оно (время) само восстанавливалось после сбоев по питанию. С появлением интернетов и NTP эта мечта стала вполне осуществимой. Однако технология GPS позволила создать устройство, максимально независимое от человека, максимально устойчивое к нештатным ситуациям и максимально самодостаточное.

Сначала о главном элементе часов.
Об индикаторах.
Судя по всему, это была какая-то индивидуальная разработка конкретного китайского подвала. Как я сказал выше, на Али уже нет ни самого лота, ни магазина, мне его продавшего. У меня остался снимок заказа, откуда я покажу фотографии продавца:

Вот так это выглядело на странице продавца



По сути, ничего особенного. Модуль собран на сдвиговом регистре 74HC595, который нагружен сегментами светодиодного индикатора. Сдвиговые регистры хороши тем, что позволяют «стекировать», «собирать» их в цепочки «друг за другом», и, последовательно загружая цифровой код на вход первого, проталкивать его вдоль всей цепочки регистров до последнего. Поэтому, занимая от контроллера всего несколько пинов, мы можем управлять достаточно большим количеством независимых элементов; в данном случае, тридцатью двумя сегментами табло (семь + «точка», умножить на 4). Тем, кому хочется больше узнать про сдвиговые регистры и их применение, поисковик но запросу «74HC595 arduino» выдаст огромное количество информации и примеров.
Главной же интересной особенностью именно этих модулей было то, что питание самих сегментов было организовано не от общей линии питания регистров, а от отдельной колодочки на плате, — там простенькая схемка на паре транзисторов. Раздельное питание сдвиговых регистров и сегментов — довольно неплохая идея. Я этой особенностью попользовался вовсю, организовав ступенчатую регулировку яркости табло, не затрагивая питание регистров.
Я сумел подобрать примерно аналогичный модуль на площадке Таобао. Он стоит ¥8.00 за односимвольный модуль (это чуть больше доллара) плюс локальная доставка. Я не покупал эти модули и не держал их в руках. Но не вижу причин, почему бы они не заработали с описываемыми часами. Они отличаются от применённых мной, прежде всего, цветом свечения (тут красный, у меня синий) — но не проблема и перепаять, при необходимости. И отсутствием дополнительной линии питания. Но зато тут применён драйвер ULN2003A, что вселяет оптимизм в уверенности по нагрузочной способности схемы и в успешную доделку под разделение линий питания сдвигового регистра и сегментов индикатора, буде это кому-то понадобится.
На Таобао сейчас есть даже более прикольная платка — за пять долларов (включая локальную доставку) мы можем получить готовое красное табло с управляемым двоеточием; (подчеркну: это именно табло, без мозгов).
Вкратце познакомлю со своими модулями; тонкие подробности. очевидно, уже не актуальны.

Ширина модуля 29 мм
Высота модуля 40 мм
Высота символа: 30 мм
Питание цифрового интерфейса: 5V
Питание символов индикатора:7-12V

Модули приходят с защитной плёнкой на рабочей плоскости:

С обратной стороны:

Флюс, конечно, не отмыт.
Клеммная колодка установлена с перекосами.
Явно «хижина дядюшки Ляо».
Тем не менее, все надписи присутствуют, а внизу даже нарисована стрелка, показывающая вход и выход последовательного потока загружаемых данных.

Все четыре модуля-индикатора я закрепил на промежуточной пластине-шасси, оставив пробел в половину ширины символа между часами и минутами, чтобы зрительно будущее двоеточие между ними смотрелось более комфортно.

Двоеточие оформил так. Взял прямоугольный кусочек листового чёрного АБС-пластика, просверлил два отверстия, разметив их с тем же углом наклона, что и сегменты на индикаторах. Вклеил в эти отверстия пятимиллиметровые молочные светодиоды и аккуратно сошлифовал дремелем полукруглые линзы диодов. Получилась чёрная плашка с молочным двоеточием. Эту плашку просто приклеил между закреплёнными индикаторами.

Цвет пластика «двоеточия» чуть-чуть не совпадает с чёрным цветом индикаторов, но за тонированным стеклом это не имеет никакого значения. И это явно лучше, чем просто впаянные и открытые светодиоды: во-первых, не видны белые торцы индикаторов, во-вторых нет боковой засветки от светодиодов.

Принципиальная схема часов довольна проста и легко понимаема:

Несколько пояснений по схеме.
1. В качестве ардуины применено Arduino Pro Mini. Но подойдёт любая, хоть Nano, хоть Uno. Я люблю ProMini за то, что их не жалко оставлять в готовых конструкциях, это самая бюджетная модель из линейки ардуино. Покупаю их сразу пригоршню и всегда имею запас «в тумбочке». Новичкам же, наверное, больше подойдёт упомянутые выше Nano или Uno, потому что они сразу, без переходников и сложностей, умеют подключаться к USB «большого брата».
2. Токоограничительные резисторы светодиодов двоеточия надо подбирать так, чтобы при дневном освещении и за тонированным стеклом яркость двоеточия не отличалась от яркости остальных символов. В зависимости от применённых светодиодов, значения этих резисторов могут лежать в пределах от 100 Ом до 6кОм.
3. К пину 3 подключён узел ночного приглушения яркости. Приглушение обеспечивает одноваттный резистор 740 Ом. Его значение подбирается в зависимости от желаемой степени затемнения. Для дневного режима (т.е. режима полной яркости), этот резистор полностью блокируется p-канальным мосфетом, управляемым от пина микроконтроллера. IRF9Z24N применён потому, что он у меня был. Подойдёт и IRF9Z14N, и любой другой, подходящий по параметрам. Почему яркость меняется мощным резистором, а не ШИМ? Чтобы полностью исключить мерцание. Часы питаются не от батарей, а стационарно. В данном случае этот ход оправдан, глаза дороже.
Если вам опция ночного затемнения не нужна, можно просто этот узел не собирать.
4. В качестве датчика освещения применён недорогой модуль на фоторезисторе и компараторе LM393. Порог чувствительности между тёмным и светлым уровнем устанавливается движком подстроечного резистора на модуле. Фоторезистор надо располагать так, чтобы на него не попадал собственный свет от индикаторов часов. У меня он расположен на боковой грани корпуса.
5. О питании в схеме. В силу того, что моим индикаторам требовалось два питания (причём, одно — не очень-то стандартное), я поступил так. Для первой ступени применил замечательные понижающие мини DC-DC преобразователи. (вот лот сразу на 10 штук, можно и поштучно). Важно только сначала выставить движком требуемое напряжение, капелькой лака (цапона или лака для ногтей) зафиксировать движок подстроечного резистора, а уж потом впаивать модуль в схему. Применение DC-DC модуля позволило в качестве источника питания использовать любой ненужный сетевой адаптер в широком диапазоне питающих напряжений (от 9 до 21 вольта). У меня это двенадцативольтовый адаптер от какого-то мертвого сетевого хаба. Для второго стабильного напряжения +5V поставил 7805 (КР142ЕН5А). Потому что он у меня был. Но ничто не мешает поставить такой же DC-DC преобразователь, настроенный на 5 вольт.
6. В качестве приёмника GPS-сигналов используется модуль GY-NEO6MV2. У меня точное время с GPS ловится, пусть и не сразу, в метре от окна в панельном доме далеко не на верхнем этаже за пластиковыми окнами с внутренним металлизированным напылением. Но уже сегодня вполне доступны более современные GPS+ГЛОНАСС модули, те же Neo-M8N, там надо только чуть сконфигурировать их параметры для работы с данным скетчем.

К программной части.

Для тех, кто решит повторить описанную тут конструкцию, я приготовил архив с исходником и всеми необходимыми библиотеками. Из папки «libraries» внутри архива надо распаковать те библиотеки, которых не хватает в папке «libraries» вашей ArduinoIDE.
На случай, если архив протухнет, повторю код ещё и тут:

Исходный код
#include <TimeLib.h>
#include <SPI.h>
#include <EEPROM.h>
#include <MsTimer2.h>
#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <Bounce.h>
#include <OneWire.h>

const int TIMEZONE = 3; //Сюда установить часовую зону
float LATITUDE = 55.5, LONGITUDE = 37.5; // первоначальная позиция для Москвы, район аэропорта Остафьево.
int MaxTimeToSync=500; // Синхронизация каждые 500 секунд
#define MaxScreenTimer 114 // С какой полусекунды каждой минуты будет отображаться температура. Значение 114 -> с 57-й секунды до конца минуты.

#define HourPin 4 // Это контакт кнопки перевода часов
#define MinutePin 8 // Это контакт кнопки перевода минут

Bounce HourButt = Bounce(HourPin,40 );
Bounce MinuteButt = Bounce(MinutePin,40 );

#define DotPin 5 // К этому пину подключены светодиоды в виде двоеточия между часами и минутами
byte DotNightBright = 9; // Это значение яркости зажжённого двоеточия в ночном режиме.

enum { REG_SELECT = 10 }; // пин, управляющий защёлкой (SS в терминах SPI)

#define BrightnessPin 3

TinyGPS gps;
SoftwareSerial ss(9, 7); // К этим пинам подключается GPS-приёмник. Tx->9, Rx->7

#define LightSensorPin 2

#define DSdatapin 6 // К этому пину подключена линия данных датчика DS18B20
#define DSpowerpin 14 // С этого пина подаётся питание на датчик DS18B20

OneWire ds (DSdatapin);

int CurMins;

byte font[23] = {0b11000000, 0b11111001, 0b10100100, 0b10110000, 0b10011001, 0b10010010, 0b10000010, 0b11111000, 0b10000000, 0b10010000, // 0 — 9
0b01000000, 0b01111001, 0b00100100, 0b00110000, 0b00011001, 0b00010010, 0b00000010, 0b01111000, 0b00000000, 0b00010000, // 0. — 9.
0b10111111,0b10011100,0xff}; // -, °

byte Blinking = false;
byte TempPresent = false;
byte DSstatus,DSphase=0;
byte CRC;
byte tempdata[12];
unsigned int tempraw;
float CurrentTemp, DSTemp;

byte DarkNight = 0;

byte Synced = 0;
int TimeToSync=0;

int kbdelay = 0; // этими двумя переменными подавляется дребезг контактов кнопки.
int kbmaxdelay = 500;

int t = 0;
int sc,mm,hh,dd,mn,ye;
byte bb,bo=0;
byte dot=true;

char GPSchar;
float flat, flon;
unsigned long age;
int GPSyear;
byte GPSmonth, GPSday, GPShour, GPSminute, GPSsecond, GPShundredths;
unsigned long GPSage;
byte GPSpresent = false;
byte NeedToShowScreen = false;
byte ScreenPage=0;
int ScreenTimer;

byte sm[9];

void TickEvery05s() // Эта процедура вызывается каждые полсекунды
{
if (dot==true)
{if (Blinking ==true) {Blinking=false;analogWrite(DotPin,0);} else {Blinking=true; if (DarkNight==1) analogWrite(DotPin,DotNightBright); else analogWrite(DotPin,255);}} else analogWrite(DotPin,0);
if (ScreenTimer<120) ScreenTimer++; else ScreenTimer=0;

NeedToShowScreen = true;

if (DSphase==0) {digitalWrite(DSpowerpin, HIGH); } else
if (DSphase==1) {
ds.reset();
ds.write(0xCC);
ds.write(0x44);} else
if (DSphase==4) {
DSstatus=ds.reset();
ds.write(0xCC);
ds.write(0xBE);
CRC=0;
for (byte i = 0; i < 9; i++) tempdata[i] = ds.read ();
digitalWrite(DSpowerpin, LOW);} else
if (DSphase==5) {CRC=ds.crc8(tempdata,9);} else
if (DSphase==6) {
tempraw = (tempdata[1] << 8) | tempdata[0]; // Пересчитываем в температуру
int signBit = tempraw & 0x8000; // Проверяем самый левый бит: 0x8000= 0b10000000 00000000
if (signBit) // Если там единица — число отрицательное и его надо преобразовать
{ // Стандартное преобразование отрицательного числа, которое в микроконтроллере в дополнительной кодировке
tempraw = (tempraw ^ 0xffff) + 1; // Путем исключающего ИЛИ плюс единица
}
float celsius = (float)tempraw / 16.0;
if (signBit) celsius=-celsius; // Если отрицательное число
CurrentTemp=celsius;
if (CRC==0) TempPresent=true; else TempPresent=false;
if (DSstatus==0) TempPresent=false;
}

DSphase++;
if (DSphase>20) DSphase=0;

}

int encodeDigit(char c)
{
int m;
if (c==’-‘) {m=20; } else
if (c==’0′) {m=0; } else
if (c==’1′) {m=1; } else
if (c==’2′) {m=2; } else
if (c==’3′) {m=3; } else
if (c==’4′) {m=4; } else
if (c==’5′) {m=5; } else
if (c==’6′) {m=6; } else
if (c==’7′) {m=7; } else
if (c==’8′) {m=8; } else
if (c==’9′) {m=9; } else
m=22;
return m;
}

void Screen()
{
String b;
char s[16];

if (TempPresent==true)
{
b = dtostrf(CurrentTemp, 3, 0, s);
if (b==" -0") b=" 0";
if (b!=" 0") { if (b.charAt(1)==’ ‘) b.setCharAt(1,’ ‘); else if ((b.charAt(0)==’ ‘) && (b.charAt(1)!=’-‘)) b.setCharAt(0,’ ‘);}
} else
{
b="—";
ScreenPage=0;
}

switch(ScreenPage) {
case 1: {
dot=false;
analogWrite(DotPin,0);
sm[0]=font[encodeDigit(b.charAt(0))];
sm[1]=font[encodeDigit(b.charAt(1))];
sm[2]=font[encodeDigit(b.charAt(2))];
sm[3]=font[21];
break;}
case 0:{
dot=true;
sm[3]=font[(byte)minute()%10];
sm[2]=font[(byte)minute()/10];
sm[1]=font[(byte)hour()%10];
sm[0]=font[(byte)hour()/10];
break;}}

digitalWrite (BrightnessPin, DarkNight);
digitalWrite(REG_SELECT, LOW);
SPI.transfer(sm[3]);
SPI.transfer(sm[2]);
SPI.transfer(sm[1]);
SPI.transfer(sm[0]);
digitalWrite(REG_SELECT, HIGH);

NeedToShowScreen = false;
}

void Synctime()
{
byte monthes [13] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; // Сколько дней по месяцам в невисокосном году.

if (Synced==1) Synced=0;

gps.f_get_position(&flat, &flon, &GPSage);
if ((flat !=TinyGPS::GPS_INVALID_F_ANGLE) && (flon !=TinyGPS::GPS_INVALID_F_ANGLE)) {GPSpresent=true;} else GPSpresent=false;
gps.crack_datetime(&GPSyear, &GPSmonth, &GPSday, &GPShour, &GPSminute, &GPSsecond, &GPShundredths, &GPSage);
if ((GPSage!=TinyGPS::GPS_INVALID_AGE) && (GPSpresent==true) && (GPShour+GPSminute>0))
{
GPShour=GPShour+TIMEZONE;
if (GPShour>23) { // Корректируем время с GPS под часовые зоны восточной долготы. Жители обоих Америк и других стран с отрицательными часовыми зонами перепишут эту процедуру под себя. :)
GPShour=GPShour-24;
if (((GPSyear % 4 == 0) && (GPSyear % 100 != 0)) || (GPSyear % 400 == 0)) {monthes[2]=29;} // Корректируем февраль, если год високосный
GPSday++;
if (GPSday>monthes[GPSmonth])
{
GPSday=1;
GPSmonth++;
if (GPSmonth>12) {GPSmonth=1; GPSyear++;}
}
}
if (flat == TinyGPS::GPS_INVALID_F_ANGLE) {flat=0.0;} else {flat=round(flat*100); flat=flat/100;}
if (flon == TinyGPS::GPS_INVALID_F_ANGLE) {flon=0.0;} else {flon=round(flon*100); flon=flon/100;}
if ((flat !=TinyGPS::GPS_INVALID_F_ANGLE) && (flon !=TinyGPS::GPS_INVALID_F_ANGLE))
{
LATITUDE = flat, LONGITUDE = flon;
if (GPSage>1500) {;} else {
setTime(GPShour, GPSminute, GPSsecond, GPSday, GPSmonth, GPSyear);
Synced=1;
}
}
}}

void setup()
{
mm=EEPROM.read(0); hh=EEPROM.read(1); dd=EEPROM.read(2); mn=EEPROM.read(3); ye=EEPROM.read(4); // При включении устройства читаем сохранённое время. Оно неточное, но это лучше, чем отображать нули.
setTime(hh, mm, 0, dd, mn, ye);
pinMode(DotPin, OUTPUT); analogWrite(DotPin,0);
pinMode(BrightnessPin, OUTPUT); digitalWrite (BrightnessPin, 1);
pinMode(LightSensorPin, INPUT);
SPI.begin();
pinMode(REG_SELECT, OUTPUT);
DarkNight=digitalRead(LightSensorPin);
Screen();
pinMode(HourPin, INPUT_PULLUP);
pinMode(MinutePin, INPUT_PULLUP);
pinMode(DSpowerpin, OUTPUT); digitalWrite(DSpowerpin, HIGH);
CurMins=hour()*60+minute();
DSphase=0;
ScreenPage=0;
ScreenTimer=0;
NeedToShowScreen = true;
CurrentTemp=85;
TempPresent=false;

Synced=0;

MsTimer2::set(500, TickEvery05s); // 500ms period
MsTimer2::start();

ss.begin(9600);
}

time_t prevDisplay = 0;

void loop()
{
bool newData = false;
if (ss.available()){
char GPSchar = ss.read();
if (gps.encode(GPSchar)) newData = true;}
if (newData==true)
{
gps.f_get_position(&flat, &flon, &GPSage);
if ((flat !=TinyGPS::GPS_INVALID_F_ANGLE) && (flon !=TinyGPS::GPS_INVALID_F_ANGLE)) {GPSpresent=true; NeedToShowScreen=true;} else GPSpresent=false;

}
if (TempPresent==true) {if (ScreenTimer>=MaxScreenTimer) {ScreenPage=1; dot=false; analogWrite(DotPin,0);} else {ScreenPage=0; dot=true;}} else {ScreenPage=0; dot=true;}

// Опрос кнопок
if (HourButt. update()) {if (HourButt. read()== LOW) {hh=hour();mm=minute();sc=second();dd=day();mn=month();ye=year(); hh++; if (hh==24) hh=0; setTime(hh, mm, sc, dd, mn, ye); Synced=2; ScreenTimer=0; ScreenPage=0; Screen(); HourButt .rebounce(500);}}
if (MinuteButt.update()) {if (MinuteButt.read()== LOW) {hh=hour();mm=minute();sc=second();dd=day();mn=month();ye=year(); mm++; if (mm==60) mm=0; setTime(hh, mm, sc, dd, mn, ye); Synced=2; ScreenTimer=0; ScreenPage=0; Screen(); MinuteButt.rebounce(500);}}
if (now() != prevDisplay) {
prevDisplay = now();
CurMins=hour()*60+ minute();
if (second() == 0) if (Synced>0) { // Если время не скомпрометировано, то записываем его в EEPROM без секунд.
t=EEPROM.read(0); if (minute() != t) EEPROM.write(0, minute());
t=EEPROM.read(1); if (hour() != t) EEPROM.write(1, hour());
t=EEPROM.read(2); if (day() != t) EEPROM.write(2, day());
t=EEPROM.read(3); if (month() != t) EEPROM.write(3, month());
t=EEPROM.read(4); if (year() != t) EEPROM.write(4, year());
}
TimeToSync++;
if (TimeToSync >= MaxTimeToSync) {
Synctime();
TimeToSync=0;}
}
if ((TimeToSync%10==0) && (Synced == 0)) Synctime(); // Если время ещё не синхронизировано, то пытаемся его синхронизировать чаще.

DarkNight=digitalRead(LightSensorPin);
if (NeedToShowScreen == true) Screen();

delay(1);
}

О работе с DS18B20. В моём решении питание датчику подаётся не постоянно, а только на требуемый для получения и считывания температуры период, оставляя всё остальное время датчик обесточенным. Такой подход сводит к минимуму самонагрев датчика, и, как следствие, завышение считываемых показаний. Вся работа по обслуживанию термодатчика содержится в процедуре, вызываемой по прерыванию таймера каждые полсекунды. Для того, чтобы не задерживать эту процедуру надолго, вся работа разбита на несколько тактов, т.е. каждые полсекунды делается один такт — маленький кусочек этой работы. Такой подход нисколько не задерживает основной цикл (в котором работают другие процессы) и полностью соответствует рекомендуемыми производителем временным интервалам. Так, в первом такте на датчик подаётся питание, в следующем такте (т.е. через полсекунды) датчику отправляется запрос на преобразование, ещё через полторы секунды готовое значение считывается из датчика и снимается питающее напряжение (оно больше не нужно до следующего цикла), в следующем такте рассчитывается контрольная сумма, ещё один такт тратится на пересчёт сырых данных в градусы Цельсия. Оставшиеся семь секунд десятисекундного цикла датчик отдыхает обесточенным.

Реализация расчёта контрольной суммы табличным методом, содержащаяся в стандартной библиотеке OneWire, взята из официального аппнота к DS18B20, но, как оказалось, мало кто сейчас читает официальные даташиты и аппноты. Бóльшая часть исходников к ардуино, доступных в сети, вообще не содержит какого-либо просчёта CRC, и, как следствие, такие «упрощённые» алгоритмы спотыкаются на неудобных значениях (+85°, 0°, +125°). Что довольно удивительно, на мой взгляд.
Вторая проблема, часто встречаемая в доступных исходниках по работе с DS18B20 — это пересчёт отрицательных значений температуры из дополнительной двоичной кодировки. Это удивительно, — ведь, вроде бы, обычная бинарная математика, но, такое впечатление, что половина авторов просто не проверяла свои скетчи на отрицательных температурах. Если ниже нуля ваша программа сразу показывает 65536 градусов, а вы не находитесь внутри Солнца, то гляньте в этот скетч, там всё в комментариях есть.

О синхронизации времени в часах. Как я уже сказал выше, текущее время я периодически пишу в EEPROM. С целью экономии износа ячеек, я не пишу секунды, а часы и минуты пишу только в том случае, если время не скомпрометировано и если значение внутри ячейки не совпадает с записываемой переменной. Более сложных алгоритмов по экономии ресурса EEPROM я не применял: не тот масштаб задачи. При включении питания значение времени читается из энергонезависимой памяти. При этом оно считается скомпрометированным и имеет наименьший приоритет важности. Тем не менее, даже такое время по ряду причин всё равно лучше, чем 00:00. Как только время будет установлено вручную с кнопок, оно получит более высокий приоритет и уже будет считаться приемлемым. Время, полученное с GPS и прошедшее проверку, считается наиболее точным, имеет наивысший приоритет и замещает собой установленное вручную и, уж конечно, считанное из памяти. Если время удачно синхронизировано, последующая синхронизация ожидается неспешно, примерно через четверть часа. В случае, если время скомпрометировано или или всё ещё не синхронизировано после включения, попытки синхронизации предпринимаются гораздо чаще, каждые десять секунд.

О конструктивном оформлении часов.


Я использовал готовый корпус производства Gainta. Дремелем в передней плоскости вырезал прямоугольное окно под индикаторы. На одной боковой грани виден фоторезистор. На другую грань выведены толкатели стандартных тактовых кнопок (установка часов и минут), и разъёмы для подключения термодатчика и адаптера питания (мой адаптер питания имеет штекер 5,5×2,1 и втыкается вот в такое гнездо).
Переднюю сторону корпуса, по хорошему, надо закрыть тоненьким тонированным стеклом.
С советских времён у меня хранилось тонированное в серый цвет оргстекло толщиной 1,5 мм. Его осталось очень мало и на данную площадь его, увы, не хватило. Но сейчас же другое время, чего сейчас нельзя найти? Оказалось, что тонированного оргстекла такой толщины сейчас не найти. Судя по всему, его не существует. Возможно найти тонированный плексиглаc толщиной 3 мм — и это всё. Но в Москве никто не продаст тебе кусочек плексигласа в 15-20 сантиметров. Покупай лист — и никаких гвоздей. Такой плексиглас (стандартными кусками формата A4) возможно купить на ebay, но это довольно дорого.
Решил проблему так. Заказал по размерам обычное прозрачное оргстекло 2 мм (оно-то вполне доступно) и оттонировал его парой слоёв (спереди и сзади) вот такой тонировочной плёнки свётло-серого цвета (покупал дешевле, были скидки). Оттонированное стекло приклеил прозрачным силиконовым герметиком к корпусу. Получилось, на мой взгляд, вполне смотрибельно.

Часы работают уже более года, проблем не выявлено. Было несколько сбоев времени из-за неустойчивого приёма GPS (у меня для приёма условия, всё-таки, довольно экстремальные), но время без вмешательств самовосстанавливалось со следующей синхронизацией. Это действительно конструкция «включил коробочку и забыл». А бóльшего от неё и не требуется.

Главное, ради чего всё это писáлось. Я искренне надеюсь, что моя статья предоставит достаточное количество идей, и информационной помощи тем, кому эта сфера интересна. А, возможно, кому-то послужит недостающим импульсом для начала собственного творчества и реализации собственных идей.

Так, даже данная конструкция предоставляет широкое поле для фантазии и творчества. Мы уже сейчас внутри алгоритма, помимо текущего времени, совершенно бесплатно имеем дату и свои координаты. Мало того, что можно просто выводить эту информацию, из неё ещё можно получать рассветы и закаты Солнца непосредственно для вашей местности, можно получать фазу Луны, день недели, високосность года, рассчитывать Пасхалию и т.д. Осталось ещё шесть свободных пинов, а, значит, можно подключать ещё какие-то датчики или исполнительные механизмы: звуковые сигнализаторы, реле, ключи коммутации, кнопки, беспроводные передатчики/приёмники и т.д.

* Все ссылки по тексту выше — исключительно для иллюстрации. Скорее всего, сегодня аналогичные товары можно купить по другой цене и у другого продавца.

PS: Никто ничего для обзора не предоставлял и не спонсировал. Всё, что описано в конструкции, покупалось в разное время за свои собственные деньги или было найдено в собственных закромах.

Добавить в избранное 0

(c) 2017 Источник материала

Рекламные ссылки