ESP8266 + Arduino + OpenWRT: проект температурного логера на датчиках DHT11 и DS18B20, пошаговое руководство

разделы: Интернет вещей , Arduino , дата: 19 декабря 2018г.

ESP8266 может работать в двух режимах: в режиме интерпретатора AT-команд или в режиме самостоятельного микроконтроллера с wifi модулем. Работу ESP8266 в режиме интерпретатора AT-команд я рассматривал в предыдущей статье, эта же статья рассматривает работу ESP8266 в качестве самостоятельного микроконтроллера.

Способов программирования ESP8266 опять же два, первый - это программирование с помощью ESP8266 фреймворка для Arduino IDE, второй - это программирование через esp-open-sdk. В первом случае мы можем использовать готовые библиотеки Arduino, во втором случае вы можем положиться только на функционал SDK и свой собственный код.

В этой статье мне хотелось бы рассмотреть программирование ESP8266 с помощью ESP8266 фреймворка для Arduino IDE. Данная тема решает широкий спектр задач обеспечения радиоканалом разного рода датчиков и простых устройств управления нагрузкой.

В качестве примера в статье рассматривается пошаговое написание прошивки для температурного логера на датчиках DHT11 и DS18B20. Первый датчик используется для определения комнатной температуры и влажности, второй используется для определения уличной температуры. Я статье используется плата ModeMCU ESP8266, т.к. там есть автозагузка прошивки, но в принципе может быть использована любая другая плата на модуле ESP12E/ESP12F. Данные модули оснащены флеш-памятью на 4 мегабайта, что позволяет забыть о жёсткой оптимизации размера прошивки, когда борьба идёт за каждый байт.

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

В данном проекте используется веб-сервер uhttpd на роутере с прошивкой OpenWRT. ESP8266 передаёт на него показания датчиков, а роутер их сохраняет и виде обычных файлов, и делает их доступными для просмотра через web-интерфейс. Можно дать новую жизнь старому смартфону или планшету настроив их на отображение таких web-страниц. Web-интерфейс универсален и может отображаться на любых браузерах любых устройств.

    Ссылки на полезные ресурсы и документацию:
  1. ESP8266 фреймворк для Arduino IDE
  2. Документация на библиотеку ESP8266WIFI
  3. Документация на Arduino библиотеку WiFi library

Содержание:

I. Начало работы с ESP8266 фреймворком для Arduino IDE

  1. Установка ESP8266 фреймворка для Arduino IDE
  2. Подключение датчика DHT11 к ESP8266
  3. Подключение датчика DS18B20 к ESP8266

II. Работа с библиотекой ESP8266WIFI

  1. Установка WiFi соединения
  2. Использование режима энергосбережения DeepSleep
  3. Класс WiFiClient, получение web-страницы от сервера на OpenWRT и отправка данных через GET запрос
  4. Отправка на web-сервер данных с датчика DHT11 через GET запрос
  5. Добавление датчика DS18B20
  6. Вывод показаний датчиков через веб-интерфейс
III. Добавлено позже

  1. Второй WiFi термометр на датчике AM2320 (добавлено 26.05.19г)

1) Установка ESP8266 фреймворка для Arduino IDE

Домашняя страница ESP8266 фреймворка для Arduino IDE расположена на гитхабе: https://github.com/esp8266/Arduino. Там имеется небольшая инструкция по устаноке фреймворка:

Установка через менеджер плат Arduino
Начиная с версии 1.6.4, Arduino IDE позволяет устанавливать пакеты поддержки сторонних платформ через менеджер плат. Мы имеем пакеты доступные для Windows, Mac OS и Linux (для 32-бит и 64-бит).
  • Установите Arduino IDE версии 1.8 или выше. Текущая версия Arduino IDE находится на сайте проекта Arduino.
  • Запустите Arduino IDE и откройте через меню окно настроек (Preferences).
  • Введите в поле адреса дополнительных плат менеджера строку: http://arduino.esp8266.com/stable/package_esp8266com_index.json. Вы можете указывать несколько адресов разделяя их запятыми.
  • Откройте менеджер плат через: меню-> инструменты -> платы -> менеджер плат и установите платформу esp8266 (не забудьте выбрать версию вашей ESP8266-платы поле установки).

Действуя в соответствии с этой инструкцией, прописываем в настройках Arduino IDE адрес репозитория "http://arduino.esp8266.com/stable/package_esp8266com_index.json":

В менеджере плат устанавливаем пакет поддержки плат esp8266:

После установки пакета поддержки esp8266, выставляем в настройках свою плату, в моем случае это nodemcu на модуле esp12e, а настройки платы можно оставить по умолчанию:

Теперь подключаем плату NodeMCU к компьютеру, выбираем последовательный порт в Arduino IDE, находим в примерах для ESP8266 тестовый скетч "Blink", компилируем, загружаем и наслаждаемся ровным миганием синего светодиода:

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

Значение константы LED_BUILTIN определенно в файле ~/.arduino15/packages/esp8266/hardware/esp8266/2.4.2/variants/nodemcu/pins_arduino.h:

#ifndef Pins_Arduino_h
#define Pins_Arduino_h

#define PIN_WIRE_SDA (4)
#define PIN_WIRE_SCL (5)

static const uint8_t SDA = PIN_WIRE_SDA;
static const uint8_t SCL = PIN_WIRE_SCL;

#define LED_BUILTIN 16

static const uint8_t D0   = 16;
static const uint8_t D1   = 5;
static const uint8_t D2   = 4;
static const uint8_t D3   = 0;
static const uint8_t D4   = 2;
static const uint8_t D5   = 14;
static const uint8_t D6   = 12;
static const uint8_t D7   = 13;
static const uint8_t D8   = 15;
static const uint8_t D9   = 3;
static const uint8_t D10  = 1;

Т.е. LED_BUILTIN это GPIO_16. Если мы хотим помигать светодиодом который установлен на модуле ESP12E, то приведем скетч к следующему виду:

setup() {
  pinMode(D4, OUTPUT);     // Initialize the LED_BUILTIN pin as an output
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(D4, LOW);   // Turn the LED on (Note that LOW is the voltage level
  // but actually the LED is on; this is because
  // it is active low on the ESP-01)
  delay(1000);                      // Wait for a second
  digitalWrite(D4, HIGH);  // Turn the LED off by making the voltage HIGH
  delay(2000);                      // Wait for two seconds (to demonstrate the active low LED)
}

2) Подключение датчика DHT11 к ESP8266

Теперь нам нужно подключить к ESP8266 несколько сенсоров чтобы впоследствии передавать данные с них через WiFi.

Для начала возьмём популярной датчик температуры и влажности DHT11. Его подключение будет выглядеть так:

Датчик имеет три рабочих пина: первый пин - питание; второй пин - вывод данных, его следует подтягивать к питанию; третий пин никуда не подключается ; четвертый пин - это земля. Для подтяжки второго пина к питанию я использовал резистор на 4.7К, но т.к. датчик не скоростной, то должны сгодиться резисторы и на 10К или даже на 47К. Датчик может питаться от 3.3 Вольт, что нас вполне устраивает.

В качестве библиотеки возьмём библиотеку от Adafruit https://github.com/adafruit/DHT-sensor-library, т.к. она универсальна и поддерживает датчики DH11, DHT22(AM2302, AM2321) и DHT21(AM2301). Правда для своей работы она требует ещё одну библиотеку: https://github.com/adafruit/Adafruit_Sensor, её тоже надо будет установить.

После установки библиотек и подключения датчика DHT11 к ESP8266, загрузим в Arduino IDE тестовый скетч:

//This example for use ESP8266 with Adafruit DHT11 library: 
//https://github.com/adafruit/DHT-sensor-library
#include "DHT.h"
// connect data pin of DHT11 to D2 ESP8266 NodeMCU
#define DHTPIN D2    

// Uncomment whatever type you're using!
#define DHTTYPE DHT11   // DHT 11
//#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor

DHT dht(DHTPIN, DHTTYPE);
bool led_status=false;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  dht.begin();
}

void loop() {
  delay(2000);
  led_status=!led_status;

  float h = dht.readHumidity();     // get humidity
  float t = dht.readTemperature();  // get temperature
  if ( !isnan(h) && !isnan(t)) {
      Serial.print("Humidity: ");
      Serial.print(h);
      Serial.print(" %  Temperature: ");
      Serial.print(t);
      Serial.println(" *C ");
  }
  digitalWrite(LED_BUILTIN,led_status);
}

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

3) Подключение датчика DS18B20 к ESP8266

Вторым датчиком который мы подключим ESP8266, будет DS18B20. Схема его подключения к NodeMCU ESP8266 показана ниже:

Здесь, так же как и в случае с DHT11, Data pin подтягивается к шине питания через резистор номиналом 4.7КОм.

Для работы с датчиком DS18B20 будем использовать библиотеку OneWire: https://github.com/PaulStoffregen/OneWire/. Эта библиотека как же поддерживает несколько моделей датчиков Dallas Semiconductor, что нам только на руку.

Подключаем датчик к плате NodeMCU, скачиваем библиотеку "OneWire" и открываем пример DS18x20_Temperature.

В примере правим строку:

OneWire ds(10);  // on pin 10 (a 4.7K resistor is necessary)

на:

OneWire ds(D1);  // on pin 10 (a 4.7K resistor is necessary)

также можно еще скорость UART поднять до значения 115200

Serial.begin(115200);

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

4) Установка WiFi соединения

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

Библиотека ESP8266WIFI имеет схожий API c библиотекой Arduino WIFI.

    Различия заключаются в следующем (взято из http://esp8266.github.io/Arduino/versions/2.1.0-rc1/doc/libraries.html):
  • Добавлен метод WiFi.mode(m), который устанавливает режим работы WiFi. Здесь m - может принимать значения: WIFI_AP, WIFI_STA, WIFI_AP_STA или WIFI_OFF.
  • Добавлен метод WiFi.softAP(ssid), который переключает WiFi к режим открытой точки доступа.
  • Добавлен метод WiFi.softAP(ssid, password), который переключает WiFi к режим закрытой точки доступа с защитой WPA2-PSK, где ключ/password должен быть не менее восьми символов.
  • WiFi.macAddress(mac) возвращает MAC-адрес в случае режима STA, WiFi.softAPmacAddress(mac) тоже самое в случае режима AP.
  • WiFi.localIP() возвращает локальный IP-адрес в случае режима STA, WiFi.softAPIP() тоже самое в случае режима AP.
  • WiFi.printDiag(Serial) печать через UART диагностической информации.
  • Класс WiFiUDP реализует отправку и приём широковещательных multicast пакетов в STA режиме. При отправке пакетов, метод udp.beginPacket(addr, port)заменяется на udp.beginPacketMulticast(addr, port, WiFi.localIP()). class supports sending and receiving multicast packets on STA interface. При приёме пакетов, udp.beginPacket(addr, port) заменяется на udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port). Вы можете использовать udp.destinationIP() для задания multicast или unicast адреса.

Опираясь на эту информацию, начинаем писать код.

В составе библиотеки ESP8266WIFI имеется 17 примеров, среди которых имеется пример для сканирования доступных точек доступа: WIFIScan. Я переписал его на свой манер:

#include "ESP8266WiFi.h"

void setup() {
    Serial.begin(115200);

    // Set WiFi to station mode and disconnect from an AP if it was previously connected
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
}

void loop() {
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    if (n) {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            String ssid=String(i+1) + ": " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ")";
            ssid=(WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ssid+"\n" : ssid+"*\n";
            Serial.print(ssid);
            delay(10);
        }
    } else
        Serial.println("no networks found");

    Serial.print("\n");
    delay(10000);
}

Результат работы программы выглядит так:

Действие программы соответствует AT-команде CWLAP, её можно свернуть в функцию с названием, скажем list_wifi(). На базе этой функции можно написать другую, которая будет искать определенную точку доступа. Можно ее будет также наделить полезным свойством, возвращать в случае обнаружения заданной точки доступа уровень ее сигнала rssi. Т.о. получим функцию двойного назначения get_rssi():

#include "ESP8266WiFi.h"

char * your_ssid="Alien";

int list_wifi();
int get_rssi(String ssid);

void setup() {
    Serial.begin(115200);
}

void loop() {
    if (get_rssi(String(your_ssid)))
        Serial.println("Success: AP was found.");
    else
        Serial.println("Error: AP was not found.");

    Serial.print("\n");
    delay(10000);
}

int get_rssi(String ssid) {
    int ret=0;
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    while (n) {
        if (ssid.equals(WiFi.SSID(--n))) {
            ret=WiFi.RSSI(n);
            break;
        }
    }
    return ret;
}

int list_wifi() {
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    if (n) {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            String ssid=String(i+1) + ": " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ")";
            ssid=(WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ssid+"\n" : ssid+"*\n";
            Serial.print(ssid);
            delay(10);
        }
    } else
        Serial.println("no networks found");
    return n;
}

Дело осталось за малым, установить соединение с точной доступа. У меня это получилось так:

#include "ESP8266WiFi.h"

char * your_ssid="Alien";
char * your_pass="password";

int list_wifi();
int get_rssi(String ssid);

void setup() {
    Serial.begin(115200);
    while(!get_rssi(String(your_ssid))) {
      delay(5000);
    }
    WiFi.begin(your_ssid,your_pass);
    while (WiFi.status() != WL_CONNECTED){
        delay(500);
    }
}

void loop() {
    Serial.print("Connected, IP address: ");
    Serial.println(WiFi.localIP());
    Serial.print("MAC address: ");
    Serial.println(WiFi.macAddress());

    Serial.print("\n");
    delay(5000);
}

int get_rssi(String ssid) {
    int ret=0;
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    while (n) {
        if (ssid.equals(WiFi.SSID(--n))) {
          ret=WiFi.RSSI(n);
          break;
        }
    }
  return ret;
}

int list_wifi() {
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    if (n) {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            String ssid=String(i+1) + ": " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ")";
            ssid=(WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ssid+"\n" : ssid+"*\n";
            Serial.print(ssid);
            delay(10);
        }
    } else
        Serial.println("no networks found");
    return n;
}

5) Использование режима энергосбережения DeepSleep

Теперь вместо задержки на функции delay() будем посылать ESP8266 в режим энергосбережения. Для этого нам нужно будет соединить выводы D0 и RST:

После этого, заменим в главном цикле стоку delay(5000) на ESP.deepSleep(3e7), которая отправит в ESP8266 в режим энергосбережения. Параметр "3e7" означает 3*10^7 микросекунд, т.е. тридцать секунд.

Лог работы программы будет выглядеть теперь так:

Здесь непечатные символы, это отладочное сообщение при выходе ESP8266 из режима энергосбережения.

Для более корректной работы, мы можем поменять алгоритм таким образом: 1)пусть в начале главного цикла сначала устанавливается WiFi соединение, 2) потом путь передаются данные, 3) после этого WiFi соединение разрывается, и ESP8266 уходит в режим энергосбережения.

Примерно так:

#include "ESP8266WiFi.h"

char * your_ssid="Alien";
char * your_pass="password";

int list_wifi();
int get_rssi(String ssid);

void setup() {
    Serial.begin(115200);
}

void loop() {
    // Connect to SSID
    while(!get_rssi(String(your_ssid))) {
      delay(5000);
    }
    WiFi.begin(your_ssid,your_pass);
    while (WiFi.status() != WL_CONNECTED){
        delay(500);
    }
    // get data
    Serial.print("\nConnected, IP address: ");
    Serial.println(WiFi.localIP());
    Serial.print("MAC address: ");
    Serial.println(WiFi.macAddress());
    Serial.print("\n");
    // disconnect
    WiFi.disconnect();
    // sleep
    ESP.deepSleep(3e7);    // 30s
}

int get_rssi(String ssid) {
    int ret=0;
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    while (n) {
        if (ssid.equals(WiFi.SSID(--n))) {
          ret=WiFi.RSSI(n);
          break;
        }
    }
  return ret;
}

int list_wifi() {
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    if (n) {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            String ssid=String(i+1) + ": " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ")";
            ssid=(WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ssid+"\n" : ssid+"*\n";
            Serial.print(ssid);
            delay(10);
        }
    } else
        Serial.println("no networks found");
    return n;
}

6) Класс WiFiClient, получение web-страницы от сервера на OpenWRT и отправка данных через GET запрос

Теперь нужно научиться скачивать что-либо с web-сервера. Так же как и в предыдущей статье , в качестве web-сервера я использовал uhttpd-сервер установленный на роутере с прошивкой OpenWRT.

Класс WiFiCliet реализует установку TCP соединения с сервером. Демонстрационный пример его использования можно найти здесь: Client — ESP8266 Arduino Core 2.4.0 documentation. Я адаптировал этот пример под свой случай, и в итоге получилось так:

#include "ESP8266WiFi.h"

char * your_ssid="Alien";
char * your_pass="password";
//const char* host = "www.example.com";
IPAddress host(192, 168, 1, 10); //  alien
const int httpPort = 8010;

int list_wifi();
int get_rssi(String ssid);

void setup() {
    Serial.begin(115200);
}

void loop() {
    // Connect to SSID
    while(!get_rssi(String(your_ssid))) {
      delay(5000);
    }
    WiFi.begin(your_ssid,your_pass);
    while (WiFi.status() != WL_CONNECTED){
        delay(500);
    }
    // get data
    Serial.print("\nConnected, IP address: ");
    Serial.println(WiFi.localIP());
    Serial.print("MAC address: ");
    Serial.println(WiFi.macAddress());
    Serial.print("\n");

    // GET request
    WiFiClient client;
    if (client.connect(host, httpPort)) {
        client.print(String("GET /") + " HTTP/1.1\r\n" +
                      "Host: " + host + "\r\n" +
                      "Connection: close\r\n" +
                      "\r\n"
                    );
        Serial.println("[Response:]");
        while (client.connected() || client.available()){
            if (client.available()) {
                String line = client.readStringUntil('\n');
                Serial.println(line);
            }
        }
        Serial.println("\n[Disconnected]");
        client.stop();
    } else
        Serial.println("connection failed");

    // disconnect
    WiFi.disconnect();
    // sleep
    ESP.deepSleep(3e7);    // 30s
}

int get_rssi(String ssid) {
    int ret=0;
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    while (n) {
        if (ssid.equals(WiFi.SSID(--n))) {
          ret=WiFi.RSSI(n);
          break;
        }
    }
  return ret;
}

int list_wifi() {
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    if (n) {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            String ssid=String(i+1) + ": " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ")";
            ssid=(WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ssid+"\n" : ssid+"*\n";
            Serial.print(ssid);
            delay(10);
        }
    } else
        Serial.println("no networks found");
    return n;
}

Результат работы программы выглядит так:

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

Как видно, соединение осуществляется без ошибок. Красная строчка показывает закрытие соединения клиентом, с помощью вызова WiFi.disconnect().

Теперь аналогично предыдущей статье: Отправка данных от ESP8266 на web-сервер OpenWRT через GET запрос, мы можем отправлять на веб-сервер свои данные через GET-запрос.

Изменим в программе строку:

client.print(String("GET /") + " HTTP/1.1\r\n" +

на:

client.print(String("GET /cgi-bin/esp8266.cgi?var1=11&var2=-30&var3=17") + " HTTP/1.1\r\n" +

После чего заново компилируем и перепрошиваем esp8266. Смотрим на результат работы программы:

Как видно, данные успешно уходят на сервер.

7) Отправка на web-сервер данных с датчика DHT11 через GET запрос

Теперь нам нужно добавить к получению данных с датчика DHT11, отправку их на web-сервер через GET-запрос, как это делалось в предыдущей статье: Отправка данных от ESP8266 на web-сервер OpenWRT через GET запрос.

Программа в данном случае получается такой:

#include "ESP8266WiFi.h"
#include "DHT.h"

int list_wifi();
int get_rssi(String ssid);

//----------- ESP8266------------------
char * your_ssid="Alien";
char * your_pass="password";
//const char* host = "www.example.com";
IPAddress host(192, 168, 1, 10); //  alien
const int httpPort = 8010;
//----------- DHT11------------------- 
#define DHTPIN D2
#define DHTTYPE DHT11   // DHT 11

DHT dht(DHTPIN, DHTTYPE);
//------------------------------------
void setup() {
    Serial.begin(115200);
    dht.begin();
}

void loop() {
    // get data from DHT11
    float h = dht.readHumidity();     // get humidity
    float t = dht.readTemperature();  // get temperature

    // Connect to SSID
    while(!get_rssi(String(your_ssid))) {
      delay(5000);
    }
    WiFi.begin(your_ssid,your_pass);
    while (WiFi.status() != WL_CONNECTED){
        delay(500);
    }
    // get data
    Serial.print("\nConnected, IP address: ");
    Serial.println(WiFi.localIP());
    Serial.print("MAC address: ");
    Serial.println(WiFi.macAddress());
    Serial.print("\n");

    if ( !isnan(h) && !isnan(t)) {
        String hum_str=String(h);
        String temp_str=String(t);
        Serial.println("Humidity: " + hum_str + " %  Temperature: " + temp_str + " *C ");

        // GET request
        WiFiClient client;
        if (client.connect(host, httpPort)) {
            client.print(String("GET /cgi-bin/sensors.cgi?var1=") +
                          hum_str + "&var2=" + temp_str + " HTTP/1.1\r\n" +
                          "Host: " + host + "\r\n" +
                          "Connection: close\r\n\r\n");
            Serial.println("[Response:]");
            while (client.connected() || client.available()){
                if (client.available()) {
                    String line = client.readStringUntil('\n');
                    Serial.println(line);
                }
            }
            Serial.println("\n[Disconnected]");

            client.stop();
        } else
            Serial.println("connection failed");

        // disconnect
        WiFi.disconnect();
    }
    // sleep
    ESP.deepSleep(6e7);    // 1 minute
}

int get_rssi(String ssid) {
    int ret=0;
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    while (n) {
        if (ssid.equals(WiFi.SSID(--n))) {
          ret=WiFi.RSSI(n);
          break;
        }
    }
  return ret;
}

int list_wifi() {
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    if (n) {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            String ssid=String(i+1) + ": " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ")";
            ssid=(WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ssid+"\n" : ssid+"*\n";
            Serial.print(ssid);
            delay(10);
        }
    } else
        Serial.println("no networks found");
    return n;
}

Для приёма данных, на стороне сервера нужно будет добавить cgi скрипт sensors.cgi следующего содержания:

#!/bin/sh
echo -en "Status: 200 OK\r\n"
echo -en "Content-type: text/html\r\n\r\n"
echo "<h1>ESP8266 -  GET Request Example</h1>"

line=$(echo "$QUERY_STRING"|sed "s/&/   /g")
for loop in $line
do
        name=$(echo $loop|awk -F = '{print $1}')
        type=$(echo $loop|awk -F = '{print $2}')
        var=$(printf "${name}=${type}")
        eval $(printf $var)
done

[ -z $var1 ] && exit
[ -z $var2 ] && exit

echo $var1
echo $var2

p0="/site/sensors"
p2="$p0/$(date +%d_%m_%y)"
[ -d "$p2" ] || mkdir -p "$p2"
echo -n $(date) >>  "$p2/log.txt"
echo -n " humidity: " >> "$p2/log.txt"
echo -n "$var1" >> "$p2/log.txt"
echo -n " temperature: " >> "$p2/log.txt"
echo  "$var2" >> "$p2/log.txt"

Данный скрипт записывает полученные показания датчиков в файлы вида: '/site/sensors/"дата"/log.txt'. Файлы автоматически разбиваются по дням. Формат файлов выглядит так:

Sat Dec 15 14:56:18 SAMT 2018 humidity: 19.00 temperature: 24.00
Sat Dec 15 14:57:21 SAMT 2018 humidity: 19.00 temperature: 24.00
Sat Dec 15 14:58:25 SAMT 2018 humidity: 19.00 temperature: 24.00
Sat Dec 15 14:59:32 SAMT 2018 humidity: 19.00 temperature: 24.00
Sat Dec 15 15:00:35 SAMT 2018 humidity: 19.00 temperature: 24.00
Sat Dec 15 15:01:39 SAMT 2018 humidity: 19.00 temperature: 24.00

8) Добавление датчика DS18B20

Теперь нужно добавить опрос датчика DS18B20, и передачу его данных на web-сервер. Для этого я добавил в программу код из примера "DS18x20_Temperature" библиотеки OneWire. Этот пример поддерживает датчики: DS18S20, DS18B20, DS1822. Я не стал его упрощать затачивая конкретно под свой DS18B20, а оставил как есть:

#include "ESP8266WiFi.h"
#include "OneWire.h"
#include "DHT.h"

int list_wifi();
int get_rssi(String ssid);

//----------- ESP8266------------------
char * your_ssid="Alien";
char * your_pass="password";
//const char* host = "www.example.com";
IPAddress host(192, 168, 1, 10); //  alien
const int httpPort = 8010;
//----------- DHT11------------------- 
#define DHTPIN D2
#define DHTTYPE DHT11   // DHT 11

DHT dht(DHTPIN, DHTTYPE);
//---------- DS18B20-------------------
OneWire  ds(D1);  // on pin 10 (a 4.7K resistor is necessary)
//------------------------------------
void setup() {
    Serial.begin(115200);
    dht.begin();
}

void loop() {
    byte i;
    byte present = 0;
    byte type_s;
    byte data[12];
    byte addr[8];
    float celsius, fahrenheit;
    // get data from DHT11
    float h = dht.readHumidity();     // get humidity
    float t = dht.readTemperature();  // get temperature

    // Connect to SSID
    while(!get_rssi(String(your_ssid))) {
      delay(5000);
    }
    WiFi.begin(your_ssid,your_pass);
    while (WiFi.status() != WL_CONNECTED){
        delay(500);
    }
    // get data
    Serial.print("\nConnected, IP address: ");
    Serial.println(WiFi.localIP());
    Serial.print("MAC address: ");
    Serial.println(WiFi.macAddress());
    Serial.print("\n");

    if ( !ds.search(addr)) {
        Serial.println("No more addresses.");
        Serial.println();
        ds.reset_search();
        delay(250);
        return;
    }

    if ( !isnan(h) && !isnan(t)) {
        String hum_str=String(h);
        String temp_str=String(t);
        Serial.println("Humidity: " + hum_str + " %  Temperature: " + temp_str + " *C ");
        // get data from DS18B20 
        Serial.print("ROM =");
        for( i = 0; i < 8; i++) {
            Serial.write(' ');
            Serial.print(addr[i], HEX);
        }

        if (OneWire::crc8(addr, 7) != addr[7]) {
            Serial.println("CRC is not valid!");
            return;
        }
        Serial.println();

        // the first ROM byte indicates which chip
        switch (addr[0]) {
            case 0x10:
                Serial.println("  Chip = DS18S20");  // or old DS1820
                type_s = 1;
                break;
            case 0x28:
                Serial.println("  Chip = DS18B20");
                type_s = 0;
                break;
            case 0x22:
                Serial.println("  Chip = DS1822");
                type_s = 0;
                break;
            default:
              Serial.println("Device is not a DS18x20 family device.");
              return;
        }

        ds.reset();
        ds.select(addr);
        ds.write(0x44, 1);        // start conversion, with parasite power on at the end

        delay(1000);     // maybe 750ms is enough, maybe not
        // we might do a ds.depower() here, but the reset will take care of it.

        present = ds.reset();
        ds.select(addr);
        ds.write(0xBE);         // Read Scratchpad

        Serial.print("  Data = ");
        Serial.print(present, HEX);
        Serial.print(" ");
        for ( i = 0; i < 9; i++) {           // we need 9 bytes
            data[i] = ds.read();
            Serial.print(data[i], HEX);
            Serial.print(" ");
        }
        Serial.print(" CRC=");
        Serial.print(OneWire::crc8(data, 8), HEX);
        Serial.println();

        // Convert the data to actual temperature
        // because the result is a 16 bit signed integer, it should
        // be stored to an "int16_t" type, which is always 16 bits
        // even when compiled on a 32 bit processor.
        int16_t raw = (data[1] << 8) | data[0];
        if (type_s) {
            raw = raw << 3; // 9 bit resolution default
            if (data[7] == 0x10) {
                // "count remain" gives full 12 bit resolution
                raw = (raw & 0xFFF0) + 12 - data[6];
            }
        } else {
            byte cfg = (data[4] & 0x60);
            // at lower res, the low bits are undefined, so let's zero them
            if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
            else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
            else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
            //// default is 12 bit resolution, 750 ms conversion time
        }
        celsius = (float)raw / 16.0;
        fahrenheit = celsius * 1.8 + 32.0;
        Serial.print("  Temperature = ");
        Serial.print(celsius);
        Serial.print(" Celsius, ");
        Serial.print(fahrenheit);
        Serial.println(" Fahrenheit");
        String ds18b20_str=String(celsius);

        // GET request
        WiFiClient client;
        if (client.connect(host, httpPort)) {
            client.print(String("GET /cgi-bin/sensors.cgi?var1=") +
                          hum_str + "&var2=" + temp_str + "&var3=" + ds18b20_str + " HTTP/1.1\r\n" +
                          "Host: " + host + "\r\n" +
                          "Connection: close\r\n\r\n");
            Serial.println("[Response:]");
            while (client.connected() || client.available()){
                if (client.available()) {
                    String line = client.readStringUntil('\n');
                    Serial.println(line);
                }
            }
            Serial.println("\n[Disconnected]");

            client.stop();
        } else
            Serial.println("connection failed");

        // disconnect
        WiFi.disconnect();
    }
    // sleep
    ESP.deepSleep(3e8);    // 5 minutes
}

int get_rssi(String ssid) {
    int ret=0;
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    while (n) {
        if (ssid.equals(WiFi.SSID(--n))) {
          ret=WiFi.RSSI(n);
          break;
        }
    }
  return ret;
}

int list_wifi() {
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    if (n) {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            String ssid=String(i+1) + ": " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ")";
            ssid=(WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ssid+"\n" : ssid+"*\n";
            Serial.print(ssid);
            delay(10);
        }
    } else
        Serial.println("no networks found");
    return n;
}

Также немного придётся поправить cgi скрипт для приёма и записи показаний датчика DS18B20:

#!/bin/sh
echo -en "Status: 200 OK\r\n"
echo -en "Content-type: text/html\r\n\r\n"
echo "<h1>ESP8266 -  GET Request Example</h1>"

line=$(echo "$QUERY_STRING"|sed "s/&/   /g")
for loop in $line
do
        name=$(echo $loop|awk -F = '{print $1}')
        type=$(echo $loop|awk -F = '{print $2}')
        var=$(printf "${name}=${type}")
        eval $(printf $var)
done

[ -z $var1 ] && exit
[ -z $var2 ] && exit
[ -z $var3 ] && exit

echo $var1
echo $var2
echo $var3

p0="/site/sensors"
p2="$p0/$(date +%d_%m_%y)"
[ -d "$p2" ] || mkdir -p "$p2"
echo -n $(date) >>  "$p2/log.txt"
echo -n " ds18b20: " >> "$p2/log.txt"
echo -n "$var3" >> "$p2/log.txt"
echo -n " humidity: " >> "$p2/log.txt"
echo -n "$var1" >> "$p2/log.txt"
echo -n " temperature: " >> "$p2/log.txt"
echo  "$var2" >> "$p2/log.txt"

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

Sat Dec 15 16:34:52 SAMT 2018 ds18b20: 20.88 humidity: 17.00 temperature: 27.00
Sat Dec 15 16:39:42 SAMT 2018 ds18b20: 5.12 humidity: 17.00 temperature: 27.00
Sat Dec 15 16:44:32 SAMT 2018 ds18b20: -2.25 humidity: 19.00 temperature: 24.00
Sat Dec 15 16:49:23 SAMT 2018 ds18b20: -4.44 humidity: 19.00 temperature: 24.00
Sat Dec 15 16:54:14 SAMT 2018 ds18b20: -5.25 humidity: 19.00 temperature: 24.00
Sat Dec 15 16:59:04 SAMT 2018 ds18b20: -5.69 humidity: 19.00 temperature: 23.00
Sat Dec 15 17:03:55 SAMT 2018 ds18b20: -5.87 humidity: 19.00 temperature: 23.00

Здесь ds18b20 снимает показания по одну сторону окна, а dht11 - по другую.

9) Вывод показаний датчиков через веб-интерфейс

Теперь нам нужно реализовать вывод данных через веб-интерфейс. Для этого напишем два CGI скрипта. Первый будет выводить лог измерений за текущие сутки, а второй будет выводить текущие значения. К сожалению я не обнаружил в uhttpd сервере поддержки SSI, поэтому генерацию html кода пришлось полностью реализовывать через шелл-скрипы.

GCI скрипт для вывода лога измерений получился таким:

#!/bin/sh
echo -en "Status: 200 OK\r\n"
echo -en "Content-type: text/html\r\n\r\n"
echo '<!DOCTYPE html>'
echo '<html>'
echo '<head>'
echo '   <meta charset="utf-8">'
echo '<style>'
echo 'body { background-color: #333333; color: white; } '
echo '</style>'
echo '<title>Weather Log</title>'
echo '</head>'
echo '<body>'
p0="/site/sensors"
p2="$p0/$(date +%d_%m_%y)"
[ -f "$p2/log.txt" ] || exit
while read LINE; do
echo "<p>"
echo "$LINE"
echo "</p>"
done < "$p2/log.txt"
echo '</body>'
echo '</html>'

Он довольно простой. Второй скрипт будет поинтереснее:

#!/bin/sh
echo -en "Status: 200 OK\r\n"
echo -en "Content-type: text/html\r\n\r\n"
echo '<!DOCTYPE html>'
echo '<html>'
echo '<head>'
echo '   <meta charset="utf-8">'
echo '<style>'
echo 'body { background-color: #333333; color: white; } '
echo '</style>'
echo '<title>Weather Station</title>'
echo '</head>'
echo '<body>'
p0="/site/sensors"
p2="$p0/$(date +%d_%m_%y)"
[ -f "$p2/log.txt" ] || exit
l1=$(tail -n1 "$p2/log.txt")
echo "<h1>"
echo "$l1" |awk -F "ds18b20" '{print $1}'
echo "</h1>"
echo "<h2>"
echo "Outdoor temperature is:"
echo "</h2>"
l2=$(echo "$l1" |awk -F : '{print $4}'|awk '{print $1}')
l3=$(echo "$l2"|awk -F . '{print $1}')
if [ $l3 -ge 0 ]; then
echo '<span style="color: red; font-size: 120pt; font-weight: bold; ">'
else
echo '<span style="color: blue; font-size: 120pt; font-weight: bold; ">'
fi
echo -n "$l2"
echo "&deg;C"
echo '</span>'
echo "<h2>"
echo "Indoor temperature is:"
echo "</h2>"
l4=$(echo "$l1" |awk -F : '{print $6}'|awk '{print $1}')
l5=$(echo "$l4"|awk -F . '{print $1}')
if [ $l5 -ge 0 ]; then
echo '<span style="color: red; font-size: 120pt; font-weight: bold; ">'
else
echo '<span style="color: blue; font-size: 120pt; font-weight: bold; ">'
fi
echo -n "$l4"
echo "&deg;C"
echo '</span>'
echo "<h2>"
echo "Indoor humidity is:"
echo "</h2>"
l6=$(echo "$l1" |awk -F : '{print $5}'|awk '{print $1}')
l7=$(echo "$l6"|awk -F . '{print $1}')
echo '<span style="color: grey; font-size: 120pt; font-weight: bold; ">'
echo -n "$l6"
echo "%"
echo '</span>'
echo '</body>'
echo '</html>'

По желанию еще можно добавить JavaScript для автоматического обновления содержимого страницы.

Результат работы последнего скрипта на экране мобильника выглядит так:

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

10) Второй WiFi термометр на датчике AM2320 (добавлено 26.05.19г)

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

Теперь же, с приближением лета, возникла необходимость в изготовлении второго термометра. Для чего вам может понадобиться второй уличный термометр? Если у вас окна выходят на север, то вы можете не волноваться за результаты показаний термометра, он скорее-всего будет всегда казать температуру приближенную к реальной. Если же у вас все окна выходят на юг, то даже если вы будете как-то пытаться экранировать сенсор, солнце все равно будет засвечивать его, и показания "поплывут". Если же у вас, как и у меня окна, выходят на восток и запад, то большую часть года вполне можно обойтись термометром на восточном окне. Однако летом, как только встает солнце мой спрятанный под карнизом DS18B20 начинает показывать температуру кипения хлорного ангидрида, что для земной атмосферы несколько ненормально.

К счастью, имеется второе окно на западной стороне на которое можно повесить дополнительный WiFi-термометр. Делать еще один термометр на DS18B20 мне было не интересно, поэтому был куплен датчик AM2320 который кроме температуры еще измеряет и относительную влажность воздуха. Согласно даташиту на датчик: "Digital Temperature and Humidity Sensor AM2320 Product Manual", диапазон измеряемой температуры датчика от -40℃ до +80 ℃, с погрешностью в полградуса. Влажность же он измеряет во всем диапазоне с погрешностью +/- 3 %RH. Датчик может питаться от 3 и 5 Вольт, и может работать на I2C протоколе или однопроводному протоколу, наподобие DHT11(не путать с 1-wire).

Я буду использовать I2C интерфейс, на ESP12E/ESP12F он расположен на GPIO4 (SDA) и GPIO5 (SCL). На NodeMCU это соответственно выводы D2 и D1:

Подключается датчик к NodeMCU таким образом:

Подтягивающие резисторы на линии SDA и SDL ставить не надо, они есть в самом датчике. Так же как и в случае с DHT11, для работы с датчиком нам понадобится библиотека https://github.com/adafruit/Adafruit_Sensor. Кроме этого, понадобится библиотека непосредствено работающая с AM2320 https://github.com/adafruit/Adafruit_AM2320. Последняя содержит скетч для проверки работы датчика:

#include "Adafruit_Sensor.h"
#include "Adafruit_AM2320.h"

Adafruit_AM2320 am2320 = Adafruit_AM2320();

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    delay(10); // hang out until serial port opens
  }

  Serial.println("Adafruit AM2320 Basic Test");
  am2320.begin();
}

void loop() {
  Serial.print("Temp: "); Serial.println(am2320.readTemperature());
  Serial.print("Hum: "); Serial.println(am2320.readHumidity());

  delay(2000);
}

После компиляции скетча и прошивки NodeMCU, в окне терминала мы должны увидеть лог работы:

Остальное - дело техники. Действуя по аналогии с датчиком DHT11, напишем скетч с соединением с точкой доступа, и уходом в спящий режим:

#include "Adafruit_Sensor.h"
#include "Adafruit_AM2320.h"
#include "ESP8266WiFi.h"

Adafruit_AM2320 am2320 = Adafruit_AM2320();

char * your_ssid="Alien";
char * your_pass="password";

int list_wifi();
int get_rssi(String ssid);

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    delay(10); // hang out until serial port opens
  }

  am2320.begin();
}

void loop() {
  // Connect to SSID
  while(!get_rssi(String(your_ssid))) {
    delay(5000);
  }
  WiFi.begin(your_ssid,your_pass);
  while (WiFi.status() != WL_CONNECTED){
      delay(500);
  }

  Serial.print("Connected, IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print("MAC address: ");
  Serial.println(WiFi.macAddress());
  Serial.print("\n");
  // get data
  Serial.print("Temp: "); Serial.println(am2320.readTemperature());
  Serial.print("Hum: "); Serial.println(am2320.readHumidity());

  // disconnect
  WiFi.disconnect();
  // sleep
  ESP.deepSleep(6e7);    // 1 minute
}

int get_rssi(String ssid) {
    int ret=0;
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    while (n) {
        if (ssid.equals(WiFi.SSID(--n))) {
            ret=WiFi.RSSI(n);
            break;
        }
    }
    return ret;
}

int list_wifi() {
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    if (n) {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            String ssid=String(i+1) + ": " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ")";
            ssid=(WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ssid+"\n" : ssid+"*\n";
            Serial.print(ssid);
            delay(10);
        }
    } else
        Serial.println("no networks found");
    return n;
}

В терминале должен пойти лог такого вида:

Теперь на роутере напишем CGI-скрипт "/site/cgi-bin/am2320.cgi" для приема и сохранения результатов измерений датчика:

#!/bin/sh
echo -en "Status: 200 OK\r\n"
echo -en "Content-type: text/html\r\n\r\n"
echo "<h1>ESP8266 -  GET Request Example</h1>"

line=$(echo "$QUERY_STRING"|sed "s/&/   /g")
for loop in $line
do
        name=$(echo $loop|awk -F = '{print $1}')
        type=$(echo $loop|awk -F = '{print $2}')
        var=$(printf "${name}=${type}")
        eval $(printf $var)
done

[ -z $var1 ] && exit
[ -z $var2 ] && exit

echo $var1
echo $var2

p0="/site/sensors"
p2="$p0/$(date +%d_%m_%y)"
[ -d "$p2" ] || mkdir -p "$p2"
echo -n $(date) >>  "$p2/log.txt"
echo -n " am2320 - " >> "$p2/log.txt"
echo -n " humidity: " >> "$p2/log.txt"
echo -n "$var1" >> "$p2/log.txt"
echo -n " temperature: " >> "$p2/log.txt"
echo  "$var2" >> "$p2/log.txt"

Осталось привести скетч ESP8266 к окончательному рабочему виду:

#include "Adafruit_Sensor.h"
#include "Adafruit_AM2320.h"
#include "ESP8266WiFi.h"

Adafruit_AM2320 am2320 = Adafruit_AM2320();

char * your_ssid="Alien";
char * your_pass="password";

IPAddress host(192, 168, 1, 10); //  alien
const int httpPort = 8010;

int list_wifi();
int get_rssi(String ssid);

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    delay(10); // hang out until serial port opens
  }

  am2320.begin();
}

void loop() {
  // Connect to SSID
  while(!get_rssi(String(your_ssid))) {
    delay(5000);
  }
  WiFi.begin(your_ssid,your_pass);
  while (WiFi.status() != WL_CONNECTED){
      delay(500);
  }

  Serial.print("Connected, IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print("MAC address: ");
  Serial.println(WiFi.macAddress());
  Serial.print("\n");
  // get data
  float t = am2320.readTemperature();
  float h = am2320.readHumidity();
  if ( !isnan(h) && !isnan(t)) {
        String hum_str=String(h);
        String temp_str=String(t);
        Serial.println("Humidity: " + hum_str + " %  Temperature: " + temp_str + " *C ");

        // GET request
        WiFiClient client;
        if (client.connect(host, httpPort)) {
            client.print(String("GET /cgi-bin/am2320.cgi?var1=") +
                          hum_str + "&var2=" + temp_str + " HTTP/1.1\r\n" +
                          "Host: " + host + "\r\n" +
                          "Connection: close\r\n\r\n");
            Serial.println("[Response:]");
            while (client.connected() || client.available()){
                if (client.available()) {
                    String line = client.readStringUntil('\n');
                    Serial.println(line);
                }
            }
            Serial.println("\n[Disconnected]");

            client.stop();
        } else
            Serial.println("connection failed");

        // disconnect
        WiFi.disconnect();
  }

  //Serial.print("Temp: "); Serial.println(am2320.readTemperature());
  //Serial.print("Hum: "); Serial.println(am2320.readHumidity());

  // disconnect
  WiFi.disconnect();
  // sleep
  ESP.deepSleep(6e8);    // 10 minutes
}

int get_rssi(String ssid) {
    int ret=0;
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    while (n) {
        if (ssid.equals(WiFi.SSID(--n))) {
            ret=WiFi.RSSI(n);
            break;
        }
    }
    return ret;
}

int list_wifi() {
    // set wifi mode
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    if (n) {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            String ssid=String(i+1) + ": " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ")";
            ssid=(WiFi.encryptionType(i) == ENC_TYPE_NONE) ? ssid+"\n" : ssid+"*\n";
            Serial.print(ssid);
            delay(10);
        }
    } else
        Serial.println("no networks found");
    return n;
}

Для отображения данных с двух датчиков, скрипт temperature.cgi нужно будет привести, например, к такому виду:

#!/bin/sh
echo -en "Status: 200 OK\r\n"
echo -en "Content-type: text/html\r\n\r\n"
echo '<!DOCTYPE html>'
echo '<html>'
echo '<head>'
echo '   <meta charset="utf-8">'
echo '<style>'
echo 'body { background-color: #333333; color: white; } '
echo '</style>'
echo '<title>Weather Station</title>'
echo '</head>'
echo '<body>'
p0="/site/sensors"
p2="$p0/$(date +%d_%m_%y)"
[ -f "$p2/log.txt" ] || exit
l1=$(grep 'ds18b20' "$p2/log.txt"|tail -n1)
p1=$(grep 'am2320' "$p2/log.txt"|tail -n1)
echo "<h1>"
echo "$l1" |awk -F "ds18b20" '{print $1}'
echo "</h1>"
echo "<h2>"
echo "Temperature on east side is:"
echo "</h2>"
l2=$(echo "$l1" |awk -F : '{print $4}'|awk '{print $1}')
l3=$(echo "$l2"|awk -F . '{print $1}')
if [ $l3 -ge 0 ]; then
echo '<span style="color: red; font-size: 120pt; font-weight: bold; ">'
else
echo '<span style="color: blue; font-size: 120pt; font-weight: bold; ">'
fi
echo -n "$l2"
echo "&deg;C"
echo '</span>'

echo "<h1>"
echo "$p1" |awk -F "am2320" '{print $1}'
echo "</h1>"
echo "<h2>"
echo "Temperature on west side is:"
echo "</h2>"
p2=$(echo "$p1" |awk -F 'temperature: ' {'print $NF'})
p3=$(echo "$p2"|awk -F . '{print $1}')
if [ $p3 -ge 0 ]; then
echo '<span style="color: red; font-size: 120pt; font-weight: bold; ">'
else
echo '<span style="color: blue; font-size: 120pt; font-weight: bold; ">'
fi
echo -n "$p2"
echo "&deg;C"
echo '</span>'

echo "<h2>"
echo "Indoor temperature is:"
echo "</h2>"
l4=$(echo "$l1" |awk -F : '{print $6}'|awk '{print $1}')
l5=$(echo "$l4"|awk -F . '{print $1}')
if [ $l5 -ge 0 ]; then
echo '<span style="color: red; font-size: 120pt; font-weight: bold; ">'
else
echo '<span style="color: blue; font-size: 120pt; font-weight: bold; ">'
fi
echo -n "$l4"
echo "&deg;C"
echo '</span>'

echo "<h2>"
echo "Outdor humidity is:"
echo "</h2>"
p6=$(echo "$p1" |awk '{print $10}')
#p7=$(echo "$p6"|awk -F . '{print $1}')
echo '<span style="color: grey; font-size: 120pt; font-weight: bold; ">'
echo -n "$p6"
echo "%"
echo '</span>'

echo "<h2>"
echo "Indoor humidity is:"
echo "</h2>"
l6=$(echo "$l1" |awk -F : '{print $5}'|awk '{print $1}')
#l7=$(echo "$l6"|awk -F . '{print $1}')
echo '<span style="color: grey; font-size: 120pt; font-weight: bold; ">'
echo -n "$l6"
echo "%"
echo '</span>'
echo '</body>'
echo '</html>'

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

В заключение хочу немного сказать об автономном режиме работы ESP8266, т.е. режиме работы от аккумулятора. Мне пока удалось добиться беспрерывной работы всего лишь двое суток. НО. У меня довольно "слабый" аккумулятор. Это одна батарея 18650, сама обычная с али. Ее фактическая емкость где-то 500 или 600 мА. Далее у меня не самая эффективная цепь питания. C батареи питание идет на повышающий до 6 Вольт DC-DC преобразователь. Затем питание проходит через понижающий до 3.3 Вольт линейный преобразователь ams1117-3.3. В итоге, я пока имею только двое суток автономной работы. Work in progress, как говорится.

поделиться: