更新日志:

2023年3月25日
统一从ArduinoIDE转移到PlatformIO


时效性以github版本为准,这个版本已经做出模块化PCB,点击这里查看

实现功能

本例将OTA/WIFIManager/Blynk整合到一起.V3版本之前做过两个版本,而且比这个版本精度更高,不过库有点问题,还没有时间研究.这个版本是最稳定最适合入手的版本,外壳以找淘宝蓝控电子,都开好孔的,12一个.最后发一张图,我就笑笑不说话,你懂的手动滑稽 :huaji:





功能:

  • 采集大气压/温湿度/光照度
  • 增加按钮开关用作复位WIFI设置功能(例如:当你需要更换设备所连接WIFI热点可以重新选择)
  • 增加WIFI连接管理界面可以更改TOKEN KEY功能(设备自身绑定后也能更改)
  • 实时显示网络信息
  • OTA更新固件

材料准备

  • ESP8266 X 1
  • BMP180 X 1
  • SHT-30 X 1
  • BH1750 - FVI X 1
  • 按钮开关 X 1

传感器用到的程序库

需要注意的编译环境(亲测高版本会出错,未解决)

ArduinoIDE编译通过的库版本:

PlatformIO编译通过的库版本:

  • Blynk:0.6.1
  • ArduinoJson:5.13.4
  • WifiManager:0.14
  • ESP8266板库:2.0.4、2.1.1、2.2.0

接线

懒得画图了,没啥难度都,是I2C接口,注意全部模块VCC接到3.3V,

设备 引脚
ESP8266 3.3 GND SDA SCL D5
SHT30 Vcc GND SDA SCL /
BMP180 Vcc GND SDA SCL /
BH1750 Vcc GND SDA SCL /
开关 COM NO

设备端代码

(如果手机端网页显示不正常,点击左上角三个点点)

#define BLYNK_PRINT Serial
//#define BLYNK_DEBUG               //详细DEBUG

//#include <Arduino.h>                //PlatformIO必须
#include <FS.h>                   
#include <BlynkSimpleEsp8266.h>
#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>

#include <DNSServer.h>
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson

#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <WEMOS_SHT3X.h>
#include <BH1750FVI.h>

Adafruit_BMP085 bmp;
SHT3X sht30(0x44);
BH1750FVI LightSensor(BH1750FVI::k_DevModeContLowRes);

const int ResetButton = D5;
const int LedPin = D4;

//这里声明"参数"的预设值, 如果和值不同,则会将他们覆盖
char blynk_token[34] = "你的Blynk Token KEY";

//用于保存数据的标志
bool shouldSaveConfig = false;
BlynkTimer timer;

//回调通知我们需要保存配置
void saveConfigCallback ()
{
  Serial.println("Should save config");
  shouldSaveConfig = true;
}

void sendSensor()
{
  sht30.get();
  uint16_t lux = LightSensor.GetLightIntensity();
  float h = sht30.humidity;
  float t = sht30.cTemp;
  float p = bmp.readPressure() ;
  float l = lux;
  Blynk.virtualWrite(V0, h);
  Blynk.virtualWrite(V1, t);
  Blynk.virtualWrite(V2, p / 100);
  Blynk.virtualWrite(V3, l);
}

void setup() {
  Serial.begin(115200);
  int ResetButtonState = digitalRead(ResetButton);
  digitalWrite(LedPin, HIGH);
  pinMode(ResetButton, INPUT);
  pinMode(LedPin, OUTPUT);
  bmp.begin();
  LightSensor.begin();

  //读取FS json的配置
  Serial.println("mounting FS...");
  if (SPIFFS.begin()) {
    Serial.println("mounted file system");
    if (SPIFFS.exists("/config.json")) {
      //文件存在,读取和加载
      Serial.println("reading config file");
      File configFile = SPIFFS.open("/config.json", "r");
      if (configFile) {
        Serial.println("opened config file");
        size_t size = configFile.size();
        // 分配缓冲区以存储文件的内容。
        std::unique_ptr<char[]> buf(new char[size]);
        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
          Serial.println("\nparsed json");
          strcpy(blynk_token, json["blynk_token"]);
        } else {
          Serial.println("failed to load json config");
        }
        configFile.close();
      }
    }
  } else {
    Serial.println("failed to mount FS");
  }
  //结束

  // 要配置的额外参数(可以是全局的,也可以只是在设置中)
  // 连接后,parameter.getValue()将获得配置的值//After connecting, parameter.getValue() will get you the configured value
  // id/name placeholder/prompt 的默认长度
  WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 34);

  //WiFiManager
  //本地初始化。一旦业务完成,就没有必要保留它
  WiFiManager wifiManager;

  //设置配置保存通知回调
  wifiManager.setSaveConfigCallback(saveConfigCallback);

  //在这里添加所有参数
  wifiManager.addParameter(&custom_blynk_token);

  //设置重置按钮的功能,这里代码可以简化,我懒得写....
  if (ResetButtonState == LOW)
  {
    Serial.println("Getting Reset ESP Wifi-Setting.......");
    digitalWrite(LedPin, LOW);
    delay(3000);
    digitalWrite(LedPin, HIGH);
    delay(300);
    digitalWrite(LedPin, LOW);
    delay(3000);
    digitalWrite(LedPin, HIGH);
    delay(300);
    digitalWrite(LedPin, LOW);
    delay(3000);
    digitalWrite(LedPin, HIGH);

    wifiManager.resetSettings();
    delay(10000);

    Serial.println("Formatting FS......");
    SPIFFS.format();

    delay(10000);
    Serial.println("Done Reboot In 5 seconds");
    digitalWrite(LedPin, LOW);
    delay(5000);
    digitalWrite(LedPin, HIGH);
    ESP.restart();
  }

  //获取ssid并传递并尝试连接
  //如果它没有连接,它将启动具有指定名称的访问点,"AutoConnectAP",并进入等待配置的阻塞循环
  if (!wifiManager.autoConnect("4合1气象采集仪V3", "")) {
    Serial.println("failed to connect and hit timeout");
    delay(3000);
    //重置并重试,或者让它深入睡眠
    ESP.reset();
    delay(5000);
  }

  //如果已经连接
  Serial.println("connected...yeey :)");
  //读取更新的参数
  strcpy(blynk_token, custom_blynk_token.getValue());
  //j将自定义参数保存到FS
  if (shouldSaveConfig) {
    Serial.println("saving config");
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
    json["blynk_token"] = blynk_token;
    File configFile = SPIFFS.open("/config.json", "w");
    if (!configFile) {
      Serial.println("failed to open config file for writing");
    }
    json.printTo(Serial);
    json.printTo(configFile);
    configFile.close();
    //end save
  }
  Serial.println("local ip");
  Serial.println(WiFi.localIP());

///////////////////////OTA部分///////////////////////////////////////////////

  // 默认主机名为:esp8266-[ChipID]
  ArduinoOTA.setHostname("4合1气象采集仪V3");

  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else { // U_SPIFFS
      type = "filesystem";
    }
    //注意:如果更新SPIFFS,这将是使用SPIFFS.end()卸载SPIFFS的地方
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) {
      Serial.println("Auth Failed");
    } else if (error == OTA_BEGIN_ERROR) {
      Serial.println("Begin Failed");
    } else if (error == OTA_CONNECT_ERROR) {
      Serial.println("Connect Failed");
    } else if (error == OTA_RECEIVE_ERROR) {
      Serial.println("Receive Failed");
    } else if (error == OTA_END_ERROR) {
      Serial.println("End Failed");
    }
  });
  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  Blynk.config(blynk_token, "chrisxs.top", 8080);
  timer.setInterval(1000L, sendSensor);
}

void loop() {
  ArduinoOTA.handle();
  Blynk.run();
  timer.run();
  Blynk.virtualWrite(V4, WiFi.localIP().toString());
  Blynk.virtualWrite(V5, WiFi.macAddress());
  Blynk.virtualWrite(V6, "RSSI:", WiFi.RSSI(), " ", "SSID: ", WiFi.SSID());
}

程序运行效果图

上传后,你会看见一个热点名为"4合1气象采集仪V3",代码中我没设密码,请自行修改.具体流程如下图,图中不是采集仪的,操作一样,不具体说了,就是连接你路由或者手机热点WIFI-SSID就行:



一般手机连接后会自动弹出验证界面,如果连接后没有弹出,就手动打开你的浏览器,输入192.168.4.1进入验证界面

APP端效果图


一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。