55021浏览
查看: 55021|回复: 0

[ESP8266/ESP32] 基于Beetle ESP32-C3的环境监测终端

[复制链接]
本帖最后由 囧大大王 于 2023-6-2 13:06 编辑

基于Beetle ESP32-C3的环境监测终端图10
最近老婆在布置花园,作为一个技术男我就想着能不能用技术给她帮帮忙,正好这边DFROBOT发起了【DIY】Beetle ESP32-C3免费试用活动,在之前选型的时候ESP32就已经进入了我的备选列表里了,这次正好进行一下尝试。

基于这个小beetle,我做了一个用于进行环境监测的小终端,用于采集环境温度、湿度、光照等信息,后续会再增加土壤湿度的采集。采集到的数据会通过一块小屏幕显示出来,也会通过mqtt发送到我自己的服务器上,后续基于这些数据就可以再继续做一些智能灌溉之类的功能了。

这次用的Beetle ESP32-C3设计非常小巧,到手后拿到的是两个部分,核心板和一块扩展板。
扩展板上引出了3组i2c接口、一组串口和9个io口。这么小的体积这么多的接口,对于开发来说真的是非常方便了,尤其是上面还集成了锂电池充电管理芯片,可以直接用锂电池供电,完美覆盖我的项目需求。




设备清单
  • Beetle ESP32-C3  *1
  • AHT10 温湿度传感器模块 *1
  • GY-302 光照传感器模块 *1
  • 电容式土壤湿度传感器模块 *1
  • ST7735 显示屏 *1
  • 3.7v锂电池 *1


制作过程
一、焊接、组装
基于Beetle ESP32-C3的环境监测终端图1
这里我选择用排母焊在扩展板上,方便随时调整,使用起来更方便。
基于Beetle ESP32-C3的环境监测终端图2
扩展板上的io口3、10两个没有用到,位置比较特别也就干脆没焊接了。
基于Beetle ESP32-C3的环境监测终端图3
实际组装效果
基于Beetle ESP32-C3的环境监测终端图4


二、程序编写
这个的项目的程序部分使用了CircuitPython进行进行开发,首先是像顺便学一点python,另外群里有大佬一直在推,据说是比较简单。
下面介绍一下程序部分的编写过程。

1、烧写CircuitPython固件;
CircuitPython确实简单,开发环境都不用准备,官方对Beetle ESP32-C3也已经做了适配,烧录固件和代码编写都是通过浏览器就可以完成。
固件下载与烧录地址 DFRobot Beetle ESP32-C3 Download (circuitpython.org)
注意:烧录前需要先将IO9拉低,然后短接RST使esp32进入下载模式。
基于Beetle ESP32-C3的环境监测终端图5

2、编写代码;
一般来说CircuitPython推荐的开发板都是有提供USB功能的,烧录固件后可以直接将代码放到CircuitPython生成的U盘里面来完成代码上传,但是咱们这个Beetle用的ESP32-C3是没有USB功能的,那这里代码和文件上传就需要通过web进行了。
烧录固件后还是通过上面的网页,要通过CircuitPython的在线Installer修改Wifi连接信息(下图)。
基于Beetle ESP32-C3的环境监测终端图6

修改完WiFi连接信息后应该会自动重启,保险起见呢也可以手动再短接RST重启一下。
重启后CircuitPython会连接到咱们设置的WiFi接入点并启动web服务,这里我们需要到路由器去看一下分配的IP地址。
基于Beetle ESP32-C3的环境监测终端图7
通过这个IP地址我们就可以访问到CircuitPython的web服务了,通过这个web服务我们上传代码或直接在线编辑代码、运行和调试。
基于Beetle ESP32-C3的环境监测终端图8
基于Beetle ESP32-C3的环境监测终端图9
下面开始编码部分

  1. ########################################
  2. # 基于Beetle ESP32-C3的环境监测终端
  3. #
  4. # @Author: 囧大大王<mail@hessian.cn>
  5. # @Date: 2023/5/31
  6. ########################################
  7. # === 内置库 ===
  8. import time
  9. import board
  10. import displayio
  11. import wifi
  12. import ssl
  13. import rtc
  14. import socketpool
  15. import terminalio
  16. import analogio
  17. # === 外部库 ====
  18. import adafruit_ahtx0
  19. import adafruit_bh1750
  20. import adafruit_minimqtt.adafruit_minimqtt as MQTT
  21. import adafruit_ntp
  22. from adafruit_display_text import label
  23. from adafruit_st7735r import ST7735R
  24. from adafruit_display_shapes.rect import Rect
  25. from adafruit_display_shapes.line import Line
  26. from adafruit_display_shapes.sparkline import Sparkline
  27. # #### 传感器配置部分 ####
  28. # Create sensor object, communicating over the board's default I2C bus
  29. i2c = board.I2C()  # uses board.SCL and board.SDA
  30. # 初始化温湿度传感器
  31. tempSensor = adafruit_ahtx0.AHTx0(i2c)
  32. # 初始化光照传感器
  33. luxSensor = adafruit_bh1750.BH1750(i2c)
  34. # 主循环计数器
  35. loopCounter = 0
  36. # #### 屏幕显示部分配置 ####
  37. spi = board.SPI()
  38. tft_cs = board.D7
  39. tft_dc = board.D1
  40. tft_rst = board.D2
  41. displayio.release_displays()
  42. display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst)
  43. display = ST7735R(display_bus, width=128, height=128, colstart=2, rowstart=1)
  44. # Make the display context
  45. def showSplash():
  46.     splash = displayio.Group()
  47.     display.show(splash)
  48.     color_bitmap = displayio.Bitmap(128, 128, 1)
  49.     color_palette = displayio.Palette(1)
  50.     color_palette[0] = 0xFF0000
  51.     bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
  52.     splash.append(bg_sprite)
  53.     # Draw a smaller inner rectangle
  54.     inner_bitmap = displayio.Bitmap(108, 108, 1)
  55.     inner_palette = displayio.Palette(1)
  56.     inner_palette[0] = 0xAA0088  # Purple
  57.     inner_sprite = displayio.TileGrid(inner_bitmap, pixel_shader=inner_palette, x=10, y=10)
  58.     splash.append(inner_sprite)
  59.     # Draw a label
  60.     text = "Hello DFRobot!"
  61.     text_area = label.Label(terminalio.FONT, text=text, color=0xFFFF00, x=20, y=64)
  62.     splash.append(text_area)
  63. # Make the display context
  64. def initMainUI():
  65.     view = displayio.Group()
  66.     display.show(view)
  67.     # BG
  68.     color_bitmap = displayio.Bitmap(128, 128, 1)
  69.     color_palette = displayio.Palette(1)
  70.     color_palette[0] = 0x7ecef4
  71.     bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
  72.     view.append(bg_sprite)
  73.     rect = Rect(4, 4, 120, 120, outline=0x666666)
  74.     view.append(rect)
  75.     return view
  76. # 显示欢迎界面
  77. showSplash()
  78. # 等待1秒
  79. time.sleep(1)
  80. # #### MQTT配置 ####
  81. MQTT_HOST = "192.168.99.7"
  82. MQTT_PORT = 1883
  83. MQTT_USER = "gardener"
  84. MQTT_PASSWORD = "53bffe07f84e0c5909ff569bb2a848e7"
  85. MQTT_SUB_TOPIC = "/garden/notify"
  86. MQTT_PUB_TOPIC = "/garden/notify"
  87. # Define callback methods which are called when events occur
  88. # pylint: disable=unused-argument, redefined-outer-name
  89. def connected(client, userdata, flags, rc):
  90.     # This function will be called when the client is connected
  91.     # successfully to the broker.
  92.     print("Connected to Adafruit IO! Listening for topic changes on %s" % MQTT_SUB_TOPIC)
  93.     # Subscribe to all changes on the onoff_feed.
  94.     client.subscribe(MQTT_SUB_TOPIC)
  95. def disconnected(client, userdata, rc):
  96.     # This method is called when the client is disconnected
  97.     print("Disconnected from Adafruit IO!")
  98. def message(client, topic, message):
  99.     # This method is called when a topic the client is subscribed to
  100.     # has a new message.
  101.     print("New message on topic {0}: {1}".format(topic, message))
  102. # Create a socket pool
  103. pool = socketpool.SocketPool(wifi.radio)
  104. ssl_context = ssl.create_default_context()
  105. # Set up a MiniMQTT Client
  106. mqtt_client = MQTT.MQTT(
  107.     broker=MQTT_HOST,
  108.     port=MQTT_PORT,
  109.     username=MQTT_USER,
  110.     password=MQTT_PASSWORD,
  111.     socket_pool=pool,
  112.     ssl_context=ssl_context,
  113. )
  114. # Setup the callback methods above
  115. mqtt_client.on_connect = connected
  116. mqtt_client.on_disconnect = disconnected
  117. mqtt_client.on_message = message
  118. # Connect the client to the MQTT broker.
  119. print("Connecting to MQTT ...")
  120. mqtt_client.connect()
  121. # #### NTP时间同步配置 ####
  122. ntp = adafruit_ntp.NTP(pool, tz_offset=0, server="ntp1.aliyun.com", socket_timeout=5)
  123. def updateTimeByNTP():
  124.     r = rtc.RTC()
  125.     try:
  126.         r.datetime = ntp.datetime
  127.     except Exception as e:
  128.         print(f"NTP fetch time failed: {e}")
  129. # #### 主界面配置 ####
  130. mainUi = initMainUI()
  131. # 字体配置,使用内置字体
  132. font = terminalio.FONT
  133. # ===上半屏信息文本===
  134. # IP地址
  135. labelIp = label.Label(font, text="255.255.255.255", color=0x333333, x=10, y=12)
  136. # 空气温湿度信息
  137. labelTemp = label.Label(font, text="TEMP: 00.0C 100%", color=0x333333, x=10, y=24)
  138. # 土壤湿度信息
  139. labelEarthHumi = label.Label(font, text="EARTH: 00000 3.3V", color=0x333333, x=10, y=36)
  140. # 光照
  141. labelLight = label.Label(font, text="Light: 9999.99lux", color=0x333333, x=10, y=48)
  142. # 温度曲线图表
  143. line_color = 0xffffff
  144. chart_width = 80
  145. chart_height = 50
  146. spkline = Sparkline(width=chart_width, height=chart_height, max_items=chart_width, x=38, y=60, color=line_color)
  147. text_xoffset = -5
  148. text_label1a = label.Label(
  149.     font=font, text=str(spkline.y_top), color=line_color
  150. )  # yTop label
  151. text_label1a.anchor_point = (1, 0.5)  # set the anchorpoint at right-center
  152. text_label1a.anchored_position = (
  153.     spkline.x + text_xoffset,
  154.     spkline.y,
  155. )  # set the text anchored position to the upper right of the graph
  156. text_label1b = label.Label(
  157.     font=font, text=str(spkline.y_bottom), color=line_color
  158. )  # yTop label
  159. text_label1b.anchor_point = (1, 0.5)  # set the anchorpoint at right-center
  160. text_label1b.anchored_position = (
  161.     spkline.x + text_xoffset,
  162.     spkline.y + chart_height,
  163. )  # set the text anchored position to the upper right of the graph
  164. bounding_rectangle = Rect(
  165.     spkline.x, spkline.y, chart_width, chart_height, outline=line_color
  166. )
  167. mainUi.append(labelIp)
  168. mainUi.append(labelTemp)
  169. mainUi.append(labelEarthHumi)
  170. mainUi.append(labelLight)
  171. mainUi.append(spkline)
  172. mainUi.append(text_label1a)
  173. mainUi.append(text_label1b)
  174. mainUi.append(bounding_rectangle)
  175. total_ticks = 5
  176. for i in range(total_ticks + 1):
  177.     x_start = spkline.x - 2
  178.     x_end = spkline.x
  179.     y_both = int(round(spkline.y + (i * (chart_height) / (total_ticks))))
  180.     if y_both > spkline.y + chart_height - 1:
  181.         y_both = spkline.y + chart_height - 1
  182.     mainUi.append(Line(x_start, y_both, x_end, y_both, color=line_color))
  183. display.show(mainUi)
  184. # ADC输入初始化(土壤湿度)
  185. adcPin = analogio.AnalogIn(board.A0)
  186. while True:
  187.     # 轮询MQTT消息
  188.     mqtt_client.loop()
  189.     if loopCounter > 86400:
  190.         loopCounter = 1
  191.     # 每两分钟重新获取一次网络时间
  192.     if loopCounter % 120 == 0:
  193.         updateTimeByNTP()
  194.     clientId = wifi.radio.hostname
  195.     ip = wifi.radio.ipv4_address
  196.     now = time.time()
  197.     json = f'{{"clientId": "{clientId}", "ip": "{ip}", "earthHumi": {adcPin.value}, "airTemp": {tempSensor.temperature}, "airHumi": {tempSensor.relative_humidity}, "time": {now} }}'
  198.     # 打印调试信息
  199.     print(f"Time: {time.localtime()}")
  200.     print("Temperature: %0.1f C" % tempSensor.temperature)
  201.     print("Humidity: %0.1f %%" % tempSensor.relative_humidity)
  202.     print("Light: %.2f Lux" % luxSensor.lux)
  203.     print(f"ADC A0 vlaue: {adcPin.value} {adcPin.reference_voltage}V")
  204.     print(json)
  205.     # 更新图表
  206.     spkline.add_value(tempSensor.temperature)
  207.     text_label1a.text = "%.1f" % max(spkline.values())
  208.     text_label1b.text = "%.1f" % min(spkline.values())
  209.     # 更新上半屏信息
  210.     labelIp.text = f'IP: {ip}'
  211.     labelTemp.text = "TEMP: %.1fC / %.1f%%" % (tempSensor.temperature, tempSensor.relative_humidity)
  212.     labelEarthHumi.text = "EARTH: %d %.2fV" % (adcPin.value, adcPin.value / 65535 * adcPin.reference_voltage)
  213.     labelLight.text = "Light: %.3f Lux" % luxSensor.lux
  214.     # 每分钟一次,发送到MQTT
  215.     if loopCounter % 60 == 0:
  216.         mqtt_client.publish(MQTT_PUB_TOPIC, json)
  217.     loopCounter += 1
  218.     time.sleep(1)
复制代码
完整项目代码下载附件beetle-env-monitor.zip

项目演示



查看数据趋势的web界面
基于Beetle ESP32-C3的环境监测终端图12


功耗情况
基于Beetle ESP32-C3的环境监测终端图11






WedMay-202305316723..png
WedMay-202305319746..png
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
关于楼主
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail