基于迪文屏的温度与磁场变化显示
本帖最后由 龘龘_ 于 2022-4-5 19:08 编辑
前言
本文使用ESP32的ULP协处理器可在深度睡眠状态访问外设资源从而获取传感器数据。通过RTC寄存器定时每秒唤醒ESP32串口传输数据给迪文屏进行数据显示,显示完毕进入深度睡眠。通过MQTT协议将采集到的数值传入到为用户搭建的HomeAssistant监控系统中,用户不仅可以通过迪文屏进行查看,还可以通过PC端、手机安卓端或linux系统显示。另外,在同等供电电压的情况下,此设备的平均工作电流从正常工作的50~120mA降至10~11mA左右。本文使用ESP32-WROOM-32模块核心板,可通过替换LDO供压芯片为BUCK-BOOST芯片和分离USB-TTL电路实现更低功耗。理论情况下,ULP协处理器的平均电流可以低于10uA。
本文使用4.3寸800*480一体黑电容触摸迪文屏,效果视频中仅展示了温度变化与磁场N极与S极的变化。
正文
步骤一:创建与设置DGUSII上位机工程
①生成背景图片icl文件,并命名为 23背景图片;
②放置2个数据变量显示控件与2个动态曲线控件。
——数据变量1控件用于显示温度数值,变量地址为1000,字体大小为32,整数位数为3。
——数据变量2控件用于显示磁场数值,变量地址为2000,字体大小为32,整数位数为3。
——动态曲线1控件用于显示温度变化,Y_Central为322,VD-Central为100,曲线颜色为1E94,纵轴放大倍数为263。
——动态曲线2控件用于显示磁场变化,Y_Central为322,VD-Central为200,曲线颜色为1E94,纵轴放大倍数为131。
如果屏幕尺寸和本文不同的话,需要参考《T5L DGUSII应用开发指南》中相应控件的参数声明进行修改。
③生成16级灰度字库。使用16级灰度字库生成工具,找到32号宋体,打钩,生成该字体字库,并命名为 0灰度字库 。
④点击保存与生成按钮。将DWIN_SET文件夹下除了图片的文件都拷贝到SD卡。(迪文屏最大支持与兼容16G的SD卡,若插入SD卡后未进入烧录状态,需要格式化或更换SD卡)
⑤出现END字样即说明烧录完成,断电,拔出SD卡,重新上电。可以看到SD卡成功上电。
步骤二:编写ESP32ULP协处理器代码
在编写之前需要确保已经搭建Arduino IDE的ESP32开发环境,并且根据 ULPTOOL环境搭建进行搭建汇编语言的编译。
①新建工程,并新建adc.s标签与ulp_main.h标签,并将以下代码复制进入相应标签。
ulp.ino
- /*
- * Must allocate more memory for the ulp in
- * esp32/tools/sdk/include/sdkconfig.h
- * -> #define CONFIG_ULP_COPROC_RESERVE_MEM
- * for this sketch to compile. 2048b seems
- * good.
- */
- #include "esp_sleep.h"
- #include "driver/rtc_io.h"
- #include "driver/adc.h"
- #include "esp32/ulp.h"
- #include "ulp_main.h"
- #include "ulptool.h"
- #include "soc/soc.h"
- #include "soc/soc_ulp.h"
- #include "driver/gpio.h"
- extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
- extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
- uint16_t a=0;
- /* This function is called once after power-on reset, to load ULP program into
- RTC memory and configure the ADC.
- */
- static void init_ulp_program();
- /* This function is called every time before going into deep sleep.
- It starts the ULP program and resets measurement counter.
- */
- static void start_ulp_program();
- void setup() {
- Serial.begin(115200);
- delay(100);
- esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
- if (cause != ESP_SLEEP_WAKEUP_ULP) {
- Serial.printf("Not ULP wakeupn");
- init_ulp_program();
- } else {
- /*打印温度值*/
- //Serial.println("--------------------------------------------------------------------------");
- Serial.printf("xffxffxff");
- delay(10);
- Serial.printf("Tsens.val=%dxFFxFFxFF", (uint16_t)ulp_tsens_value); //除以4.524
- Serial.printf("x5AxA5x0Dx82x03x10x5axa5x01");
- Serial.write(0);
- Serial.write(0);
- Serial.printf("x01");
- if((uint16_t)ulp_tsens_value<256)
- {
- Serial.write(0);
- Serial.write((uint16_t)ulp_tsens_value);
- }else
- {
- Serial.write((uint16_t)ulp_tsens_value);
- }
- Serial.printf("xFFxFF");
- delay(10);
- Serial.printf("rn");
- Serial.printf("x5AxA5x05x82x10");
- Serial.write(0);
- if((uint16_t)ulp_tsens_value<256)
- {
- Serial.write(0);
- Serial.write((uint16_t)ulp_tsens_value);
- }else
- {
- Serial.write((uint16_t)ulp_tsens_value);
- }
- Serial.printf("xFFxFF");
- /*打印霍尔值*/
- //Serial.println("--------------------------------------------------------------------------");
- Serial.printf("ULP wakeup, printing hall sensor valuen");
- Serial.printf("ulp_hall_sensor:rnSens_Vp0:%drn,Sens_Vn0:%drn,Sens_Vp1:%drn,Sens_Vn1:%drn",
- (uint16_t)ulp_Sens_Vp0, (uint16_t)ulp_Sens_Vn0, (uint16_t)ulp_Sens_Vp1, (uint16_t)ulp_Sens_Vn1);
- Serial.printf("xffxffxff");
- delay(10);
- //Serial.printf("offset.val=%dxFFxFFxFF", ((uint16_t)ulp_Sens_Vp0 - (uint16_t)ulp_Sens_Vp1) - ((uint16_t)ulp_Sens_Vn0 - (uint16_t)ulp_Sens_Vn1));
- a=((uint16_t)ulp_Sens_Vp0 - (uint16_t)ulp_Sens_Vp1) - ((uint16_t)ulp_Sens_Vn0 - (uint16_t)ulp_Sens_Vn1);
- a=a+200;
- Serial.printf("offset.val=%dxFFxFFxFF",a);
- //delay(5000);
- Serial.printf("x5AxA5x0Dx82x03x10x5axa5x01");
- Serial.write(0);
- Serial.printf("x01");
- Serial.printf("x01");
- if(a<256)
- {
- Serial.write(0);
- Serial.write(a);
- }else
- {
- Serial.write(a);
- }
- Serial.printf("xFFxFF");
- Serial.printf("x5AxA5x05x82x20");
- Serial.write(0);
- a=fabs(a-200);
- if(a<256)
- {
- Serial.write(0);
- Serial.write(a);
- }else
- {
- Serial.write(a);
- }
- Serial.printf("xFFxFF");
- //delay(5000);
-
- delay(10);
- Serial.printf("rn");
- //Serial.println("--------------------------------------------------------------------------");
- }
- Serial.printf("Entering deep sleepnn");
- start_ulp_program();
- ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() );
- esp_deep_sleep_start();
- }
- void loop() {
- }
- static void init_ulp_program()
- {
- esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start,
- (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
- ESP_ERROR_CHECK(err);
- /* Configure ADC channel */
- /* Note: when changing channel here, also change 'adc_channel' constant
- in adc.S */
- /* The ADC1 channel 0 input voltage will be reduced to about 1/2 */ /*霍尔通道D36*/
- adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_6);
- /* The ADC1 channel 3 input voltage will be reduced to about 1/2 */ /*霍尔通道D39*/
- adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_6);/*衰减*/
-
- adc1_config_width(ADC_WIDTH_BIT_12);
- adc1_ulp_enable();
- /* Set ULP wake up period to 1000ms */
- ulp_set_wakeup_period(0, 1000 * 1000);
- /* Disable pullup on GPIO15, in case it is connected to ground to suppress
- boot messages.
- */
- rtc_gpio_pullup_dis(GPIO_NUM_15);
- rtc_gpio_hold_en(GPIO_NUM_15);
- }
- static void start_ulp_program()
- {
- /* Start the program */
- esp_err_t err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
- ESP_ERROR_CHECK(err);
- }
复制代码
adc.s
- /* ULP Example: using ADC in deep sleep
- This example code is in the Public Domain (or CC0 licensed, at your option.)
- Unless required by applicable law or agreed to in writing, this
- software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied.
- This file contains assembly code which runs on the ULP.
- ULP wakes up to run this code at a certain period, determined by the values
- in SENS_ULP_CP_SLEEP_CYCx_REG registers. On each wake up, the program
- measures input voltage on the given ADC channel 'adc_oversampling_factor'
- times. Measurements are accumulated and average value is calculated.
- Average value is compared to the two thresholds: 'low_thr' and 'high_thr'.
- If the value is less than 'low_thr' or more than 'high_thr', ULP wakes up
- the chip from deep sleep.
- */
- /* ULP assembly files are passed through C preprocessor first, so include directives
- and C macros may be used in these files
- */
- #include "soc/rtc_cntl_reg.h"
- #include "soc/soc_ulp.h"
- #include "soc/sens_reg.h"
- #include "soc/rtc_io_reg.h"
- .set adc_channel1, 7
- /* 霍尔Configure the number of ADC samples to average on each measurement.
- For convenience, make it a power of 2. */
- .set adc_oversampling_factor_log, 2
- .set adc_oversampling_factor, (1 << adc_oversampling_factor_log)
- .bss
-
- .global tsens_value/*定义变量为0 declaration of label*/
- tsens_value:
- .long 0
- .global Sens_Vp0/*霍尔*/
- Sens_Vp0:
- .long 0
- .global Sens_Vn0
- Sens_Vn0:
- .long 0
- .global Sens_Vp1
- Sens_Vp1:
- .long 0
- .global Sens_Vn1
- Sens_Vn1:
- .long 0
- /* Code goes into .text section */
- .text
- .global entry
- entry:
- /* do measurements using ADC */
- /* Force power up */ /*测温*/
- WRITE_RTC_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, 2, SENS_FORCE_XPD_SAR_PU)
- tsens r0, 1000
- move r3, tsens_value /*MOVE R1, label // R1 = address_of(label)*/
- st r0, r3, 0 /*ST Rsrc, Rdst, 偏移量*/
- /*霍尔值*/
- /* SENS_XPD_HALL_FORCE = 1, hall sensor force enable, XPD HALL is controlled by SW */
- WRITE_RTC_REG(SENS_SAR_TOUCH_CTRL1_REG, SENS_XPD_HALL_FORCE_S, 1, 1)
- /* RTC_IO_XPD_HALL = 1, xpd hall, Power on hall sensor and connect to VP and VN */
- WRITE_RTC_REG(RTC_IO_HALL_SENS_REG, RTC_IO_XPD_HALL_S, 1, 1)
- /* SENS_HALL_PHASE_FORCE = 1, phase force, HALL PHASE is controlled by SW */
- WRITE_RTC_REG(SENS_SAR_TOUCH_CTRL1_REG, SENS_HALL_PHASE_FORCE_S, 1, 1)
- /* RTC_IO_HALL_PHASE = 0, phase of hall sensor */
- WRITE_RTC_REG(RTC_IO_HALL_SENS_REG, RTC_IO_HALL_PHASE_S, 1, 0)
- /* SENS_FORCE_XPD_SAR, Force power up */
- WRITE_RTC_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, 2, SENS_FORCE_XPD_SAR_PU)
- /* do measurements using ADC */
- /* r2, r3 will be used as accumulator */
- move r2, 0
- move r3, 0
- /* initialize the loop counter */
- stage_rst
- measure0:
- /* measure Sar_Mux = 1 to get vp0 */
- adc r0, 0, 1
- add r2, r2, r0
- /* measure Sar_Mux = 4 to get vn0 */
- adc r1, 0, 4
- add r3, r3, r1
- /* increment loop counter and check exit condition */
- stage_inc 1
- jumps measure0, adc_oversampling_factor, lt
- /* divide accumulator by adc_oversampling_factor.
- Since it is chosen as a power of two, use right shift */
- rsh r2, r2, adc_oversampling_factor_log
- /* averaged value is now in r2; store it into Sens_Vp0 */
- move r0, Sens_Vp0
- st r2, r0, 0
- /* r3 divide 4 which means rsh 2 bits */
- rsh r3, r3, adc_oversampling_factor_log
- /* averaged value is now in r3; store it into Sens_Vn0 */
- move r1, Sens_Vn0
- st r3, r1, 0
-
- /* RTC_IO_HALL_PHASE = 1, phase of hall sensor */
- WRITE_RTC_REG(RTC_IO_HALL_SENS_REG, RTC_IO_HALL_PHASE_S, 1, 1)
- /* do measurements using ADC */
- /* r2, r3 will be used as accumulator */
- move r2, 0
- move r3, 0
- /* initialize the loop counter */
- stage_rst
- measure1:
- /* measure Sar_Mux = 1 to get vp1 */
- adc r0, 0, 1
- add r2, r2, r0
- /* measure Sar_Mux = 4 to get vn1 */
- adc r1, 0, 4
- add r3, r3, r1
- /* increment loop counter and check exit condition */
- stage_inc 1
- jumps measure1, adc_oversampling_factor, lt
- /* divide accumulator by adc_oversampling_factor.
- Since it is chosen as a power of two, use right shift */
- rsh r2, r2, adc_oversampling_factor_log
- /* averaged value is now in r2; store it into Sens_Vp1 */
- move r0, Sens_Vp1
- st r2, r0, 0
- /* r3 divide 4 which means rsh 2 bits */
- rsh r3, r3, adc_oversampling_factor_log
- /* averaged value is now in r3; store it into Sens_Vn1 */
- move r1, Sens_Vn1
- st r3, r1, 0
-
- jump wake_up
- /* Get ULP back to sleep */
- .global exit
- exit:
- halt
- .global wake_up
- wake_up:
- /* Check if the SoC can be woken up */
- READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1)
- and r0, r0, 1
- jump exit, eq
- /* Wake up the SoC and stop ULP program */
- wake
- /* Stop the wakeup timer so it does not restart ULP */
- WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)
- halt
复制代码
ulp_main.h
- #include "Arduino.h"
- extern uint32_t ulp_entry;
- extern uint32_t ulp_tsens_value;
- extern uint32_t ulp_Sens_Vp0;
- extern uint32_t ulp_Sens_Vn0;
- extern uint32_t ulp_Sens_Vp1;
- extern uint32_t ulp_Sens_Vn1;
复制代码
②保存并连接ESP32,编译并上传烧录代码。烧录完成后可以通过串口助手查看都如下图情况,ESP32每1s睡眠唤醒传输一次数据。
③串口助手调试迪文屏
如上图,我们可以知道动态曲线控件的 通信方式,我们如法炮制可以知道单数据发送到通道0的通信方式。本文使用串口数据调试迪文串口屏是否能够接受数据。发送以下16进制并观察串口屏曲线控件与数据变量控件是否显示数据。如果发送后串口助手接收到迪文屏返回的信息即表示通信成功,如果返回的是乱码,则需要勾选16进制显示。如果没有返回任何信息,请检查所使用的u***数据线是否具有通信功能或者使用u***-ttl模块。效果如下图:
- 5A A5 0D 82 0310 5AA5 0100 0001 0077
复制代码
其中0077即为通信传输到曲线通道0的数据。
- 5A A5 0D 82 0310 5AA5 0100 0101 0155
复制代码
其中0155即为通信传输到曲线通道1的数据。
其中0042即为通信传输到温度数据变量显示控件地址1000的数据。
其中00bc即为通信传输到磁场数据变量显示控件地址2000的数据。
串口数据发送结束要加上0xffff表示发送完毕(在串口助手中勾选 发送新行 ),如果在串口助手通信过程中出现电脑蓝屏现象,建议使用SSCOM串口工具进行通信或者使用USB-TTL工具连接迪文串口屏的URX与UTX进行通信。
③将ESP32与迪文屏供电,RX与TX相连,然后迪文屏将显示与顶部视频一样的效果。如果没有效果,则对调连接RX与TX的线。
步骤三:通过MQTT协议上传监控数据至HomeAssistant系统
将ULP采集传感器的ESP32通过串口、蓝牙或WIFI可以将数据传入另一个中控ESP32,中控ES32负责采集其余ESP32的数据并上传。(ulp的esp32代码中并未添加与中控ESP32通信的代码,需要的自行添加)。以下是MQTT上传至HomeAssistant系统的步骤:
①搭建HomeAssistant系统,没接触过的可以参考HomeAssistant搭建教程,更多的视频教程可以看该视频作者的其余视频,本文不再长篇概述。
②安装PubSubClient库用于MQTT协议上传数据。
③创建工程复制以下代码
- #include "WiFi.h" //默认,加载WIFI头文件
- #include "PubSubClient.h" //默认,加载MQTT库文件
- //********************需要修改的部分*******************//
- const char* ssid = "***********"; //修改,你的路由去WIFI名字
- const char* password = "************"; //你的WIFI密码
- #define ID_MQTT "****************" //用户私钥,控制台获取
- #define humidity_topic "mqttmag"
- #define temperature_topic "mqtttemp"
- //**************************************************//
- const char* mqtt_server = "bemfa.com"; //默认,MQTT服务器
- const int mqtt_server_port = 9501; //默认,MQTT服务器
- WiFiClient espClient;
- PubSubClient client(espClient);
- void setup_wifi() {
- delay(10);
- Serial.println();
- Serial.print("Connecting to ");
- Serial.println(ssid);
- WiFi.begin(ssid, password);
- while (WiFi.status() != WL_CONNECTED) {
- delay(500);
- Serial.print(".");
- }
- Serial.println("");
- Serial.println("WiFi connected");
- Serial.println("IP address: ");
- Serial.println(WiFi.localIP());
- }
- void reconnect() {
- // Loop until we're reconnected
- while (!client.connected()) {
- Serial.print("Attempting MQTT connection...");
- // Attempt to connect
- if (client.connect(ID_MQTT)) {
- Serial.println("connected");
- Serial.print("subscribe:");
- Serial.println(topic);
- //订阅主题,如果需要订阅多个主题,可发送多条订阅指令client.subscribe(topic2);client.subscribe(topic3);
- client.subscribe(topic);
- } else {
- Serial.print("failed, rc=");
- Serial.print(client.state());
- Serial.println(" try again in 5 seconds");
- // Wait 5 seconds before retrying
- delay(5000);
- }
- }
- }
- void setup() {
- pinMode(B_led, OUTPUT); //设置引脚为输出模式
- digitalWrite(B_led, HIGH);//默认引脚上电高电平
- Serial.begin(115200); //设置波特率115200
- setup_wifi(); //设置wifi的函数,连接wifi
- client.setServer(mqtt_server, mqtt_server_port);//设置mqtt服务器
- }
- // 检查数据是否有变化
- bool checkBound(float newValue, float prevValue, float maxDiff) {
- return !isnan(newValue) &&
- (newValue < prevValue - maxDiff || newValue > prevValue + maxDiff);
- }
- long lastMsg = 0;
- float temp = 0.0;
- float mag= 0.0;
- float diff = 1.0;
- void loop() {
- if (!client.connected()) {
- reconnect();
- }
- client.loop();
- long now = millis();
- if (now - lastMsg > 1000) {
- lastMsg = now;
- float newTemp = 10;
- float newMag = 80;
- if (checkBound(newTemp, temp, diff)) {
- temp = newTemp;
- Serial.print("New temperature:");
- Serial.println(String(temp).c_str());
- client.publish(temperature_topic, String(temp).c_str(), true);
- }
- if (checkBound(newHum, hum, diff)) {
- mag = newMag;
- Serial.print("New humidity:");
- Serial.println(String(mag).c_str());
- client.publish(humidity_topic, String(mag).c_str(), true);
- }
- }
- }
复制代码
其中,float newTemp = 10; float newMag = 80;为已定义的上传的数值,通过改变接收传感器ESP32的变量并改变这两个变量可以实现实时上传数据。
④登录巴法云云端,创建MQTT主题。如下图,私钥需要添加进入③的代码。创建mqtttemp与mqttmag主题。
⑤配置HomeAssistant配置文件,建议通过vscode等代码编辑工具打开,使用记事本打开可能会因为编码不同而导致乱码。
打开后,在底部添加如下代码
- #巴法云MQTT
- # mqtt服务设置
- mqtt:
- # MQTT Broker的IP地址或者域名
- broker: bemfa.com
- # MQTT Broker的端口号
- port: 9501
- #客户端ID(私钥)
- client_id: ********
- # 心跳设置
- keepalive: 60
- sensor 1:
- # 环境接收温湿度
- - platform: mqtt
- name: temp
- "unit_of_measurement": "摄氏度"
- state_topic: "mqtttemp"
- sensor 2:
- - platform: mqtt
- name: mag
- "unit_of_measurement": "data"
- state_topic: "mqttmag"
复制代码
⑥保存,浏览器打开homeassistant系统,点击配置-服务控制-检查配置,显示配置有效则说明上述代码添加正确,如果错误,则可能是复制之后格式出错了。检查配置完之后点击重新启动。如下图
⑦重启完毕后,点击概览,编辑仪表盘,添加我们在配置代码中添加的两个实体。退出编辑仪表盘,点开控件,可以看到控件已经接收到了上传的温度。
⑧除了通过HomeAssistant可以查看外,我们还可以通过巴法云来查看最近的的温度变化。在巴法云MQTT设备页面,点击需要查看的MQTT主题的“更多设置”,点击折线图,我们可以查看到如下图
结束语
笔者写完这篇文章已经是半夜1点了,因为篇幅较长,并不是每一步都非常详细,有一些需要花时间学习才能学会,一些基础的东西没有一一概述。我该去洗澡睡觉啦。最后就是,迪文屏的显示效果十分好,在各种串口屏里,迪文屏显示效果性价比十分高,很荣幸能够体验到。
|