Перейти к публикации
iT4iT.CLUB

Kitsum

Пользователи
  • Публикации

    424
  • Зарегистрирован

  • Посещение

  • Дней в лидерах

    234

Сообщения, опубликованные пользователем Kitsum


  1. 3 часа назад, marsergei76 сказал:

    Может загрузчик не прошился?

    Удалите из проекта вызов сторожевого таймера, я указывал ранее на нужные строки. Проверьте будет ли зависание, возможно Ваша проблема совсем не связана с программой.


  2. @Devilisimo еще раз доброе время суток. Озадачился я Вашим последним вопросом, пришлось найти микроконтроллер и все протестировать в живую.

    Что получилось в итоге - была выявлена проблема хранения адреса MQTT сервера. Точнее даже не проблема, просто моя не внимательность т.к библиотека PubSubClient обращается к переменной с адресом через указатель то хранить данную переменную в функции setupMQTT не представляется возможным из-за освобождения памяти после ей выполнения. Соответственно, к тому моменту, как планировщик переходит к заданию по контролю соединения с брокером, адреса сервера уже не существует. Я упустил этот момент, когда подгонял в голове работу издателя и подписчика в рамках services.h

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

    15.10.2018 в 16:29, Devilisimo сказал:

    Пока сделал тупо для вывода на экран. Ничего не получилось, хотя данные на броккер отправляются.

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

    15.10.2018 в 16:29, Devilisimo сказал:

    Еще вопрос как будут разделены данные при подписке на несколько топиков.

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

    15.10.2018 в 16:29, Devilisimo сказал:

    Почему Вы указываете название топика с двумя "/", т.е. может я как-то не так указываю адрес, хотя не телефоне все отображается?

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

    Вот как это выглядит на практике.

    mqtt-rw.jpg

    Синим выделены отправленное на брокер mqtt.it4it.club в топик test/esp8266/work сообщение.


  3. 5 часов назад, Devilisimo сказал:

    Можно ли сделать функцию числовую

    Это отдельная история, связанная с реализацией функции callback. Если Вы безоговорочно доверяете пришедшим данным от брокера, то в контексте описанной ранее функции можно преобразовать String в Int с помощью метода toInt

    msg.toInt();

    Но так лучше не делать, особенно в интернете.

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


  4. @Devilisimo доброе время суток. Боюсь, что у Вас все перемешалось. Давайте разбираться подробнее.

    В первую очередь необходимо понять, что из себя представляет MQTT протокол.

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

    Если смотреть со стороны издателя, то поддерживать постоянную связь нет необходимости. Когда требуется опубликовать новые данные издатель подключается к брокеру и передает адрес топика и полезную нагрузку. Далее просто отключается.

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

    Как вижу решения я

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

    void callback(char* topic, byte* payload, unsigned int length) {
      String msg;
      for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
      #ifdef console
        console.printf("\nСообщение[%s]: %s\n", topic, msg.c_str());
      #endif
    }

    Описать её можно в файле services.h, по хорошему её нужно назвать иначе, но мы уже начали использовать это имя поэтому я буду его придерживаться.

    У нас есть обработчик, теперь необходимо его задействовать. Для большего порядка создадим дополнительную функцию.

    void setupMQTT() {
      if (conf.param("mqtt_server").length()) {
        /* Указываем сервер и порт для подключения. Сервер можно указать через WEB интерфейс, порт измените на свой. */
        mqttServer = conf.param("mqtt_server");
        mqttAPI.setServer(mqttServer.c_str(), 1883);
      
        /* Устанавливаем обработчик */
        mqttAPI.setCallback(callback);  
      
        /* Задача для восстановления соединения */
        cron.add(cron::time_1m, [&](){
          if (!mqttAPI.connected() and wifi.transferDataPossible()) {
            if(mqttAPI.connect(WiFi.hostname().c_str(),
              (conf.param("mqtt_login").length() ? conf.param("mqtt_login").c_str() : 0),
              (conf.param("mqtt_pass").length() ? conf.param("mqtt_pass").c_str() : 0)
            )) {
              /* Список топиков на которые необходимо оформить подписку */
              mqttAPI.subscribe("test/esp8266/work");
            }
            #ifdef console
              console.printf("Connecting to MQTT server\nanswer: %s\n", mqttCodeStr(mqttAPI.state()).c_str());
            #endif
          }
        }, false);        
      }
      /* end if */
    }

    Думаю, тут все понятно. Функция описывает к какому серверу подключаться, устанавливает обработчик и добавляет в планировщик задачу для подключения к серверу в случае разрыва связи.

    В функции используется переменная объявленная в глобальном пространстве имен.

    String mqttServer;

    Объявить переменную можно прямо в файле services.h

    Вынос её за пределы функции обусловлен необходимостью сохранения имени MQTT сервера т.к библиотека PubSubClient использует обращается к ней через указатель. Все это очередное напоминание о необходимости доработки модуля хранения настроек конфигурации микроконтроллера.

    Данную функцию мы будем единожды вызывать при старте контроллера поэтому добавим её в конец функции setup в основном файле.

    void setup() {
      /* ... */
      
      setupMQTT();
    }

    Но этого все еще недостаточно чтобы мы начали получать сообщения. Необходимо добавить обработчик MQTT для завершения реализации режим "подписчика".

    void loop() {
      /* ... */
      
      /* Обработчик MQTT для режима "подписчика" */
      mqttAPI.loop();
    }

    Можно считать основную работу законченной, НО мы помним, что ранее ESP работал только в режиме издателя, а это значит, что мы можем попасть на ошибки, связанные с добавлением нового режима. Давайте все перепроверим и избавимся от них. Идем в функцию, отвечающую за публикацию данных с сенсоров метеостанции и удаляем из ней все, что связанно с подключением к брокеру т.к теперь эта задача возложена на плечи планировщика задач.

    /* mqtt.it4it.club */
    void sendDataToMQTT() {
      if (wifi.transferDataPossible() and conf.param("mqtt_server").length() and mqttAPI.connected()) {
        #ifdef console
          console.println(F("services: send data to MQTT server"));
        #endif
          
        mqttPublish("light",            sensors.get("out_light"));
        mqttPublish("temperature",      sensors.get("out_temperature"));
        mqttPublish("humidity",         sensors.get("out_humidity"));
        mqttPublish("pressure",         sensors.get("out_pressure"));
        mqttPublish("co2",              sensors.get("out_co2"));
        mqttPublish("absoluteHumidity", sensors.get("out_absoluteHumidity"));
    
        #ifdef console
          console.printf("answer: %s\n", mqttCodeStr(mqttAPI.state()).c_str());
        #endif
      }
    }

    Также обязательно убираем вызов метода отвечающего за разрыв соединения с брокером.

    mqttAPI.disconnect();

    А в основное условие с проверками возможности передачи данных добавляем проверку наличия подключения к MQTT брокеру.

    mqttAPI.connected();

    На этом можно считать модернизацию законченной.

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

    PS: если в ваших модификация для проекта присутствует дополнительный код, связанный с работой MQTT протокола, он должен быть упразднен или модифицирован в соответствии с описанной реализацией механизма "издатель-подписчик". Говорю об этом т.к в теме уже есть упоминания о таких модификациях от других форумчан и их использование будет приводить к постоянному разрыву соединения. Ну и как всегда, все проверяйте, т.к мои мысли могут быть ошибочны.


  5. @marsergei76 доброе время суток.

    Какой загрузчик залит в той или иной плате сказать не так просто. Но можно посмотреть какой загрузчик используется платформой Arduino в родной IDE. В корне среды разработки (windows) по адресу hardware/arduino/avr/boards.txt располагается файл с описанием всех настроек для той или иной платы.

    NANO

    nano.name=Arduino Nano
    # ...
    nano.menu.cpu.atmega328=ATmega328P
    # ...
    nano.menu.cpu.atmega328.bootloader.file=atmega/ATmegaBOOT_168_atmega328.hex
    # ...
    nano.menu.cpu.atmega168=ATmega168
    # ...
    nano.menu.cpu.atmega168.bootloader.file=atmega/ATmegaBOOT_168_diecimila.hex

    UNO

    uno.name=Arduino/Genuino Uno
    # ...
    uno.bootloader.file=optiboot/optiboot_atmega328.hex

    Так что в теории, с UNO быть проблем не должно, но это не точно. А вот с NANO проблемы будут однозначно.

    11 часов назад, marsergei76 сказал:

    перепрошить загрузчик на optiboot как описано здесь? 

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

    Смею заверить, что если у Вас проблемы с загрузчиком, то это все решаемо.

    Для начала, до подключения платы к компьютеру, зажмите Reset и не отпуская его подключите плату. Это должно остановить выполнение основного кода программы где активируется сторожевой таймер, при этом UART TTL контроллер определиться в системе и для него будет установлен драйвер. После этого запустите IDE, выберите Вашу плату и начните загрузку Blink. В тот момент, когда программа будет собираться (ближе к концу процесса) отпустите Reset. С этого момента начнется отсчет сторожевого таймера, TTL контроллер должен сбросить плату и начать загрузку программы.

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


  6. @pasha413 esp01 отличается от, например, esp12 только количество портов разведенных на плате и которыми Вы можете пользоваться. Во всем остальном это один и тот же контроллер. Все, что Вам нужно проконтролировать, так это чтобы на плате esp01 была распаяна flash память объемом не менее 4Mb.


  7. Доброе время суток

    13.10.2018 в 17:36, marsergei76 сказал:

    кнопка RESET одним контактом висит в воздухе, его куда цеплять на "землю" или на + питания?

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

    56 минут назад, marsergei76 сказал:

    С начало всё работало нормально, потом стало зависать

    Обратите внимание, что в программе задействован сторожевой таймер

    wdt_disable();
    wdt_enable(WDTO_8S);

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

    Также стоит исключить ошибку подключения всей обвязки. Разберите схему, оставьте только плату с микроконтроллером. После подключения её к компьютеру у Вас будет 8 секунд для загрузки другой программы, например, Blink.

    Оттолкнитесь от этой информации, все внимательно проверяйте.


  8. 12.10.2018 в 09:20, Devilisimo сказал:

    Как реализовать функцию чтения данных с сервера MQTT, для дальнейшего вывода на дисплей.

    Используйте возможности библиотеки PubSubClient, в её примерах есть интересующий Вас код. Сама библиотека используется метеостанцией и объект, через который необходимо работать, уже объявлен в файле services.h

    PubSubClient mqttAPI(wifiClient);

    Создайте функцию, отвечающую за обработку данных преступаемых от брокера (mqtt сервера) и опишите в ней весь необходимый функционал для обработки сообщений. Опишите её в основной (.ino) файле или в фале services.h, что более правильно.

    void callback(char* topic, byte* payload, unsigned int length) {
      /* Ваш код, подсказки смотрите в библиотеке PubSubClient */
    }

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

    void setup() {
    	/* ... */
      mqttAPI.setServer("mqtt.it4it.club", 1883);
      mqttAPI.setCallback(callback);
    }

    В функции loop необходимо добавить вызов обработчика, который будет отслеживать появление новых сообщений

    void loop() {
      /* ... */
      
      /* Тут не хватает одной функции для подключения/переподключения к брокеру */
      /* Обработчик MQTT */
      mqttAPI.loop();
    }

    Также Вам необходимо реализовать функцию, отвечающую за подключение и переподключение к брокеру. Без неё связь не будет установлена. Не стоит использовать функцию переподключения описанную в примерах библиотеки т.к в случае отсутствия связи с сервером вы получите практически постоянно висящий контроллер, что явно убьет весь остальной функционал. Я советую использовать планировщик задач с интервалом в 1, а лучше в 5 минут, который будет вызывать функцию проверки связи и подключаться к серверу если это необходимо.

    cron.add(cron::time_5m, [&](){
        if (!mqttAPI.connected() and wifi.transferDataPossible()) {
            if(mqttAPI.connect(WiFi.hostname().c_str())) {
                mqttAPI.subscribe("weather_station/lcd/topic_name");
            }
        }
    }, false);

    Топики, на которые необходимо оформить подписку указываются через метод subscribe

    mqttAPI.subscribe("weather_station/lcd/topic_name_1");
    mqttAPI.subscribe("weather_station/lcd/topic_name_2");
    mqttAPI.subscribe("weather_station/lcd/topic_name_3");

    Обратите внимание, что я указал false в качестве последнего параметра метода add. Это запрет холодного старта, хоть он по умолчанию и не используется, мы оставляем явное указание для себя в будущем, чтобы не допустить вызова этой задачи до того момента пока контроллер не перейдет к циклическому вызову всех обработчиков. Ведь на раннем этапе выполнения кода ESP8266 даже не имеет подключения к базовой станции. Для аналогичной защиты используется проверка с вызовом функции

    wifi.transferDataPossible()

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

    PS: Забыл упомянуть. Я бы советовал Вам не выводить на дисплей данные сразу после их поступления. Лучше их накапливать. Далее в планировщик задач добавить функция вызываемую, например, каждую минуту и проверяющую отличаются ли поступившие данные от тех, что уже выведены на дисплей. Если да, то выводить новые данные. Таким образом Вы будите экономит процессорное время и избавите контроллер от лишних задач по выводу одинаковых данных на дисплей.


  9. 12 минут назад, Devilisimo сказал:

    Хотел уточнить что нужно исправить и где указать порт, так понимаю в программе порт  1883?

    Да, Вы совершенно правы, порт 1883. Я не добавлял выбор порта через web интерфейс т.к не видел в этом необходимости, но это легко можно исправить. О том, как добавлять свои параметры так чтобы ESP8266 их "запоминал" уже упоминалось в теме, но если вдруг будут сложности, дайте мне знать об этом и я постараюсь описать как это сделать.

    Если это не требуется, то просто замените в коде порт 1883 на тот, который Вам предоставит CloudMQTT.

    https://www.cloudmqtt.com/docs.html

    instance-details[1].png

    Помните, что шифрование не поддерживается метеостанцией (но возможно) и выбирайте соответствующий порт. Все остальные настройки производятся через Web интерфейс ESP8266.

    PS: прошу Вас не пренебрегать разметкой, в частности блоком "Код", когда публикуете части исходников, а то совсем не удобно читать без подсветки синтаксиса.


  10. Доброе время суток @marsergei76

    Да, с этим не должно быть проблем. Найдите все места в коде где используется цикл такого вида

    for (byte b = 0; b < 4; b++)

    И замените его на

    for (byte b = 0; b < 7; b++)

    Перед загрузкой программы очистите EEPROM микроконтроллера удалив тем самым все записанные ранее ключи, в том числе и Master Key.


  11. @Devilisimo К сожалению, Вы не предоставили схему подключения датчика или подробное фото. Мне кажется, что проблема может скрываться как раз в подключении. Возможно не подключен порт 7 он же nWAKE (на платах часто отмечен как WAK). В официальной документации к датчику ccs811 на четвертой странице указано, что порт nWAKE должен быть подтянут к низкому логическому уровню до начала передачи данных по I2C шине. Я не стал занимать порт микроконтроллера для этих целей т.к у меня не автономное устройство, а просто кинул перемычку с nWAKE к соседнему GND.


  12. @post125 Это хороший и довольно интересный вариант.

    Вам будут доступны несколько интерфейсов для связи. Разработчик заложил связь через UART, но Вы вполне можете связать ATmega 2560 и ESP8266 через I2C, порты GPIO4 и GPIO5 доступны и выведены на колодку #1.

    image.png

    Указано, что на плате распаяна флешь на 32 мегабайта, это хорошо т.к на старых ревизиях с 1-м мегабайтом было бы совсем грустно и пришлось бы облегчать оформление web интерфейса ESP.

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


  13. @Devilisimo давайте разбираться.

    Для начала исправим ошибки. У Вас все перемешалось поэтому предлагаю разобрать процесс объявления нового сенсора на каком ни-ть примере. Я опять возьму за основу программный сенсор для сбора и отображения данных по уровню сигнала базовой станции к которой подключается ESP8266. Это очень простой пример, и он не перегружен лишним кодом.

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

    sensors.add(new knob_t(-100, 0, "1", "RSSI", "dbm"), device::in, "rssi",[&](){ 
        return wifi.isConnected() ? WiFi.RSSI() : 0; 
    });

    Выделим в отдельные элементы компоненты, указанные в качестве параметров.

    Первым будет идти код описывающий все необходимо для построения круговой шкалы с указанием визуального имени сенсора и подписи с единицами измерения.

    knob_t *R = new knob_t(-100, 0, "1", "RSSI", "dbm");

    Объект knob_t представляет из себя небольшой контейнер в котором хранятся все характеристики Вашего сенсора. Его можно создать один для целой группы сенсоров, например, датчиков DS18B20. Но в данном случае нам это не интересно. Посмотрим, что из себя представляют все параметры, кстати, посмотреть их можно в файле sensors.h

    knob_t(int min, int max, const char *step, const char *title, const char *unit)

    Параметры:

    1. min - минимальное значение шкалы
    2. max - максимальное значение шкалы
    3. step - шаг с которым будет произведена визуализация изменения показаний (это не числа!): "1" - единица, ".1" - одна десятая, ".01" - одна сотая и т.д
    4. title - заголовок, по сути название сенсора, не используется нигде кроме визуальной составляющей
    5. unit - описание, задумывался как индикатор единиц измерения

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

    float get_rssi() {
        return wifi.isConnected() ? WiFi.RSSI() : 0;
    }

    Вообще её можно упростить до следующего вида, но только для примера

    float get_rssi() {
        return WiFi.RSSI();
    }

    К данной функции есть очень строгие требования и это не просто так:

    1. Функция обязана возвращать числовое значение с типом float
    2. Функция не может принимать никаких параметров. Все с чем она работает должно быть объявлено в глобальной области видимости (в основном файле и ВНЕ функций setup и loop, для простоты понимания) или в самой функции.

    С чем связаны эти тонкости. 

    В первую очередь мы работаем с числовыми данными, мы можем использовать их для каких-либо расчетов и т.п. Позволю себе небольшое отступление - вообще использование типа float это плохой тон, но в первой версии метеостанции, где использовался тип int и ему подобные, люди сталкивались с большими трудностями и много путались. Все данные, в том числе и с плавающей точкой хранились как целые числа и на определенном этапе работы с ними требовалось произвести математические действия, по сути умножение и деление на нужное для каждого сенсора значение - 10, 100, 1000 и т.д Хранимые и обрабатываемые данные микроконтроллером преобразовывались в целочисленные, это позволяло производить все операции максимально быстро и требовало меньше затрат памяти, а в web интерфейсе производилось их преобразование в значения с типом float, если вообще это преобразование требовалось. Перейдя в дальнейшем на тип float мы усложнили жизнь микроконтроллеру, но лично мое субъективное мнение, что это допустимая жертва в пользу упрощения кода для других людей. И замечу, что все вопросы по ошибкам преобразования и хранения данных ушли.

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

    Осталось затронуть метод add принадлежащий объекту sensors, но т.к метод имеет много переопределений, которые можно посмотреть в том же файле sensors.h, мы рассмотрим его совместно с описанным выше кодом.

    /* Описание сенсора для его визуализации плагином knob в web интерфейсе */
    knob_t *R = new knob_t(-100, 0, "1", "RSSI", "dbm");
    
    /* Функция, описывающая как получить полезные данные от сенсора */
    float get_rssi() {
        return wifi.isConnected() ? WiFi.RSSI() : 0;
    }
    
    /* Так мы объявляем новый сенсор и указываем основной программе, что это и как с этим работать */
    sensors.add(R, device::in, "rssi", get_rssi);

    Что мы передаем в параметрах метода add:

    1. R - указатель на объект knob с описанием всего необходимого для отрисовки сенсора в web интерфейсе одноименным плагином
    2. device::in - идентификатор указывающий к какой группе сенсоров относится этот
    3. rssi - идентификатор датчика, может содержать только латинские буквы, цифры и знак подчеркивания. Используется java скриптами web интерфейса.
    4. get_rssi - имя функции которая будет вызываться для обновления данных

    Когда нет необходимости в создании отдельной функции для сбора данных с сенсора, как с данным сенсором ведь его код очень мал и не требует дополнительных библиотек или математических преобразований, можно заменить имя функции на лямбда-выражение.

    [&](){ return wifi.isConnected() ? WiFi.RSSI() : 0; }

    И получим что-то похожее на начальный код

    /* Описание сенсора для его визуализации плагином knob в web интерфейсе */
    knob_t *R = new knob_t(-100, 0, "1", "RSSI", "dbm");
    
    /* Так мы объявляем новый сенсор и указываем основной программе, что это и как с этим работать */
    sensors.add(R, device::in, "rssi", [&](){ return wifi.isConnected() ? WiFi.RSSI() : 0; });

    Это один из множеств вариантов как добавить новый датчик или что-то еще в код метеостанции.

    Я настоятельно советую просмотреть

    1. Примеры использования плагина knob http://anthonyterrien.com/demo/knob/
    2. Исходники с описанием плагина knob https://github.com/aterrien/jQuery-Knob
    3. Немного С++ с описанием лямбда-выражений https://msdn.microsoft.com/ru-ru/library/dd293608.aspx
    4. И обязательно изучите файл sensors.h в исходниках метеостанции, там много важной информации.

    Уверен, что теперь Вы увидите ошибки и сможете их поправить.

    3 часа назад, Devilisimo сказал:

    встал вопрос как сделать строку с меняющимся значением не числовым.

    Силами плагина Knob никак, он предназначен только для визуализации числовых данных, но Вы можете схитрить. Смотрите в сторону обработки вашей функции на стороне браузера. В файле index.htm имеется описание функции, вызываемой каждый раз, когда требуется обновить данные сенсоров в web интерфейсе. Простите за давнишнюю опечатку в имени функции, но это тот момент, когда исправление только навредит.

    function chenge(fast) {
        if(allowAjaxConnection || fast) {
            $.ajax({
                dataType: "json",
                url:  domain + "api/sensors" + ($(".settings #sl-system").is(":visible") ? "?system=true" : ""),
                type: "GET",
                cache: false,
                timeout: 3000,
                success: function(data) {
                    $.each(data, function(name, value) { if (name !== 'system') animate("." + name, value); });
                    if (data.system) apiSetSystemInfo(data.system, true);
                    //$('.out_temperature').trigger('configure', {"fgColor":"#FF0000"});
                }
            });
        }
        setTimeout(chenge, 5000);
    }

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

    $.each(data, function(name, value) { 
      if (name !== 'system') animate("." + name, value);
    });

    В данной коде мы получаем имя (идентификатор) каждого сенсора и его последние показания. Вы можете видеть, что имеется проверка на имя, совпадающее с 'system', если совпадение найдено, то функция animate вызвана НЕ будет. Это прекрасный пример того, как можно выловить нужный Вам сенсор.

    $.each(data, function(name, value) { 
      if (name !== 'system') animate("." + name, value);
      
      /* Ищем нужный нам сенсор */
      if (name === 'rssi') {    
        var description;
        if (value > 50) description = 'плохой'; else description = 'хороший';
        /* Выводим текст куда ни-ть */
        $("input." + .name).parent(".sensor").find(".unit").text(description);
      }  
    });

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

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

    Ну и наконец я дам Вам еще одну идею. Не всегда текст бывает наглядным выводом информации, особенно если он на определенном удалении от наблюдателя. Воспользуйтесь цветовой индикацией. Цвета от зеленого до красного прекрасно сигнализируют о качестве воздуха и очень наглядны. А в коде функции chenge (опять извиняюсь за её имя) имеется подсказка как менять цвет визуального элемента сенсора в web интерфейсе.

    //$('.out_temperature').trigger('configure', {"fgColor":"#FF0000"});

    Все, что Вам требуется, это изменить указанный чуть ранее код таким образом, чтобы можно было заменить

    "#FF0000"

    на переменную содержащую HEX код цвета необходимого для индикации показаний, попадающих в установленный придел для конкретного сенсора.

    var color = "#FF0000";
    $('.' + name).trigger('configure', {"fgColor":color});

     


  14. Доброе время суток.

    22 часа назад, post125 сказал:

    Вопрос достаточно абстрактный - хватит ли входов и памяти у esp8266 для реализации этого.

    @post125 Я не вижу причин не позволяющих реализовать то, что Вы задумали. Конечно есть нюансы, но они будут всегда.

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

    1. I2C (2 порта)
    2. 1-Wire (1 порт)
    3. NRF (зависит от реализации подключения)

    С первыми двумя все понятно, главное помнить что на шине I2C не могут работать два устройства с одним адресом.

    А вот с NRF все не так просто т.к используется SPI шина и она подразумевает наличие 4-х свободных портов, но если мы будем только слушать, то в теории можно задействовать только 2. Также скорее всего понадобится еще один порт для IRQ сигнала, так мы будем работать с NRF только в том случае, если что-то есть в эфире. При такой реализации Вам придется отказаться от управления нагрузкой со стороны ESP8266.

    Другой вариант подключения NRF24 и ESP8266, который я бы использовал в рамках данного проекта, это отказ от SPI шины в пользу I2C. Но для этого понадобится контроллер посредник, например ATmega328 к которому по SPI шине подключается NRF24, а сам контроллер ATmega висит в качестве Slave устройства на I2C шине ESP8266. Логику работы вижу в виде сбора данных контроллером посредником со всех устройств в эфире которые передают телеграмму определенного формата.

    В телеграмме должны передаваться как минимум:

    1. Уникальный идентификатор устройства, например значение с типом uint8_t
    2. Уникальный идентификатор сенсора (uint8_t), если их несколько, например гирлянда из датчиков DS18B20
    3. Данные с сенсора, пусть это будет int16_t

    Дополнительно можно передавать:

    1. Тип сенсора, но необходимо заранее составить таблицу типов и придерживаться ею в будущем при добавлении новых устройств в эфир, при необходимости расширять таблицу
    2. Информацию о заряде аккумулятора, если таковой используется
    3. Контрольную сумму

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

    Далее уже в ESP8266 реализовать обмен данными с ATmega по I2C шине и просто запрашивать данные из таблицы, благо все идентификаторы нам и так известны. Читать данные, двигать запятую, если требуется и получаем удовольствие.

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

    Что касаемо хранения логов, то от SD карты я бы посоветовал отказаться в пользу центрального сервера, который Вы упомянули в п.9. Тем более, при наличии сервера, можно хранить данные в полноценной СУБД, той же MySQL, с удобными выборками и онлайн доступом откуда угодно. А вот SD карта опять потребует использования SPI, возвращаемся к дележке портов, на обработку логов потребуется время, да и сама карточка может выйти из строя, что само по себе не приятно и может привести к сбоям в работе программы ESP8266.

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

     

    6 часов назад, Devilisimo сказал:

    Хотя беру свои слова обратно, отображение появилось после нескольких обновлений страницы!!!

    @Devilisimo рад, что у Вас все получилось.

    4 часа назад, Devilisimo сказал:

    Хотелось бы сделать несколько вкладок для разных комнат. Сейчас есть переключение наружные/внутренние датчики.

    В каком месте это настраивается?

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

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

    sensors.add(new knob_t(-100, 0, "1", "RSSI", "dbm"), device::in, "rssi",[&](){ 
        return wifi.isConnected() ? WiFi.RSSI() : 0; 
    });

    За выбор страницы где будет отображен индикатор отвечает значение одного из параметров, в данном случае это

    device::in

    Что указывает на принадлежность сенсора к внутренней группе датчиков.

    Список доступных обозначений описан в классе device в файле sensors.h как тип list_t

    class device {
      public:
        typedef enum list_t {out = 1, in = 2};
    	/* ... */
    };

    это сделано для того, чтобы исключить ошибку с указанием порядкового номера для не существующего списка сенсоров в самом web интерфейсе. Вам необходимо расширить его с указанием порядковых номеров для новых групп (помните, что они должны быть описаны в файле index.htm).

    Теперь переходим к самому web интерфейсу, описанному в файле index.htm

    Слои (группы) на которых располагаются индикаторы в первую очередь описаны в секции с html кодом в блоке div с классом mainContainer

    <div class="mainContainer">
        <!-- лишнее удалено -->
        <div class="sensorsContent" hidden>
            <!-- лишнее удалено -->
            <div id="list1"></div>
            <div id="list2" hidden></div>
            <!-- лишнее удалено -->
        </div>
        <!-- лишнее удалено -->
    </div>

    В данном случае числовая часть имен list1 и list2 соответствует числовому значению, которое скрыто за именами out и in см. чуть выше. Если вы добавите новое значение, например, какую ни-ть комнату, пусть это будет ванная

    typedef enum list_t {out = 1, in = 2, bathroom = 3};

    то необходимо добавить новый слой для визуализации

    <div id="list1"></div>
    <div id="list2" hidden></div>
    <div id="list3" hidden></div>

    Далее отработает java функция sensorsStructure которая разместит сенсоры помеченные как device::bathroom на слое с идентификатором list3

    Это реализовано через обновление содержимого div

    $(".sensorsContent #list" + param.list).append(sensor);

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

    $(".menu #sensors").click(function() {
        var list = $(this).attr("class");
        switch (list) {
            case 'list1': $("#list1").fadeToggle('slow', function(){ $("#list2").fadeToggle('slow'); }); break;
            case 'list2': $("#list2").fadeToggle('slow', function(){ $("#list1").fadeToggle('slow'); }); break;
        }
        $(this).toggleClass("list1", !$(this).hasClass("list1")).toggleClass("list2", !$(this).hasClass("list2"));
    });

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


  15. Доброе время суток

    28.09.2018 в 15:07, Devilisimo сказал:

    Добавил в данный проект датчик CO2 mh-z19, вроде все скомпилировалось, но данные не приходят с датчика.

    Обратите внимание, что используемые Вами порты для SoftwareSerial уже задействованы под управление нагрузкой при превышение определенных показаний. Найти их можно в файле gpio.h и в настройках web интерфейса, раздел "Контроль состояния GPIO". Для начала уберите управление нагрузкой с этих портов, закомментируйте в основном файле программы вызов функции

    gpio_12_13();

    Далее я порекомендую проконтролировать что приходит в ответ на запрос данных с сенсоров. Для этого в браузере откройте режим разработчика и загрузите страницу метеостанции. Смотрите на запрос "sensors". Выглядеть это должно примерно так.

    image.png

    Можно вывести в консоль данные о поэтапной работе функции чтения данных с сенсора mh-z19

    readCO2();

    Также можно упростить инициализацию датчика CO2 и выбросить лямбда-выражение указав в качестве последнего аргумента функцию чтения данных с сенсора.

    sensors.add(PPM, device::in, "co2_ppm", readCO2);

    И не используйте пробельные символы для задания внутреннего имени датчика в web интерфейсе. В Вашем случае замените "CO2 ppm" на "co2_ppm", как это было показано выше при инициализации датчика. Допустимо использовать пробельные символы только для указания видимого имени сенсора, это делается через объект knob_t

    knob_t *PPM = new knob_t(-400, 5000, ".1", "CO2", "ppm");

    Идентификатор датчика указывается как класс для объекта сенсора в web интерфейсе, а имена классов не допускают наличия пробельных символов т.к они являются разделителями имен. Таким образом задав имя "CO2 ppm" у нашего сенсора появятся два отдельных класса, это "CO2" и "ppm", что не позволит найти интересующий нас объект.

    Внесите эти поправки, и я уверен, что данные появятся.

    28.09.2018 в 15:10, Devilisimo сказал:

    Еще хотел спросить у автора, где находятся настройки для построения графиков, как добавить туда дополнительные переменные?

    В первую очередь за ведение логов с сенсора отвечает последний параметр метода add от класса sensors, посмотрите файл sensors.h

    class sensors {    
      public:  
        bool add(knob_t *knob, device::list_t list, byte address, const char *name, device::initFn_t init, device::dataFn_t data, bool log);
        bool add(knob_t *knob, device::list_t list, byte address, const char *name, device::dataFn_t data, bool log);
    
        bool add(knob_t *knob, byte address, const char *name, device::initFn_t init, device::dataFn_t data, bool log);
        bool add(knob_t *knob, byte address, const char *name, device::dataFn_t data, bool log);
    
        bool add(knob_t *knob, device::list_t list, const char *name, device::dataFn_t data, bool log);
    
        bool add(knob_t *knob, const char *name, device::dataFn_t data, bool log);
      
      /* ... */
      byte logSize = 144;
    } sensors;

    Если параметр

    bool log

    принимает значение true, то будет выделена память для массива типа float с размером, указанным в переменной logSize. Каждый новый сенсор является объектом типа device. По умолчанию память не выделяется.

    class device {
      public:
        /* ... */
        device(knob_t *knob, list_t list, byte address, const char *name, initFn_t init, dataFn_t data, byte log, device *next) {
          /* ... */      
          this->log = log ? new float[log]{0} : 0;      
          /* ... */
        }
        /* ... */  
        float *log;
        /* ... */
    };

    За автоматическое наполнение массива данных отвечает задача в планировщике вызывающая метод logUpdate класса sensors.

    cron.add(cron::time_10m, [&](){ sensors.logUpdate();  }, "httpSensorsLog"); // Обновление журнала (httpSensorsLog - не обязательный уникальный ID для быстрого поиска задания другими программными модулями)

    По сути происходит перебор всех сенсоров, запись новых значений в логический конец массива и смещение логического конца массива.

    Теперь переходим к самому сложному, это web интерфейс и построение суточного графика.

    В файле index.htm имеется функция описывающая модель графика с помощью плагина Highcharts

    function chart() {
        graph = Highcharts.chart('graph24h', {
            yAxis: [
                /* ... */
            ],
            series: [
                /* ... */
            ]
        });
    }

    По аналогии с уже описанными линиями данных для других сенсоров можно добавить новый параметр. Обратите внимание на параметр yAxis для объекта series, он уникален для каждой линии данных и содержит порядковый номер соответствующего ему объекта yAxis описанного в начале функции. Это может выглядеть сложно, но это не так. Вы можете найти всю документацию по плагину на официальном сайте https://www.highcharts.com/

    Теперь останется только наполнить данными суточный график. За это отвечает обработчик висящий на событии клика по иконке суточного графика.

    $("#graph").click(function() {
    	/* ... */
    });

    Добавление массива данных для всех сенсоров идентичны и выглядит примерно так, я приведу пример из своей домашней метеостанции для датчика eCO2 с идентификатором out_co2

    graph.series[4].update({
        animation: animation,
        data: obj.out_co2,
        pointStart: pointStart,
        pointInterval: pointInterval
    }, false);

    Самое главное, на что стоит обратить внимание, это порядковый номер series для которого предназначены эти данные. В данном случае

    series[4]

    Нумерация начинается с 0 и описывалась в упомянутой ранее функции chart() в секции series

    PS: отмечу, что в коде я не умышленно оставил опечатки, за что искренне прошу прощения и советую внимательно все перепроверять.


  16. 1 час назад, mrmarkalex сказал:

    Подскажите, пожалуйста, как можно уменьшить время обновления информации с сервера.

    Если Вы имеете в виду интервал между запросами информации, то в файле config.ini, который располагается в корне самого клиента, имеется параметр, отвечающий за интервал между запросами данных в секундах.

    update="60"

    Но имеется еще и ограничение в серверной части т.к данные кэшируются для уменьшения нагрузки при массовом обновлении информации на клиентах. Управлять временем жизни кэша на сервере можно с помощью параметра в файле index.php

    $cacheInterval = 60;

    Время жизни кэша также указывается в секундах.

    • Thanks 1

  17. @mrmarkalex в используемой Вами версии Zabbix отсутствует возможность построения изображений, в частности карт сетей, в виде растровой графики, что делает "парсинг" картинок посредством curl не актуальным. Стоит поискать в сети упоминания об актуальных способах для Zabbix v3.4 и выше, возможно есть экспорт графиков и карт в API или альтернативные способы описывались в интернете, это нужно проверять, просмотреть актуальную документацию.

    • Thanks 1

  18. @mrmarkalex каталог на который Вы ссылаетесь используется самим Zabbix, хранить там что-то постороннее не стоит. Для теста используйте каталог, прописанный в VirtualHost начальной конфигурации Apache, если конечно Вы используете Apache, я буду думать, что так оно и есть.

    Например, для Ubuntu Server путь можно посмотреть в /etc/apache2/sites-enabled/000-default.conf а интересующий нас каталог прописан как

    DocumentRoot /var/www/html

    Вот по этому адресу и создайте каталог с любым именем и в нем разместите index.php с содержимым указанным Выше.

    Также у Вас должен быть установлен PHP и модуль к нему pdo_mysql

    PS: Помните, что в каждой системе могут быть свои пути и конфигурационные файлы.

    • Thanks 1

  19. 03.09.2018 в 09:53, mrmarkalex сказал:

    при настройке config файла, почему-то не видит Zabbix-сервер если указать http://xxx.xxx.xxx.xxx/zabbix/zabbix-gui/ хотя файл сервера index.php лежит по указанному адресу.

    Пройдите по указанному адресу с помощью браузера и убедитесь, что скрипт Вам ответил, и предоставленные им данные визуально соответствуют формату json. Отсутствуют ошибки в ходе исполнения php на сервере. Также, ip адрес хоста с которого производится подключение к серверной части должен быть указан в массиве разрешенных адресов.

    Скрытый текст
    
    $client = array(
        # Администраторы
        '10.10.10.100',
        '10.10.10.101',
        '10.10.10.102',
        # Дежурные
        '10.10.10.200',
        # Начальство
        '10.10.10.210',
    );

     

     

    • Thanks 1

  20. Доброе время суток @mrmarkalex

    К сожалению под рукой нет сервера версии 3.4, но я Вам посоветую действовать опираясь на уже имеющееся по умолчанию во всех версиях Zabbix действие - "Report problems to Zabbix administrators". Скопируйте его и просто переопределите действие с оповещения на выполнение внешнего скрипта.

    • Thanks 1

  21. 20 часов назад, Dark FeniX сказал:

    Конечно, мелковат экран у 1306, надо было брать больший, типа TFT 2.4".

    Осмелюсь предложить взглянуть в сторону микросхемы MAX7219 в связке с сегментными индикаторами 8х8.

    С простым выводом данных проблем быть не должно. Возможно будут сложности с бегущей строкой из-за нагруженности контроллера другими задачами, но над этим стоит подумать, может я зря нагнетаю. А вообще можно взять вторую ESP8266 и сделать из неё чисто информер. Данные брать напрямую с метеостанции через API или через MQTT протокол.

    Ссылка на модули в Китае

     


  22. @Dark FeniX если необходимо добавить что-то свое (без модификаций основного кода), то действуйте точно также как если бы писали все с нуля. Работайте с основным файлом. Подключайте необходимые библиотеки, объявляйте переменные, дорабатывайте содержимое setup и loop, в общем делайте все как обычно опираясь на примеры, идущие в комплекте с используемыми библиотеками.

    Что касаемо упоминаемого кода, то он не действителен для текущей версии проекта поэтому давайте рассмотрим, как это должно выглядеть на текущий момент. 

    1. Допустим Вас интересует библиотека https://github.com/ThingPulse/esp8266-oled-ssd1306, будем опираться на её примеры для дисплея ssd1306 подключенного по i2c шине. Все работы будем проводить в основном файле проекта (.ino)
    2. Подключаем библиотеку и объявляем переменные
      #include "SSD1306Wire.h"  // Подключаем интересующую нас библиотеку
      SSD1306Wire display(0x3c, 4, 5); // Объявляем переменную через которую будем работать с дисплеем

      Сделать это можно в любом месте, например, после подключения уже используемых библиотек

    3. Чтобы все выглядело красиво создадим собственную функцию, которая будет выводить информацию на дисплей, пусть это будет температура

      void displayWeather() {
        display.clear(); // Очищаем дисплей
        display.setFont(ArialMT_Plain_10); // Устанавливаем шрифт 
        display.setTextAlignment(TEXT_ALIGN_CENTER); // Указываем выравнивание
        display.drawString(64, 22, "Temperature " + String(sensors.get("out_temperature"))); // Выводим температуру
        display.display(); // Производим отрисовку данных
      }

      Сделать это можно, например, сразу перед функцией Setup или в любом месте файла если Вы используете актуальную версию Arduino IDE

    4. В функции Setup описываем инициализацию дисплея

      display.init(); // Инициализация дисплея
      display.flipScreenVertically(); // Переворачивает дисплей верх ногами (если требуется)

      Сделать это можно прямо в конце этой функции

    5. И следом после этого, также в функции Setup, добавляем новую задачу в планировщик задач

      cron.add(cron::minute, displayWeather, true);

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

    Выглядеть это будет примерно так (все работы произведены в одном файле).

    image.png

    И еще хочу добавить, что я просто взял первую попавшуюся библиотеку и основываясь на одном из примеров и описанию методов от разработчика набросал вышеупомянутый список. Проверить полную работоспособность не могу т.к у меня попросту нет данного дисплея. Но суть и принципы реализации должны быть понятны. Таким же образом можно организовывать работу с другой периферией, например, управлять внешним освещением на дачном участке, выводить информацию на дисплей 16x2 (или любой другой), собирать данные с других готовых устройств и т.д и т.п

    • Like 1
    • Thanks 1

  23. 11 часов назад, PulFred сказал:

    Я уже задавал вопрос по поводу "будки", может у Вас есть ссылка изготовления (3D печати)на заказ такой как  реализована в проекте. Буду признателен... 

    Файлы  3D модели для печати будки Стивенсона идут в дополнительном архиве вместе с прошивкой, также в первом посте есть ссылка на эти же файлы но от самого автора модели. Я заказывал печать просто по объявлению, найденному в одной из социальной сети, главным критерием было производство на территории моего города. Цена вопроса 100р за каждую секцию, итого 600р. Думаю, что вам стоит поступить точно также и найти печать в своем регионе. Это также избавляет от рисков повреждения во время транспортировки, как работает почта России объяснять не нужно. Но уж если будут сложности, тогда пишите в приват, договоримся о заказе в моем городе и переправке к Вам.

    11 часов назад, Maikl9999 сказал:

    На графике первая точка 1июля, время 16:17, а на ПК  3 июля, время 10:37.

    Да, я поспешил и упустил из внимания, что для ключевых элементов хранения логов выделен тип byte, что приводило к переполнениям и в последствии к сбоям в расчетах.

    И так, необходимо внести еще ряд правок в файле sensors.h

    1. В классе device имеется одноименная функция (конструктор) device в описании параметров которого имеется параметр byte log, тип byte необходимо заменить на uint16_t
      device(knob_t *knob, list_t list, byte address, const char *name, initFn_t init, dataFn_t data, uint16_t log, device *next) {
      	/* код убран для уменьшения размера блока */
      }
    2. Аналогичную операцию замены типа необходимо провести в классе device у переменной byte logPosition и привести её к следующему виду

      uint16_t logPosition = 0;
    3. В классе sensors проводим аналогичную операцию с приватной переменной byte logSize

      uint16_t logSize = 144 * 3;

      В примере я указал три дня, Вы выставляете свое значение.

    4. В классе sensors имеется функция json sensors::log(device *sensor) отвечающая за формирование списка в формате json для передачи его в WEB интерфейс, её необходимо привести к следующему виду.

      json sensors::log(device *sensor) {
        String log;
        if (sensor) {
          if (sensor->log) {
            for (uint16_t i = sensor->logPosition; i < this->logSize; i++) {
              log += (log.length() ? "," : "") + this->clear(sensor->log[i]);
            }
            for (uint16_t i = 0; i < sensor->logPosition; i++) {
              log += (log.length() ? "," : "") + this->clear(sensor->log[i]);
            }
            if (log.length()) log = "\"" + String(sensor->name) + "\":[" + log + "]";
          }
        } return log;
      }

      По сути мы просто в двух циклах for изменили тип переменной i с byte на uint16_t и позволили циклам проходить по всему диапазону данных.

    5. В файле index.htm дату формирования первой точки вычисляем по первоначальной формуле

      new Date().getTime() - obj.timeAdjustment - (144 * 3 - 1) * 10 * 60 * 1000,

       

    Все должно получиться, но на всякий случай прикладываю файл sensors.h со всеми правками. Естественно файл устареет со временем, что может повлиять на работоспособность при совместной работе в будущих версиях, если конечно обновления будут востребованы сообществом.

    sensors.zip

    • Thanks 2

  24. 12 часов назад, Maikl9999 сказал:

    Попробовал. Почему то первая точка на графике не соответствует дате и времени .

    Возможно я ошибся и не учел сдвиг на 10 минут для каждых суток. Какое расхождение у Вас получилось?

    Попробуйте вариант со сдвигом на каждые сутки (например, для трех суток).

    new Date().getTime() - obj.timeAdjustment - (143 * 3) * 10 * 60 * 1000;

     

    4 часа назад, PulFred сказал:

    В процессе экспериментов с внедрением на страницу прогноза погоды активировал в Arduino IDE Меню-Инструменты- Sketch Data Upload, в результате перезалилась SPIFF , после чего в браузере пропал доступ странице. Подскажите, пожалуйста как восстановить прежнее работающее  состояние или в ESP8266 сбросить настройки Wi-Fi 

    Спасибо

    На данный момент Вы должны иметь контроллер без конфигурационного файла и при перезагрузке он поднимает собственную точку доступа с именем WeatherStation, без пароля. Подключитесь к ней и настройте контроллер по своим потребностям. Страница для доступа к WEB серверу располагается по адресу http://espws.local (если поддерживается mDNS протокол) или http://192.168.4.1/ в противном случае.

×
×
  • Создать...