Comments 82
P.S. Никита Сергеевич, для вашего развития, могу порекомендовать к начальному прочтению пару книг по некоторому срезу понимания программирования.
ЛЕО БРОУДИ «СПОСОБ МЫШЛЕНИЯ — Ф О Р Т ЯЗЫК И ФИЛОСОФИЯ ДЛЯ РЕШЕНИЯ ЗАДАЧ»
Л.Броуди «Начальный курс программирования на языке Форт»
Сам же начинал понимние идей заложенных в Форт дизайн языка с С.Н.БАРАНОВ Н.Р. НОЗДРУНОВ «ЯЗЫК ФОРТ И ЕГО РЕАЛИЗАЦИИ»
Одна из популярных Форт-систем для AVR amForth, но для этого и других контроллеров есть и другие Форт системы.
P.S. Здесь подборка другой Форт литературы
а здесь Рускоязычный Форт-форум (активен с 2006г)
Forth Haiku — «Развлекалочка» по мотивам Форт языка
А, Вы его пробовали понять?
P.S. Здесь В Online книги Броуди по основам языка Форт (сейчас, правда, актуален стандарт 94г) приводится пример с сортировкой куринных яиц по размеру. :)
Top 10 languages to learn 2018:

Хотя список и немного спорный имхо, но Форта там по-любому нет.
Изучая «попсу», можно не пoлучить необходимые результаты по времени.
Есть люди, которые смотрят на тренды, а есть кто выбирает свой путь и следует ему. :)
P.S. Здесь Форт по версии EEE на 43-м текущем месте (был на 39), но об нём ещё не забыли
Хотя деньги, конечно, платит энтерпрайз и менеджеры от него.
Что из этого списка Вам действительно было полезно и Вы использовали в своей работе?
А их тут точно десять?
Потом такой роскоши уже не будет.
А учить то, что нужно вот прям сейчас, — так оно уже через 5 лет никому не нужно будет.
В применении к бытовой автоматике более интересна поддержка железа. К ардуине подключить термометр по i2c — несколько строчек кода, например. Если использовать С++. Как это будет выглядеть на forth?
библиотека поддержки выглядит так
P.S. А, здесь, один из примеров её применения в репозитории кода
Ничего сложного, если понимать синтаксис и семантику Форт языка.
Понятно, что раз программа может запускаться на микроконтроллере, и есть доступ к его регистрам — все можно написать. Но в случае С++ и ардуины всё уже написано, и оно под рукой. А ардуино + forth это закат солнца вручную. Искать решения на давно забытых форумах (последний пост в 2008 году) и на покойной rapidshare? Да и в чем профит от натягивания виртуальной стековой машины на процессор с регистрами тоже не совсем понятно.
Работать это будет быстрее. И будет иметь преимущество в переносимости и скорости написания по сравнению с чистым wasm. Еще одним из преимуществ является то, что форт реально красив с точки зрения элегантности технических решений — в процесс изучения можно случайно получить море удовольствия
Изучая только то что изучают другие вы вступаете в очень невыгодную для себя конкуренцию, по возможности избегайте этого.
Надо переименоваться в HHaskell и начать пиарить понятно что.
Загадочный Forth. Знакомимся с одним из важнейших языков программирования, о котором мало кто знает
Forth и arduino — странная комбинация.
Программирование на любом языке высокого уровня не требует знания архитектурных особенностей железа. Это норма. Даже на С можно писать, не задумываясь о том, RISC или CISC платформа будет выполнять этот код.
А по поводу странного — можно просто сравнить количество проектов под Atmega на С, С++ и forth ;) Например, на Хабре. Или на примере объяснить, почему скетч из этой статьи на forth будет выглядеть лучше чем на С++.
Таймер для курятника — достаточно серьезный проект? ;)
А потом что-то пошло не так и С оказалось удобнее.
Но на схеме видна только Ардуино, RTC и Bluetooth. А как собственно свет-то управляется — реле, транзистор, тиристор? И непонятен вопрос питания и электробезопасности — для устройства, работающего от сети, еще и в потенциально сырой среде, это важно.
Если уж управлять светом, то нужен датчик освещенности, чтобы не просто по таймеру его включать, а тогда когда он реально нужен. Ну и термодатчик тогда уж добавить — если в курятнике жарко/холодно, можно посылать уведомление (реальный случай, недавно в одном большом хозяйстве 2000 цыплят сдохли от жары +40).
Пожелания по улучшению кода:
— продумать логику хранения переменных, например глобальная переменная value не нужна, ее нужно объявить там, где она реально используется, это облегчает поддержку и понимание кода.
— продумать работу с EEPROM, например, возможно, не нужно очищать всю память при записи лишь нескольких значений (for (int i = 0; i < EEPROM.length(); i++)). Eeprom кстати имеет ограниченное число циклов записей.
— Не очень понятны строки типа EEPROM.write(EEPROM.read(EEPROM.length() — 1) * 7 + i, Serial.read()); — это по замыслу, запись в конец блока памяти? Зачем делается EEPROM.read, если только что eeprom очищалась нулями? (или это хитрый сдвиг такой?). Кстати, признак хорошего тона в программировании это как раз такие вещи писать в комментариях, это облегчает понимание кода. Также если EEPROM.length() это константа, равная объему eeprom в Arduino, то ее как раз стоило бы объявить глобальной переменной типа int eeprom_len = EEPROM.length(), это облегчило бы читаемость кода и немного его ускорило (быстрее прочитать готовую переменную, чем обращаться каждый раз заново к функции).
Это не придирки, а пожелания на будущее, как сделать еще лучше.
В общем и целом, очевидно, что архитектуры и моделирования работы программы сделано не было. Т. Е., использовался метод проб и ошибок. Но это очень нормально для 14 летнего начинающего программиста.
В качестве улучшений, надо ещё что то сделать с протоколом передачи, одно неловкое движение(чаще передавать, большее количество значений, сбой начала сообщения, хотя его тут как такого и нет. Я понял, что это просто последовательность значений таймеров, причём ни конца ни начала сообщения явно не определяется. ) и все рухнет.
Ну т. Е лучше сделать параметры для каждого таймера отдельно и задавать их отдельно и тогда не надо будет всю EEPROM переписывать при каждой настройке, а только конкретный параметр.
С другой стороны для 14 летнего подростка похвально… да и настраивать не так часто надо. Так что EEPROM на всю жизнь хватит. Молодец!
EEPROM это встроенный класс в Arduino www.arduino.cc/en/Reference/EEPROM, и думаю, length() возвращает объем флеш-памяти на контроллере, т.е. это всегда константа. Проверять лень :)
Стоять пишется через о :)
На схеме не заметил проводков, по которым, собственно, управляется лампочка в курятнике.
Трансформатор в блоке питания — это, думаю, устаревший опыт отца. И еще: не злоупотребляйте перезаписями в флеш-память.
Трансформатор в блоке питания — это, думаю, устаревший опыт отца.
Зато — почти «железобетонный».
Зато — почти «железобетонный».
Не спорю, но добавьте сюда мостик, электролиты, микросхему стабилизатора и сравните с модулем понижающего преобразователя AC-DC объемом около 3 см3 со всевозможными защитами. Совсем не очевидно, что «железобетоннее» в целом.
но добавьте… и сравните с ...
И, весьма вероятно, фэйковой надёжностью. Так-что — варианты для выбора есть.
И, весьма вероятно, фэйковой надёжностью.
Делать вывод о фейковой надежности модуля на том основании, что в его схеме есть «ненадежные» электролиты — сомнительная идея.
Ресурс электролитического конденсатора зависит, в частности, от соотношения между постоянной, переменной (пульсацией) составляющими напряжения на конденсаторе и его номинальным напряжением.
И если разработчик в официальной спецификации приводит величину надежности
(MTBF 100000h), то — эта цифра обоснована.
Другое дело — китайская реализация модуля, впрочем, как и всего остального…
Я ничего не соображаю в электронике, но есть у меня пара советов по организации кода. Организация кода даже в таком маленьком проекте очень важна, программу будет проще понять и даже вы сами когда захотите добавить тот же датчик температуры через полгода, быстрее поймете что же там как работает.
Итак, во-первых и самое главное, в программе мало названий, относящихся к предметной области (управлению устройствами по таймерам), все названия очень асбтрактные и относятся к платформе (values, dititalWrite, EEPROM и т.п.). Нет даже ни одного названия timer. Из-за этого логика работы плохо понятна, даже в такой небольшой программе.
iarduino_RTC time(RTC_DS1307);
Лучше назвать эту переменную clock. На это кстати даже намекает название класса RTC (Real Time Clock)
int values;
Эта переменная используется только внутри функции loop, там её лучше и объявить. Глобальные переменные плохи тем, что их значение может меняться где угодно в программе, из-за этого разные функции оказываются слишком сильно связанны между собой, через глобальную переменную.
Кроме того, название во множественном числе values предполагает массив значений, а тут оно только одно, лучше value или data
Но в целом эта переменная вообще не нужна, ниже будет видно почему
unsigned int to_minutes(unsigned int hours, unsigned int minutes)//функция для перевода часов и минут в минуты
{
return (60*hours)+minutes;
}
Вот например хорошая функция, все понятно что она делает благодаря названиям переменных и самой функции. Ну а коментарий тут вообще ненужен, он ничего нового не объясняет, все итак понятно из кода
if(Serial.available() > 0) //если что-то пришло
Опятьже, такие коментарии объясняющие очевидное только увеличивают размер текста необходимый для прочтения и не несут полезной информации
Функция loop и делает слишком много работы, и устанавливает настройки с телефона в энергонезависимую память, и включает/выключает устройства по таймерам. Лучше разбить её на две, а лучше три или четыре
Например
void storeTimers()
{
for (int i = 0 ; i < EEPROM.length() ; i++)
EEPROM.write(i, 0);
unsigned int timers = 0;
while (Serial.read() > 0) { //пока есть что получать
delay(50);
for(int i = 0; i < 7; ++i) {
EEPROM.write(timers * 7 + i, Serial.read());
delay(50);
}
++timers;
EEPROM.write(EEPROM.length() - 1, timers);
}
}
Как уже правильно написали, логика записи настроек таймеров не очень понятна, скорее всего работает запись настроект только одного таймера.
Далее, комментарий типа
if(EEPROM.read(i * 7 + 0) == 1) //если таймер нужно проверять
он как раз и написан потому что без него вообще непонятно чего к чему. Но, если добавить функцию с подходящим названием, коментарий становится ненужен. Вот например так
bool enabled(int timer)
{
return EEPROM.read(i * 7) == 1;
}
Соответственно проверка превращается в
if (enabled(i))
Гораздо понятнее и не требуется коментарий
Далее, повторяющиеся конструкции вроде
if((to_minutes(EEPROM.read(i * 7 + 1), EEPROM.read(i * 7 + 2)) <= to_minutes(time.Hours, time.minutes)) && //если пришло время для работы
(to_minutes(EEPROM.read(i * 7 + 3), EEPROM.read(i * 7 + 4)) > to_minutes(time.Hours, time.minutes)))
и pinMode(EEPROM.read(i * 7 + 5), OUTPUT); //настраиваем пин таймера как выход
digitalWrite(EEPROM.read(i * 7 + 5), EEPROM.read(i * 7 + 6)); //посылаем на пин нужное значение
тоже вынесем в функции
bool ready(int timer)
{
return (to_minutes(EEPROM.read(timer * 7 + 1), EEPROM.read(timer * 7 + 2)) <= to_minutes(clock.Hours, clock.minutes)) &&
(to_minutes(EEPROM.read(timer * 7 + 3), EEPROM.read(timer * 7 + 4)) > to_minutes(clock.Hours, clock.minutes));
}
void set(int timer, bool invert)
{
pinMode(EEPROM.read(timer * 7 + 5), OUTPUT);
unsigned int val = EEPROM.read(timer * 7 + 6);
digitalWrite(EEPROM.read(timer * 7 + 5), invert ? !val : val);
}
Итого получается следующий код
#include <Wire.h>
#include <iarduino_RTC.h>
#include <EEPROM.h>
iarduino_RTC clock(RTC_DS1307);
unsigned int to_minutes(unsigned int hours, unsigned int minutes)
{
return (60*hours)+minutes;
}
void setup()
{
time.begin();
Serial.begin(9600);
}
// читает текущее время из последовательного порта
void storeTime()
{
clock.settime(0, -1, Serial.read());
delay(50);
clock.settime(0, Serial.read());
}
// читает настройки таймеров из последовательного порта и сохраняет в энергонезависимой памяти
void storeTimers()
{
for (int i = 0 ; i < EEPROM.length() ; i++)
EEPROM.write(i, 0);
unsigned int timers = 0;
while (Serial.read() > 0) {
delay(50);
for(int i = 0; i < 7; ++i) {
EEPROM.write(timers * 7 + i, Serial.read());
delay(50);
}
++timers;
EEPROM.write(EEPROM.length() - 1, timers);
}
}
bool enabled(int timer)
{
return EEPROM.read(timer * 7) == 1;
}
bool ready(int timer)
{
return (to_minutes(EEPROM.read(timer * 7 + 1), EEPROM.read(timer * 7 + 2)) <= to_minutes(clock.Hours, clock.minutes)) &&
(to_minutes(EEPROM.read(timer * 7 + 3), EEPROM.read(timer * 7 + 4)) > to_minutes(clock.Hours, clock.minutes));
}
void set(int timer, bool invert)
{
pinMode(EEPROM.read(timer * 7 + 5), OUTPUT);
unsigned int val = EEPROM.read(timer * 7 + 6);
digitalWrite(EEPROM.read(timer * 7 + 5), invert ? !val : val);
}
void loop()
{
if(Serial.available() > 0) {
storeTime();
storeTimers();
}
//проход по каждому таймеру
int timers = EEPROM.read(EEPROM.length() - 1);
for(int timer = 0; timer < timers; ++timer) {
if(enabled(timer)) {
clock.gettime();
if(ready(timer))
set(timer, false);
else {
bool found = false;
//ищем таймер с таким же пином
for (int t = 0; t < timers && !found; ++t) {
if ((t != timer) && enabled(t) && ready(t) &&
EEPROM.read(timer * 7 + 5) == EEPROM.read(t * 7 + 5)) {
set(t, false)
found = true;
}
}
if(!found)
set(timer, true);
}
}
}
}
Примерно так. Такой код намного легче понять и в нем не требуются коментарии, потому что все функции маленькие и имеют названия из предметной области. Надеюсь будет полезно, удачи.
PS: Имя, ник и теги огонь
Для полного кайфа лучше таймер как сущность вынести в struct. Сходу, правда, не скажу, можно ли в ардуине читать и писать на флеш сразу пачку байт, но если можно, то код станет ещё куда читабельнее и удобнее.
Автору же хотелось бы ещё посоветовать, если есть желание заниматься электроникой и дальше, отходить от fritzing и потихоньку осваивать полноценные принципиальные схемы — они тоже куда читабельнее и понятнее в роли документации к проекту :-)
Если есть, тогда вообще отлично :-)
Говоря про архив проекта — попробуйте использовать Github или Bitbucket. Так большему числу людей будет удобнее видеть код (например, я сейчас на работе и скачивание с яндекса заблокировано). Плюсом также — вся история изменений в проекте (если что-то сломалось или стало работать не так, можно найти, из-за чего), возможность вести список задач и проблем, собирать багрепорты, получать улучшения и доработки от других в виде пулл-реквестов.
ESP32? Там уже блютус встроен, плюс можно было поставить модуль камеры, датчик температуры, а в приложении настроит получение фотоотчета.
Предлагаю дальнейшее развитие. Курам для увеличения яйценоскости увеличивают световой день и таймер для этого — хорошая штука. Но при этом хорошо бы и поставить себя на место если и не курицы, то хотя бы петуха — резкое отключение света — стресс. У меня в курятник заходят по мере захода солнца и не все сразу, а сильно по очереди. При этом садятся на насест пока видят. С учетом этой физиологической особенности (ака куриная слепота) хорошо бы сделать плавное гашение света в течении длительного времени, от 15 до 30 минут. Включение тоже было бы неплохо плавным, но не обязательно таким длительным, а, к примеру, в течении трех минут
Тут уже вопрос к силовой части, которая в проект не вошла. Если там просто реле, то особо яркостью не порулить.
Ради эксперимента делал диммер для светодиодной лампы на цоколе Е27. Конструкция работает безупречно, разве что дороговата для простого диммера и размерами великовата).
Не совсем переменник там. Переменник там бы очень дико грелся и не очень долго работал :-)
Там стоит тиристор и компаратор, который при нарастании напряжения по модулю выше какой-то точки отключает выход. То есть получаем на выходе не синусоиду, а как бы её огрызок.
Так как средней светодиодной лампе с её импульсником на входе до чисто активной нагрузки как пешком до Китая, то получаем полный набор спецэффектов от нерегулируемости и до мигания как накуренный стробоскоп на дискотеке.
Зато таким диммером, оставшимся после ремонта, вполне себе можно регулировать старый советский паяльник ватт на 40-60 — только термометр прикрутить, и колхозная паяльная станция готова :-)
Там стоит тиристор и компаратор, который при нарастании напряжения по модулю выше какой-то точки отключает выход.
Включает выход. Т.к. тиристор (симистор) — проще включить, чем выключить. Выключение делается автоматически, за счёт перехода сети через ноль.
… у меня дома в диммере стоит обычный переменный резистор… Ограничение нагрузки (согласно надписи) — 150Вт.
Не может быть потому, что для ограничения мощности на нагрузке до 75 Вт — на резисторе придётся сжечь сравнимую мощность.
Курам для увеличения яйценоскости увеличивают световой день ...
Уточню — для увеличения производительности кур сутки в помещениях курятников птицефабрик без естественного освещения делят на два «световых дня». В результате на выходе — два яйца вместо одного.
Продолжительность жизни курицы при таком режиме (0,5 года) — не в счет.
Извините за повтор — не там разместил пост.
Курам для увеличения яйценоскости увеличивают световой день ...
Уточню — для увеличения производительности кур сутки в помещениях курятников птицефабрик без естественного освещения делят на два «световых дня». В результате на выходе — два яйца вместо одного.
Продолжительность жизни курицы при таком режиме (0,5 года) — не в счет.
Извините за повтор — не там разместил пост.
2. Нет ли ложных срабатываний при переключении реле? У меня с этим были проблемы одно время в проекте wi-fi розетки www.youtube.com/watch?v=QAulFZaALm4
3. Влияет ли использование в коде функции delay(50), которая подвешивает код, на производительность и таймер?
2. Обнаружены не были.
3. Все окей.
Но удобно же. Благодаря встроенному WIFI — -1 модуль.
Очень много памяти (по сравнению с ардуиной), можно, к примеру, рассчитать заранее по дням время восхода-заката солнца, и включать свет когда это действительно нужно.
Нужно немного упороться в астрономию. Или взять готовую, уже рассчитанную таблицу. Или спросить у openweathermap…
Но лично я сейчас больше предпочитаю esp8266(nodemcu). Там тоже wifi на борту.
Курям, нужен день продолжительностью 14ч, так что это прекрасно автоматизируется. Вынести датчик освещенности вне курятника и записывать время рассвета, при закате вычислять продолжительность дня, если меньше 14 — то включать освещение для компенсации недостатка.
Arduino таймер