ESP32: отправка данных на Telegram, Народный мониторинг и MQTT брокер, сигнализация.

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

  1. ИгорьК

    ИгорьК Гуру

    ОК. Ясно, что Espruino пока сыровата.
    Но есть еще чем развлечься :) mongoose.os, LuaNode.

    В любом случае, те примеры что я выложил работают и вполне устойчиво.
     
  2. ИгорьК

    ИгорьК Гуру

    На тестирование - у кого хватит памяти, а у кого - нет?
    Код (C++):
    var ssid = 'ВАША_ТОЧКА';
    var password = 'ВАШ_ПАРОЛЬ';
    var topicc = "test/esp32/";
    var myhost = "iot.eclipse.org";
    var myport = "1883";
    var mynarod = "#12:23:BB:0C:AA:24\n";
    var pinDS = "D32"; // DS18b20
    var pinHIH = "D33"; // Аналоговый датчик
    var pinAlarm = "D0"; //Сигнализация любого рода
    var HIHDSNumber = 0;
    // Получить от Телеграм
    var bot = "bot123456789:aaaaaaaaaaaaaaa-bbbbbbbbbb";
    var id = "123456789";

    var cl = console.log;
    var wifi = require('Wifi');
    var http = require("http");

    var mod = {
        broker : false,
        alarm : "ON",
        wifi : false,
        narodPermit : true,
        oldalarm : 0
    };

    var dattable = {};
    var mqtt;
    var doAll;
    var tick;
    var nextCheck;
    var ow = new OneWire(pinDS);
    var sensors = ow.search().map(function (device) {
        return require("DS18B20").connect(ow, device);
    });

    mod.HIHDSNumber = sensors[HIHDSNumber].sCode;
    function repeat(call, timeSec) {
        setTimeout(call, timeSec * 1000);
    }
    function mqttnow() {
        mqtt = require("MQTT").connect({
            host: myhost,
            port: myport
        });
        mqtt.on('connected', function() {
            cl('Got!');
            mqtt.publish(topicc, "ON");
            mqtt.subscribe(topicc + "command/#");
            mod.broker = true;
            doAll();
            setInterval(doAll, 30000);
        });

        mqtt.on('publish', function (pub) {
            cl(pub.topic, pub.message);
            if(pub.topic === "test/esp32/command/alarm") mod.alarm = pub.message;
        });

        mqtt.on('disconnected', function() {
            mod.broker = false;
            mod.wifi = false;
            cl("recon.");
            tick();
        });
    }

    function getwifi(){
      wifi.connect(ssid, {password: password}, function(er) {
        cl(wifi.getIP().ip);
        mod.wifi = true;
        mqttnow();
      });
    }

    tick = function() {
        wifi.getIP(function(tb){
            cl(tb.ip);
            if(tb.ip !=="0.0.0.0") {
                mod.wifi = true;
                try {
                    if (mqtt == "undefined"){
                        mqttnow();
                    }
                    else {
                        if (mqtt.connected) return;
                        mqtt.connect();
                    }
                } catch(e){
                    repeat(tick, 15);
                }
            }
            else {
                repeat(tick, 15);
                getwifi();
            }
        });
    };

    function telegram(alarm){
        let al = alarm || 'Alarm Now!';
        let mes = "https://api.telegram.org/" + bot + "/sendMessage?chat_id=" + id + "&text=" + al;
        try{
        http.get(mes, function(res) {
            res.on('data', function(data) {
                console.log(data);
            });
        });
        } catch(er) {
          cl(er);
        }
    }


    function checkalarm(){
      let alarmnow = digitalRead(pinAlarm);
      if(alarmnow !== mod.oldalarm) {
        mod.oldalarm = alarmnow;
        cl("alarmnow = ", alarmnow);
        if((mod.broker === false) || mod.alarm == "OFF") return;
        if(alarmnow) {
          mqtt.publish(topicc + "alarm", "1");
          telegram("Alarm IR Sensor!");
        }
        else {
          mqtt.publish(topicc + "alarm", "0");
        }
      }
    }

    /*---- Ask Sensors & Send INFO By Promise  ---- */
    function askhumi(resolve, reject){
        let GetHue = function (pin, temp) {
            let RH =  (analogRead(pin) - 0.1515)/ 0.00636;
            let TrueRH = ((RH)/(1.0546 - 0.00216 * temp)).toFixed(1);
            cl("Hum is " + TrueRH+" %");
            return parseFloat(TrueRH);
        };
        let hum = GetHue(pinHIH, dattable[mod.HIHDSNumber]);
        dattable.hum = hum;
        resolve('Got Humidity!');
    }

    function askDS18b20(resolve, reject){
        sensors.forEach(function (sensor, index) {
            let dska = 0;
            sensor.getTemp(function (temp) {
                var tt = temp.toFixed(2);
                dska = sensors[index].sCode;
                dattable[dska] = tt;
                cl(dska + ": " + tt + "°C");
                if((index + 1) == sensors.length) resolve('Got Temperature');
            });
        });
    }

    function sendmqtt(resolve, reject){
        if(mod.broker === false){
            reject("Lost MQTT Broker!");
            return;
        }
        let sm = [];
        let send = function(item){
            let topikk = topicc + item[0];
            if(mod.broker) {
                mqtt.publish(topikk, item[1]);
                cl(topikk, item[1]);
                transfer();
            }
        };

        let transfer = function (){
            let item = sm.pop();
            if(item){
                setTimeout(send, 1000, item);
            }
            else {
                resolve("Sent MQTT Messages!");
            }
        };

        for (var key in dattable) sm.push([key, dattable[key]]);
        transfer();
    }

    function sendnm(resolve, reject) {
        if((mod.wifi === false) || (mod.narodPermit === false) ) {
            reject("Too Early or not WiFi!");
            return;
        }
        mod.narodPermit = false;
        setTimeout(()=> mod.narodPermit = true, 5*60*1000);
        let tomon = mynarod;
        for (var key in dattable) {
            tomon = tomon + "#"+key+"#"+dattable[key]+"\n";
        }
        tomon = tomon + "##\n";
        cl(tomon);

        var client = require("net").connect({host: "narodmon.ru", port: 8283}, function() {
            cl('connected');
            client.write(tomon);
            client.on('data', function(data) {
                cl(">"+JSON.stringify(data));
            });
            client.on('end', function() {
                resolve("Narodmon is OK!");
            });
        });
    }

    function doAll(){
        function makePromice(call){
            return new Promise((resolve, reject) => {
            call(resolve, reject);
            });
        }

        let promise = makePromice(askDS18b20);

        promise
            .then(
                result => {
                  cl("\nFulfilled: " + result);
                  return makePromice(askhumi);
                }
            )
            .then(
                result => {
                  cl("\nFulfilled: " + result);
                  return makePromice(sendmqtt);
                }
            )
            .then(
                result => {
                  cl("\nFulfilled: " + result);
                  return makePromice(sendnm);
                }
            )
            .then(
                result => cl("\nFulfilled: " + result),
                error => cl("Rejected: " + error)
            );
    }

    /*---- Start! ---- */
    setInterval(checkalarm, 100);
    repeat(tick, 15);
    getwifi();

    Для работы скрипта хватит одного датчика DS18b20 на 32 ноге.
    Скрипт отрабатывает, но памяти для отправки Телеграм не хватает, try/catch ловит ошибку:
    upload_2017-12-19_13-48-24.png

    Есть кто повторит?
     
    Последнее редактирование: 19 дек 2017
  3. IvanUA

    IvanUA Гуру

    Игорь на вашем прежнем коде, телеграм у меня работает, но потом кажись таки перестает, еще руки не дошли найти почему. Как видно по скрину, почти пол тысячи удачно доставленных сообщений.
    Безымянный.jpg

    Единственное что не дает покоя так это какая то ошибка в 186 строке. 'Missing semicolon'

    Если надо потестить на Вашем последнем скрипте, то я могу залить.
     
  4. ИгорьК

    ИгорьК Гуру

    Точка с запятой в конце строки потеряна :)
     
    IvanUA нравится это.
  5. IvanUA

    IvanUA Гуру

    Подключил я к ESP экранчик...
    Сейчас у меня там живет температура))))
    IMG_20171219_231852.jpg
     
    ИгорьК нравится это.
  6. ИгорьК

    ИгорьК Гуру

    Вышла прошивка 1.95 Espruino:
    SmartSelectImage_2017-12-21-02-49-35.png
    Будем пробовать!
     
  7. ИгорьК

    ИгорьК Гуру

    На прошивке 1.95 получился очень устойчивый охранник:

    [​IMG]

    Код (Javascript):
    var ssid = '************';
    var password = '*************';
    var bot = "bot*******************-****************";
    var id = "*************";

    var cl = console.log;
    var wifi = require('Wifi');
    var http = require("http");

    var mod = {
       alarm : "ON",
       wifi : false,
       oldalarm : 0
    };

    function getwifi(){
      wifi.connect(ssid, {password: password}, function(er) {
       cl(wifi.getIP().ip);
       mod.wifi = true;
      });
    }

    wifi.on('disconnected', function(details) {
       cl("Lost WiFi!");
       mod.wifi = false;
       getwifi();
    });

    function telegram(alarm){
       let al = alarm || 'Alarm Now!';
       let mes = "https://api.telegram.org/" + bot + "/sendMessage?chat_id=" + id + "&text=" + al;
       try{
       http.get(mes, function(res) {
         res.on('data', function(data) {
           cl(data);
         });
         res.on('close', function(data) {
            console.log("Connection closed");
         });
       });
       } catch(er) {
           cl(er);
       }
    }

    setWatch(function(e) {
      telegram("Line 0");
    }, D21, { repeat: true, edge: 'rising' });

    // Можно  добавить еще парочку:
    /*
    setWatch(function(e) {
      telegram("Line 1");
    }, D0, { repeat: true, edge: 'rising' });
    */


    getwifi();

    setInterval(()=>cl(process.memory().free), 15000);

    Проверкой установлено, что память не течёт.
     
    Последнее редактирование: 25 дек 2017
  8. IvanUA

    IvanUA Гуру

    Игорь, а есть возможность проверить (в вашем же скрипте немного поправить)
    Код (Javascript):
    function getwifi(){
      console.log("Start connect WiFi!");
      wifi.connect(ssid, {password: password});
      wifi.on("connected", function(details) {
        console.log("Connected with IP " + details.ip);
       });
      wifi.on("disconnected", function(details) {
        console.log("Disconnected from WLAN " + ssid);
        wifi.connect(ssid, {password: password});
      });
      wifi.on("dhcp_timeout", function() {
        console.log("DHCP timeout!");
      });
    }
    Каждые 5 минут происходит дисконнект. В принципе добавил в функцию дисконнекта, повторное подключение, но мне кажется что это уже костыли(((
     
  9. ИгорьК

    ИгорьК Гуру

    Пока нет... :)
    SmartSelectImage_2018-01-01-11-37-28.png
     
    SergeiL и IvanUA нравится это.
  10. ИгорьК

    ИгорьК Гуру

    Я же в скрипте все отвалы связи отрегулировал. Там не все так однозначно с callback. Нужно действовать более замысловато. Работоспособность проверял: 1,5 суток отработал без сбоев. Кроме того через насилие роутера и брокера ронял все много раз - все стабильно.
     
    Последнее редактирование: 7 янв 2018
  11. SergeiL

    SergeiL Оракул Модератор

    Красивое место! Где?
     
    alampw нравится это.
  12. ИгорьК

    ИгорьК Гуру

    Зимняя Майорка, мертвый сезон. Днем +16 - правильная температура для белого вина :)
     
  13. SergeiL

    SergeiL Оракул Модератор

    Классное место! Были этим летом, правда, жарко было, сейчас самое то! Съездите на мыс с маяком, там классные виды, дорога – просто супер!
    Мы сейчас чуть севернее, Рим, +17, самое то для знакомства с городом и достопримечательностями!
     
    Последнее редактирование: 7 янв 2018
  14. ИгорьК

    ИгорьК Гуру

    Вот он:
    SmartSelectImage_2018-01-06-23-03-12.png

    SmartSelectImage_2018-01-06-23-03-39.png

    SmartSelectImage_2018-01-06-23-04-19.png
     
    SergeiL нравится это.
  15. SergeiL

    SergeiL Оракул Модератор

    Сколько температура моря? Не купались? Мы далеко от моря, а в прошлом году, в это же время меня жена не пустила искупаться на пляже Барселоны.
     
  16. yden

    yden Гик

    Здравствуйте.
    Можно вклиниться, плиз.
    В начале ветки вы затронули тему callback при работе с mqtt. В 2 словах поясните, плиз, логику ее работы.
    У меня:
    Код (C++):
    // Функция получения данных от сервера
    void callback(const MQTT::Publish& pub)
    {
      if  (pub.topic() == "ihouse/climat/t_out")
      {
        hours = String(pub.payload_string());
      }

      if  (pub.topic() == "ihouse/climat/p_atm")
      {
        minutes = String(pub.payload_string());
      }

    }

    WiFiClient wclient;
    PubSubClient client(wclient, mqtt_server, mqtt_port);
    Вызов:
    Код (C++):
    // подключаемся к MQTT серверу
      if (WiFi.status() == WL_CONNECTED)
      {
        if (!client.connected()) {
          Serial.println("Connecting to MQTT server");
          if (client.connect(MQTT::Connect("informer_esp").set_auth(mqtt_user, mqtt_pass)))
          {
            Serial.println("Connected to MQTT server");
            client.set_callback(callback);
            client.subscribe("ihouse/clock/h"); // подписывааемся по топик
            client.subscribe("ihouse/clock/m"); // подписывааемся по топик
          }
          else
          {
            Serial.println("Could not connect to MQTT server");
          }
        }

        if (client.connected())
        {
          client.loop();
          MQTT_Send();
        }
    У меня непонятки с вызовом в месте:
    client.set_callback(callback);
    client.subscribe("ihouse/clock/h"); // подписывааемся по топик
    client.subscribe("ihouse/clock/m"); // подписывааемся по топик

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

    Благодарю
     
  17. ИгорьК

    ИгорьК Гуру

    Здесь callback не вызов функции, а ссылка на нее - скобок то нет. Функция будет вызвана не до отработки client.set_callback а по результатам ее работы.
    Это настолько просто, что... сразу и не поймешь.
     
  18. ASU5

    ASU5 Нерд

    Игорь, спасибо вам за ваши труды.
    Запустил ваш код на ESP8266 ESPRUINO v1.96, для отправки данных о температуре, с двух DS18B20, на народный мониторинг.
    Подключил экран на HD44780, но не знаю каким образом поиметь данные о температуре из обьекта "dattable", чтобы вывести их на ЖКИ.
    Пробовал через глобальные переменные, но ни фига у меня не вышло.
    Код (C++):
    var HT = 0;
    var OT = 0;

    start = function(){
        var ow = new OneWire(D2);
        var sensors = ow.search().map(function (device) {
            return require("DS18B20").connect(ow, device);
        });

        sensors.forEach(function (sensor, index) {
            cl(sensor, index);
        });

        setInterval(function() {
            sensors.forEach(function (sensor, index) {
                cl("\n");
                let dska = 0;
                sensor.getTemp(function (temp) {
                    var tt = temp.toFixed(2);
                    dska = sensors[index].sCode;
                    dattable[dska] = tt;
                    cl(dska + ": " + tt + " C");
                    dska = sensors[0].sCode;
                    OT = dattable[dska];
                    cl(OT);
                    dska = sensors[1].sCode;
                    HT = dattable[dska];
                    cl(OT);
                  });
            });
        }, 15000);
    };

    function printlcd() {     //пробовал писать function printlcd(OT,HT) { - не помогло
        var i2c = new I2C();
        i2c.setup({scl:D5,sda:D4, bitrate:100000});
        var lcd = require("HD44780").connectI2C(i2c,0x3F);
        lcd.setCursor(0,0);
        lcd.print("HomeTemp "+HT+" \x22"+"C");
        lcd.setCursor(0,1);
        lcd.print("OutdoorTemp "+OT+" \x22"+"C");
        lcd.setCursor(16,0);
        lcd.print("IP: "+wifi.getIP().ip);
    }
    При таком варианте, на ЖКИ вместо температуры выводится ноль в обох строках, хотя в консоле видно, что переменным "OT" и "HT" температура присвоена. Но она сволось не присваевается за пределами функции.

    Если встроить код вывода на ЖКИ в функцию наполнения объекта "dattable", то все работает, но есть одно "но". Экран ЖКИ будет обновляеться столько раз, сколько датчиков подключено, а это не есть гуд.

    Прошу помощи гуру, так как сам только начинаю осваивать JS на Espruino.
    Каким образом можно передать данные из "dattable" в функцию вывода на ЖКИ?
    Как это правильно сделать?
     
  19. ИгорьК

    ИгорьК Гуру

    Я далеко от компьютера, поэтому только общие советы.
    1. Данные втыкаются в таблицу dattable - она должна стать глобальной, объявите ее вначале скрипта.
    2. Выводить данные на дисплей надо через конструкцию dattable.forEach(...
    в зависимости от того, как эта таблица устроена.
    3. dattable.forEach(... вызывать через отдельный таймер.

    Замечу, что я почти не знаю JavaScript, я больше по Lua. И, к сожалению, нет компьютера сейчас.
     
  20. ASU5

    ASU5 Нерд

    И на том спасибо, буду ковырять дальше.