本帖最后由 aramy 于 2024-4-3 20:51 编辑
FireBeetle 2 ESP32 C6到手把玩了几天了。是时候去实现申请时定下的目标了:尝试一下esp32系列的低功耗功能。用太阳能电池板,制作一个室外的温湿度气象站。
首先分析使用场景:计划在房屋外边搭建一个气象站。离房屋不会太远,所以WiFi是能够覆盖到,计划使用物联网通讯,收集温湿度信息。虽然离房屋不远,但是室外是没有电源的,使用电池供电,就存在更换电池的问题,所以打算使用电池加太阳能板补能的方式解决能源问题。白天通过太阳能给锂电池补能,夜间就通过锂电池给单片机供电,同时单片机必须实现低功耗,用来节约电能。
ESP32低功耗可以通过睡眠模式实现。可以进入light-sleep和deep-sleep模式。
light-sleep:CPU暂停运行,wifi/蓝牙基带和射频关闭。RTC、ULP运行,任何唤醒事件都会唤醒芯片。数字外设、大部分内存和CPU都会被停用(停用时钟),电源功耗也会降低,从light-sleep模式下唤醒后外设和CPU会接回时钟源并继续工作,他们的外部状态会被保存。
deep-sleep:CPU、大部分外设掉电,wifi/蓝牙基带和射频关闭,进有RTC、ULP运行,wifi和蓝牙连接数据被转移到RTC内存中。仅有一部分中断源会唤醒芯片。由APB_CLK时钟提供是时钟源的CPU、大部分内存和所有数字外设都会掉电;只有片上RTC控制器、RTC外设、ULP和RTC内存会被保留电源。
了解了两种睡眠模式,这里当然就是去使用最节能的deep-sleep模式啦!有睡眠就要有唤醒,这里选用的是时间唤醒。
- esp_sleep_enable_timer_wakeup(time_in_us)
复制代码
为了适应低功耗,就去掉了自制的扩展板。接了个额外的SHT30模块,使用板载的I2C接口,和模块连接起来。这里我使用了 Arduino-sht的库,用来驱动SHT30;还使用了adafruit_mqtt库,这样收集到的信息就通过mqtt上传到物联网上了,这里物联网选用了百度的物联网平台。
与传统的Arduino编程方式有所区别。传统的Arduino运行方式为:step方法中对系统进行初始化,然后程序在loop方法中不停的循环。而使用deep-sleep方式工作,则每次唤醒都需要对系统进行初始化,包括连wifi、连mqtt、读取温湿度、上传传感器信息,然后进入deep-sleep状态,不停地重复。
基本流程就比较简单,在连接wifi和连接mqtt过程中,遇到错误会重试,重试一定次数依然失败就进入睡眠状态,等待时钟的唤醒。 - #include<WiFi.h>
- #include <esp_sleep.h>
- #include "driver/rtc_io.h"
- #include "WiFiClient.h"
- #include "Adafruit_MQTT.h"
- #include "Adafruit_MQTT_Client.h"
- #include <Wire.h>
- #include "SHTSensor.h"
-
- #define WIFI_SSID "AAA"
- #define WIFI_PASSWORD "xxxxxxxx"
-
- const char *MQTT_SERVER = "afgsmmu.iot.gz.baidubce.com";
- const int MQTT_PORT = 1883;
- const char *MQTT_USRNAME = "your_mqtt_username";
- const char *MQTT_PASSWD = "your_mqtt_passwd";
- const char *TOPIC = "$iot/AICAM/user/ismask";
-
- WiFiClient espClient;
- Adafruit_MQTT_Client mqtt(&espClient, MQTT_SERVER, MQTT_PORT, MQTT_USRNAME, MQTT_PASSWD);
- Adafruit_MQTT_Publish pub = Adafruit_MQTT_Publish(&mqtt, TOPIC); // 发布主题
-
- #define ANALOG_PIN_0 0 //电池AD管脚
- SHTSensor sht;
-
- #define uS_TO_S_FACTOR 1000000ULL //计时单位 秒
-
- void mcusleep() {
- digitalWrite(15, LOW); //关灯
- Serial.flush();
- esp_deep_sleep_start();
- }
-
- void MQTT_connect()//连接mqtt服务器,5s连接一次 连接超过5次还连不上 就休眠
- {
- int8_t ret;
- if (mqtt.connected())
- {
- return;
- }
- Serial.print("Connecting to MQTT... ");
- uint8_t retries = 5;//尝试5次
- while ((ret = mqtt.connect()) != 0)
- {
- Serial.println(mqtt.connectErrorString(ret));
- Serial.println("Retying MQTT connection in 5 seconds...");
- mqtt.disconnect();
- // digitalWrite(15, LOW); //关灯
- delay(5000);
- retries--;
- if (retries == 0) //如果五次都没连接上进入死循环
- {
- esp_sleep_enable_timer_wakeup(60 * uS_TO_S_FACTOR);
- Serial.println("MQTT connect fail! Going to sleep now");
- mcusleep();
- }
- Serial.println("MQTT Connected!");
- }
- }
-
- void setup() {
- Serial.begin(115200);
- pinMode(15, OUTPUT);
- digitalWrite(15, HIGH); //亮灯
- // rtc_gpio_isolate(GPIO_NUM_15);
- Wire.begin();
- pinMode(ANALOG_PIN_0, INPUT); //电源电池电压测量
- uint8_t retries = 10;//尝试连接wifi 10次
- WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
- while (!WiFi.isConnected())
- {
- digitalWrite(15, LOW); //关灯
- delay(5000);
- digitalWrite(15, HIGH); //亮灯
- retries--;
- Serial.print(".");
- if (retries == 0) //如果五次都没连接上进入死循环
- {
- esp_sleep_enable_timer_wakeup(60 * uS_TO_S_FACTOR);
- Serial.println("wifi connect fail! Going to sleep now");
- mcusleep();
- }
- }
- Serial.println("WIFI connect success!");
-
- MQTT_connect();
- sht.init();
- sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM); // only supported by SHT3x
- }
- void loop() { //空循环
- uint analog_value = 0;
- char msg[100];
- analog_value = analogRead(ANALOG_PIN_0); //读取电池电压
- sht.readSample(); //读取sht30
- Serial.printf("bat:%.1f\t RH:%.2f\t T:%.2f\n", float(analog_value) / 497.5, sht.getHumidity(), sht.getTemperature());
- sprintf(msg, "{"Bat":%.2f,"Humidity":%.2f,"Temperature":%.2f}", float(analog_value) / 497.5, sht.getHumidity(), sht.getTemperature());
- Serial.println(msg);
- pub.publish(msg);
-
- esp_sleep_enable_timer_wakeup(30 * uS_TO_S_FACTOR);
- Serial.println("Finsh work! Going to sleep now");
- delay(100);
- mcusleep();
- delay(10000); //代码到不了这里
- }
复制代码
1、管脚0,是接到通过电阻分压后的电源上,可以通过管脚0测量电源电池的电压。 2、板子一旦进入deep-sleep状态了,那么就是连串口都会消失掉的。所以在调试烧写代码时,会找不到串口号,无法烧写。此时烧写板子就需要按住板子上的BOOT按键,再按下RST按键,然后松开,进入烧写模式,即可有串口出现,此时可以正常烧写。
使用Python写个简单的上位机,用来接收mqtt的消息,只是简单地测试一下物联网。接下来就是要使用电池做测试,看看一天能耗电池多少电量,然后决定用多大的太阳能板进行补能。- #!/usr/bin/env python
- # -*- coding:utf-8 -*-
- # @FileName :mqtt_recvmsg.py
- # @Time :2024/3/25 11:20
- # @Author :aramy
- import paho.mqtt.client as mqtt
- from unit.mqttinfo import MQTT_INFO
-
-
- class MqttMsg():
- def __init__(self):
- super(MqttMsg, self).__init__()
- self.mqttinfo=MQTT_INFO()
- self.client=mqtt.Client()
- self.message=""
-
- def on_connect(self,client, userdata, flags, rc):
- print("Connected with result code: " + str(rc))
-
- def on_message(self,client, userdata, msg):
- print(msg.topic + " " + str(msg.payload))
- # self.sinGetNewMsg.emit(str(msg.payload,encoding = "utf-8"))
-
-
- def run(self):
- self.client.username_pw_set(self.mqttinfo.mqtt_name, self.mqttinfo.mqtt_pwd) # 设置用户名,密码
- self.client.connect(self.mqttinfo.broker_address, self.mqttinfo.port, 60) # 连接服务 keepalive=60
- self.client.on_connect = self.on_connect
- self.client.on_message = self.on_message
- self.client.subscribe(self.mqttinfo.publishtopic[0], qos=0)
- self.client.loop_forever() # 保持连接
-
- if __name__ == "__main__":
- mqtt=MqttMsg()
- mqtt.run()
复制代码
|