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

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

  1. ИгорьК

    ИгорьК Оракул Модератор

    15.2.1. Еще код для Ардуино.
    Все данные, как управляющие, так и мониторинговые заталкиваются в массив, а оттуда последовательно отправляются брокеру. Так правильнее. По крайней мере в случае с OpenHAB. Если вы отправляете данные на какой-то другой сервис, иногда можно без этого обойтись, а иногда - вообще запрещено отправлять данные чаще чем через 5 минут.

    Для тестирования заливаем в Ардуино, соединяемся с ней через COMпорт, смотрим что там она выдает, а также посылаем команды, типа 13:1 или 13:0 . Наблюдаем за состоянием. Когда все устраивает, удаляем все информационные выводы в Serial, которые помечены. Должны остаться только те, которые отправляют данные.
    Дальше Ардуино соединяем с ESP8266.

    Код (C):
    String command;        // String для чтения всей команды
    String temp1,temp2;      // Временные
    char inByte;              // Byte для чтения
    int pin;                // Нога для управления
    int data;              // Данные для управления
    int place = 0;          // Позиция, где обнаруживается знак "двоеточие".
                // Когда следующий опрос датчика
    unsigned long timeToCheckPins;
                // Задержка между опросами
    #define DELAY_TO_NEXT_CHECK_PINS 10000
    int pinMassive[4] [2];    // Массив, куда складываются текущие значения датчиков и состояния ног

    int nextPin = 0;          // Первая ячейка массива с информаией для оповещения брокера

    void setup(){
        Serial.begin(9600);
        Serial.println("Programm: UARTTest001004"); // Это удалить потом
        pinMode(13, OUTPUT);
        digitalWrite(13,0);
        timeToCheckPins = millis()+ DELAY_TO_NEXT_CHECK_PINS;
        pinMassive[0][0]=9;  // Забиваем в массив номера управляемых ног и ног данных
        pinMassive[1][0]=13;
        pinMassive[2][0]=14;
        pinMassive[3][0]=15;
    }
    // 3:22.35 -  формат отправки и получения данных

    void loop(){
      if (Serial.available() > 0){ // Читаем UART и формируем управляющий стринг
        inByte = Serial.read();
        if ((inByte >= 48 && inByte <=58) || inByte ==46 ) { // Цифры, двоеточие и точка
        command.concat(inByte); // command - управляющий стринг
        }
      }
      if  (inByte == 0x0A || inByte == 0x0D) { // Если знак перевода или возврата каретки
        inByte = 0; // Обнуляем
        if (command != "") { // Это удалить потом
          Serial.println(command);
        }
        place = command.indexOf(":"); // Находим позицию ":"
      if (place == 1 || place == 2) { // Если она первая или вторая, то есть первая цифра из одного или двух знаков, иное - ошибка
        Serial.println("Yes!"); // Это удалить потом
        temp1 = command.substring(0,place); // Стринг номера управляемой ноги
        temp2 = command.substring(place+1); // Стринг полученного значения
        Serial.println(temp1); // Это удалить потом
        Serial.println(temp2); // Это удалить потом
        pin = temp1.toInt();  // Переводим стринги в цифры
        data = temp2.toInt();

        if ( pin == 9) { // Здесь ваши условия, в зависимости от управляемых ног
            analogWrite(pin,data);
            pinMassive[0][1]=data;
        }
        else if ( pin == 13) {  // Если набрать 13:0 или 13:1 можно зажигать диод на плате
            digitalWrite(pin,data);
            pinMassive[1][1]=data;
        }
        place = 0;
      }
      command = "";
      }

      if(millis() > timeToCheckPins) {  // Если пришло время проверки, то определяем новое
        timeToCheckPins = millis()+ DELAY_TO_NEXT_CHECK_PINS;
        pinMassive[2][1] = analogRead(A0); // Читаем данные с аналоговых ног в массив
        pinMassive[3][1] = analogRead(A1);

        String toUART = (String) pinMassive[nextPin][0]; // Формируем стринг для отправки через UART
        toUART += ":";
        toUART += (String) pinMassive[nextPin][1];
        Serial.println(toUART);
        nextPin++; // Выбираем пин для чтения в следующий раз
        if (nextPin >=4) { // Есди дошли до последнего - возвращаемся к первому
          nextPin = 0;
        }
      }
    }
    Дальше.
     
    Последнее редактирование: 11 июл 2016
  2. ИгорьК

    ИгорьК Оракул Модератор

    15.2.2 Пруф, как это работает на стороне Ардуино.
    Немое кино. Субтитры заранее.
    Соединены вместе Ардуино Нано, блочок питания на 3.3 вольта, переходник UART 3в/5в и ESP8266.
    В ESP8266 код как в соответствующем посте. В Нано - еще чуть допиленный код, добавлен софтверный сериал, чтобы по обычному наблюдать картину. На видео она слева, под основным окошком.
    Основное окно - MQTT Spy - он ловит сигналы от системы и выводит внизу на желтом фоне. На верхней его части я отправляю команду 13 ноге, которая успешно зажигает и гасит диодик.



    Код с софтверным сериалом. Надеюсь, как все это приаттачить к OpenHAB уже объяснять не нужно. А если нужно смотрим сообщения с тикетом 15.2.3. ниже.

    Код (C):
    #include <SoftwareSerial.h>
    // software serial #1: RX = digital pin 4, TX = digital pin 5
    SoftwareSerial portOne(4, 5);

    String command;    // String для чтения всей команды
    String temp1,temp2;  // Временные
    char inByte;      // Byte для чтения
    int pin;        // Нога для управления
    int data;      // Данные для управления
    int place = 0;      // Позиция, где обнаруживается знак "двоеточие".
                // Когда следующий опрос датчика
    unsigned long timeToCheckPins;
                // Задержка между опросами
    #define DELAY_TO_NEXT_CHECK_PINS 10000
    int pinMassive[4] [2]; // Массив, куда складываются текущие значения датчиков и состояния ног

    int nextPin = 0;  // Первая ячейка массива с информаией для оповещения брокера

    void setup(){
      Serial.begin(9600);
      portOne.begin(9600);
      delay(3000);
      Serial.println("Programm: UARTTest001006"); // Это удалить потом
      pinMode(13, OUTPUT);
      digitalWrite(13,0);
      timeToCheckPins = millis()+ DELAY_TO_NEXT_CHECK_PINS;
        pinMassive[0][0]=9;
        pinMassive[1][0]=13;
        pinMassive[2][0]=14;
        pinMassive[3][0]=15;
    }
    // 3:22.35 -  формат отправки и получения данных

    void loop(){
      if (portOne.available() > 0){ // Читаем UART и формируем управляющий стринг
        inByte = portOne.read();
        if ((inByte >= 48 && inByte <=58) || inByte ==46 ) { // Пропускаем лишь цифры, двоеточие и точку
        command.concat(inByte); // command - управляющий стринг
        }
      }
      if  (inByte == 0x0A || inByte == 0x0D) { // Если знак перевода или возврата каретки
        inByte = 0; // Обнуляем
        if (command != "") { // Это удалить потом
          Serial.println(command);
        }
        place = command.indexOf(":"); // Находим позицию ":"
      if (place == 1 || place == 2) { // Если она первая или вторая, то есть первая цифра из одного или двух знаков, иное - ошибка
        Serial.println("Yes!"); // Это удалить потом
        temp1 = command.substring(0,place); // Стринг номера управляемой ноги
        temp2 = command.substring(place+1); // Стринг полученного значения
        Serial.println(temp1); // Это удалить потом
        Serial.println(temp2); // Это удалить потом
        pin = temp1.toInt();  // Переводим стринги в цифры
        data = temp2.toInt();

        if ( pin == 9) { // Здесь ваши условия, в зависимости от управляемых ног
            analogWrite(pin,data);
            pinMassive[0][1]=data;
        }
        else if ( pin == 13) {  // Если набрать 13:0 или 13:1 можно зажигать диод на плате
            digitalWrite(pin,data);
            pinMassive[1][1]=data;
        }
        place = 0;
      }
      command = "";
      }



      if(millis() > timeToCheckPins) {  // Если пришло время проверки, то определяем новое
        timeToCheckPins = millis()+ DELAY_TO_NEXT_CHECK_PINS;
        pinMassive[2][1] = analogRead(A0); // Читаем данные с аналоговой ноги
        pinMassive[3][1] = analogRead(A1); // Читаем данные с аналоговой ноги
        String toUART = (String) pinMassive[nextPin][0]; // Формируем стринг для отправки через UART
        toUART += ":";
        toUART += (String) pinMassive[nextPin][1];
        portOne.println(toUART);
        Serial.println(toUART);
        nextPin++; // Выбираем пин для чтения в следующий раз
        if (nextPin >=4) { // Есди дошли до последнего - возвращаемся к первому
          nextPin = 0;
        }
      }
    }
    Дальше.
     
    Последнее редактирование: 11 июл 2016
  3. killeo

    killeo Нуб

    Можно подключить к GPIO на RPI ds18b20, кнопку, реле сразу без arduino и пр.? Биндинги вроде как есть, а какие пакеты доставить, как конфиг и items выглядят?
     
  4. ИгорьК

    ИгорьК Оракул Модератор

    Подключить можно, биндинг есть, но я никогда этим не занимался. Опыта нет.
    Быстрый взгляд на мануалы позволяет сделать вывод о том, что есть два пути:
    1. Первый описан по адресу https://github.com/openhab/openhab/wiki/GPIO-Binding
    Но воткнуться в него я не смог. Более того, по этому адресу ничего не говорится и на тему малины.
    2. Второй - отработать GPIO стандартными для малины путями, а затем связать их с OpenHAB через REST API. По этому адресу есть четыре примера, как связаться с OpenHAB путем программирования Shell, Python, JQuery и PHP.
    К сожалению, я и стандартными путями никогда не работал с GPIO малины - я уже вообще не помню где она у меня находится: знаю лишь ее IP-адрес. Не помню ни разу, чтобы ее приходилось перезагружать кроме как через PuTTy.
    Будет здорово, если вы разберетесь и поделитесь своими результатами.
     
  5. Доброго времени суток ИгорьК!
    Прошу все же подсказать как правильно парсить строку в openHAB данных при связке Arduino+ESP.
    Я столкнулся с проблемой, MQTT Spy, видит и принимает все значения, но openHAB почему то нет.
    Строка биндинга выглядит так:<[mosquitto:/arduino/15/state:state:default], но значений я не получаю.
    Смею предположить, что когда я создаю новый item, я выбираю не тот тип данных...
    Не могу понять куда копать, наставьте на путь истинный)
    За ранее премного благодарен вам за помощь, и за все проекты в общем!
    p.s. Учусь по вашим проектам)
     
  6. ИгорьК

    ИгорьК Оракул Модератор

    А что там у Вас от Ардуино идет? Какие, конкретно, данные?
    Вот пример биндинга для вкл/выкл, то есть, когда передаются команды ON, OFF:
    Код (Java):
    Switch lamp "Моя лампа" { mqtt=">[mosquitto:/myhome/out/lamp01/command:command:on:ON],>[mosquitto:/myhome/out/lamp01/command:command:off:OFF],<[mosquitto:/myhome/out/lamp01/state:state:default]" }
    Или у Вас вопрос по последнему примеру?
    15.2.3 .(а)
    Если по последнему, то принимать любой цифровой сигнал (для датчиков), следует не на итем Switch а на итем типа Number:
    Код (Java):
    Number ArduinoA15 "Arduino 15" { mqtt="<[mosquitto:/arduino/15/state:state:default]" }
     
    Последнее редактирование: 3 сен 2015
  7. ИгорьК

    ИгорьК Оракул Модератор

    15.2.3.(б)
    Теперь что касается "0" и "1" как выключено/включен0.
    Предположим, у вас 13 нога включается и выключается.
    Создадим такой итем:
    Код (Java):
    Switch Arduino13 "Arduino 13" { mqtt=">[mosquitto:arduino/13/command:command:on:1],>[mosquitto:arduino/13/command:command:off:0],<[mosquitto:arduino/13/state:state:MAP(switchMQTT.map)]" }
    Обратим внимание на следующие особенности:
    "/command:command: on:1" этот кусочек биндинга говорит выключателю: "если тебя включили - отправь в MQTT цифру 1 вместо команды ON . То же касается команды OFF: вместо нее передается 0.
    Теперь смотрим на кусочек "13/state:state:MAP(switchMQTT.map)]" .
    Это команда на обратную трансформацию получаемого сигнала о состоянии 13 ноги, которая возвращается тоже в виде 0 и 1.
    Чтобы провести эту трансформацию, в папку Transform надо положить файл, с названием switchMQTT.map и таким содержанием:
    Код (Text):
    0=OFF
    1=ON
    Теперь все должно заработать.
     
    Последнее редактирование: 27 окт 2015
  8. Спасибо большое за объяснение, даже наперед предвидели мои вопросы))
    С проблемой разобрался, видимо за целый день глаза замылились и я не замечал лишнего слеша.
    Благодарен вам за ваш труд и помощь в начинаниях для новичков как я.
    Спасибо!
     
  9. ИгорьК

    ИгорьК Оракул Модератор

    15.2.3.(В) Пруф, как это работает на стороне OpenHAB.
    Субтитры:
    Справа в окне три итема, которые связаны с ногами А1, А2 и 13 Ардуино. Сигналы от ардуино и на нее эмулируются MQTTSpy - левая часть видео.



    Созданы Итемы:
    Код (Java):
    Number    ArduinoA1    "Arduino A1 [%.2f °C]"    <temperature>    (Test)    { mqtt="<[mosquitto:arduino/14/state:state:default]" }
    Number    ArduinoA2    "Arduino A2 [%.2f °C]"    <temperature>    (Test)    { mqtt="<[mosquitto:arduino/15/state:state:default]" }
    Switch    Arduino13    "Arduino 13" { mqtt=">[mosquitto:arduino/13/command:command:on:1],>[mosquitto:arduino/13/command:command:off:0],<[mosquitto:arduino/13/state:state:MAP(switchMQTT.map)]" }
    И вставлены в сйатмэп:
    Код (Java):
    Frame {
            Text item=ArduinoA1
            Text item=ArduinoA2
            Switch item=Arduino13
        }
     
    Последнее редактирование: 27 окт 2015
  10. ИгорьК

    ИгорьК Оракул Модератор

    Последнее редактирование: 14 сен 2015
    Securbond и valeraba нравится это.
  11. ИгорьК

    ИгорьК Оракул Модератор

    16. GPIO Raspberry Pi.
    Очередное кино. На этот раз будем управлять GPIO самой Малины
    , на которой сидит OpenHab. Сюжет фильма. В черной коробочке спряталась Малина. Из нее идут провода, коими передается информация к модулю MT1132 от Ноолайт, а также два секретных провода (черный и серый) к светодиоду. В фильме его роль исполняет зеленый светодиод. В правой части видно тестовое окно OpenHab c кнопкой. Кнопка называется Blink Dist. Это ее реальное имя.
    Нажимание на кнопку освобождает яростное желание светодиода выдать свет, но повторное нажатие гасит его изо всех сил.

     
    Последнее редактирование: 27 окт 2015
  12. valeraba

    valeraba Нерд

    Хорошая работа. А чтобы посмотреть исторические данные за другие дни, что вы делаете? Просто, никакой навигации для этого, я не углядел.
     
  13. ИгорьК

    ИгорьК Оракул Модератор

    Есть ряд тонкостей. Я делаю (и пользуюсь ими) графики только за сутки.
    Можно создавать графики и на больший период. Вместе с тем, система хранения информации по умолчанию -rrd4. Она хранит полную информацию о данных в течение месяца, а потом начинает усреднять ее. В усредненном виде - она так себе. Полезность ее сомнительна, по крайней мере - для меня.
    Выход - настраивать другие виды хранения: db4o или MySql. Но мне это не особо нужно.
    В общем, решение есть, но я им не занимался. Для контроля состояния дачи этого мне вполне хватает.
    - кроме того, все это полностью передается на сайт Народного Мониторинга. Там тоже можно посмотреть.
    - и еще (забыл уже, не пользуюсь) - в приложении Habmin все сроки можно задавать вручную. Ссылок нет, потому что это надстройка над Openhab.
    Вот как это выглядит за месяц:
    22334.jpg
     
    Последнее редактирование: 22 сен 2015
  14. ИгорьК

    ИгорьК Оракул Модератор

    16.1. GPIO Raspberry Pi. Как это устроено.
    Кратко.
    Устанавливаем библиотеку bcm2835 http://www.airspayce.com/mikem/bcm2835/
    В директории /root/myprog/ пишем две программки.
    GPIO18-on.c:
    Код (C++):

    #include <bcm2835.h>
    // http://elinux.org/RPi_Low-level_peripherals#Interfacing_with_GPIO_pins
    #define PIN RPI_V2_GPIO_P1_12 //Для RPi ревизии v2

    int main()
    {
      int step;
    if (!bcm2835_init())  // Инициализация GPIO
      return 1;   //Завершение программы, если инициализация не удалась
    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);  //Устанавливаем порт 1_12 на вывод
    bcm2835_gpio_write(PIN, HIGH);  // Устанавливаем порт в 1, светодиод горит
      return 0;  // Выход из программы
    }
     
    GPIO18-off.c :
    Код (C++):

    #include <bcm2835.h>
    #define PIN RPI_V2_GPIO_P1_12 // RPi ревизии v2
    int main()
    {
      int step;
    if (!bcm2835_init())  //  GPIO
      return 1;   //Завершение программы, если инициализация не удалась
    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);  //Устанавливаем порт Ц1_12 на вывод
    bcm2835_gpio_write(PIN, LOW);  // Устанавливаем порт в 0, светодиод не горит
      return 0;  // Выход из программы
    }
     
    Компилируем:
    Код (C++):
    gcc -o GPIO18-on GPIO18-on.c -lrt -lbcm2835
    gcc -o GPIO18-off GPIO18-off.c -lrt -lbcm2835
    Создаем Item и помещаем в Sitemap:
    Код (Java):
    Switch  Blink_ON_OFF  "Blink On Off"   { exec="ON:/root/myprog/GPIO18-on, OFF:/root/myprog/GPIO18-off"}
    Биндинг:
    Код (Bash):
    apt-get install openhab-addon-binding-exec
    Изготавливаем вот такое устройство:
    [​IMG]
    И, собственно, все должно заработать.
    UPD 29.08.2016. Или не заработать.
    После установки через apt-get у ОпенХаб возникают конфликты с правами доступа к GPIO.
    Поэтому, делаем так:
    gpio1.jpg

    Или, вместо указанного выше, следует осуществить пару коррекций:
    Код (Bash):
    # /etc/default/openhab
    USER_AND_GROUP=root:root

    #/usr/lib/systemd/system/openhab.service
    User=root
    Group=root
    После чего:
    Код (Bash):
    service openhab restart
     
    Последнее редактирование: 17 окт 2016
  15. ИгорьК

    ИгорьК Оракул Модератор

    17. Noolite MT1132 - управляем светом.
    Следующий фильм. Фильм рассказывает о тяжелой жизни силового блока и модуля Noolite, которых заставляют работать через интернет. В частности, MT1132 получает команды непосредственно от UART Малины. Один из наших героев остается за кадром, и по команде лишь освещает жизнь Малине и MT1132.
    Актером второго плана выступает зеленый светодиод, который тоже навечно привязан к UART Raspberry Pi.
    На зднем плане можно заметить готовящегося вступить в игру нового героя - nRF24L01+



    И еще видео. Модуль MT1132 имеет очень хорошую фичу: если команда на него послана правильно, он отзывается красным огонечком. В первой серии это не слишком заметно, поэтому пришлось снять вторую:



    А теперь меняем яркость:



    Продолжение.
     
    Последнее редактирование: 30 мар 2016
  16. opyzhov

    opyzhov Нуб

    Игорь добрый день!
    Интересные у вас статьи.. во многом помогают..

    Накопилось пару вопросов.. может поможете разобраться..
    Смонтирована система домашней автоматизации на базе ПЛК с протоколом modbus/tcp. Это основной мозг. Теперь пытаюсь добавить к системе "умностей" посредством raspberry pi + openhab. (raspberry выступает как шлюз во внешний мир)

    есть устройство с GPS .. к нему обращение можно сделать по API
    я делаю (curl "http://api.name.......content=json")
    в ответ получаю строку типа
    (
    {"gps":{"eventId":60xxx,"latitude":59.xxxxxx,"longitude":30.xxxxxx,"eventTime":1443632038187}}
    )

    либо если
    content=xml
    (
    <?xml version="1.0" encoding="UTF-8"?><last>eventId="60xxx" eventTime="1443632038187" latitude="59.xxxxxx" longitude="30.xxxxxx"/></last>
    )
    как бы мне это перевести в переменные openhab. и параметры времени не очень понятны ..
     
  17. ИгорьК

    ИгорьК Оракул Модератор

    Со временем все просто: https://ru.wikipedia.org/wiki/UNIX-время Там есть и пример на С как с ним работать.
    Ну а что касается GPS - что Вы хотите сделать в итоге? О каких переменных идет речь?
    Вот пример OpenHab как работать с JSON: https://github.com/openhab/openhab/wiki/JSON-Transformations
     
    Последнее редактирование: 1 окт 2015
  18. Привет АрдуБратья!
    Наконец-то пришли мои датчики из поднебесной!
    И столкнулся с проблемой, публикации нескольких данных...
    Пересмотрел пункт 5.3, без комментариев сложновато разобраться что и для чего...
    Хотелось бы все же понять, как правильно опубликовать данные с DHT22?
    Взял по образу и подобию кода из пункта 5.2, но почему-то публикует только одну переменную...

    Код (C++):
    m = mqtt.Client("******", 180, "***", "***")
    m:on("offline", function(con)
        print ("reconnecting...")
        print(node.heap())
        tmr.alarm(1, 10000, 0, function()
              m:connect("****", 1883, 0)
              collectgarbage()
        end)
    end)

    tmr.alarm(1, 30000, 1, function()
    if wifi.sta.status() == 5 then
        PIN = 4
        dht22 = require("dht22")
        dht22.read(PIN)
        t = dht22.getTemperature()
        h = dht22.getHumidity()
        if h == nil then
           print("Error reading from DHT22")
        else
           m:publish("/myhome/out/TemperatureOut/state",t,0,0)
           print("Temperature: "..((t-(t % 10)) / 10).."."..(t % 10).." deg C")
           m:publish("/myhome/out/HumidityOut/state",h,0,0)
           print("Humidity: "..((h - (h % 10)) / 10).."."..(h % 10).."%")
    end
    dht22 = nil
    package.loaded["dht22"]=nil
        collectgarbage()
        print(node.heap())
    end
    end)
    tmr.alarm(0, 1000, 1, function()
    if wifi.sta.status() == 5 then
        tmr.stop(0)
        m:connect("*****", 1883, 0, function(conn)
              print("connected")
        end)
    end
    end)
     
  19. opyzhov

    opyzhov Нуб

    > Ну а что касается GPS
    В итоге идея такая:
    переменная типа Number - расстояние от заданных координат до координат по GSM.
    переменная типа Number - время последнего обновления координат.

    все это по modbus передается на плк.

    по вашей ссылке я смотрел .. но там формат другой судя по всему.. ковычки на значениях стоят..
    я так понимаю переменные типа Number я записываю в items с привязкой к modbus
    далее через rules делаю проверку раз в 5 минут например.. это все понятно..
    далее процедура сама.. как мне отпарсить json
    var String json = ???

    со временем попроще... на ПЛК я писал процедуру перевода можно туда просто переменную закинуть... у меня вчера просто перевод какие то странные цифры выдавал.. попробую еще раз..
     
  20. ИгорьК

    ИгорьК Оракул Модератор

    Так он действительно опубликует только одну. Публикация требует определенного времени и получения ответа от брокера. Причем, задержку таймером здесь ставить нельзя - обен информацией не страстется. Процессы, происходящие в модуле при этом скрыты от юзера.
    Поэтому и был сделан пример 5.3. Пробуйте его понять и использовать.
     
    Последнее редактирование: 2 окт 2015