Как пощупать батарею по SMS

Тема в разделе "Глядите, что я сделал", создана пользователем andrei.mejov, 5 янв 2019.

  1. andrei.mejov

    andrei.mejov Нерд

    Привет всем!

    Решил поделиться собственной поделкой, которую, будучи человеком с относительно кучерявыми руками(и без паяльника), сумел-таки изготовить. Получилось простое, но работающее и практически полезное устройство.

    Итак, вводная:
    • не очень хорошо работающий котел в частном доме
    • уже начавшийся отопительный сезон, который надо пройти с тем что есть, оперативно реагируя на отключение отопления
    • ваш покорный слуга, практически без никаких навыков по железу
    В итоге, решил попробовать реанимировать свою же собственную, заброшенную, идею с контролем температуры отопления по SMS, собрав все на arduino с максимальным использованием готовых модулей и с минимальной работой руками.

    Состав "изделия" у меня получился следующим:
    • Iskra Neo
    • GRPS Shield
    • светодиод зеленый
    • пара резисторов (один в цепь к светодиоду на 220 Ом, второй, на 4.7кОм - к датчику температуры)
    • датчик температуры DS18B20
    • макетная плата для соединения без пайки
    • 3 одножильных провода "папа-мама", которые пошли на изготовление удлинителя для датчика температуры (единственное место где есть пайка)
    • пищевой контейнер в качестве корпуса
    • microUSB зарядка как блок питания
    • несколько болтов с гайками м3 и капроновых стяжек для фиксации внутри корпуса искры с шилдом, макетной платы и кабеля от зарядки с удлинителем.
    Схема получилась примерно такая, с поправкой на то, что в реальном устройстве сверху устанавливается GPRS шилд, через который уже идут все соединения:
    [​IMG]

    Вид в сборе без крышки:
    [​IMG]

    Все максимально просто, на основе примеров по каждому компоненту. Датчик снимает температуру с батареи, зеленый светодиод мигает, сигнализируя о нормальной работе основного цикла программы.При наличии SMS запроса формируется ответ со снятой температурой, при падении температуры ниже 30 (если до этого она поднималась выше) посылается SMS с предупреждением на номер забитый в скетче.

    Скетч:
    Код (C++):
    #include <GPRS_Shield_Arduino.h>
    #include <sim900.h>
    #include <OneWire.h>

    #define DEBUG 1
    #define ALERT_TEMP 30.0
    #define ALERT_PHONE "___"
    #define ALERT_PHONE_2 "___"

    OneWire ds(10);
    GPRS gprs(Serial1);

    char _message[160];
    char _phone[16];
    char _datetime[24];

    int alert_control = 0;

    void led_ok() {
        digitalWrite(12, HIGH);
        delay(1000);
        digitalWrite(12, LOW);
    }

    double get_term() {
        byte i;
        byte present = 0;
        byte type;
        byte data[12];
        byte addr[8];
        if (!ds.search(addr)) {
        if (DEBUG)
            Serial.println("No more adresses.");
            ds.reset_search();
            delay(250);
            return 0.0;
        }
        if (DEBUG) {
            Serial.print("ROM =");
            for (i = 0; i < 8; i++) {
                Serial.print(" ");
                Serial.print(addr[i], HEX);
            }
            Serial.println("");
        }
        ds.reset();
        ds.select(addr);
        ds.write(0x44);
        delay(1000);
        present = ds.reset();
        ds.select(addr);
        ds.write(0xBE);
        if (DEBUG) {
            Serial.print("Data = ");
            Serial.print(present, HEX);
            Serial.print(" ");
        }
        for (i = 0; i < 9; i++) {
            data[i] = ds.read();
            if (DEBUG)
            Serial.print(data[i], HEX);
        }
        if (DEBUG)
            Serial.println("");
        int16_t raw = (data[1] << 8) | data[0];
        byte cfg = (data[4] & 0x60);
        if (cfg == 0x00) {
            raw = raw & -7;
        } else if (cfg == 0x20) {
            raw = raw & -3;
        } else if (cfg == 0x40) {
            raw = raw & -1;
        }

        ds.reset_search();
        double term = (double)raw / 16.0;
        if (DEBUG) {
            Serial.print("T=");
            Serial.println(term);
        }
        return term;
    }

    char* has_request_from() {
        if (gprs.ifSMSNow()) {
            delay(100);
            gprs.readSMS(_message, _phone, _datetime);

            if (DEBUG) {
                Serial.print("From number: ");  
                Serial.println(_phone);
                Serial.print("Datetime: ");
                Serial.println(_datetime);
                Serial.print("Recieved Message: ");
                Serial.println(_message);
            }
            String s = String(_message);
            if (s == String("451")) {
                return _phone;
            } else {
                return 0;
            }
        }
        return 0;
    }

    void send_term(char *into, double term, int alert) {
        char term_str[10];
        char message[12];
        dtostrf(term,2,0,term_str);
        if (alert == 0) {
            sprintf(message, "T=%s", term_str);
        } else {
            sprintf(message, "AT=%s", term_str);
        }
        if (DEBUG) {
            Serial.print("Send SMS: ");
            Serial.print(message);
            Serial.println("");
        }
        gprs.sendSMS(into, message);
    }

    void setup() {
        pinMode(12, OUTPUT);
        Serial1.begin(9600);
        if (DEBUG)
            Serial.println("Serial init OK");
        gprs.powerOn();
        while (!gprs.init()) {
            delay(1000);
            if (DEBUG)
            Serial.println("Init error");
        }
        if (DEBUG)
            Serial.println("GPRS init success");
    }

    void loop() {
        led_ok();
        double term = get_term();
        if (alert_control == 0 && term >= ALERT_TEMP) {
            alert_control = 1;
        }
        char* from = has_request_from();
        if (from != 0) {
            if (DEBUG) {
                Serial.println("Send temperature!");
            }
            send_term(from, term, 0);
        }
        if (alert_control == 1 && term < ALERT_TEMP) {
            alert_control = 0;
            send_term(ALERT_PHONE, term, 1);
            send_term(ALERT_PHONE_2, term, 1);
        }
    }
    В принципе, на этом этапе уже был весь нужный мне функционал и взаимодействовать можно было с любого телефона через SMS, отправляя "451" в тексте, но хотелось большего и была написана отдельная программка для Android, реализующая некий пользовательский интерфейс поверх все тех же SMS, номер телефона опрашиваемой симки при этом задается в настройках:

    [​IMG]

    Исходники прилагаю.

    В целом же, если не считать редких (раз в N недель) зависаний где-то на опросе GRPS шилда все работает достаточно стабильно и свою задачу по отслеживанию температуры отопления выполняет. В эксплуатации с начала ноября.

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

    Вложения:

    • term.zip
      Размер файла:
      136,8 КБ
      Просмотров:
      198
    alp69 нравится это.
  2. andrei.mejov

    andrei.mejov Нерд

    Ну что я могу сказать, отопительный сезон с этой поделкой пройден.
    Отключения котла отслеживались исправно. Единственную правку пришлось внести в клиента для смартфона, в классе MainActivity.java надо поменять местами строчки 78 и 79, чтобы получилось:

    sms.getText()
    .replace("AT=", "")
    .replace("T=", "")

    В целом же SMS транспорт показал себя не самым удачным и технологичным решением - вай-фай модуль и какой-нибудь самописный телеграмм бот, возможно, были бы лучше (хоть и с дополнительной зависимостью от ростелекома с роскомнадзором).
     
  3. ИгорьК

    ИгорьК Гуру

    Эммм... http://forum.amperka.ru/threads/Контроль-работы-газового-электрического-твердотельного-котла.17310/
     
  4. andrei.mejov

    andrei.mejov Нерд

    В рамках работы над v2, играюсь с искрой жс, сделал работающий в наших условиях класс для телеграм бота
    Код (Javascript):
    function Telegram(_http, _proxyList, _botName, _botToken, _resetOnError) {
        var http = _http;
        var proxyList = _proxyList;
        var botName = _botName;
        var botToken = _botToken;
        var self = this;
        this.lastUpdateId = null;
     
        this.invokeGet = function(url, onData, onError, proxyIndex) {
            var i = Math.round(Math.random() * (proxyList.length - 1));
            if (proxyIndex) {
                i = proxyIndex;
            }
            var host = proxyList[i].split(":")[0];
            var port = proxyList[i].split(":")[1];
            console.log("Use proxy " + host + ":" + port);
            var options = {
                host: host,
                protocol: "https",
                port: port,
                path: url,
                method: "GET",
            };
            http.get(options, function(res) {
                var contents = "";
                res.on('data', function(data) {
                    contents += data;
                });
                res.on('close', function() {
                    onData(contents, i);
                });
            });
        };
        this.sendMessage = function(chatId, text, proxyIndex) {
            self.invokeGet(
                "https://api.telegram.org/bot" + botToken + "/sendMessage?chat_id=" + chatId + "&text=" + text,
                function(data) {
                    console.log("Message sent:" + text);
                },
                function(error) {
                },
                proxyIndex
            );
        };
        this.readMessages = function() {
            self.invokeGet(
                "https://api.telegram.org/bot" + botToken + "/getUpdates" +
                (self.lastUpdateId ? "?offset=" + (self.lastUpdateId + 1) : ""),
                function(data, proxyIndex) {
                    //console.log("Messages: " + data);
                    var res = JSON.parse(data);
                    self.onMessage(self, res, proxyIndex);
                },
                function(error) {
                  console.log(error);
                }
            );
        };
        this.onMessage = function(self, data, proxyIndex) {};
     
        this.invokeGet(
            "https://api.telegram.org/bot" + botToken + "/getMe",
            function(data) {
                var res = JSON.parse(data);
                if (res.result.first_name == botName) {
                    console.log("Bot found: " + res.result.first_name);
                    setInterval(self.readMessages, 5 * 1000);
                }
            },
            function (error) {
                setTimeout(function() {
                    if (_resetOnError) {
                        reset();
                        load();
                    }
                }, 5 * 1000);
            }
        );
    }
     
    Пример использования в варианте эхо бота, не работает кирилица в уникоде
    Код (Javascript):
    ...
    var telegram = new Telegram(http, proxyList, botName, botToken, true);
        telegram.onMessage = function(self, data, proxyIndex) {
            for (var i in data.result) {
                var m = data.result[i];
                var chatId = m.message.chat.id;
                var from = m.message.from.first_name + " " + m.message.from.last_name;
                var text = m.message.text;
                console.log("Chat:" + chatId);
                console.log("From: " + from);
                console.log("Text: " + text);
                self.lastUpdateId = m.update_id;
                console.log("UpdateId:" + self.lastUpdateId);
                self.sendMessage(chatId, "->" + text, proxyIndex);
            }  
        };
    Список проксей можно наполнить поковырявшись в списках "free proxy", выбирая те, которые работают поверх http/https (URL целевого ресурса передается в HTTP запросе). 10-ок пригодных за час легко можно нацыганить.
     
    Последнее редактирование: 23 июн 2019
  5. andrei.mejov

    andrei.mejov Нерд