Русский
Русский
English
Статистика
Реклама

Максимально универсальный семисегментный дисплей. Часть вторая Software

КДПВ


<irony>
Не прошло и полугода Но зато конструкция прошла проверку временем!
</irony>

В продолжение первой части о проектировании максимально универсального семисегментного дисплея сделаем на получившихся модулях первое, что приходит в голову конечно же часы! Так что это очередная статья про очередные часы. Без кнопок, на ESP8266, на NodeMCU и Lua. Кому до сих пор интересно прошу под кат.

Кусочек hardware


Для создания часов требуется четырехразрядный индикатор (или шести, если отображать еще и секунды). Так как часы планируются полностью автономными настенными я решил делать их из двух модулей по два трехдюймовых индикатора. В наличии такие были красные с общим анодом, так что устанавливаем элементы master-платы согласно первой части статьи, для slave-платы устанавливает только боковой разъём и индикаторы. Соединяем вместе и вперед программировать!



Стартуем с NodeMCU


Писать на arduino-вых скетчах мне не позволяет религия, извините, а bare-metal прошивка под ESP8266 для данной задачи это явно перебор. Так что выбор вполне логично пал на NodeMCU и скриптовый язык lua. Вкратце, что такое NodeMCU это открытый бесплатный проект на основе lua, имеющий отличную гибкость и достаточную мощность, что позволяет быстро и эффективно создавать разнообразные проекты. NodeMCU модульная прошивка, а это значит, что можно собрать вариант конкретно под свой проект без лишних модулей. Благодаря обширной комьюнити NodeMCU уже умеет работать с разными протоколами обмена данных поверх WiFi (HTTP, MQTT, JSON, CoAP), периферией, с несколькими десятками популярных датчиков, с дисплеями, и даже умеет в файловую систему FatFS.

Для того, чтобы собрать прошивку под свой проект переходим на сайт www.nodemcu-build.com, вводим свою электронную почту, отмечаем галочками нужные модули и жмем Start your build.

Shit happens
Мне несказанно повезло и все мои модули ESP-07 оказались с флешем 512кБ на борту. Хотя по документации, описанию на сайте продавца и фото в интернете должно быть 1Мб. В связи с чем я целый вечер искал причину, почему модуль или не шьется вовсе или шлёт мусор в СОМ-порт при включении неистово мигая синим светодиодом. Оказалось master branch NodeMCU требует от 1 Мб флеша. Для таких же счастливчиков, как я нужно поставить галочку на сайте рядом с branch-ем версии 1.5.4.1 это финальная версия, которая работает с 512кБ.

Для часов нам потребуется минималистичный набор модулей:
wifi окно во внешний мир
enduser_setup удобный интерфейс для подключения к сети WiFi
file проект будет состоять из разных файлов, нужно уметь с ними работать
gpio дергать ножками
net модуль сетевого клиента
rtctime часы реального времени
sntp синхронизация часов по сети, кнопок то нет
spi интерфейс для MAX7219
tmr таймеры

Отметили, нажали на большую синюю кнопку и ждем пару минут, пока на почту упадет ссылка на готовый бинарный образ для заливки в контроллер. Система просто отличная.

Для заливки образа, как и для сохранения lua-скриптов используется UART. Для подключения внешнего адаптера USB-to-UART (3.3V!) используется разъём J3 UART. Как упоминалось в первой части, на плате присутствует посадочное место под преобразователь CH340. В случае его использования все общение с контроллером (и питание платы) будет производится через порт USB на плате. Удобно если проект требует частых изменений или длительного процесса разработки программы. Для переключения в режим записи во флеш нужно предварительно установить на плате перемычку J4. Скорость UART 115200 бод, номер правильного СОМ порта оставляю на вас.

Для прошивки образа рекомендую утилиту NodeMCU-PyFlasher. Возможно, она покажется не такой простой как популярная NodeMCU-Flasher, но является более универсальной и помогает в ситуациях, когда NodeMCU-Flasher просто молча глохнет при попытках прошивки.

Конкретнее
В некоторых непонятных ситуациях при смене обычной прошивки на NodeMCU модули на ESP8266 перестают правильно инициализироваться. Это лечится или предварительной зашивкой файла esp_init_data_default.bin по адресу 0x7C000 или установкой галочки Erase Chip в NodeMCU-PyFlasher.

Процесс успешной заливки образа должен выглядеть следующим образом:


Теперь перемычку J4 можно снять, перезапустить плату и начать писать скрипты в программе ESPlorer. Я не преследую цели написать курс по программированию на lua, эта тема хорошо освещена на многих ресурсах. Лично от себя могу дать рекомендацию на блог avislab там понятным языком написана целая серия статей, в которых освещаются вопросы от азов до общения с облачными хранилищами.
Ниже приведу минимальный набор скриптов для реализации вполне себе функциональных (показывающих время!) часов, требующих только стартовой настройки подключению к сети WiFi. Часики прошли уже проверку временем, все работает отлично, не сбоит, за более чем полугода работы зависли один раз, как я понял, через проблемы с интернетом, полечились простым перезапуском.

Библиотека по работе с MAX7219 - max7219.lua

local spi_index = 1;local cs_pin = 3;-- MAX7219 SPI Master Initializationfunction max7219_spi_init()    print('SPI init');        spi.setup(spi_index, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 16, 80, spi.HALFDUPLEX);    gpio.mode(cs_pin, gpio.OUTPUT, gpio.PULLUP);    gpio.write(cs_pin, gpio.HIGH);end-- MAX7219 Outputfunction max7219_output(digit, value)    local data = digit*256 + value;    gpio.write(cs_pin, gpio.LOW);    spi.send(spi_index, data);    gpio.write(cs_pin, gpio.HIGH);end-- MAX7219 set intensityfunction max7219_intensity(value)    local data = 0x0A00 + value;    gpio.write(cs_pin, gpio.LOW);    spi.send(spi_index, data);    gpio.write(cs_pin, gpio.HIGH);end-- MAX7219 Initializationfunction max7219_init(digits, intensity)    print(string.format("MAX7219 init for %d digits", digits));        gpio.write(cs_pin, gpio.LOW);    -- Display test mode off    spi.send(spi_index, 0x0F00);    gpio.write(cs_pin, gpio.HIGH);        gpio.write(cs_pin, gpio.LOW);    -- Normal Operation mode    spi.send(spi_index, 0x0C01);    gpio.write(cs_pin, gpio.HIGH);        gpio.write(cs_pin, gpio.LOW);    -- Intensity duty cycle     -- [min 0x0A00 .. 0x0A0F max]    spi.send(spi_index, 0x0A00 + intensity);    gpio.write(cs_pin, gpio.HIGH);        gpio.write(cs_pin, gpio.LOW);    -- Decode-Mode     -- [0 - no decode, 1 - B-Code mode]    spi.send(spi_index, 0x09FF);    gpio.write(cs_pin, gpio.HIGH);        gpio.write(cs_pin, gpio.LOW);    -- Scan-Limit Register Format    spi.send(spi_index, 0x0B04);    gpio.write(cs_pin, gpio.HIGH);    -- Set blank as default    for d=0, digits do         max7219_output(d, 0x0F);    endendcollectgarbage();


main cкрипт - init.lua
local point = 0;local time_zone = 3;local sntp_cnt = 1;local cur_intensity = 0x0F;function timer_do()    tm = rtctime.epoch2cal(rtctime.get());    if point == 0 then point = 1; else point = 0; end;    max7219_intensity(cur_intensity);    max7219_output(5, tm["min"]%10);    max7219_output(4, tm["min"]/10);    max7219_output(2, tm["hour"]%10 + (128*point));    max7219_output(1, tm["hour"]/10);    if tm["hour"] <= 7 then        -- from 0 to 8        cur_intensity = 0x01;    else         if tm["hour"] <= 18 then            -- from 8 to 19            cur_intensity = 0x0F;        else            if tm["hour"] <= 22 then                -- from 19 to 22                cur_intensity = 0x05;            else                -- from 23 to 24                cur_intensity = 0x01;              end        end    endendfunction sntp_sync()    print ("SNTP sync");    sntp.sync("194.54.161.214",        function(sec, usec, server, info)            rtctime.set(sec + 3600*time_zone)            tm = rtctime.epoch2cal(rtctime.get());            print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]));            sntp_cnt = 4320;        end,        function(err, str)            print("Nope...")        end    )endfunction timer_sntp()    if sntp_cnt > 0 then        sntp_cnt = sntp_cnt - 1;    else        if wifi.sta.status() == wifi.STA_GOTIP then             print("Connected to WiFi as:" .. wifi.sta.getip());            sntp_cnt = 6;            sntp_sync();        else             print("No WiFi");         end;    endendrequire("max7219");max7219_spi_init();max7219_init(5, cur_intensity);rtctime.set(1577872800 + 3600*time_zone);tm = rtctime.epoch2cal(rtctime.get());print(string.format("%02d:%02d:%02d", tm["hour"], tm["min"], tm["sec"]));enduser_setup.start(  function()    print("Connected to WiFi as:" .. wifi.sta.getip())    sntp_sync();  end,  function(err, str)    print("enduser_setup: Err #" .. err .. ": " .. str)  end);local mytimer = tmr.create();mytimer:register(500, tmr.ALARM_AUTO, timer_do);mytimer:start()local sntp_timer = tmr.create();sntp_timer:register(10000, tmr.ALARM_AUTO, timer_sntp);sntp_timer:start()collectgarbage();


Файл для шаринга параметров enduser_setup - enduser_setup.lua
local p = {}p.wifi_ssid="ssid"p.wifi_password="password"-- your own parameters:p.utc_zone="xxx"return p


Во флеш контроллера также нужно залить страницу enduser_setup.html с интерфейсом подключения к сети WiFi.
Несмотря на такой компактный скрипт часы действительно получаются функционально законченными. Реализован следующий сценарий: при включении, на основе enduser_setup модуля создаётся открытая WiFi-точка с названием SetupGaget_xxx.


При подключении к которой и попытке перейти по какому-либо адресу (или просто по 192.168.4.1) открывается интерфейс подключения к доступным сетям.


Такая себе landing-page, куда нужно ввести название сети и пароль. Можно ввести вручную или выбрать из списка доступных. При нажатии на кнопку контроллер пытается подключится к выбранной сети и в случае успеха выводит радостное сообщение и отключает WiFi-точку. Дополнительно я добавил на страницу настройку часового пояса.


После подключения к Интернету часы синхронизируются с сервером точного времени по протоколу SNTP и начинают тихо выполнять свою основную функцию отображать время на дисплее, помигивая точкой второго разряда.
Буквально в несколько строчек можно добавить периодическую синхронизацию времени и изменение яркости в зависимости от времени суток. Если вы счастливый обладатель модулей с 512кБ памяти придется писать проверками, как в коде выше, если же есть возможность использовать master branch версию рекомендую использовать модуль простого планировщика событий cron. Аналогично и с функцией изменения яркости дисплея, которая выше также реализована на банальных проверках.

cron
cron.schedule("0 */12 * * *", function(e)  print("Every 12 hours");  sntp_sync();end)



Сразу прошу прощения за фото, съемка ярких светодиодных индикаторов оказалась той еще задачей, даже при хорошем фронтальном освещении картинка выглядит не очень. В жизни часы выглядят яркими, равномерными и вокруг солнечный день :)

Что еще?..


Теперь пара слов о других идеях. С помощью универсального семисегментного дисплея и простого lua-скрипта под NodeMCU можно буквально за час сделать настольные/настенные счетчики событий (клиенты, коммиты, факапы) или отсчитыватели времени до чего-то, будь до дедлайн или отпуск. Или считать дни без падений сервера.

Возможно несколько вариантов решения. Самый простой использовать все тот же модуль enduser_setup добавив на стартовую страницу необходимые параметры, например, инкрементировать или декрементировать число и с каким периодом.
Второй, более гибкий вариант подвязать дисплей к какой-либо странице в Интернете, откуда он будет брать актуальные данные. Этот вариант подходит для отображения курсов валют, температуры воздуха на улице или количества выздоровевших от коронавируса и любых других часто обновляемых данных.
Возможен так же вариант прямого управления дисплеем с телефона используя любую из множества программ для прямой коммуникации с esp8266 по WiFi. Такое решение будет подходящим для отображения счета в настольных играх или на спортивных событиях, например, школьного масштаба.

И конечно же, никто не запрещает подключить всевозможные датчики к esp8266 и отображать температуру, влажность или давление. Хоть уровень углекислого раза в помещение.

Как простенький пример, и как раз по случаю грядущего праздника, я запилил счетчик дней до Нового Года.





NY ждун
local time_zone = 3;local sntp_cnt = 1;local cur_intensity = 0x0F;local days = 189;function print_days()    max7219_intensity(cur_intensity);    max7219_output(3, days%10);    max7219_output(2, (days%100)/10);    max7219_output(1, days/100);    if tm["hour"] <= 7 then        -- from 0 to 8        cur_intensity = 0x01;    else         if tm["hour"] <= 18 then            -- from 8 to 19            cur_intensity = 0x0F;        else            if tm["hour"] <= 22 then                -- from 19 to 22                cur_intensity = 0x05;            else                -- from 23 to 24                cur_intensity = 0x01;              end        end    endendlocal dpm = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};function days_till_ny()    tm = rtctime.epoch2cal(rtctime.get());    days = dpm[tm["mon"]-1] - tm["day"];    if (tm["year"]%4) and (tm["mon"]<=2) then days = days - 1; end;    local month = tm["mon"];    while(month < 12)    do        days = days + dpm[month];        month = month + 1;    end;    print_days();endfunction sntp_sync()    if wifi.sta.status() == wifi.STA_GOTIP then         print("Connected to WiFi as:" .. wifi.sta.getip());        print ("SNTP sync");        sntp.sync("194.54.161.214",            function(sec, usec, server, info)                rtctime.set(sec + 3600*time_zone)                tm = rtctime.epoch2cal(rtctime.get());                print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]));                days_till_ny();            end,            function(err, str)                print("Nope...")            end    );    else         print("No WiFi");     end;    end;require("max7219");max7219_spi_init();max7219_init(3, cur_intensity);rtctime.set(1577872800 + 3600*time_zone);tm = rtctime.epoch2cal(rtctime.get());print(string.format("%02d:%02d:%02d", tm["hour"], tm["min"], tm["sec"]));enduser_setup.start(    function()        sntp_sync()    end,    function(err, str)        print("enduser_setup: Err #" .. err .. ": " .. str)    end)cron.schedule("0 */12 * * *", function(e)  print("Every 12 hours");  sntp_sync();end)collectgarbage();


Идей, как и вариантов их воплощения, великое множество. Наличие на борту контроллера с подключением к сети Интернет, модульная конструкция и возможность установки разного количества индикаторов разного размера и цветов открывает целое поле для полета фантазии. Вот такой вот универсальный индикатор получился.

Буду рад почитать конструктивную критику или интересные предложения.
Всем спасибо за внимание!
И всех с наступающими праздниками!
Источник: habr.com
К списку статей
Опубликовано: 28.12.2020 12:07:24
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Схемотехника

Diy или сделай сам

Электроника для начинающих

Семисегментный индикатор

Esp8266

Lua

Nodemcu

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru