Ещё один Wi-Fi выключатель


Данная статья будет посвящена ESP8266 Wi-Fi модулю, языку программирования Lua и прошивке nodeMCU. SDK от производителя рассматриваться не будет.

Примерно года три назад я пробовал реализовать выключатель по 1-wire шине. Как все работало мне очень не понравилось.

  • Единая точка отказа т.к. вся логика на сервере;
  • Медленная скорость;
  • К каждому выключателю придется тянуть от 2х проводов(идеально «витуху»).


В следствии чего все это было удачно заброшено, другие беспроводные решения рассматривались, но были исключены в виду дороговизны, небезопасного протокола и сложности реализации. Хотелось чего то простого с минимумом компонентов, со своей логикой и дешёвого. Не давно заказал 2 штуки esp8266 просто для забавы, не зная, чего конкретного с ними можно сделать. После 2-х вечеров разборок с чипом вспомнил незавершённое дело с кнопкой и решил довести до логического конца.

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

Железо


Себестоимость важный фактор для простого выключателя, так что пытался использовать как можно меньше частей. Решил сделать из того что было дома, но пришлось купить твердотельное реле. Кстати, «релюшка» стоит дороже wifi модуля и ее можно заменить на оптопару, симистор и обвязку, схемы включения легко ищутся в интернете. Был случай, когда плохой контакт в патроне лампочки выбил симистор на коротко. Посмотрим, как покажет себя оптореле, ведь раньше с ними не работал. Стоит учесть для большой нагрузки установка радиатора обязательна.

Тут сразу столкнулся с проблемой, если на gpio при включении он уходил на землю, плата переходила или в режим прошивки или в непонятный режим, т.к кнопка у нас нормально разомкнута, с ней переделывать нечего не стал и так и оставил замыкаться на землю, а оптореле повесил на плюс через резистор и включал подачей 0, выключал подачей 1, соответственно. В итоге получилась такая схема:



Внимание схему стоит улучшить! Выход на реле стоит подать через транзистор, а кнопку подтянуть через резистор от плюса. Ингредиенты получились такие:

  • выключатель;
  • пружинка(для переделки выключателя в кнопку);
  • сам esp8266;
  • твердотельное реле использовал(S202T02);
  • платка для конструирования;
  • резистор 470 Ом;
  • провода;
  • разъемы по вкусу;
  • зарядка от телефона 400мА 5v;
  • стабилизатор 1117 3.3v;
  • пара конденсаторов.


Переделка выключателя не заняла много времени, выкинул стандартный светодиод. Протянул провода от модуля в центре выключателя, сам модуль расположил снаружи под пластмассовой кнопкой, а силовая часть внутри. Не много фотографий процесса (фото с телефона):







nodeMCU


Прошивка использует Lua язык программирования, данный язык похож чем то на Javascript. Версия ещё сыроватая, но уже базовый функционал вполне не плохо реализован. Сразу после загрузки модуль начинает исполнять файл скрипт init.lua, в чистой прошивке этого файла нет, вам приходиться его создать руками. Все операции можно осуществлять через консоль подключенным к «com» порту, для упрощения заливки файлов в модуль есть скрипт luatool. Заливка работает следующим образом и данный код полностью показывает процесс записи в файл.

file.open("init.lua","w")
file.writeline([[print("Hello World!")]])
file.writeline([[--comment]])
file.close()


Пример чтения конфигурационного файла. Выглядит не очень. Может есть и другой вариант сериализованных данных.

file.open('config')
c_wifi_ssid = string.gsub(file.readline(), "\n", "")
c_wifi_key  = file.readline()
file.close()


Пример цикла с использованием API с паузой в 1000 миллисекунд представлен ниже:

tmr.alarm(1000, 1, function()
  if wifi.sta.getip()=="0.0.0.0" then --текущий ip
    print("connecting to AP..."..c_wifi_ssid.."/"..c_wifi_key)  
  else
    print('ip: ',wifi.sta.getip())
    tmr.stop() -- alarm stop
  end
end)


Работа с GPIO

Если у вас модель модуля ESP-01 новой ревизии, то вам доступно всего 2 gpio, не прибегая к грязному хаку.



Решил отказаться от такого хака и воспользоваться тем, что есть.

Один gpio кнопка и второй выход на твердотельное реле. Есть еще Tx, но заставить работать как gpio у меня не получилось, и для индикации я просто передаю сообщения в консоль print(). Пока закостылил именно так. Чем длиннее сообщение, тем дольше и ярче вспыхивает светодиод. Владельцы данной модификации пролетают лесом и с такими функциями как (node.key, node.led), т.к. они могут использовать только GPIO16, который тоже не разведен на плате.

Все gpio могут работать в нескольких режимах (OUTPUT, INPUT, INT), но интересно то, что функция gpio.read(), прежде чем считать, подает низкий уровень, даже если установлен режим OUTPUT. То есть, чтобы получить текущее состояние выхода, это не подходит. Пришлось использовать внешнюю переменную и писать две функции для удобства, а уже через переменную определять активность.

function on()
  gpio.write(8,gpio.LOW)
  oo=1
end

function off()
  gpio.write(8,gpio.HIGH)
  oo=0
end


В качестве событий можно использовать callback gpio.trig(pin, type, function(level)), второй параметр может принимать следующие значения «up», «down», «both», «low», «high». Тут, кажется, все ясно. Если у вас вывод находится в состоянии 1 и мы его опускаем на землю, срабатывает down, потом при поднятии срабатывает up, но, к моему сожалению, такого не происходило, в консоли я видел только down в зависимости от скорости нажатия кнопки событие срабатывало 1 или 2 раза. Решил поставить цикл с паузой и бряк по 1 на gpio.

for i=1,1000 do
  print(i)
  tmr.delay(10)
  tmr.wdclr()   -- сбрасывает счетчик и предотвращая авто перезагрузку
end


Но пауза не отработала, а без паузы устройство уходило в перезагрузку. Зато print(i) вносил хорошую задержку. Сделал через tmr.alarm, но в текущий момент активный цикл может быть только один, что не очень подходит.

function down()
  tmr.alarm(100, 1, function()
    timer = timer + 1
    -- ok
    if gpio.read(9) == 1 then
      print(timer)
      tmr.stop()
      if timer < 20 then
        switch()
      else
        -- ...
      end
      timer = 0
    end
    tmr.wdclr()
  end)
end
gpio.trig(9, "down", function (gp)
  if timer == 0 then
    timer = 1
    down()
  end  
end)


HTTP сервер

Сервер запускается как 2 пальца, но никакого массива параметров запроса не получите. Пока непонятно, как оптимальнее: или писать свой велосипед, или find по подстроке. Согласитесь, выглядит ужасно. В данном примере ищется 2 параметра key и mode=off,on,party. Последний режим – это простое мигание лампочкой каждые 200мс, можно поставить и побыстрее, но побоялся за лампочку и оптореле.

function HTTPd()
  print('start http serv')
  srv=net.createServer(net.TCP, 5)
  srv:listen(80,function(conn)
    conn:on("receive",function(conn,payload)
      print(payload)

      if string.find(payload, "key="..c_api_key) then
        msg = "key_ok"      
        if string.find(payload,"mode=on") then
          on()
        else
          if string.find(payload,"mode=off") then
            tmr.stop()
            off()
          else
            if string.find(payload, "mode=party") then
              party(200)
            end
          end
        end
      else
        msg = "error_key"
      end
      conn:send("<html><head></head><body><h1> mode=[on,off,party] key='api_key' </h1><p>"..msg.."</p></body></html>")
    end)
    conn:on("sent",function(conn) conn:close() end)
  end)
end


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

Wi-Fi

Для подключения в качестве клиента хватит такого кода, ip получим по DHCP:

wifi.setmode(wifi.STATION)
wifi.sta.config(c_wifi_ssid,c_wifi_key)
wifi.sta.autoconnect(1)


В третьем примере видно, как можно определить, подключилась точка или нет. Другие режимы wifi я не использовал, так что возможны подводные камни.

В целом, с небольшими хитростями (костылями) была написана логика простого выключателя. А кнопкой можно управлять прямо из консоли. Поставив программу BetterTouchTool, можно комбинацией клавиш «curl`ить» сервер выключателя. Нашлось применение кнопке EJECT.

Резюме


Нисколько не жалею, что выбрал nodeMCU: не пришлось залезать в дебри официального SDK. Не надо каждый раз компилировать код, а саму программу легко разбить на модули и заливать отдельными кусочками. За это пришлось поплатиться иногда непонятным поведением. Главное, что вся логика находится на самом выключателе, и он при недоступности wifi будет выполнять свою главную функцию, ну, только если сгорит. Самому проекту nodeMCU я желаю скорейшего развития и в дальнейшем попробую сделать что-нибудь ещё, благо остался еще один модуль.

Полезные ссылки

Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 22
  • +2
    Неплохо… Но я предпочитаю Си )) Кстати, выводы UART тоже можно использовать, они имеют нумерацию 1 и 3.
    • 0
      Я не очень сильно интересуюсь темой, но все эти идеи мне не очень нравятся, потому что используется Wi-Fi и реализация децентрализована.
      Сама идея умного дома мне нравится, но почему не разработать какую-то «шину данных» высокочастотную и не пустить ее пямо по электрическим проводам? Преимущество такой схемы — довольно простые по реализации узлы. Возможно идея не нова, но на хабре видел пока только wi-fi розетки.
      И да, статья больше подходит для хабра. Но это сугубо мое лично ИМХО.
      • +2
        Предположил что DIY почему то лучше публиковать здесь, т.к. это моя первая статья. Буду иметь в виду. Спасибо.
        • 0
          децентрализована? Ну можно отправлять состояние реле и датчиков на головной сервер умного дома если я понял правильно. НО зато в такой схеме при сбое сервера светом управлять все равно можно.

          Кстати, для хабра DIY — это не профильный хаб, а тут как раз он профильный.
          • 0
            децентрализована? Ну можно отправлять состояние реле и датчиков на головной сервер умного дома если я понял правильно. НО зато в такой схеме при сбое сервера светом управлять все равно можно.

            Дело в том, что слишком много ненужной логики «на местах». Я бы не назвал Wi-Fi простым устройством, тем самым оно дает сильное и главное ненужное удорожание каждого узла. Под децентрализацией я имел ввиду именно это.

            Кстати, для хабра DIY — это не профильный хаб, а тут как раз он профильный.

            Ну тогда стоит поздравить mail.ru group, потому что еще больше запутать наверно невозможно. Для меня geetimes это некая газетенка с последними новостями из мира IT и около IT. А хабр всевозможные рецепты и DIY.
            • +1
              По поводу Rx/Tx не мог включить светодиод, как мне кажется надо отключать вывод консоли в порт, но мне пока не хотелось терять консоль.

              Можно состояния одних датчиков отправлять другим и забирать по запросу и обойтись без сервера вопрос хватит ли «ума» у модуля. Памяти там 512кб, доступно 256к в оригинальной прошивке(не уверен), в nodeMCU может и меньше, даже не понятно как узнать свободную память.

              • 0
                Да, надо отключать уарт, надо искать какую-то команду в nodeMCU…

                В си SDK отключается так: system_set_os_print(0);
                • +1
                  Видел что то подобное, значит надо реализовывать скрипт загрузчик или telent.
                  Вы работали с TOUT?
                  • 0
                    Нет, с TOUT не работал, уж очень мелко там к нему припаиваться… Китайцы уже штампуют модули ESP-12 -у них этот вывод распаян.
            • 0
              Про шину по проводам на хабре уже много писали…
              Много жалоб на помехи, как от своей техники, так и «от соседа»…
            • 0
              >>а кнопку подтянуть через резистор от плюса.

              Если подтянуть вывод к плюсу программно, то можно и не делать, я не знаю как там в nodeMCU, но на Си для ESP8266 это делается так:

              // откл. pulldown
              PIN_PULLDWN_DIS(PERIPHS_IO_MUX_GPIO0_U);
              // вкл. pull up
              PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO0_U);
              // GPIO0 на ввод
              gpio_output_set(0, 0, 0, BIT0);

              • 0
                Но тем не менее, лучше подтянуть аппаратно. Встроенные резисторы имеют очень большой номинал, и потому их использование в целом снижает помехоустойчивость. А для чего-то, что вынесено с платы на проводах, 100% надо ставить отдельный резистор, если, конечно, мы говорим о правильной промышленной схемотехнике.
              • +3
                Медленная скорость, дешёвая цена и тяжёлый вес.
                • 0
                  Все эти микросхемки очень интересны, но мой личный интерес больше вызывает механизм выключателя. Как работает выключатель, как кнопка (нажал — включилось, нажал — выключилось) или как две кнопки (нажал вверх — включилось, нажал вниз — выключился)?
                  Можно фотки механизма и вид выключателя спереди?
                  • +1
                    Это был обычный выключатель(нажал вверх — включилось, нажал вниз — выключился). После переделки в кнопку(фото 3) он не фиксируется. Посмотрите видео и фотографии не понадобятся.
                  • 0
                    Спасибо за отличную статью! Теперь точно возьмусь за эти модули!
                    • 0
                      А может быть кто-нибудь знает готовый модуль управления розеткой (обычная розетка в которую нужно воткнуть светильник) по wi-fi\bluetooth через приложение по ios (либо пультом управления)? Очень надоедает идти выключать торшер, а потом в темноте идти в кровать в другом конце комнаты.
                      И чтобы два раза не вставать: может быть кто-нибудь встречал модуль управления розеткой в пару таймер + датчик освещенности?
                      • 0
                        wifi/bluetooth — не в курсе (не сплю со смартфоном).

                        Розеток масса:
                        image

                        Отдельно продаются также модули для люстр:

                        image

                        Конкретных ссылок не привожу — все легко гуглится.
                      • 0
                        Идя по этому пути и пощелкав релюшкой запустив webap_toggle_pin.lua случайно наткнулся на Вашу статью.
                        Интересно можно ли при потере связи со стационарной wifi сетью перевести модуль в режим точки доступа, чтобы не потерять возможность беспроводного управления, это буду пробовать сегодня вечером.
                        Так же интересует заливка скриптов по wifi, так как хочу замуровать все в стену.
                        • +1
                          Да можно. Сделали прототип розетки.

                          Там банальный цикл с счетчиком. Если в течении и 30 секунд не подрубился к wifi, то переходим в AP.
                          • Интересно взглянуть на этот участок скрипта, я реализовал то же самое на таймерах, только ещё с проверкой раз в минуту на возможность вернуться в штатную сеть, но после заливки модуль стал уходить в ребут после включения и я пока отложил все в ящик.
                            • 0
                              Тоже были такие проблемы, когда разухабистый web сервер сделал. Попробую заново переписать, на новой прошивке(на неделе).
                              Кажется память или пёсик.

                              for i=1,10000 do 
                                print(i)
                                tmr.wdclr()   -- should call tmr.wdclr() in a long loop to avoid hardware reset caused by watchdog.
                              end
                              


                              Вижу метод node.compile(), с ним то лучше по памяти должно быть или я не прав?

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.