Usage Note on WeMos D1

D1 WiFi UNO R3开发板基于ESP8266 ESP-12N F 模块,够自于淘宝。初步使用WeMos D1的代码如下(WeMosTest)。需要注意的地方包括:1)采用DHTesp或者DHT库应该都是可以的。2)连接A0口获取不到数据,连接D2口可以获取到相应的DHT11的数据。参考链接[1]。

/*  WeMos D1 Web Server, modified by Ma Can
 *  
 *  get weather info from internet, connect DHT11 to get home temptrature and humidity
*/
#include <DHTesp.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>  // http web access library
#include <ArduinoJson.h>        // JSON decoding library

#define DHTTYPE   DHT11         // define sensor type

const int DHTPIN = D2;
float h = 0;
float t = 0;

DHTesp dht;

const char* ssid     = "ap1";
const char* password = "ap1pass";

ESP8266WebServer server(80);
 
String webString="";     // String to display
// Generally, you should use "unsigned long" for variables that hold time
unsigned long previousMillis = 0;        // will store last temp was read
const long interval = 2000;              // interval at which to read sensor
 
void handle_root() {
  server.send(200, "text/plain", "Hello from WeMos Test Server, read from /feeder");
  delay(100);
}
 
void setup(void)
{
  // You can open the Arduino IDE Serial Monitor window to see what the code is doing
  Serial.begin(115200);  // Serial connection from ESP-01 via 3.3v console cable

  dht.setup(DHTPIN, DHTesp::DHT11);
  
  // Connect to WiFi network
  WiFi.begin(ssid, password);
  Serial.print("\n\r \n\rWorking to connect");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WeMos Test Server");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
   
  server.on("/", handle_root);
  
  server.on("/feeder", [](){
    webString = "Feeder Info:\n";
    getWeather("1");
    getLocalTempAndHum("2");
    server.send(200, "text/plain", webString);            // send to someones browser when asked
  });
  
  server.begin();
  Serial.println("HTTP server started");
}
 
void loop(void)
{
  server.handleClient();
}

void getLocalTempAndHum(String seq)
{  
  float t = dht.getTemperature();
  int h = dht.getHumidity();

  // Check if any reads failed and exit early (to try again).
    if (isnan(h) || isnan(t)) {
      Serial.println("Failed to read from DHT sensor!");
      return;
    }
  
  // print data
  Serial.printf("Temperature = %.2f°C\r\n", t);
  Serial.printf("Humidity    = %d %%\r\n", h);

  webString += seq;
  webString += ". Weather of Home:\n";
  webString += "\tTemperature = ";
  webString += t;
  webString += " C\n\tHumidity    = ";
  webString += h;
  webString += "\n";
}

void getWeather(String seq) 
{
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;

    http.begin("http://api.openweathermap.org/data/2.5/weather?q=BEIJING,cn&APPID=52acd267dd4d194c14d1e0b0d6a1e861");

    int httpCode = http.GET();

    if (httpCode > 0) {
      String payload = http.getString();
      
      DynamicJsonDocument doc(1024);

      // Parse JSON object
      DeserializationError err = deserializeJson(doc, payload);
      if (err) {
        Serial.print(F("deserializeJson() failed with code "));
        Serial.println(err.c_str());
        return; 
      }
 
      float temp = (float)(doc["main"]["temp"]) - 273.15;        // get temperature
      int   humidity = doc["main"]["humidity"];                  // get humidity
      float pressure = (float)(doc["main"]["pressure"]) / 1000;  // get pressure
      float wind_speed = doc["wind"]["speed"];                   // get wind speed
      int  wind_degree = doc["wind"]["deg"];                     // get wind degree
      const char *w = doc["weather"][0]["main"];
 
      // print data
      Serial.printf("Temperature = %.2f°C\r\n", temp);
      Serial.printf("Humidity    = %d %%\r\n", humidity);
      Serial.printf("Pressure    = %.3f bar\r\n", pressure);
      Serial.printf("Wind speed  = %.1f m/s\r\n", wind_speed);
      Serial.printf("Wind degree = %d°\r\n\r\n", wind_degree);
      
      webString += seq;
      webString += ". Weather of Beijing:\n";
      webString += "\tWeather = ";
      webString += w;
      webString += "\n\tTemperature = ";
      webString += temp;
      webString += " C\n\tHumidity    = ";
      webString += humidity;
      webString += " %\n\tPressure    = ";
      webString += pressure;
      webString += " bar\n\tWind speed  = ";
      webString += wind_speed;
      webString += " m/s\n\tWind degree = ";
      webString += wind_degree;
      webString += "\n";
    }
    http.end();
  }
}

Arduino IDE配置如下:

Weekend Project Progress

This post is a progress diary for my weekend project. The final goal of the project is building a tank surveillance system by using ESPs, relays and other electronic devices.

  • ESP8266

如何上传代码,请参考链接[1],与温湿度传感器相连接请参考链接[2],[3],DHT11的使用方法和介绍请见[4].

代码如下(ESP8266WebServer),注意调整DHTTYPE为DHT11,调整温度读取单位为摄氏度。

/* DHTServer - ESP8266 Webserver with a DHT sensor as an input

   Based on ESP8266Webserver, DHTexample, and BlinkWithoutDelay (thank you)

   Version 1.0  5/3/2014  Version 1.0   Mike Barela for Adafruit Industries

   Modified by Ma Can
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <DHT.h>
#define DHTTYPE DHT11
#define DHTPIN  2

const char* ssid     = "GOGINGKO";
const char* password = "GOGINGKO";

ESP8266WebServer server(80);
 
// Initialize DHT sensor 
// NOTE: For working with a faster than ATmega328p 16 MHz Arduino chip, like an ESP8266,
// you need to increase the threshold for cycle counts considered a 1 or 0.
// You can do this by passing a 3rd parameter for this threshold.  It's a bit
// of fiddling to find the right value, but in general the faster the CPU the
// higher the value.  The default for a 16mhz AVR is a value of 6.  For an
// Arduino Due that runs at 84mhz a value of 30 works.
// This is for the ESP8266 processor on ESP-01 
DHT dht(DHTPIN, DHTTYPE, 11); // 11 works fine for ESP8266
 
float humidity, temp_c;  // Values read from sensor
String webString="";     // String to display
// Generally, you should use "unsigned long" for variables that hold time
unsigned long previousMillis = 0;        // will store last temp was read
const long interval = 2000;              // interval at which to read sensor
 
void handle_root() {
  server.send(200, "text/plain", "Hello from the weather esp8266, read from /temp or /humidity");
  delay(100);
}
 
void setup(void)
{
  // You can open the Arduino IDE Serial Monitor window to see what the code is doing
  Serial.begin(115200);  // Serial connection from ESP-01 via 3.3v console cable
  dht.begin();           // initialize temperature sensor

  // Connect to WiFi network
  WiFi.begin(ssid, password);
  Serial.print("\n\r \n\rWorking to connect");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("DHT Weather Reading Server");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
   
  server.on("/", handle_root);
  
  server.on("/temp", [](){  // if you add this subdirectory to your webserver call, you get text below :)
    gettemperature();       // read sensor
    webString="Temperature: "+String((int)temp_c)+" C";   // Arduino has a hard time with float to string
    server.send(200, "text/plain", webString);            // send to someones browser when asked
  });

  server.on("/humidity", [](){  // if you add this subdirectory to your webserver call, you get text below :)
    gettemperature();           // read sensor
    webString="Humidity: "+String((int)humidity)+"%";
    server.send(200, "text/plain", webString);               // send to someones browser when asked
  });
  
  server.begin();
  Serial.println("HTTP server started");
}
 
void loop(void)
{
  server.handleClient();
} 

void gettemperature() {
  // Wait at least 2 seconds seconds between measurements.
  // if the difference between the current time and last time you read
  // the sensor is bigger than the interval you set, read the sensor
  // Works better than delay for things happening elsewhere also
  unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis >= interval) {
    // save the last time you read the sensor 
    previousMillis = currentMillis;   

    // Reading temperature for humidity takes about 250 milliseconds!
    // Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor)
    humidity = dht.readHumidity();          // Read humidity (percent)
    temp_c = dht.readTemperature();     // Read temperature
    // Check if any reads failed and exit early (to try again).
    if (isnan(humidity) || isnan(temp_c)) {
      Serial.println("Failed to read from DHT sensor!");
      return;
    }
  }
}

另外,需要注意的是,DHT11 v1.0需要使用3.7v-12V DC供电(支持3.7V锂电池),输出电源除了3.3v之外,还需要至少提供5v以供DHT11使用。

在使用Arduino IDE编译和上传时,开发版配置参数如下:

  1. Flash Mode: “DIO”
  2. Flash Frequency: “40MHz”
  3. CPU Frequency: “80 MHz”
  4. Flash Size: “1M (64K SPIFFS)”
  5. Debug Port: “Disabled”
  6. Debug Level: “None”
  7. Reset Method: “ck”
  8. Upload Speed: “115200”
  • ESP32 CAM

ESP32-CAM是一个低成本的CAMERA解决方案。本次使用的目标是将其变为一个鱼缸乌龟台的视频监控。ESP32-CAM在通过TTL上传代码时需要确保3.3v供电,相机类型使用CAMERA_AI_THINKER。同时,根据测试,ESP32-CAM在实际使用时需要确保有5V供电。

ESP32-CAM上传代码时连接3.3v电源,GPIO0保持GND以进入flash模式,TXD/RXD根据标识与USB TTL引脚相连。ESP32-CAM的引脚图如下所示。需要注意的是如何TTL连线上传代码,请参考需要注意的是如何TTL连线上传代码[5]。,ESP32-CAM的代码如下(ESP32CAMWebServer)

// Modified by Ma Can
#include "esp_camera.h"
#include <WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <PubSubClient.h>
#include <DHTesp.h>
//
// WARNING!!! Make sure that you have either selected ESP32 Wrover Module,
//            or another board which has PSRAM enabled
//

// Select camera model
//#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_ESP_EYE
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE
#define CAMERA_MODEL_AI_THINKER

#include "camera_pins.h"

const char* ssid[] = { "ap1", "ap2" };
const char* password[] = { "ap1pass", "ap2pss" };
int ssid_idx = 0, pwd_idx = 0;

const char *mqtt_server[] = { "192.168.21.228", "192.168.11.249" };
int sid = 0;

// 定义DS18B20数据口连接arduino的15号IO上
#define ONE_WIRE_BUS 15

// 初始连接在单总线上的单总线设备
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

#define DHTTYPE   DHT11         // define sensor type

const int DHTPIN = 14;
float h = 0;
float t = 0;

DHTesp dht;

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0, boot = 0;
char msg[50];
int value = 0;

extern void startCameraServer();

int connect_wifi(const char *ssid, const char *password) {
  long last, now;

  // Connect to WiFi network
  WiFi.begin(ssid, password);
  Serial.print("\n\r \n\rWorking to connect");
  last = now = millis();

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    now = millis();
    if ((now - last) >= 30000)
      break;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("");
    Serial.print("WiFi connect to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    return 1;
  } else {
    Serial.println("");
    Serial.print("WiFi connect to ");
    Serial.print(ssid);
    Serial.println(" failed.");
    return 0;
  }
}

int select_server() {
  return sid++ % (sizeof(mqtt_server) / sizeof(mqtt_server[0]));
}

int select_ssid() {
  return ssid_idx++ % (sizeof(ssid) / sizeof(ssid[0]));
}

int select_password() {
  return pwd_idx++ % (sizeof(password) / sizeof(password[0]));
}

void setup() {
  boot = millis();
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();

  sensors.begin();

  dht.setup(DHTPIN, DHTesp::DHT11);

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  //init with high specs to pre-allocate larger buffers
  if (psramFound()) {
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

#if defined(CAMERA_MODEL_ESP_EYE)
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);
#endif

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  sensor_t * s = esp_camera_sensor_get();
  //initial sensors are flipped vertically and colors are a bit saturated
  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1);//flip it back
    s->set_brightness(s, 1);//up the blightness just a bit
    s->set_saturation(s, -2);//lower the saturation
  }
  //drop down frame size for higher initial frame rate
  s->set_framesize(s, FRAMESIZE_QVGA);

#if defined(CAMERA_MODEL_M5STACK_WIDE)
  s->set_vflip(s, 1);
  s->set_hmirror(s, 1);
#endif

  for (int i = 0; i < sizeof(ssid) / sizeof(ssid[0]); i++) {
    if (connect_wifi(ssid[i], password[i]) == 1) {
      // it is ok
      break;
    }
  }

  startCameraServer();

  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  Serial.println("' to connect");

  client.setServer(mqtt_server[select_server()], 1883);
  client.setCallback(callback);
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP32CAM")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("/esp32/sensor/state", "connected");
      // ... and resubscribe
      client.subscribe("/esp32/switch/cmd");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds with another server");
      // Wait 5 seconds before retrying
      client.setServer(mqtt_server[select_server()], 1883);
      delay(5000);
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Switch
  int val;
  if ((char)payload[0] == '1') {

  } else if ((char)payload[0] == '2') {

  }
}

void publish_sensor_data(char *msg)
{
  double ctemp;
  
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // 发送命令获取温度
  ctemp = sensors.getTempCByIndex(0);
  Serial.println("DONE");

  Serial.print("Temperature for the device 1 (index 0) is: ");
  Serial.println(ctemp);

  if (ctemp != -127) {
    snprintf(msg, 40, "%0.2f", ctemp);
    client.publish("/esp32/sensor/waterTemp/state", msg);
    Serial.print("Publish message: ");
    Serial.println(msg);
  }
}

void publish_dht11_data(char *msg)
{
  float t = dht.getTemperature();
  int h = dht.getHumidity();

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }

  // print data
  Serial.printf("Temperature = %.2f°C\r\n", t);
  Serial.printf("Humidity    = %d %%\r\n", h);

  snprintf(msg, 40, "%0.2f", t);
  client.publish("/esp32/sensor/airTemp/state", msg);
  Serial.print("Publish message: Temp:");
  Serial.println(msg);
  snprintf(msg, 40, "%d", h);
  client.publish("/esp32/sensor/airHumidity/state", msg);
  Serial.print("Publish message: Humidity:");
  Serial.println(msg);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  long now = millis();
  if (now - lastMsg > 30000) {
    lastMsg = now;
    ++value;
    snprintf (msg, 49, "connected", value);
    Serial.print("Publish message: ");
    Serial.println(msg);
    client.publish("/esp32/sensor/state", msg);
    publish_sensor_data(msg);
    publish_dht11_data(msg);
  }
  if (now - boot > 86400000) {
    ESP.restart();
  }
}

Arduino IDE配置如下:

  • DS18B20

连线方式如下图所示,单一总线协议,DATA线需要连接4.7K欧的上拉电阻。

两种测温度的方法(代码)都可以使用。

备注:使用ESP32采样的话很可能需要调整电阻值!!

备注2:使用ESP8266验证DS18B20的工作还没有开展,现在尝试结果如下。使用5V电源的话,代码是一致的,可以直接使用。

  • ESP-01S 2 Way Relay

代码请见ESP8266Relay2Way,原理比较简单,关键是如何连接2-way relay。目前在面包版的电源模块上实验,按照下图的电路方式连接(12v电压忽略)。隔离JD-VCC和VCC,不加ESP-01S直接测试继电器是成功的,加上ESP-01S测试继电器是失败的,观察了一下VCC的电压,加上ESP-01S之后,电压下降到了2.8V,推测该电压太低,已经不能驱动继电器工作了(原因是面包板的电源模块负载能力不够)。截至到2019年5月27日,通过220v->5V电源驱动JD-VCC,3.3v电源驱动VCC和INx的模式,已经成功工作并集成完毕。

  • 目前的工作进展(截至到2019年6月1日,验证时间超过1周)

1 完成了一个2路的继电器模块,通过220V AC to 5V DC的模块供电,由ESP01S连接到wifi并通过MQTT协议实现控制,与HA进行了集成,能够控制照明灯和乌龟灯;

2 完成了基于ESP32 CAM的水温、气温、湿度采集和视频采集的模块,通过220V AC to 5V DC的模块供电,实现MQTT协议,提供采集数据,与Home Assistant进行了集成,能够实现基本功能,如下图所示。

但是,还存在以下的缺陷:1)数据采样过程存在一定的时间延迟,在此期间整个MCU是无法为视频采集提供服务的,因此会出现一定的视频卡顿现象。

建议如下:

1)继电器模块增加至4路控制,考虑怎么样连接多路之间的线路来减小多路控制时插头的数量;

2)继电器模块增加至多路控制时考虑ESP01S的输出口是否还够用,默认只有IO0和IO2两个口;

3)ESP32 CAM模块增加其他的采样功能时需要考虑是否会对主要功能产生影响;(回复意见:降低温湿度采集的频率为每分钟一次,修正后ESP32 CAM稳定性获得了一定的增强)

4)ESP32 CAM模块的稳定性堪忧,特别是长时间运行后video stream服务会崩溃;(实际上,除了video stream服务崩溃外,还出现过传感器采集故障)

Connect Raspberry Pi to Arduino Nano by TTL Serial

Several days ago, I had received my low price pulse sensors. Today, I am working on using them. The sensors are very easy to use. By connect 3 pins to my Nano, it works fine. Then I am trying to connect my Nano to Raspberry Pi 3 B+ by TTL serial.

First, write a little code to run Nano. Reading pulse sensor’s analog output from pin A0. Map the analog value to digit output pin D9, and power on a LED with changing brightness.

 int sensorPin = A0;    // select the input pin for the sensor
int brightness = 0;
int ledPin = 9; // select the pin for the LED
int sensorValue = 0; // store the value coming from the sensor
unsigned long delayMillis = 20;

void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}

void loop() {
// read the value from the sensor:
sensorValue = analogRead(sensorPin);
// mapping and shifting
brightness = map(sensorValue, 0, 1023, 0, 255) - 80;
// write to the ledPin
analogWrite(ledPin, brightness);
// print the results to the Serial line:
Serial.println(brightness);
// delay for milliseconds and resample:
delay(delayMillis);
}

When you put your finger on the sensor, the LED blinks with your heart beat, and the HB amplifications are sent to serial line.

Final setup that powers on

Then, connect Nano to Raspberry Pi in next 3 steps.

  • Step 1: Power on Nano by Rpi’s +5V pin;
  • Step 2: Connect Rpi’s pin 10 to Nano’s TX1. Remember to convert Nano’s +5V signal to 3V3 level. Do NOT connect the two pins directly;
  • Step 3: Open ‘screen’ or ‘minicom’ on Rpi, connect to ttyS0 or ttyAMA0 or something else to get the sensor values.