数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 318|回复: 6

[Arduino] esp32c2(也即esp8684)大夏龙雀DX-WF25的吃灰板子做“久坐提醒器”更新2

[复制链接]
发表于 6 天前 | 显示全部楼层 |阅读模式
esp32c2(也即esp8684)大夏龙雀DX-WF25的吃灰板子做“久坐提醒器”用了一段时间,需要增加一个站立工作模式。

还是运营在micropython下,很方便通过webrepl改代码,比烧录方便很多。
boot.py
  1. import network
  2. import webrepl
  3. import ntptime
  4. import time
  5. import machine

  6. # WiFi
  7. WIFI_SSID = "xxxxx"
  8. WIFI_PASSWORD = "12345678"

  9. def connect_wifi():
  10.     wlan = network.WLAN(network.STA_IF)
  11.     wlan.active(True)
  12.     # 已连接
  13.     if wlan.isconnected():
  14.         print("本机IP", wlan.ifconfig()[0])
  15.         return wlan
  16.     print("连接WiFi", WIFI_SSID)
  17.     wlan.connect(WIFI_SSID, WIFI_PASSWORD)
  18.     timeout = 10
  19.     while not wlan.isconnected() and timeout > 0:
  20.         timeout -= 1
  21.         time.sleep(1)
  22.         print(".", end="")
  23.    
  24.     if wlan.isconnected():
  25.         print("\nWiFi成功")
  26.         print("本机IP", wlan.ifconfig()[0])
  27.         # --------开启mDNS,主机名esp32,域名 esp32.local --------
  28. #         try:
  29. #             import mdns
  30. #             mdns.start("esp32", "MicroPython-ESP32")
  31. #             print("mDNS已开启,访问地址:http://esp32.local")
  32. #         except Exception as e:
  33. #             print("mDNS启动失败,固件不支持mdns模块:", e)
  34.         try:
  35.             ntptime.settime()
  36.             print('时间同步成功')
  37.         except Exception as e:
  38.             print('时间同步失败:', e)
  39.     else:
  40.         print("\nWiFi失败,请检查账号密码")
  41.     return wlan

  42. try:
  43.     connect_wifi()
  44.     # 开WebRepl
  45.     webrepl.start()
  46.     time.sleep(1)
  47.     print("WebRepl working...")
  48. except Exception as err:
  49.     print("运行异常", err)
复制代码
main.py
  1. # 创建时间:2026.08.13
  2. # 功能:DXWF-25+DL2402模块,实现定时久坐休息提醒
  3. # 版本:v1.1
  4. # 增加网页,方便查看统计情况
  5. # 增加站立工作模式开关(建议干簧管或霍尔开关,配合磁吸固定非常优雅)
  6. # 磁吸到指定位置,可以保障雷达对准指定区域。从磁吸拿下来,就判定为站立模式。
  7. from machine import Pin, PWM, freq
  8. import time
  9. # 导入封装好的web_log模块
  10. import web_log

  11. # ===================== 低功耗设置=====================
  12. # 关闭ADC
  13. # pin0 = Pin(0, Pin.IN, value=0)  # ESP8266仅支持PULL_UP




  14. # ===================== 初始化配置 =====================
  15. person_pin = Pin(3, Pin.IN, Pin.PULL_UP)  # DL2402人体检测引脚(GPIO3,输入模式,默认高电平)
  16. standing_pin = Pin(4, Pin.IN, Pin.PULL_UP)  # GPIO4用于判断是否进入站立工作模式,默认上拉高电平
  17. led_pwm_pin = PWM(Pin(8), freq=1000, duty=0)  # LED PWM初始化(GPIO8,频率1000Hz)
  18. # 状态变量初始化
  19. sum_sit_time= 0       # 总坐时长
  20. sum_rest_time = 0     # 总休息时长
  21. standing_work_time= 0 # 总站立工作时间
  22. sit_time = 0       # 有人计时(秒)
  23. no_person_time = 0    # 无人计时(秒)
  24. led_state = "no_person"  # led状态:初始无人
  25. # 时间阈值配置(秒)
  26. PERSON_ALARM_THRESHOLD = 45*60  # 持续有人45分钟触发报警
  27. NO_PERSON_RESET_THRESHOLD = 9*60  # 持续无人9分钟重置有人计时;持续有人5分钟重置无人计时;

  28. # ===================== 功能函数部分 =====================
  29. def update_person_state():
  30.     """更新人体检测状态及累计时间"""
  31.     global sit_time, no_person_time, led_state, sum_rest_time, sum_sit_time, standing_work_time
  32.     # 检测人体传感器状态(高电平=有人)
  33.     if standing_pin.value() == 1:
  34.         led_state = "stand_working"
  35.         standing_work_time += 1
  36.         no_person_time -= 1 #相当于半休息模式
  37.     else:
  38.         if person_pin.value() == 1:
  39.             led_state = "sit"
  40.             sum_sit_time += 1
  41.             sit_time += 1
  42.         else:
  43.             led_state = "no_person"
  44.             sum_rest_time += 1
  45.             no_person_time += 1

  46. def check_alarm_condition():
  47.     """检查报警/重置条件"""
  48.     global sit_time, no_person_time, led_state
  49.     # 条件1:有人超过xx分钟 → 触发报警
  50.     if sit_time > PERSON_ALARM_THRESHOLD:
  51.         led_state = "alarm"
  52.     # 条件2:无人时间超过(久坐时间每5分钟需要1分钟休息时间) → 重置所有计时
  53.     if no_person_time > (NO_PERSON_RESET_THRESHOLD+round((sit_time - PERSON_ALARM_THRESHOLD) / 300)*60):
  54.         sit_time = 0
  55.         no_person_time = 0
  56.     # 有人每过5分钟 → 无人时间清0
  57.     if sit_time % 300 == 0:
  58.         no_person_time = 0

  59. def control_led_by_state():
  60.     """根据状态控制LED,注意取反值"""
  61.     if led_state == "alarm":
  62.         # 报警:高亮闪烁(亮0.5秒,灭0.5秒)
  63.         led_pwm_pin.duty(0)  # 最大亮度(10位PWM:0-1023)
  64.         time.sleep(0.5)
  65.         led_pwm_pin.duty(1023)
  66.         time.sleep(0.5)
  67.     if led_state == "sit":
  68.         # 有人:低亮度常亮(50%亮度)
  69.         led_pwm_pin.duty(512)
  70.         time.sleep(1)
  71.     if led_state == "stand_working":
  72.         # 有人:低亮度常亮(10%亮度)
  73.         led_pwm_pin.duty(920)
  74.         time.sleep(1)
  75.     if led_state == "no_person":
  76.         # 无人:熄灭
  77.         led_pwm_pin.duty(1023)
  78.         time.sleep(1)
  79.         
  80. # 启动Web服务
  81. web_log.start()
  82. # ===================== 主循环 =====================
  83. if __name__ == "__main__":
  84.     # print("定时休息提示器启动...")
  85.     try:
  86.         while True:
  87.             update_person_state()    # 更新人体状态
  88.             check_alarm_condition()  # 检查报警/重置条件
  89.             control_led_by_state()   # 控制LED
  90.             # 打印调试信息
  91.             tm = time.localtime()
  92.             time_str = f"{tm[3]+8:02d}:{tm[4]:02d}:{tm[5]:02d}"
  93.             web_log.log_print(f"{time_str}\nsum_sit:{sum_sit_time//60}m,sum_rest:{sum_rest_time//60}m,sum_stand:{standing_work_time//60}m\n{led_state},sit:{sit_time//60}m,no_person:{no_person_time//60}m\n")
  94.             web_log.process_request()
  95.     except KeyboardInterrupt:
  96.         # 终止程序时关闭PWM
  97.         led_pwm_pin.deinit()
  98.         led = Pin(8, Pin.OUT)  # 重新初始化 GPIO8 为数字输出
  99.         led.value(0)  # LED常亮以方便看设备是否开启
  100.         print("程序终止!")
复制代码
web_log.py
  1. # web_log.py
  2. import socket

  3. # ========== 内部变量与函数(隐藏实现) ==========
  4. LOG_BUF = []
  5. MAX_LOG = 20
  6. _sock = None

  7. def _get_html():
  8.     """内部函数:生成暗色主题的HTML页面"""
  9.     # 将日志内容用 <br> 标签拼接,保证换行效果
  10.     # 每秒刷新日志<meta http-equiv='refresh' content='1'>
  11.     log_content = "<br>".join(LOG_BUF)
  12.     html = """
  13. <!DOCTYPE html>
  14. <html>
  15. <head>
  16.     <meta charset="utf-8">
  17.     <meta http-equiv='refresh' content='1'>    /* 实现网页每秒自动刷新 */
  18.     <title>ESP32 运行日志</title>
  19.     <style>
  20.         body {
  21.             background-color: #1e1e1e;
  22.             color: #d4d4d4;
  23.             font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  24.             margin: 0;
  25.             padding: 20px;
  26.             font-size: 14px;
  27.             line-height: 1.6;
  28.         }
  29.         h2 {
  30.             color: #569cd6;
  31.             border-bottom: 1px solid #333;
  32.             padding-bottom: 10px;
  33.             margin-bottom: 20px;
  34.         }
  35.         pre {
  36.             background-color: #252526;
  37.             padding: 15px;
  38.             border-radius: 5px;
  39.             border: 1px solid #333;
  40.             white-space: pre-wrap;       /* 允许长文本换行 */
  41.             word-wrap: break-word;       /* 兼容旧浏览器 */
  42.             /* 核心优化:限制最大高度并强制滚动条保持在最底部 */
  43.             max-height: 60vh;
  44.             overflow-y: auto;
  45.         }
  46.     </style>
  47. </head>
  48. <body onload="document.querySelector('pre').scrollTop = document.querySelector('pre').scrollHeight;">
  49.     <h2>设备运行日志</h2>
  50.     <pre>""" + log_content + """</pre>
  51. </body>
  52. </html>"""
  53.     return html

  54. def _web_task():
  55.     """内部函数:处理web网页请求"""
  56.     global _sock
  57.     try:
  58.         conn, addr = _sock.accept()
  59.     except OSError:
  60.         return
  61.     try:
  62.         conn.recv(1024)
  63.         html = _get_html()
  64.         resp = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n" + html
  65.         conn.send(resp.encode("utf-8"))
  66.     except Exception as e:
  67.         log_print(f"http err:{e}")
  68.     finally:
  69.         conn.close()

  70. # ========== 对外暴露的接口 ==========
  71. def log_print(msg):
  72.     """
  73.     记录日志并输出到控制台
  74.     :param msg: 日志内容
  75.     """
  76.     print(msg)
  77.     global LOG_BUF
  78.     LOG_BUF.append(str(msg))
  79.     if len(LOG_BUF) > MAX_LOG:
  80.         LOG_BUF.pop(0)

  81. def start():
  82.     """
  83.     启动Web日志服务
  84.     """
  85.     global _sock
  86.     s = socket.socket()
  87.     s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  88.     s.bind(("0.0.0.0", 80))
  89.     s.listen(1)
  90.     s.setblocking(False)
  91.     _sock = s
  92.     log_print("web服务启动,端口80,请访问设备IP")

  93. def process_request():
  94.     """
  95.     对外暴露的Web请求处理接口
  96.     """
  97.     _web_task()
复制代码
webrepl_cfg.py
  1. PASS = '1111'
复制代码





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

打赏

参与人数 1家元 +12 收起 理由
jf201006 + 12 謝謝分享

查看全部打赏

 楼主| 发表于 6 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 1 反对 0

使用道具 举报

发表于 6 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

 楼主| 发表于 3 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 3 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 前天 10:47 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

 楼主| 发表于 10 小时前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|数码之家-技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2026-8-20 22:06 , Processed in 0.156001 second(s), 10 queries , Gzip On, Redis On.

Powered by Discuz!

© MyDigit.Net Since 2006

快速回复 返回顶部 返回列表