Arduino, ESP8266 Lua, Raspberry Pi 2 && OpenHab. Умный дом: азы управления.

Тема в разделе "Глядите, что я сделал", создана пользователем ИгорьК, 12 май 2015.

  1. Olymp

    Olymp Нуб

    Доброго времени суток всем!
    На сегодня пока остановился на таком варианте:
    Код (Lua):
    mqtt_broker="mqtt.server.ru"
    mqtt_port=1883
    mqtt_client_id="home-esp-test"
    mqtt_login="MQTT-login"
    mqtt_passw="MQTT-passw"
    mqtt_client = nil
    mqtt_data = nil

    pub_interval = 20 * 1000                -- интервал публикации данных
    swd_interval = pub_interval / 1000 * 3  -- время жизни до перезагрузки, если не было публикации

    gpio.mode(6,gpio.OUTPUT)    -- светик старта ESP
    gpio.write(6, gpio.HIGH)    -- погасим через 10 сек после старта
    gpio.mode(7,gpio.OUTPUT)    -- светик статуса MQTT брокера
    gpio.write(7, gpio.LOW)     -- если мигает, не может подключиться, загорается на момент публикации данных
    state_flash = 0;            -- статус светика MQTT брокера в режиме мигания

    tmr.softwd(swd_interval)    -- перезагрузка через...

    print("-- 10 сек. на остановку таймера --")
    tmr.alarm(1, 10000, tmr.ALARM_SINGLE, function()
        gpio.write(6, gpio.LOW)
        if wifi.sta.status() == 5 and wifi.sta.getip() ~= nil then
            print("IP: "..wifi.sta.getip())
            mqtt_connect()      -- если есть Wi-Fi, коннектимся к MQTT брокеру, иначе ESP перезагрузится (см. swd_interval)
        else
            print("нет подключения wi-fi")
        end
    end)

    function get_sensors_data()
        sensor_data = {}
        local status,temp,humi = dht.read(4)        -- dht22
        if (status == dht.OK) then
            sensor_data["street_temper"] = temp
            sensor_data["street_humi"] = humi      
        end
    --    local status,temp,humi = dht.read(5)      -- dht22
    --    if (status == dht.OK) then
    --        sensor_data["tam_temper"] = temp
    --        sensor_data["tam_humi"] = humi      
    --    end
        -- серверная (bme280)
    --    local status = bme280.init(1, 2)
    --    if (status == 2) then   -- инициализирован BME280
    --        local press, temp = bme280.baro()
    --        if (press ~= nil) then
    --            press = press/1000*0.750062
    --            sensor_data["press"] = press      
    --        end
    --        humi, temp = bme280.humi()
    --        if(humi ~= nil) then
    --            temp = temp/100 - 1.23    -- 1.23 скорректировали, чтоб одинаково с dht
    --            humi = humi/1000 + 7      -- тоже корректировка
    --            sensor_data["tut_temper"] = temp
    --            sensor_data["tut_humi"] = humi      
    --        end
    --    end
        return sensor_data
    end

    function callback_mqtt_publish()
        print(" publish " .. sensor .. ":" .. value)    -- что опубликовалось
        gpio.write(7, gpio.LOW)                         -- выключили светик как данные отправились  
        mqtt_publish()                                  -- публикуем данные следующего датчика
    end

    function mqtt_publish()
        if next(mqtt_data) == nil then
            tmr.softwd(swd_interval)                    -- все данные отправили, всё хорошо, отложим перезагрузку
            return
        end
        gpio.write(7, gpio.HIGH)                        -- включим светик, что пошла отправка

        sensor,value = next(mqtt_data)
        mqtt_data[sensor] = nil
        print(sensor)                                   -- что публикуем

        mqtt_client:publish(mqtt_client_id .. "/" .. sensor, value, 0, 0, callback_mqtt_publish)
    end

    function callback_timer()
        print("получим данные с датчиков")
        ok, data = pcall(get_sensors_data)
        if ok then
            uptime = tmr.time()
            mqtt_client:publish(mqtt_client_id .. "/uptime", uptime, 0, 1, function(client) print("Опубликован аптайм") end,
                                                                           function(client) print("Ошибка публикации аптайма") end)
            mqtt_data = data
            mqtt_publish()
        else
            print("Ошибка получения данных с датчиков: " .. data)
        end
    end

    function callback_mqtt_offline()
        gpio.write(7, gpio.LOW) -- если связь сломалась в момент отправки
        print("Нет связи с брокером MQTT")
       
        tmr.unregister(0)
        print("Остановлен основной цикл")
       
        -- убиваем MQTT клиента
        if (mqtt_client ~= nil) then
            mqtt_client:close()
            mqtt_client = nil
        else
            print("MQTT клиент не был создан")
        end
        -- создаем MQTT клиента и подключаемся к брокеру
        mqtt_connect()
    end

    function callback_mqtt_connected(client)
        tmr.unregister(2) -- мигали при попытке подключения к mqtt
        gpio.write(7, gpio.LOW)
       
        print("Подключились к MQTT брокеру: " .. mqtt_broker)
        mqtt_client = client
        mqtt_client:publish(mqtt_client_id .. "/state", "online", 0, 1,
            function(client) print("Опубликован статус") end,
            function(client) print("Ошибка публикации статуса") end
        )
        print("Запускаем основной цикл")
        tmr.alarm(0, pub_interval, tmr.ALARM_AUTO, callback_timer)
        callback_timer()      -- не ждем первый раз
    end

    function callback_mqtt_failed(client, reason)
        print("Ошибка подключения к MQTT брокеру" .. reason)
    end

    function callback_timer_flash()
        if(state_flash == 1) then state_flash = 0 else state_flash = 1 end
        gpio.write(7, state_flash)
    end

    function mqtt_connect()
        print("Создаем MQTT клиента")
        m = mqtt.Client(mqtt_client_id, 120, mqtt_login, mqtt_passw)
        m:lwt(mqtt_client_id .. "/state", "offline", 0, 1)
        m:on("offline", callback_mqtt_offline)
        -- при подключении к брокеру мигаем светодиодом (для случая недоступности брокера)
        tmr.alarm(2, 200, tmr.ALARM_AUTO, callback_timer_flash)
        print("Попытка подключения к MQTT брокеру")
        m:connect(mqtt_broker, mqtt_port, 0, 0, callback_mqtt_connected, callback_mqtt_failed)
    end
     
    Пока огорчает только один момент, в терминале отправка выглядит следующим образом:
    street_humi
    publish street_humi:33.3
    street_temper
    publish street_temper:24.7
    publish street_temper:24.7

    т.е публикация street_temper происходит один раз, как и было задумано, а коллбэк отрабатывает 2 раза, заполнение двух дополнительных датчиков закомментировал для отладки этого процесса, если с ними, все так же, от последней публикации двойной коллбэк. Что я не увидел, что не так сделал, может кто-нибудь подскажет? Ну и критика :)
     
  2. ИгорьК

    ИгорьК Гуру

    Посмотрю код когда доберусь до компьютера: не раньше понедельника. Может кто другой пока...
     
  3. Pochemushka

    Pochemushka Нерд

    По ходу дела проблема наверное в windows 10, потому как удаляю hab designer. загружаю и устанавливаю с сайта java.com java, распаковываю hab designer запускаю его и еще даже не указав папку где лежать конфиги вижу в логах hab designer (./workspace/.metadata/.log) кучу ошибок. как только указываю папку с конфигами, еще даже не щелкнув ни на один файл уж вижу в логах еще два мешка ошибок. после выхода из программы получаю сообщение(даже если ничего не делать просто открыть и тут же закрыть): An error has ocuurred. see the log file E:\openhab_designer\workspace\.metadata\.log.
    Кстати на iMac designer взлетел и все конфиги открываются и редактируются. прикольная штука нажимая Ctrl-space на каждом углу можно выучить openhab вдоль и поперек.
    Спасибо за помощь, вопрос снимается с повестки дня. Буду курочить свой боевой бук :(
    ниже на всякий случай ошибка из логов вдруг кто сталкивался.

    Код (C++):
    !ENTRY org.openhab.action.dscalarm 4 0 2016-09-08 08:45:46.323
    !MESSAGE [SCR] Method was not found: resetDSCAlarmActionProvider(...)
    Details: Problematic reference = Reference[name = DSCAlarmActionProvider, interface = org.openhab.binding.dscalarm.DSCAlarmActionProvider, policy = static, cardinality = 1..1, target = null, bind = setDSCAlarmActionProvider, unbind = resetDSCAlarmActionProvider] of service component = org.openhab.action.dscalarm.action component implementation class = org.openhab.action.dscalarm.internal.DSCAlarmActionService located in bundle with symbolic name = org.openhab.action.dscalarm bundle location = reference:file:plugins/org.openhab.action.dscalarm_1.8.3.jar
     
     
  4. ИгорьК

    ИгорьК Гуру

    ИМХО, даже с мобильного телефона видно определенное нарушение логики .
    Вы выводите в компорт сообщение об отправке а потом проверяете есть данные для этой отправки или нет.

    -----------------------------------------------------------------
    Вы делаете два вызова инструкции next подряд за один заход.
    Это неправилно. При таблице в две записи вы получаете именно такую странность.
    Не происходит последовательного перебора для отправки. Это лучше переделать.

    Код (C++):
    function mqtt_publish()
        if next(mqtt_data) == nil then   -- Первый вызов [B]NEXT[/B]. Данные из таблицы потреблены.
            tmr.softwd(swd_interval)            
            return
        end
        gpio.write(7, gpio.HIGH)

        sensor,value = next(mqtt_data) -- Второй вызов [B]NEXT[/B]. Это следующие данные из таблицы.
        mqtt_data[sensor] = nil
        print(sensor)        

        mqtt_client:publish(mqtt_client_id .. "/" .. sensor, value, 0, 0, callback_mqtt_publish)
    end
    Имхо, Вы слишком сложны в коде.
    1. Защищенный вызов pcall следует делать лишь в тех случаях, когда ошибка железно завалит систему. Если Вы проверяли, что ошибка одного из датчиков валит код - тогда это обснованно. Но, полагаю, здесь pcall не нужен: из--за ошибки одного датчика Вы не увидите показаний всех рабочих.

    Я часто делаю так. Назначаю датчику некоторое значение , затем проверяю его в реальности. После этого выбор результата (or) значения.Это решает проблему ошибки при неисправном датчике.

    Тот же неисправный DS18b20, например, всегда выдает 85.

    2. В функции get_sensors_data() таблица sensor_data объявлена как глобальная, затем Вы ее возвращаете данными и сохраняете в другую таблицу. Смысл?
    3. Публикация аптайма осуществляется хотя и с коллбэком, но без проверки успешности в смысле дальнейшецй публикации, это некошерно.
    4. Ну и... все телодвижения связанные с работой клиента ИМХО(!!!) очень сложны и избыточны. Но это на мой взгляд. Ни на что не претендую.

    Если Вы все это сделали сами, то конечно, круто.
    Если это взято с какого-то умного сайта, то все таки присмотритесь к тому что я предлагаю.
    Задача удивительно простая: заполнить таблицу а потом последовательно скормить все пары в одну единственную функцию. Включая и время аптайм.
    Все эти лампочки - баловство. Смотрите как работает через MQTTSpy. Если работает - и будет работать.
    Присмотритесь и к моему способу организации взаимодействия с брокером. Он оттестирован и для случаев, когда wifi и сеть появились, а дальний брокер, тем не менее недоступен.
    В общем, как-то очень все избыточно сложно у Вас. Но это исключительно ИМХО.
    результата
     
    Последнее редактирование: 9 сен 2016
  5. Pochemushka

    Pochemushka Нерд

    Коллеги нашел решение своей проблемы.
    Если у кого то при попытке открыть файлы конфигурации ( item, sitemap,rules) в openhab designer и вылетает ошибка Failed to create the part's controls. Удаляем папку ./workspace/.metada/.plugins и заново запускаем openhab desiger и все.. designer при запуске создаст заново эту папку и насует туда все что ему нужно.
    Если будет продолжать тупить и выдавать ошибки и у вас 64битная система. снесите java64 и поставьте 32битную java.
     
    ip-ua и ИгорьК нравится это.
  6. dimm71

    dimm71 Нерд


    Столкнулся с такой проблемой. Если в rc.local прописать /etc/init.d/openhab start, то OpenHab стартует из под системного пользователя и подхватывается системное время, что влияет на отображение времени на графиках и в других местах, где используется время - в моем случае меньше на 3 часа. Я не знаю, как на Малине (вполне вероятно, что системного времени там нет, сразу вытягивает с NTP сервера), а у меня на VPS берется именно системное время.
    Исправляется запуском сервиса как "service openhab start".

    Кстати, вместо Малины можно использовать VPS от Amazon - 1 год бесплатно. Это так, кто не знал.
     
  7. ИгорьК

    ИгорьК Гуру

  8. dimm71

    dimm71 Нерд

    При добавлении этих item всё работает, но Openhab ругается на то, что изменился стиль. И отправляет на страничку https://github.com/openhab/openhab/wiki/Astro-binding. Там теперь item по другому описывается. Например:
    Number Azimuth "Azimuth [%.2f]" {astro="planet=sun, type=position, property=azimuth"}
    Number Elevation "Elevation [%.2f]" {astro="planet=sun, type=position, property=elevation"}

    Может поправить сразу на правильное написание в посте?
     
  9. Karakurt

    Karakurt Нерд


    Решение с "WatchDog " уже затрагивалась, вот пару ссылок по теме, может кому-то пригодится:
    для Raspberry Pi - http://lab409.ru/raspberry_pi_watchdog/
    для Arduino - https://geektimes.ru/post/255800/ (на сколько я понял, в последних версиях бутлодера все работает)
    для ESP8266 NodeMCU - http://kirill1985.ru/pokupka/3318-esp8266-nodemcu.html
     
    ИгорьК и alp69 нравится это.
  10. alp69

    alp69 Форумчанин

    Спасибо, погляжу.
    Однако моя проблема локализована. Причина на стороне роутера.
     
  11. ИгорьК

    ИгорьК Гуру

    Вынесу в заголовок.
     
  12. dimm71

    dimm71 Нерд

    По неосторожности при настройке mosquitto подписался на топик mosquitto_sub -d -t /myhome/#
    Как отписаться от него не нашел. Может кто подсказать как и стоит ли отписываться?
     
  13. alp69

    alp69 Форумчанин

    Кого подписали?
     
  14. dimm71

    dimm71 Нерд

    mosquitto_sub -d -t /myhome/#
     
  15. alp69

    alp69 Форумчанин

    Какое устройство подписали? С консоли на Малине ввели?
     
  16. dimm71

    dimm71 Нерд

    Да я только настраиваю. Ввел по какой то инструкции и вбил в консоли mosquitto_sub -d -t /myhome/#. Вместо Малины у меня VPS, но это сути не меняет. Насколько я понял теперь только этот топик Openhab будет слушать? Никаких физических устройств ещё нет. Настраиваю для самообразования там всякие погоды, астрономию и определитель "найди себя". Вот как раз он и не заводится. В логах mosquitto есть запись, что приконектился, а других данных передачи в логах нет. Ни в mosquitto ни в Openhab`е. Вот и думается, что Openhab не слушает нужный топик. Или я не прав?
     
  17. alp69

    alp69 Форумчанин

    Введите нужный. А про тот забудьте. Для успокоения души можете ребутнуть ;)
     
  18. alp69

    alp69 Форумчанин

    Опенхаб топики не слушает. Он транслирует информацию между итемами и брокером (mqtt). На топики подписываются итемы (т.н. биндинг). Итемов может быть столько, сколько Вы их создадите. И каждый может быть подписчиком на свой топик. И я так подозреваю, что даже не на один (пока не экспериментировал).
    Так что не переживайте. То, на что Вы случайно подписались, не помешает никому.
    Введенной командой Вы просто приказали своему VPC отлавливать данные из этого топика и показывать Вам. Введите другую команду и наблюдайте другие данные.
     
    Последнее редактирование: 16 сен 2016
  19. ИгорьК

    ИгорьК Гуру

    Да! Оно работает!
    1234.jpg
    cellar000.jpg
     
    Последнее редактирование: 21 сен 2016
  20. Olymp

    Olymp Нуб

    Здесь не понимаю пока, пожалуйста разжуйте, если не сложно.
    Я вижу так, строка print(sensor) говорит, что будем публиковать, потом публикуем и в коллбэке публикации пишем, что опубликовали. В результате print(sensor) отработал один раз, а коллбэк дважды на последней записи таблицы (2 записи или 6 не важно, будет один лишний коллбэк). NEXT хоть и выглядит криво, но строка print(sensor) отрабатывает ровно столько раз, сколько записей в таблице. Глядя на лог mqtt сервера видно, что дважды значение одного датчика не приходит. Не пойму, как NEXT умудряется заставить отработать коллбэк публикации?
    Публикация происходит ровно столько раз, сколько записей было положено в таблицу.
    Все правильно говорите, первоначальная версия эволюционировала долгое время, сейчас хоть пришел к алгоритму работы, теперь самое время оптимизации, как раз первое до чего дошли руки, это двойной коллбэк :).
    Элементарное незнание матчасти, плюс видимо пережиток какого-то из прошлых алгоритмов, спасибо, что указали, думаю сам бы не скоро обратил внимание.
    Да, да, да! Аптайм сразу планилось вместе с данными отправлять, но пока тока в планах и повисло.
    Исключительно в отладочных целях, плюс понять работу mqtt клиента nodemcu. По документации, честно говоря не все понятно, например m:eek:n("connect" .... хрен когда отработает, если в коннекте к брокеру указать коллбэк успешного коннекта, но в документации в примере есть и то и то.
    Нет, не сам. Взят ваш сама первый пример, проверен, потом началось добавление датчиков, причем бездумно, опубликовали один, в коллбэке прочитали второй и опубликовали итд. Когда разросся первый коллбэк вложениями, трудно стало понимать код, решил, что коллбэки нужно выносить отдельными функциями, следом пришла мысль с таблицей, варианты, пробы, ошибки, изучение чужих листингов на разные темы, бывает натолкнут на мысль, ну а как дошел к этому варианту, увидел и ваш пост про отправку много данных :) Лампочки мне хорошо, именно для этой поделки, уже ни раз помогали понять ситуацию, сам не любитель лишнего, чем меньше, тем ошибок меньше и работа надежней. MQTTSpy, да, может это вариант, но это нужно куда- то установить, мой вариант путти к удаленному mqtt и tail -f /var/log/mosquitto/mosquitto.log, пока достаточно, чтобы понять, что происходит. Все ваши способы я изучил ;) Хочу сказать, что копи пастить тоже можно, но хочется понимать полностью как работает, например убил массу времени, чтобы понять (не всем дано чужие мысли наскоряк расшифровать) алгоритм работы роутероперезагружалки. Блин, толково, мне понравилось с подходом расчета времени, даже пришлось свою перезагружалку немного переработать :) Избыточность и сложность явление временное. Спасибо Игорь, все по существу!