Часы своими руками [DIY] Part II - Final
Доброго всем вечера!
Знаете, сначала хотел сделать отдельно пост по софту, и отдельно по железу.
Но передумал, и забегая наперёд, в конце вас ждут ссылки на печатную плату и скетч, а пока что слой объяснения "Что, куда, зачем?".
И заранее маленькое лирическое отступление.
Этот проект получился случайно, чисто часами это назвать нельзя, нельзя и метеостанцией назвать, это, скорее, демонстрационный стенд.
Началось всё с того, что я захотел сделать часы, и мне понадобился термометр для ванны, таким образом параллельно были написаны обе программы.
Но постоянно менять скетчи местами надоело, поэтому и был написан скетч, совмещающий в себе оба экрана. И засунул до кучи ещё один, который уже был, где выводилась информация о температуре, давлении и влажности.
После я решил сделать печатную плату, и уместить туда весь получившийся пучок проводов.
Итак, использовались (фото не мои, но представленные на них платы, или модули в точности такие) следующие элементы:
- Китайский клон Arduino
- LCD знакогенерирующий, 4 строки по 20 символов
- i2c адаптер для дисплея, позволяющий подключить дисплей по 2 проводам
- Модуль часов реального времени DS1307 с автономным питанием
- Наипаршивейший (!!!) датчик влажности и температуры DHT11
- Прекрасный датчик температуры и давления BMP180
- Выносной датчик температуры DS18B20
- Фоторезистор
Ну, и окинув взглядом всю эту банду, понимаем, что только два товарища не работают по i2c.
Объяснять не буду. Объясняльщик теории я так себе, поверьте.
Всё, что я скажу, что эта шина позволяет подключать к одной линии данных кучку устройств, при условии, что у них у всех разные адреса. Поэтому я взял паяльник, и спаял переходник и мини-шилд.
Мини-шилд предназначен для управления подсветкой, а так как фоторезист у меня на руках всего один, то выпаивать я его из часов не стал. Штырьковые коннекторы дополнительно выводят питание и землю.
Ну а переходник позволил подключить все i2c устройства в вот такую "красоту":
Ну и ладно, оно же пашет! А для прототипа что ещё нужно!?!
Ну и DHT11 был подключен на макетке, которая влезает в Arduino Uno.
Как я ранее упоминал, я использовал среду разработки Arduino, версию использую 1.6.5 (последнее время они что-то изменили внутри всего этого безобразия, и добрая часть библиотек накрылась медным тазиком).
Ну а теперь сам код.
Весь я его сюда, конечно, пихать не буду, остановлюсь лишь на нескольких моментах.
Объяснять, как опрашивать датчики тоже не буду, см. выше, а если лень, то я хреново объясняю теорию.
_________________________________________________________________________________
void loop()
{
newBackLigth(map(analogRead(0), 0, 1023, 3, 255));
if (millis() > watchTime) showClock();
if (millis() > tempoMillis) tempo();
}
Это всё, что у меня есть в loop.
Задаю новую подсветку,
Если пришло время обновить часы, обновляем,
Аналогично с температурой
_________________________________________________________________________________
void newBackLigth(byte newBLnum)
{
int dif = nowBLnum - newBLnum;
if (abs(dif) > 25)
{
for (; nowBLnum != newBLnum; dif < 0 ? nowBLnum++ : nowBLnum--)
{
analogWrite(LEDpin, nowBLnum);
delay(5);
}
}
}
Этой функции мы скармливаем значение новой подсветки, считанное из нулевого аналогового порта, и конвертированное из диапазона от 0 до 1023 в диапазон от 3 (чтобы в полной темноте было видно) до 255.
Далее вычисляем разницу между текущей подсветкой и новой,
Смотрим на эту разницу по модулю, и если она больше 25, то
Постепенно наращиваем подсветку.
_________________________________________________________________________________
void showClock()
{
watchTime = millis() + 1000;
updateWatch();
if (mode == 0) bigWatch();
if (mode == 1 || mode == 2) smallWatch();
if (h == 0 && m == 0 && s < 2) showDate();
}
Тут я запоминаю, когда я должен в следующий раз сюда зайти,
Обновляю время,
Если 0 режим - чешем в функцию отрисовки больших часов,
Если 1, или 2, то рисуем маленькие часы (да-да-да, тут можно было юзать else),
Ну и если начался новый день, обновим дату, которую, к слову, мы уже показали в setup.
_________________________________________________________________________________
updateWatch ничего интересного из себя не представляет, она лишь загоняет в глобальные переменные количество часов минут и секунд. К слову, если считать время не удалось, то мы получим 165 вместо времени. Этим и воспользуемся тут. Но забудем воспользоваться этим в больших часах, и вспомним, когда будем пилить длиннопост.
void smallWatch()
{
lcd.setCursor(15, 3);
if (h == 165) lcd.print("ERROR");
else
{
if (h != hl)
{
hl = h;
if (h < 10) lcd.print("0");
lcd.print(h);
}
s % 2 == 0 ? lcd.print(" ") : lcd.print(":");
if (m != ml)
{
ml = m;
lcd.setCursor(18, 3);
if (m < 10) lcd.print("0");
lcd.print(m);
}
}
}
Тут всё просто. Выходим на исходную позицию,
Если 165, вместо часов, тупо пишем "ошибка", и валим оттуда,
Иначе смотрим поменялось ли количество часов, если нет, проходим мимо,
Если сменилось количество, запоминаем этот факт,
Смотрим нужно ли добавить "0" перед количеством часов, если нужно добавляем,
И пишем количество часов.
Потом проверяем количество секунд на чётность, и, если количество чётное,
пишем двоеточие, иначе, стираем его пробелом.
Делаем с количеством минут то же самое, что и с количеством часов чуть ранее.
Кстати, чтобы часы и минуты точно были написаны при включении я просто записал в переменные для сравнения 99. Попробуй, блин, не обнови!
_________________________________________________________________________________
void bigWatch()
{
if (h != hl)
{
hl = h;
numBuilder(h / 10, 0);
numBuilder(h % 10, 4);
}
if (m != ml)
{
ml = m;
numBuilder(m / 10, 10);
numBuilder(m % 10, 14);
}
if (s % 2 == 0)
{
lcd.setCursor(8, 0);
lcd.write(4);
lcd.setCursor(8, 2);
lcd.write(1);
}
else
{
lcd.setCursor(8, 0);
lcd.write(130);
lcd.setCursor(8, 2);
lcd.write(130);
}
lcd.setCursor(18, 2);
if (s < 10) lcd.print("0");
lcd.print(s);
}
Тут примерно то же самое, только теперь при несовпадении старых значений с новыми, мы запоминаем новые, и разбиваем их на десятки и единицы, после чего скармливаем с какой-то цифрой новой функции с именем numBuilder!
А ещё теперь смотрим на чётность секунд и рисуем большие точки, либо затираем их пустыми 130-ми символами. Да, так можно!
А ещё пишем количество секунд. Именно тут, в принципе, тоже надпись "ERROR" замутить, но не сейчас...
_________________________________________________________________________________
void numBuilder(byte num, byte pos)
{
if (num == 0) for (byte i = 0; i <= 8; i++)nowNumber[i] = number0[i];
if (num == 1) for (byte i = 0; i <= 8; i++)nowNumber[i] = number1[i];
if (num == 2) for (byte i = 0; i <= 8; i++)nowNumber[i] = number2[i];
if (num == 3) for (byte i = 0; i <= 8; i++)nowNumber[i] = number3[i];
if (num == 4) for (byte i = 0; i <= 8; i++)nowNumber[i] = number4[i];
if (num == 5) for (byte i = 0; i <= 8; i++)nowNumber[i] = number5[i];
if (num == 6) for (byte i = 0; i <= 8; i++)nowNumber[i] = number6[i];
if (num == 7) for (byte i = 0; i <= 8; i++)nowNumber[i] = number7[i];
if (num == 8) for (byte i = 0; i <= 8; i++)nowNumber[i] = number8[i];
if (num == 9) for (byte i = 0; i <= 8; i++)nowNumber[i] = number9[i];
for (byte i = 0; i <= 8; i++)
{
if (i == 0) lcd.setCursor(pos, 0);
if (i == 3) lcd.setCursor(pos, 1);
if (i == 6) lcd.setCursor(pos, 2);
lcd.write(nowNumber[i]);
}
}
А вот и моя радость! В общем, именно эта функция и рисует большие цифры!
Она получает значения цифры и позиции (откуда начинать рисовать самый верхний левый символ).
В зависимости от того, какую именно цифру мы хотим отобразить, выбираем соответствующий массив, и загоняем его в "буферный массив". Для каждой цифры я сделал массив с номерами символов в дисплее, из которых мы можем получить цифру. Размер цифры 3 на 3 символа, то есть 9, таким образом, девять номеров этих самых символов пойдут в массив, и в любом случае оттуда попадут прямо на дисплей!
_________________________________________________________________________________
В температурных экранах тот же принцип, по сути, запоминаем, когда можно залезть туда снова, в зависимости от режима, уходим в разные экраны.
Только несколько моментов:
- TempBigNum я перепишу, так как у меня появилась новая фитча, расскажу о ней позже.
- там же есть возможность работы с минусовой температурой, если она таковая, я рисую "минус", умножаю температуру на (-1) и спокойно отрисовываю.
- там же я отрисовал из доступных мне символов слово "ERROR":
А ещё иконку влажности перерисовал! Прям красота получилась!
_________________________________________________________________________________
Ну и по поводу влажности. Вот калькулятор абсолютной влажности.
Так как DHT11 возвращает только относительную влажность, необходимо иметь возможность высчитать абсолютную, чтобы не нагнать в гараж более влажный воздух по факту, когда относительно насыщенного пара он кажется суше. Всё просто, скармливаем функции 3 параметра: температуру, давление, влажность в %, она в ответку выплёвывает абсолютную влажность. Я очень долго бился над этой функцией, и я её доделал. К слову, она жрёт ОЧЕНЬ МНОГО памяти. Ну прям очень. Кстати, ей можно скармливать давление и в миллиметрах ртутного столба и в паскалях. Очень полезная фитча!
float CAH(float temperature, float pressure, float humidity)
{
// float A1 = (pressure * 133.322) / 100; // if pressure in mmHg
float A1 = pressure / 100; // if pressure in Pa
float A2 = 6.112 * exp((17.62 * temperature) / (243.12 + temperature));
float A3 = 1.0016 + (3.15 * pow(10, (-6))) * A1 - 0.074 / A1;
float A4 = A2 * A3;
float A5 = (humidity / 100) * A4;
float A6 = temperature + 273.15;
float A7 = A5 * 100 / (461.5 * A6) * 1000;
return A7;
}
_________________________________________________________________________________
Программа есть, скетч написан, адаптирован под использование БЕЗ i2c адаптера для LCD, проверени готов к заливке. Но... КУДА???
Чтож, раз уж просили, держите, но я сразу говорю, не торопитесь! Тут часы не настроить без прошивки. Причём 2 раза! Причём, приблатыкиваться надо! Подождите немного, там и будильник будет, и аккумулятор добавлю, и часы в разы точнее.
Развожу я платы в программе Sprint-Layout 6, всё нравится.
По сути ничего сложного нет. Размещаем отверстия, проводим дорожки, заканчиваем, смотрим фотовид, материмся на то, что некоторые элементы зазеркалили, переделываем 20 раз, рисуем обозначения, создаём макросы... Короче говоря, набиваем руку!
Далее идёт засверливание (по крайней мере у меня) на фрезерном станке с ЧПУ.
После чего МИР РАЗЛАМЫВАЕТСЯ НА ДВЕ ЧАСТИ!
Но не для меня. Я уже определился с выбором, и я использую фоторезист (не путать с фоторезистором!), кто-то использует ЛУТ.
В общем, ЛУТ расшифровывается, как лазерно-утюжная технология, когда дорожки зеркально печатаются на бумаге, с помощью утюга переносятся на текстолит, и травится.
У меня принтер струйный, поэтому фоторезист сам выбрал меня. А я и не против!
В общем, печатаем фотошаблон!
Накатываем фоторезистивную плёнку на текстолит, сверху фотошаблон, если есть отверстия, совмещаем. Я засвечивал фоторезист минуту. А так как поверхность слегка клейкая, сверху ничем прижимать не нужно.
После засветки снимаем защитную плёнку с фоторезиста, и купаем в растворе кальцинированной соды, лишнее слезает, но качество фотошаблона подводит...
Фото как раз перед ванной!
Точки, где не надо - это просветы. В принципе, от большинства избавился, но проблемные места до сих пор нахожу... Хорошо, что не коротит!
Начинаем травить!
Медь начинает поддаваться!
Уже почти готово. Пара минут, и медь сдалась.
Снимал остатки в более концентрированном растворе кальцинированной соды, травил в перекиси водорода с лимонной кислотой.
_________________________________________________________________________________
Ну и дело за малым, залудить, распаять элементы, и зашить программу.
Прошивал с помощью этого красавца:
В конце только выставил фьюзы, как на Arduino Uno, и всё работает.
Лично я фьюзы выставляю в Extreme Burner - AVR.
_________________________________________________________________________________
Ну и возвращаясь к разговору о комплекте для сборки.
Комплект получается достаточно дорогой - около 2000.
Плюсы: это цена с доставкой, программатором, печатной платой, и дисплеем с кириллицей и янтарной подсветкой. Если желание не отпало - отпишитесь!
Скетч - https://cloud.mail.ru/public/6CKU/wJhoUHikk
Плата - https://cloud.mail.ru/public/5ejN/VR8Xgmn3G
К слову, я уместился в 50 блоков и pikabu больше не даёт мне ничего добавить, а мне и добавить, по сути, нечего. Всем удачи!