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