跳转至内容
BakeHub 工科开源社群

社区讨论

把具体问题说清楚

补充可复现的背景,比较可行路径,为下一位实践者留下有用的线索。

  • MSPM0火焰

    开源求助 工具测试
    2
    2赞同
    2帖子
    38浏览
    @KongChar 佬,太强了吧
  • 看群主的前端源码,如何爆破思路

    经验分享
    2
    2赞同
    2帖子
    90浏览
    @KongChar 给捞哥网页底裤都说出来了
  • 1赞同
    1帖子
    21浏览
    本文是 shaojiec932-web/CCD 项目的开源分享帖,原 README 同步搬运过来,方便社群同学在 BakeHub 内直接讨论。仓库整理了 2026 嵌入式芯片应用赛道(乐鑫科技赛道)参赛过程中三个核心 ESP32-S3 节点工程:主控节点端、图传视觉端、门牌节点端。主控与图传端基于小智开源框架二次开发,门牌端用 Arduino 实现。致敬虾哥 xiaozhi。 TrackDock 室内智能感知运载平台 基于 ESP32-S3、小智开源框架、MQTT、RFID、UWB、图传视觉与本地运动控制构建的室内智能运载与感知系统。 本仓库整理了本项目中三个核心 ESP32-S3 节点工程,分别为:主控节点端、图传视觉端和可编辑门牌节点端。其中主控节点端与图传视觉端均基于小智开源代码框架进行二次开发,在原有语音交互、联网通信和设备框架基础上,扩展了面向室内运载场景的本地实时控制、MCP 工具服务、MQTT 数据同步、RFID 货物识别、UWB 区域定位、串口屏交互和摄像头图传等能力。门牌节点端采用 Arduino 框架实现,用于室内区域标识与 MQTT 配置同步。 项目背景 在医院、养老院、仓库、图书馆、教学楼和实验室等复杂室内环境中,药品、文件、器材、样本、工具和日常物资需要频繁流转。传统方式主要依赖人工搬运、人工登记和人工核对,容易出现效率低、定位难、状态不可追踪、错拿错送、远程感知不足等问题。 TrackDock 的目标是通过低成本嵌入式节点与多端协同系统,将"任务发起、语音理解、货物识别、区域定位、远程图传、运动执行、状态反馈"打通,形成一套面向室内场景的智能运载与感知原型平台。 工程目录 嵌入式大赛/ ├─ ESP32-S3主控节点端/ │ ├─ application.cc │ ├─ application.h │ ├─ mcp_server.cc │ ├─ mcp_server.h │ └─ bread-compact-wifi-lcd/ │ ├─ compact_wifi_board_lcd.cc │ ├─ config.h │ ├─ mcp_rfid_cargo_manager.cc │ ├─ mcp_serial_display.cc │ ├─ mcp_stm32_motor_control.cc │ ├─ mcp_storage_lid.cc │ ├─ mcp_uwb_navigation.cc │ ├─ trackdock_mqtt_client.cc │ └─ trackdock_scene.cc │ ├─ ESP32-S3图传视觉端/ │ └─ lichuang-dev/ │ ├─ config.h │ ├─ lichuang_dev_board.cc │ ├─ mcp_uart_tools.cc │ └─ mcp_uart_tools.h │ └─ ESP32-S3门牌节点端/ ├─ NumberIP.ino └─ 门牌.txt 1. ESP32-S3 主控节点端 主控节点端是系统的核心控制节点,基于小智开源框架进行二次开发,主要负责语音交互接入、本地 MCP 工具注册、外设数据解析、串口屏交互、货物识别、UWB 定位接口、MQTT 状态同步以及底盘控制指令转发。 主要功能 基于小智框架接入语音交互能力,支持语音触发任务。 扩展 MCP 本地工具服务,将大模型意图映射为本地硬件操作。 通过 RFID 模块完成货物标签识别与货物信息采集。 通过 UWB 模块接口实现室内定位与目标区域导航能力预留。 通过串口屏实现本地人机交互,包括状态显示、按钮控制、音量调节等。 通过 UART 与 STM32 底盘控制端通信,实现前进、后退、左转、右转、停止等运动控制。 通过 MQTT 与客户端进行数据同步,实现远程控制、运行状态反馈和场景切换。 支持收纳盖控制、场景模式管理和任务状态记录。 关键文件说明 文件 说明 application.cc / application.h 小智应用层逻辑扩展,承载语音状态、任务流程和系统初始化相关逻辑。 mcp_server.cc / mcp_server.h MCP 工具服务核心文件,用于注册和管理本地可调用工具。 compact_wifi_board_lcd.cc 主控板级初始化与硬件资源配置。 config.h 主控端 IO、串口、SPI、I2C、外设参数配置。 mcp_rfid_cargo_manager.cc RFID 货物扫描、标签读取、货物信息反馈。 mcp_serial_display.cc 串口屏数据解析、控件同步、指令接收与显示输出。 mcp_stm32_motor_control.cc STM32 底盘运动指令封装与串口发送。 mcp_uwb_navigation.cc UWB 定位、目标点、导航状态与路径指令接口。 trackdock_mqtt_client.cc MQTT 客户端连接、订阅、发布与远程控制同步。 trackdock_scene.cc 多场景模式切换与场景语义管理。 2. ESP32-S3 图传视觉端 图传视觉端基于立创实战派 ESP32-S3 开发板方案和小智开源框架进行二次开发,主要用于摄像头图像采集、JPEG 编码、MQTT 图传发布和远程视觉控制台显示。该节点作为独立视觉感知节点,不承担主控运动决策,重点提供远程观察与辅助驾驶能力。 主要功能 基于 ESP32-S3 摄像头接口采集实时图像。 将图像压缩为 JPEG 数据并通过 MQTT 上传。 客户端可实时显示视觉画面,用于远程观察和辅助控制。 可配合主控端完成室内运行状态监控、路径观察和物品识别辅助展示。 保留小智框架的基础联网和设备初始化能力,并扩展图传任务循环。 关键文件说明 文件 说明 lichuang_dev_board.cc 图传视觉端核心实现,包含摄像头初始化、图像采集、MQTT 发布和任务循环。 config.h 摄像头、板级 IO 和基础参数配置。 mcp_uart_tools.cc / mcp_uart_tools.h 原框架中的 UART 工具能力,保留用于后续扩展。 3. ESP32-S3 门牌节点端 门牌节点端用于构建可编辑的室内区域标识系统。该节点采用 Arduino 框架实现,便于快速开发和部署。门牌节点通过 Wi-Fi 和 MQTT 与客户端同步,客户端可远程配置门牌名称,例如"仓库""收纳区""充电区""病房区"等,节点本地屏幕显示当前门牌信息。 主要功能 支持 Wi-Fi 连接与 MQTT 通信。 支持客户端远程修改门牌名称。 支持本地 TFT/OLED 屏幕显示区域名称。 可作为 UWB 室内定位系统中的空间语义节点。 可与主控端运动任务结合,实现"目标区域名称"和"定位参考点"的对应关系。 关键文件说明 文件 说明 NumberIP.ino 门牌节点主程序,包含 Wi-Fi、MQTT、屏幕显示和门牌配置逻辑。 门牌.txt 门牌端说明、配置记录或辅助资料。 系统总体链路 用户语音 / 客户端 / 串口屏 ↓ ESP32-S3 主控节点 ↓ 小智语音交互 + MCP 本地工具调用 ↓ RFID 货物识别 / UWB 区域定位 / STM32 底盘控制 / 收纳盖控制 ↓ MQTT 状态同步 ↓ 电脑端、手机端、视觉控制端、门牌节点 图传视觉端作为独立感知节点持续上传摄像头画面,门牌节点作为空间语义节点提供区域标识,主控节点负责语音理解后的本地执行和多端状态同步。三个节点共同构成室内智能运载平台的基础闭环。 二次开发说明 本项目主控节点端和图传视觉端基于小智开源框架进行二次开发,主要扩展方向包括: 面向室内运载场景的 MCP 工具接口。 面向硬件外设的本地实时控制逻辑。 面向客户端的 MQTT 数据同步链路。 面向串口屏的人机交互协议。 面向 RFID、UWB、STM32、摄像头等模块的板级适配。 面向多场景应用的任务状态与场景语义管理。 开源时请保留原小智项目相关版权与许可证说明,并在仓库中注明本项目是在其基础上进行的场景化二次开发。 技术栈 主控框架:Espressif ESP-IDF、小智开源框架 主控芯片:ESP32-S3 图传节点:ESP32-S3 摄像头开发板 门牌节点:ESP32-S3 + Arduino 框架 通信方式:Wi-Fi、MQTT、UART、SPI、I2C、GPIO 识别模块:RFID RC522 定位模块:UWB / DW1000 系列模块接口 执行控制:STM32F103C8T6 底盘控制端 交互方式:语音、串口屏、电脑端客户端、手机端客户端 适用场景 医院药品、样本、护理用品转运 养老院照护用品配送 仓储货物转运与盘点辅助 实验室器材和样本流转 图书馆资料搬运与区域配送 教学楼、办公楼内的低成本智能配送 后续扩展方向 将主控、图传、门牌和底盘控制进一步集成化,减少分散模块数量。 优化机械结构,使车体更接近真实运载平台。 完善 UWB 多基站定位解算,提高室内定位精度。 增强视觉识别能力,支持货物外观识别和障碍检测。 完善客户端任务管理,实现多车、多门牌、多任务调度。 增加电源管理和安全保护,提高系统稳定性。 开源说明 本仓库为 TrackDock 室内智能感知运载平台的嵌入式端工程整理版本,包含主控节点端、图传视觉端和门牌节点端三个部分。项目用于嵌入式系统开发、物联网多节点协同、智能运载平台原型验证和比赛展示。 其中涉及小智开源框架的部分为基于原项目的二次开发与场景化扩展,建议开源时同步附带原项目许可证、二次开发说明和本项目新增功能说明。 仓库地址:https://github.com/shaojiec932-web/CCD 欢迎在评论区交流硬件选型、MCP 工具注册、UWB 定位解算、MQTT 图传带宽优化等细节问题。
  • BakeHub 外部 Markdown 发帖工具

    经验分享 pythonnodebb自动化
    1
    1赞同
    1帖子
    47浏览
    背景:为什么要写一个外部发帖工具? BakeHub 是基于 NodeBB 搭建的工科开源社群,日常发帖通常要在网页编辑器里手敲 Markdown。但当你有一篇几千字的技术复盘、或者想批量迁移本地笔记库时,网页编辑器就成了瓶颈——没有本地版本控制、不能 vim 键位、不能 grep、不能 diff。于是我用 F12 抓了一下 NodeBB 的发帖请求,顺手写了一个 零依赖 的 Python 脚本,直接用本地 Markdown 文件发帖。这篇帖子既是工具发布,也是把抓包分析过程完整记录下来,方便后来人复用。 第一步:F12 抓包分析 NodeBB 发帖流程 打开 BakeHub 首页,按 F12 切到 Network 面板,勾上 Preserve log,然后手动走一遍「登录 → 进版块 → 发帖」的流程。关键请求按时间顺序如下: 1. 登录请求 POST /login HTTP/1.1 Host: 203.195.163.189 Content-Type: application/x-www-form-urlencoded x-csrf-token: <从 /api/config 拿到的 token> Referer: http://203.195.163.189/ username=KongChar&password=********&remember=on&returnTo= 服务器返回 {"next":"/"} 并 Set-Cookie: express.sid=...,这个 session cookie 就是后续所有请求的身份凭证。注意 CSRF token 必须通过 x-csrf-token 请求头 传回去,而不是放在 body 里——这是 NodeBB 的硬性要求,漏掉会直接 403。 2. 获取 CSRF token GET /api/config HTTP/1.1 Accept: application/json 返回的 JSON 里关键字段: 字段 含义 csrf_token 写操作必须带的 token,每次会话变化 loggedIn true/false,判断当前 session 是否已登录 uid 当前用户 id,游客为 0 maximumPostLength 单帖最大字符数(本站 32767) maximumTagsPerTopic 每帖最多标签数(本站 5) 3. 发帖请求(NodeBB v3 API) POST /api/v3/topics HTTP/1.1 Content-Type: application/json x-csrf-token: <同上> { "cid": 3, "title": "帖子标题", "content": "Markdown 正文", "tags": ["工具", "Python"] } 返回 {"topic": {"tid": 123, "slug": "123/xxx", ...}},帖子 URL 就是 /topic/{tid}/{slug}。 4. 列版块 GET /api/categories 返回 categories 数组,每项含 cid / name / slug / topic_count。本站当前版块: cid 名称 用途 1 项目复盘 复盘总结 2 开源求助 提问 3 经验分享 技术分享(本帖所在) 4 学习路线 路线规划 第二步:踩过的坑 抓包看似简单,实际写脚本时踩了三个坑,值得单独说一下: 坑 1:Connection reset by peer。 服务器对短时间高频请求会直接 RST 连接,第一次跑 cats 子命令就挂了。解决办法是给底层请求加 指数退避重试,失败后 sleep 1.5s / 3s / 4.5s 再试。 坑 2:Accept 头重复。 一开始在 opener.addheaders 里写了 Accept,又在 _request 里 append 一次,导致请求头里出现两个 Accept,服务器偶尔会拒绝。统一在 _request 里追加,addheaders 只放 User-Agent 和 Referer。 坑 3:CSRF token 会过期。 长时间不操作后再发帖,会收到 403 [[error:invalid-csrf-token]]。所以每次发帖前都重新 GET /api/config 刷新一次 token,而不是登录时拿一次就缓存到底。 第三步:完整工具代码 下面是 bakehub-poster.py 的完整源码,零第三方依赖,只用 Python 标准库。把它存成文件直接 python3 bakehub-poster.py 就能跑。 #!/usr/bin/env python3 """ bakehub-poster.py — BakeHub (NodeBB) 外部 Markdown 发帖工具 用法: python bakehub-poster.py post --file post.md --title "帖子标题" --cid 3 --tags "工具,Python" python bakehub-poster.py cats # 列出所有版块 python bakehub-poster.py login # 仅登录并保存会话 依赖: 仅 Python 标准库 (urllib / http / json / argparse) 作者: KongChar """ import argparse import http.cookiejar import json import os import time import urllib.parse import urllib.request # ===== 配置区 ===== BASE_URL = "http://203.195.163.189" DEFAULT_CID = 3 # 经验分享 COOKIE_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bakehub.cookies.txt") UA = ("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36") class BakeHubClient: def __init__(self, base_url=BASE_URL): self.base = base_url.rstrip("/") self.cj = http.cookiejar.MozillaCookieJar(COOKIE_FILE) if os.path.exists(COOKIE_FILE): self.cj.load(ignore_discard=True, ignore_expires=True) self.opener = urllib.request.build_opener( urllib.request.HTTPCookieProcessor(self.cj)) self.opener.addheaders = [ ("User-Agent", UA), ("Referer", self.base + "/"), ] self.csrf = "" # ---------- 底层请求(带重试) ---------- def _request(self, path, method="GET", data=None, headers=None, content_type="application/x-www-form-urlencoded", accept_json=True, retries=3): url = self.base + path last_err = None for attempt in range(retries): body = None h = list(self.opener.addheaders) if accept_json: h.append(("Accept", "application/json")) if headers: h.extend(headers.items()) if data is not None: if isinstance(data, (dict, list)): body = json.dumps(data).encode("utf-8") h.append(("Content-Type", "application/json")) elif isinstance(data, str): body = data.encode("utf-8") h.append(("Content-Type", content_type)) else: body = data h.append(("Content-Type", content_type)) req = urllib.request.Request(url, data=body, method=method) for k, v in h: req.add_header(k, v) try: with self.opener.open(req, timeout=30) as r: return r.status, r.read().decode("utf-8", errors="replace") except urllib.error.HTTPError as e: return e.code, e.read().decode("utf-8", errors="replace") except (urllib.error.URLError, ConnectionError, TimeoutError) as e: last_err = e if attempt < retries - 1: time.sleep(1.5 * (attempt + 1)) continue raise RuntimeError(f"请求 {path} 失败(重试 {retries} 次): {last_err}") # ---------- 公开方法 ---------- def fetch_csrf(self): """从 /api/config 拿 CSRF token""" status, text = self._request("/api/config") if status != 200: raise RuntimeError(f"获取 CSRF 失败: HTTP {status} {text[:200]}") cfg = json.loads(text) self.csrf = cfg.get("csrf_token", "") if not self.csrf: raise RuntimeError("CSRF token 为空") return self.csrf def login(self, username, password): """登录并保存 cookie""" self.fetch_csrf() form = urllib.parse.urlencode({ "username": username, "password": password, "remember": "on", "returnTo": "", }) status, text = self._request( "/login", method="POST", data=form, headers={"x-csrf-token": self.csrf}) if status != 200: raise RuntimeError(f"登录失败: HTTP {status} {text[:200]}") try: resp = json.loads(text) except json.JSONDecodeError: resp = {"raw": text} # 保存 cookie self.cj.save(COOKIE_FILE, ignore_discard=True, ignore_expires=True) # 验证登录 self.fetch_csrf() status2, text2 = self._request("/api/config") cfg = json.loads(text2) if not cfg.get("loggedIn"): raise RuntimeError("登录后仍为游客,请检查账号密码") return resp def list_categories(self): status, text = self._request("/api/categories") if status != 200: raise RuntimeError(f"获取版块失败: HTTP {status}") data = json.loads(text) return data.get("categories", []) def create_topic(self, cid, title, content, tags=None): """创建新主题。content 为 Markdown 文本""" if not self.csrf: self.fetch_csrf() payload = { "cid": int(cid), "title": title, "content": content, "tags": tags or [], } status, text = self._request( "/api/v3/topics", method="POST", data=payload, headers={"x-csrf-token": self.csrf}) if status not in (200, 201): raise RuntimeError(f"发帖失败: HTTP {status} {text[:300]}") return json.loads(text) def reply(self, tid, content): if not self.csrf: self.fetch_csrf() payload = {"tid": int(tid), "content": content} status, text = self._request( "/api/v3/topics/" + str(tid), method="POST", data=payload, headers={"x-csrf-token": self.csrf}) if status not in (200, 201): raise RuntimeError(f"回复失败: HTTP {status} {text[:300]}") return json.loads(text) # ===== CLI ===== def cmd_login(args): c = BakeHubClient() r = c.login(args.user, args.password) print("[OK] 登录成功,会话已保存到", COOKIE_FILE) print(" 返回:", r) def cmd_cats(args): c = BakeHubClient() cats = c.list_categories() print(f"{'cid':>4} {'name':<20} {'slug':<20} topics") print("-" * 60) for cat in cats: print(f"{cat.get('cid'):>4} {cat.get('name'):<20} " f"{cat.get('slug'):<20} {cat.get('topic_count')}") def cmd_post(args): with open(args.file, "r", encoding="utf-8") as f: content = f.read() tags = [t.strip() for t in (args.tags or "").split(",") if t.strip()] c = BakeHubClient() # 自动登录(若会话失效) try: c.fetch_csrf() except Exception: c.login(args.user, args.password) # 二次确认登录态 status, text = c._request("/api/config") cfg = json.loads(text) if not cfg.get("loggedIn"): print("[INFO] 会话已过期,重新登录...") c.login(args.user, args.password) result = c.create_topic(args.cid, args.title, content, tags) print("[OK] 发帖成功!") if isinstance(result, dict): topic = result.get("topic", result) slug = topic.get("slug") or topic.get("tid") print(" tid:", topic.get("tid")) print(" URL:", f"{BASE_URL}/topic/{topic.get('tid')}/{slug}") def main(): p = argparse.ArgumentParser( description="BakeHub (NodeBB) 外部 Markdown 发帖工具") p.add_argument("--user", default="KongChar", help="用户名") p.add_argument("--password", default="csj123456", help="密码") sub = p.add_subparsers(dest="cmd", required=True) sub.add_parser("login", help="仅登录并保存会话").set_defaults(func=cmd_login) sub.add_parser("cats", help="列出所有版块").set_defaults(func=cmd_cats) pp = sub.add_parser("post", help="发布 Markdown 帖子") pp.add_argument("--file", required=True, help="Markdown 文件路径") pp.add_argument("--title", required=True, help="帖子标题") pp.add_argument("--cid", type=int, default=DEFAULT_CID, help="版块 cid") pp.add_argument("--tags", help="标签,逗号分隔") pp.set_defaults(func=cmd_post) args = p.parse_args() args.func(args) if __name__ == "__main__": main() 第四步:使用方法 把上面的代码存为 bakehub-poster.py,然后: 1. 首次登录(保存会话,后续免登录): python3 bakehub-poster.py login # 输出: [OK] 登录成功,会话已保存到 .../bakehub.cookies.txt 2. 查看版块列表: python3 bakehub-poster.py cats # cid name slug topics # ------------------------------------------------------------ # 1 项目复盘 1/项目复盘 3 # 2 开源求助 2/开源求助 5 # 4 学习路线 4/学习路线 2 # 3 经验分享 3/经验分享 6 3. 用本地 Markdown 文件发帖: cat > my-post.md <<'EOF' ## 我的第一篇外部发帖 这是用脚本发的帖子,正文支持完整 Markdown 语法。 - 列表 - **加粗** / *斜体* / `代码` - [链接](https://example.com) EOF python3 bakehub-poster.py post \ --file my-post.md \ --title "我的第一篇外部发帖" \ --cid 3 \ --tags "工具,Python,自动化" # 输出: # [OK] 发帖成功! # tid: 17 # URL: http://203.195.163.189/topic/17/我的第一篇外部发帖 这篇帖子本身就是用这个工具发出来的 —— 自举成功。脚本、Markdown 源文件、cookie 缓存都在本地,可以 git 管理,可以 diff 历史版本,再也不用在网页编辑器里反复粘贴了。 设计取舍说明 为什么不用 requests 库?因为想保证 零依赖,任何一台装了 Python 3 的机器都能直接跑,不用 pip install。urllib 的 API 确实啰嗦,但封装一层 _request 之后用起来和 requests 差不多。 为什么 cookie 用 Netscape 格式落盘?因为 http.cookiejar.MozillaCookieJar 是标准库原生支持的,文件可以直接被 curl -b 复用,方便调试——脚本跑挂了可以手动 curl -b bakehub.cookies.txt ... 排查。 为什么 CSRF token 每次都重新取?因为 NodeBB 的 token 会随 session 状态变化,缓存反而更容易踩 403。一次 GET /api/config 才几 KB,代价可以忽略。 后续可扩展的方向 当前版本只覆盖了「登录 + 列版块 + 发主题 + 回复」四个核心动作。如果有人想接着做,几个方向值得考虑: 图片上传:NodeBB 的上传接口是 POST /api/v3/topics/upload,需要 multipart/form-data,目前脚本没实现,长帖里的图片还得手动传。 草稿同步:把本地 .md 文件的 frontmatter(标题、tags、cid)解析出来,做到一篇文件 = 一篇帖子,git diff 就能看出改了啥。 批量迁移:遍历一个目录下所有 .md 文件,按 frontmatter 里的 cid 自动分发到对应版块,适合从 Obsidian / Hexo 仓库一键搬迁。 WebSocket 监听:NodeBB 用 socket.io 推实时通知,接上之后可以做到「别人回复你的帖子时本地弹通知」。 欢迎在评论区交流改进思路,或者直接 fork 这个脚本改一版发上来。
  • 兄弟们,有谁打通过支付通道

    开源求助
    3
    0赞同
    3帖子
    81浏览
    ZPAY
  • 使用deepssk做出的BakeHub自动发帖工具,证明爱群主

    经验分享
    2
    1赞同
    2帖子
    107浏览
    @鲁迪乌斯 能用来经验+3吗
  • STM32超频开源项目合集

    项目复盘
    1
    1赞同
    1帖子
    70浏览
    项目概述 本项目收集整理了GitHub上三个优秀的STM32超频开源项目,覆盖STM32F103、STM32F303、STM32F37x三个系列,从极限挑战到实用工程模板都有涉及,适合喜欢折腾硬件超频的爱好者参考学习。 项目一:stm32-overclocking-challenge(蓝药丸极限超频竞赛) GitHub地址: https://github.com/m3y54m/stm32-overclocking-challenge 目标芯片: STM32F103C8T6(BluePill蓝药丸开发板) 项目特点 专门针对STM32F103的极限超频挑战项目 使用汇编语言编写时钟配置代码,追求极致性能 支持测试不同PLL配置下的极限频率 包含GPIO翻转速度测试,直观验证超频效果 附带稳定性测试代码 玩法亮点 STM32F103官方标称最高72MHz,但很多板子实际能跑到128MHz甚至更高。这个项目就像一场硅片体质抽奖,不同芯片的极限频率差异很大,用示波器看GPIO翻转波形就能直观感受到超频效果。 项目二:stm32f303k8-template(F303超频工程模板) GitHub地址: https://github.com/dimtass/stm32f303k8-template 目标芯片: STM32F303 Nucleo开发板 项目特点 完整的STM32F303超频工程模板,开箱即用 稳定超频至128MHz(官方标称72MHz) 已配置好Flash等待周期,系统运行稳定 完整Makefile工程,适合直接用于实际开发 支持Nucleo开发板,硬件门槛低 实用价值 F303系列自带硬件浮点单元(FPU),超频后DSP运算性能提升明显。这个项目不是单纯的极限超频demo,而是真正可用于实际项目的工程模板,外设都能正常工作。 项目三:overclocking-STM32F37x_16(F37x时钟魔改) GitHub地址: https://github.com/qidfpv/overclocking-STM32F37x_16 目标芯片: STM32F37x系列 项目特点 修改系统时钟配置文件system_stm32f37x.c 从官方72MHz超频到80MHz,约11%性能提升 超频幅度保守,稳定性有保障 直接替换时钟文件即可完成超频 适合新手入门MCU超频 适合人群 这种"甜点频率"超频基本所有芯片都能稳住,不需要挑体质,风险低成就感高,非常适合第一次玩MCU超频的新手。 超频注意事项 失去质保:超频超出芯片官方规格,会失去原厂质保 功耗发热:超频后功耗和发热会明显增加,注意散热 Flash等待周期:必须根据频率调整Flash等待周期,否则直接HardFault 外设时钟:UART/SPI/I2C等外设时钟会变化,通信波特率可能错乱 芯片体质:同型号芯片极限频率差异很大,不要盲目追求高频 风险提示:极限超频有烧坏芯片的风险,请做好电流监控和散热措施 技术标签 STM32 单片机超频 PLL时钟配置 嵌入式开发 硬件DIY
  • 1赞同
    1帖子
    7浏览
    大家好!今天给大家分享几个GitHub上超有意思的STM32超频开源项目,喜欢折腾硬件的朋友们千万别错过~ MCU超频这件事,就像给芯片"开外挂",虽然超出原厂规格有风险,但那种把一块便宜板子榨干性能的快感,只有玩过的人才懂!下面三个项目分别对应不同的STM32系列,各有各的玩法。 项目一:stm32-overclocking-challenge(蓝药丸极限超频竞赛) 项目地址: https://github.com/m3y54m/stm32-overclocking-challenge 目标芯片: STM32F103C8T6(经典蓝药丸BluePill) 这个项目有多野? 这是一个专门针对STM32F103的极限超频挑战项目,作者直接用汇编来写时钟配置代码,就是为了榨干每一滴性能! 亮点: 测试不同PLL配置下的极限频率 GPIO翻转速度测试,看看到底能跑多快 纯汇编级别的时钟寄存器操作,硬核到底 附带稳定性测试代码 有趣在哪? STM32F103官方标称最高72MHz,但很多"大雷"板子其实能跑到128MHz甚至更高!这个项目就像一场硅片体质大抽奖,有的人的蓝药丸只能稳80MHz,有的神仙体质能冲150MHz+。 而且用GPIO翻转来测频率特别直观——用示波器一看就知道你的芯片到底超没超成功,波形密到什么程度。 ️ 项目二:stm32f303k8-template(F303超频工程模板) 项目地址: https://github.com/dimtass/stm32f303k8-template 目标芯片: STM32F303 Nucleo开发板 实用派超频代表 这个项目就比较"工程化"了,不是单纯追求极限,而是提供了一个完整的、超频到128MHz的工程模板,拿来就能用。 亮点: 直接超频到128MHz稳定运行(官方标称72MHz) ️ 完整的Makefile工程模板,开箱即用 配置好了Flash等待周期,不会跑飞 支持Nucleo开发板,硬件门槛低 有趣在哪? F303系列带硬件浮点单元(FPU),超频之后做DSP运算性能提升特别明显!如果你用它做电机控制、数字信号处理,超个频相当于免费升级了芯片。 而且这个项目最良心的地方是——它不是那种"能点亮就算成功"的demo,而是真的把整个系统时钟树都调好了,外设也能正常工作,拿来做实际项目都没问题。 项目三:overclocking-STM32F37x_16(F37x时钟魔改) 项目地址: https://github.com/qidfpv/overclocking-STM32F37x_16 目标芯片: STM32F37x系列 温和超频派 这个项目相对保守一些,把F37x从官方72MHz超到80MHz,属于"安全小超"的范畴。 亮点: 修改系统时钟文件system_stm32f37x.c 从72MHz提升到80MHz,约11%性能提升 ️ 超频幅度小,稳定性有保障 直接替换时钟配置文件即可 有趣在哪? 别看只超了8MHz,对于很多对时序要求严格的应用来说,这已经是很大的提升了!而且这种"甜点频率"基本所有芯片都能稳住,不需要挑体质,量产项目都敢用。 这个项目最适合新手入门超频——改动小,风险低,成功了有成就感,失败了也不容易烧片子。 超频小科普:为什么MCU能超频? 很多人可能好奇,为什么官方标72MHz的芯片能跑到128MHz?其实原因很简单: 厂商留有余量:为了保证量产良率和长期稳定性,标称频率通常都比较保守 温度影响: datasheet里的参数一般是工业级温度范围(-40~85℃),室温下潜力更大 体质差异:同一条生产线出来的芯片,极限频率也不一样,这就是所谓的"大雷"和"大雕" ️ 重要提醒 超频虽爽,但一定要注意: 超频会失去原厂质保 功耗和发热会明显增加 Flash等待周期必须对应调整,否则直接HardFault 外设时钟(UART/SPI/I2C)会跟着变,通信波特率可能错乱 极限超频有烧坏芯片的风险,做好散热和电流监控 最后 STM32超频就像是硬件圈的"装机超频"文化,便宜的几块钱的蓝药丸,超一超性能翻倍,那种DIY的乐趣真的很棒! 如果你也玩过STM32超频,欢迎在评论区分享你的战绩——你的芯片最高稳在了多少MHz?
  • 51单片机+ESP8266连接mqtt的智能火灾报警系统设计与实现

    项目复盘
    1
    0赞同
    1帖子
    47浏览
    <p>声明:本项目为自研,(前端是小伙伴帮忙做的)开发不易,大家点点赞支持一下主播吧(目前网上没有看到基于51的mqtt相关项目,感觉51搞这些太容易超内存了,并且大佬都在搞32)</p><p>本博客不包含emqx的搭建教程,不会搭建emqx的可以先看这个博主的视频: <span><a href="http:// https://b23.tv/fuq6wbk" target="_blank" rel="noopener noreferrer">https://b23.tv/fuq6wbk</a></span></p><p>结尾附网盘链接</p><h2>项目概述</h2><p>本项目基于STC89C52RC单片机,结合ESP8266  WiFi模块、DHT11温湿度传感器、MQ-2气体传感器等硬件,设计并实现了一个智能火灾报警系统。系统能够实时监测环境温湿度和可燃气体浓度,通过MQTT协议将数据上传到云平台,并支持远程报警和阈值设置。</p><p> <span>​<img src="/assets/uploads/files/1785077888776-image.png" alt="image.png" data-size="large" /></span></p><h2>系统架构</h2><h3>硬件组成</h3><ul><li><p><strong>主控芯片</strong>:STC89C52RC单片机</p></li><li><p><strong>通信模块</strong>:ESP8266 WiFi模块(ESP-01S)</p></li><li><p><strong>传感器</strong>:</p><ul><li><p>DHT11温湿度传感器</p></li><li><p>MQ-2气体浓度传感器</p></li></ul></li><li><p><strong>AD/DA转换模块</strong>:PCF8591转换模块</p></li><li><p><strong>显示模块</strong>:LCD1602液晶显示屏</p></li><li><p><strong>报警模块</strong>:蜂鸣器、LED报警灯</p></li></ul><h3>软件架构</h3><ul><li><p><strong>开发环境</strong>:Keil C51</p></li><li><p><strong>通信协议</strong>:MQTT</p></li><li><p><strong>数据格式</strong>:JSON</p></li></ul><h2><strong>PCB设计</strong></h2><p><span></span></p><p>1.原理图</p><p><img src="/assets/uploads/files/1785077910964-image.png" alt="image.png" data-size="large" /></p><p>2.PCB</p><p><img src="/assets/uploads/files/1785078011677-image.png" alt="image.png" data-size="large" /></p><p><span>​</span></p><h2>核心功能实现</h2><h3>1. 传感器数据采集</h3><p>1.mq2烟雾传感器</p> <blockquote><code><span></span></code><code><span><span></span></span></code><span>#include "mq2.h''<br /></span><p><span><strong>#include "i2c.h"</strong></span><strong><span></span></strong></p><p><strong><span>#define PCF8591_ADDR  0x90</span><span> </span><span>// PCF8591 I2C地址</span><span></span></strong></p><p><strong><span> </span></strong></p><p><strong><span>// 读取PCF8591指定通道的模拟值(MQ2接通道0)</span><span></span></strong></p><p><strong><span>unsigned char MQ2_ReadByte(unsigned char Channel)</span><span></span></strong></p><p><strong><span>{</span><span></span></strong></p><p><strong><span> </span><span>unsigned char Data;</span><span></span></strong></p><p><strong><span> </span><span>I2C_Start();</span><span></span></strong></p><p><strong><span> </span><span>I2C_SendByte(PCF8591_ADDR);</span><span> </span><span> </span></strong><span><strong>// </strong><strong>写地址</strong></span><span></span></p><p><span></span></p><p><span></span></p></blockquote><p><span></span><span></span><span></span></p> <p><span></span></p><p>2.dht11温湿度传感器</p> <pre><code><span>#<span>include</span> <span>"dht11.h"</span></span> <span>#<span>include</span> <span><intrins.h></span></span> <span>// 30us延时</span> <span><span>void</span> <span>DHT11_Delay30us</span><span>(<span>void</span>)</span> </span>{ <span>unsigned</span> <span>char</span> i; i = <span>12</span>; <span>while</span> (--i); } <span>// 40us延时</span> <span><span>void</span> <span>DHT11_Delay40us</span><span>(<span>void</span>)</span> </span>{ <span>unsigned</span> <span>char</span> i; i = <span>17</span>; <span>while</span> (--i); } <span><span>uint8_t</span> <span>DHT11_Init</span><span>(<span>void</span>)</span> </span>{ DHT11_DQ = <span>1</span>; DHT11_DQ = <span>0</span>; <span>Delay_xms</span>(<span>20</span>); <span>// 至少给18ms</span> DHT11_DQ = <span>1</span>; <span>DHT11_Delay30us</span>(); <span>if</span>(DHT11_DQ == <span>0</span>) { <span>while</span>(DHT11_DQ == <span>0</span>); <span>while</span>(DHT11_DQ == <span>1</span>); <span>return</span> <span>0</span>; } <span>return</span> <span>1</span>; } <span><span>uint8_t</span> <span>DHT11_ReceiveByte</span><span>(<span>void</span>)</span> </span>{ <span>uint8_t</span> i, Byte = <span>0x00</span>; <span>for</span>(i=<span>0</span>; i<<span>8</span>; i++) { Byte <<= <span>1</span>; <span>while</span>(DHT11_DQ == <span>0</span>); <span>DHT11_Delay40us</span>(); <span>if</span>(DHT11_DQ == <span>1</span>) Byte |= <span>0x01</span>; <span>while</span>(DHT11_DQ == <span>1</span>); } <span>return</span> Byte; } <span><span>uint8_t</span> <span>DHT11_GetData</span><span>(<span>uint8_t</span> *Humi_H, <span>uint8_t</span> *Humi_L, <span>uint8_t</span> *Temp_H, <span>uint8_t</span> *Temp_L)</span> </span>{ <span>uint8_t</span> i, Buffer[<span>5</span>]; <span>if</span>(<span>DHT11_Init</span>() == <span>0</span>) { <span>for</span>(i=<span>0</span>; i<<span>5</span>; i++) Buffer[i] = <span>DHT11_ReceiveByte</span>(); <span>if</span>(Buffer[<span>0</span>] + Buffer[<span>1</span>] + Buffer[<span>2</span>] + Buffer[<span>3</span>] == Buffer[<span>4</span>]) { *Humi_H = Buffer[<span>0</span>]; *Humi_L = Buffer[<span>1</span>]; *Temp_H = Buffer[<span>2</span>]; *Temp_L = Buffer[<span>3</span>]; <span>return</span> <span>0</span>; } } <span>return</span> <span>1</span>; } </code></pre> <p><span></span> </p><pre><code><span>#<span>ifndef</span> DHT11_H</span> <span>#<span>define</span> DHT11_H</span> <span>#<span>include</span> <span><reg52.h></span></span> <span>#<span>include</span> <span>"delay.h"</span></span> <span>typedef</span> <span>unsigned</span> <span>char</span> <span>uint8_t</span>; sbit DHT11_DQ = P2^<span>0</span>; <span>// 引脚设置</span> <span><span>uint8_t</span> <span>DHT11_Init</span><span>(<span>void</span>)</span></span>; <span><span>uint8_t</span> <span>DHT11_ReceiveByte</span><span>(<span>void</span>)</span></span>; <span><span>uint8_t</span> <span>DHT11_GetData</span><span>(<span>uint8_t</span> *Humi_H, <span>uint8_t</span> *Humi_L, <span>uint8_t</span> *Temp_H, <span>uint8_t</span> *Temp_L)</span></span>; <span>#<span>endif</span></span> </code></pre> <p><span></span></p><h3>2. ESP8266 MQTT通信</h3><p>注意:客户端ID随意但是必须唯一,用户名称和密码就是自己在emqx上设置的</p><p> <span>​<img src="/assets/uploads/files/1785078045468-image.png" alt="image.png" data-size="large" /></span></p><p>注意:mqtt的IP地址用ipconfig查看不了,可以用网络调试助手等软件看,不知道是哪个可以先用串口调试ESP8266,发送AT指令连接尝试:(串口发送AT指令记得末尾加上换行)</p><blockquote><p>AT+CWMODE=1</p><p>AT+CWJAP="WiFi名","WiFi密码"</p><p>AT+MQTTUSERCFG=0,1,"客户端ID","用户名","密码",0,0,""</p><p>AT+MQTTCONN=0,"mqtt的IP地址",1883,0</p></blockquote><p><span></span></p><p>网络调试助手:</p><p><img src="/assets/uploads/files/1785078101073-image.png" alt="image.png" data-size="large" /></p><p><span>​</span></p><p>esp8266.c</p> <pre><code><span>#<span>include</span> <span>"esp8266.h"</span></span> <span>#<span>include</span> <span><reg52.h></span></span> <span>#<span>include</span> <span>"lcd1602.h"</span></span> <span>#<span>include</span> <span>"usart.h"</span></span> <span>#<span>include</span> <span>"delay.h"</span></span> <span>#<span>include</span> <span>"time.h"</span></span> <span>// 硬件引脚定义</span> sbit WIFI_RST = P1^<span>7</span>; <span>// ESP8266复位</span> sbit Bee = P1^<span>6</span>; <span>// 蜂鸣器</span> sbit ALARM_LED= P1^<span>5</span>; <span>// 报警灯</span> <span>// 全局变量:传感器数据</span> <span>unsigned</span> <span>char</span> Humi_H, Temp_H, Gas; <span>unsigned</span> <span>char</span> Threshold = <span>29</span>; <span>// 温度报警阈值</span> <span>// ESP8266初始化连接MQTT(使用AT指令)</span> <span><span>void</span> <span>ESP_conect_sev</span><span>(<span>void</span>)</span> </span>{ <span>// ESP8266复位</span> WIFI_RST = <span>0</span>; <span>Delay_xms</span>(<span>1000</span>); WIFI_RST = <span>1</span>; <span>Delay_xms</span>(<span>1000</span>); <span>// 本地MQTT AT指令配置</span> <span>uart1_sendstring</span>(<span>"AT+CWMODE=1\r\n"</span>); <span>Delay_xms</span>(<span>1000</span>); <span>uart1_sendstring</span>(<span>"AT+CWJAP=\"WiFi名\",\"密码\"\r\n"</span>); <span>// 连接WiFi</span> <span>Delay_xms</span>(<span>2000</span>); <span>uart1_sendstring</span>(<span>"AT+MQTTUSERCFG=0,1,\"客户端ID\",\"用户名\",\"密码\",0,0,\"\"\r\n"</span>); <span>// MQTT用户配置</span> <span>Delay_xms</span>(<span>1000</span>); <span>uart1_sendstring</span>(<span>"AT+MQTTCONN=0,\"IP地址\",1883,0\r\n"</span>); <span>// 连接MQTT服务器</span> <span>Delay_xms</span>(<span>2000</span>); } <span>// 数字转字符串(发送数据用)</span> <span><span>void</span> <span>num_to_str</span><span>(<span>unsigned</span> <span>char</span> num, <span>unsigned</span> <span>char</span> *num_str1)</span> </span>{ <span>if</span>(num != <span>0</span>) { <span>unsigned</span> <span>int</span> num_temp = num; <span>unsigned</span> <span>char</span> cnt = <span>0</span>; <span>while</span>(num_temp) { cnt++; num_temp /= <span>10</span>; } num_temp = num; <span>while</span>(cnt) { cnt--; num_str1[cnt] = (num_temp % <span>10</span>) + <span>48</span>; num_temp /= <span>10</span>; } } <span>else</span> { num_str1[<span>0</span>] = <span>'0'</span>; } } <span>// 发送第一个传感器数据(湿度)</span> <span><span>void</span> <span>first_data</span><span>(<span>unsigned</span> <span>char</span> *str2, <span>unsigned</span> <span>char</span> num)</span> </span>{ <span>unsigned</span> <span>char</span> str1[<span>3</span>] = {<span>0</span>}; <span>// MQTT发布主题:testtopic/1,保留JSON数据格式</span> <span>uart1_sendstring</span>(<span>"AT+MQTTPUB=0,"testtopic/1","{\""</span>); <span>uart1_sendstring</span>(str2); <span>uart1_sendstring</span>(<span>"\":"</span>); <span>num_to_str</span>(num, str1); <span>uart1_sendstring</span>(str1); } <span>// 追加传感器数据(温度、气体浓度)</span> <span><span>void</span> <span>add_data</span><span>(<span>unsigned</span> <span>char</span> *str2, <span>unsigned</span> <span>char</span> num)</span> </span>{ <span>unsigned</span> <span>char</span> str1[<span>3</span>] = {<span>0</span>}; <span>uart1_sendstring</span>(<span>"\,\""</span>); <span>uart1_sendstring</span>(str2); <span>uart1_sendstring</span>(<span>"\":"</span>); <span>num_to_str</span>(num, str1); <span>uart1_sendstring</span>(str1); } <span>// 每秒发送传感器数据到MQTT</span> <span><span>void</span> <span>ESP_send_data</span><span>(<span>void</span>)</span> </span>{ <span>if</span>(time0_ms > <span>1000</span>) <span>// 1秒发送一次</span> { <span>first_data</span>(APP_Humi, Humi_H); <span>// 湿度</span> <span>add_data</span>(APP_Temp, Temp_H); <span>// 温度</span> <span>add_data</span>(APP_Gas, Gas); <span>// 气体浓度</span> <span>uart1_sendstring</span>(<span>"}",0,0\r\n"</span>); <span>Delay_xms</span>(<span>200</span>); <span>uart1_sendstring</span>(<span>"AT+MQTTPUB=0,"订阅主题名",""</span>); <span>if</span>(Gas > <span>50</span> || Temp_H > Threshold) { <span>uart1_sendstring</span>(<span>"fire"</span>); <span>// 报警发送fire</span> } <span>else</span> { <span>uart1_sendstring</span>(<span>"normal"</span>);<span>// 正常发送normal</span> } <span>uart1_sendstring</span>(<span>"",0,0\r\n"</span>); <span>Delay_xms</span>(<span>200</span>); time0_ms = <span>0</span>; <span>// 重置定时器</span> } } <span>// 字符串比较</span> <span>bit <span>str_cmp</span><span>(<span>char</span> *str1, <span>char</span> *str2, <span>char</span> len)</span> </span>{ <span>char</span> *str3 = str1; <span>char</span> *str4 = str2; <span>while</span>(len) { <span>if</span>(*str3 != *str4) <span>return</span> <span>0</span>; len--; str3++; str4++; } <span>return</span> <span>1</span>; } <span>// 字符串转数字</span> <span><span>int</span> <span>str_to_num</span><span>(<span>char</span> *str1)</span> </span>{ <span>unsigned</span> <span>char</span> num = <span>0</span>; <span>while</span>(*str1 >= <span>'0'</span> && *str1 <= <span>'9'</span>) { num += (*str1) - <span>48</span>; str1++; <span>if</span>(!(*str1 >= <span>'0'</span> && *str1 <= <span>'9'</span>)) <span>break</span>; num *= <span>10</span>; } <span>return</span> num; } <span>// 处理本地MQTT下发指令</span> <span>unsigned</span> <span>char</span> Deliver_num; <span><span>void</span> <span>Proces_distribut_data</span><span>(<span>void</span>)</span> </span>{ <span>unsigned</span> <span>char</span> i; <span>// C89:提前声明循环变量,修复语法错误</span> <span>if</span>(RX_ok) <span>// 接收完成</span> { Deliver_num = <span>str_to_num</span>(Deliver_data); <span>// 解析指令数值</span> <span>// 仅保留温度阈值修改逻辑(可扩展其他本地指令)</span> <span>if</span>(<span>str_cmp</span>(Deliver_identifier, APP_Threshold, <span>8</span>)) { Threshold = Deliver_num; } <span>// 清空接收缓存</span> <span>for</span>(i=<span>0</span>; i&lt;<span>6</span>; i++) { Deliver_identifier[i] = <span>0</span>; } <span>for</span>(i=<span>0</span>; i&lt;<span>4</span>; i++) { Deliver_data[i] = <span>0</span>; } Deliver_cnt = <span>0</span>; Deliver_data_cnt = <span>0</span>; RX_ok = <span>0</span>; } } <span>// 传感器报警逻辑(气体浓度/温度过高)</span> <span><span>void</span> <span>Alarm_Check</span><span>(<span>void</span>)</span> </span>{ <span>if</span>(Gas > <span>50</span> || Temp_H > Threshold) <span>// 气体浓度>阈值 或 温度>阈值</span> { <span>LCD_ShowString</span>(<span>2</span>, <span>7</span>, <span>"fire!"</span>); Bee = <span>0</span>; <span>// 蜂鸣器响</span> ALARM_LED = <span>1</span>; <span>// 报警灯亮</span> } <span>else</span> { <span>LCD_ShowString</span>(<span>2</span>, <span>7</span>, <span>" "</span>); Bee = <span>1</span>; <span>// 蜂鸣器关</span> ALARM_LED = <span>0</span>; <span>// 报警灯灭</span> } } </code></pre> <p><span></span></p><p>esp8266.h</p> <pre><code><span>#<span>ifndef</span> ESP8266_H</span> <span>#<span>define</span> ESP8266_H</span> <span>#<span>include</span> <span><reg52.h></span></span> <span>#<span>include</span> <span>"usart.h"</span></span> <span>#<span>include</span> <span>"time.h"</span></span> <span>// 传感器数据宏定义</span> <span>#<span>define</span> APP_Humi <span>"Humi"</span> <span>// 湿度</span></span> <span>#<span>define</span> APP_Temp <span>"Temp"</span> <span>// 温度</span></span> <span>#<span>define</span> APP_Gas <span>"Gas"</span> <span>// 气体浓度</span></span> <span>#<span>define</span> APP_Threshold <span>"Threshold"</span> <span>// 温度阈值</span></span> <span>extern</span> <span>unsigned</span> <span>char</span> Humi_H, Temp_H, Gas; <span>extern</span> <span>unsigned</span> <span>char</span> Threshold; <span><span>void</span> <span>ESP_conect_sev</span><span>(<span>void</span>)</span></span>; <span><span>void</span> <span>ESP_send_data</span><span>(<span>void</span>)</span></span>; <span><span>void</span> <span>num_to_str</span><span>(<span>unsigned</span> <span>char</span> num, <span>unsigned</span> <span>char</span> *num_str1)</span></span>; <span>bit <span>str_cmp</span><span>(<span>char</span> *str1, <span>char</span> *str2, <span>char</span> len)</span></span>; <span><span>int</span> <span>str_to_num</span><span>(<span>char</span> *str1)</span></span>; <span><span>void</span> <span>Proces_distribut_data</span><span>(<span>void</span>)</span></span>; <span><span>void</span> <span>Alarm_Check</span><span>(<span>void</span>)</span></span>; <span><span>void</span> <span>first_data</span><span>(<span>unsigned</span> <span>char</span> *str2, <span>unsigned</span> <span>char</span> num)</span></span>; <span><span>void</span> <span>add_data</span><span>(<span>unsigned</span> <span>char</span> *str2, <span>unsigned</span> <span>char</span> num)</span></span>; <span>#<span>endif</span></span> </code></pre> <p><span></span></p><h2>关键技术点</h2><h3>1. 数据格式转换</h3><p>由于51的时钟频率差异(11.0592MHz相比32性能相差数十倍,所以数字转换算法必须极度优化,我在这个问题上卡了两天,后面查到一种比较好的轻量级数字转换算法</p> <pre><code><span><span>void</span> <span>num_to_str</span><span>(<span>unsigned</span> <span>char</span> num, <span>unsigned</span> <span>char</span> *num_str1)</span> </span>{ <span>if</span>(num != <span>0</span>) { <span>unsigned</span> <span>int</span> num_temp = num; <span>unsigned</span> <span>char</span> cnt = <span>0</span>; <span>while</span>(num_temp) { cnt++; num_temp /= <span>10</span>; } num_temp = num; <span>while</span>(cnt) { cnt--; num_str1[cnt] = (num_temp % <span>10</span>) + <span>48</span>; num_temp /= <span>10</span>; } } <span>else</span> { num_str1[<span>0</span>] = <span>'0'</span>; } }</code></pre> <p><span></span></p><h3>2. JSON数据构造</h3><p>由于51单片机内存限制,无法使用标准库,需要采用手动构造JSON格式数据:</p> <pre><code><span><span>void</span> <span>first_data</span><span>(<span>unsigned</span> <span>char</span> *str2, <span>unsigned</span> <span>char</span> num)</span> </span>{ <span>unsigned</span> <span>char</span> str1[<span>3</span>] = {<span>0</span>}; <span>// MQTT发布主题:testtopic/1,保留JSON数据格式</span> <span>uart1_sendstring</span>(<span>"AT+MQTTPUB=0,"testtopic/1","{\""</span>); <span>uart1_sendstring</span>(str2); <span>uart1_sendstring</span>(<span>"\":"</span>); <span>num_to_str</span>(num, str1); <span>uart1_sendstring</span>(str1); }</code></pre> <p><span></span></p><h3>3. 中断处理的能力限制</h3><p>51单片机中断向量少,响应慢。32则有着丰富的中断源,NVIC优先级管理</p> <pre><code><span><span>void</span> <span>UART1_IRQ_Handle</span><span>(<span>void</span>)</span> interrupt 4 <span>using</span> 1 </span>{ <span>if</span> (TI) { TI = <span>0</span>; uart1_busy = <span>0</span>; <span>// 只做标志位操作</span> } <span>if</span> (RI) { <span>Pro_deliver_data</span>(); <span>// 立即处理,不缓存</span> RI = <span>0</span>; } }</code></pre> <p><span></span></p><h2>系统特点</h2><ol> <li> <p><strong>实时监测</strong>:持续监控环境参数</p> </li> <li> <p><strong>双重报警</strong>:本地声光报警 + 远程状态推送</p> </li> <li> <p><strong>远程控制</strong>:支持通过MQTT修改报警阈值</p> </li> <li> <p><strong>稳定可靠</strong>:采用工业级传感器和成熟的通信协议</p> </li> <li> <p><strong>低成本</strong>:基于廉价硬件组件</p> </li> </ol><h2>应用场景</h2><ul> <li> <p>家庭火灾预警</p> </li> <li> <p>仓库环境监控</p> </li> <li> <p>实验室安全监测</p> </li> <li> <p>工业生产环境监控</p> </li> </ul><h2>总结</h2><p>本系统成功实现了基于51单片机和ESP8266的智能火灾报警功能,展示了如何将传统单片机与现代物联网技术相结合。系统具有良好的实用性和扩展性,为小型化、低成本的智能安防系统开发提供了参考方案。</p><hr /><p><strong>网盘链接:</strong> <span><a href="https://pan.baidu.com/s/1cfd2bHt5SNVlaVzUCct9XQ " target="_blank" rel="noopener noreferrer">https://pan.baidu.com/s/1cfd2bHt5SNVlaVzUCct9XQ </a></span>提取码: sdjw<br /> 欢迎在评论区或群留言讨论!</p>
  • MimiClaw - 5美元芯片上的口袋AI助手

    项目复盘
    2
    2赞同
    2帖子
    113浏览
    @KongChar 佬
  • 0赞同
    1帖子
    7浏览
    今天给大家分享一个超级有意思的开源项目 —— MimiClaw! 这个项目有多牛?它能在一颗只要5美元的 ESP32-S3 芯片上运行完整的 AI 助手,不需要 Linux,不需要 Node.js,甚至不需要操作系统,纯 C 语言编写,直接跑在微控制器上! 核心亮点: 完整的 AI Agent 循环,支持工具调用(ReAct 模式) 本地记忆存储,数据全部存在芯片 Flash 里,隐私优先 通过 Telegram 就能和它对话,随时随地用 功耗只有 0.5W,USB 供电就能 24/7 运行 支持 Anthropic Claude 和 OpenAI GPT 双模型,运行时可切换 ️ 自带 Cron 定时任务、心跳检测、OTA 升级等实用功能 一个拇指大小的芯片,插上电连上网,就是你的专属 AI 助理,能记事儿、能定时提醒、能联网搜索,简直是嵌入式 AI 的极致玩物! 项目地址:https://github.com/memovai/mimiclaw 高呼群主万岁!群主带我们发现这么多好玩的开源项目,跟着群主混,技术蹭蹭涨! [image: 60dV1wyZ96]
  • 网站多处BUG问题汇总反馈

    开源求助
    1
    0赞同
    1帖子
    24浏览
    最近在浏览论坛帖子时,发现Markdown格式的内容存在显示异常的问题。 具体现象: 部分Markdown语法标记没有被正确解析渲染,直接以原始代码形式展示在页面上 代码块、列表、引用等格式经常出现排版错乱,影响正常阅读 同样的内容在不同浏览器或设备上显示效果不一致 临时建议: 目前建议大家发布内容时尽量使用纯文本格式,避免因Markdown渲染问题导致内容排版混乱,影响其他用户阅读体验。 希望技术团队能够尽快排查一下Markdown渲染器的问题,修复这个显示BUG,提升论坛的整体使用体验。 补充其他发现的页面问题: 二、首页搜索交互问题 在首页搜索框输入内容时,右侧的"热门讨论"区域内容会直接消失,变成空白区域。正常应该是保持热门讨论显示,或者有加载状态提示,直接空白体验不太好。 三、项目页面按钮重复 项目页面同时存在两个"创建项目"按钮,一个在页面右上角,一个在页面中间内容区,属于重复的功能入口,建议合并保留一个即可。 四、AI助手页面布局问题 AI助手页面整体布局不太协调,输入区域很大但"发送"按钮位置偏下,页面右侧和下方留白过多,空间利用率不高,视觉上显得比较空旷。 五、搜索结果页布局空旷 执行搜索后,搜索结果页面的筛选按钮都在顶部,但下方内容区域非常空旷,没有结果时也没有友好的空状态提示,整体布局不够紧凑。
  • 1赞同
    4帖子
    92浏览
    @鲁迪乌斯 老哥太佬了
  • 有啥,简单。好玩的开源项目吗

    开源求助
    1
    0赞同
    1帖子
    31浏览
    最好,无脑一点的。但是要好玩
  • 2赞同
    1帖子
    17浏览
    使用 Docker Compose 部署 sing-box + Hysteria2 本文介绍 sing-box 的作用,并以 Hysteria2 服务端为例,给出一套可维护的 Docker Compose 部署与客户端使用方法。文中的域名、密码和路径均为示例,请替换后再使用。 一、项目地址与官方资料 开源仓库:SagerNet/sing-box 官方文档:sing-box.sagernet.org Docker 部署文档:Docker - sing-box Hysteria2 服务端配置:Hysteria2 Inbound Hysteria2 客户端配置:Hysteria2 Outbound 官方图形客户端:Graphical Clients sing-box 官方将其定义为“The universal proxy platform”,即通用代理平台。项目采用 Go 开发,主体按 GPL-3.0-or-later 发布。 二、sing-box 有什么作用 sing-box 是一个可同时充当代理服务端和客户端的网络代理核心。它通过 JSON 配置文件组合入站、出站、DNS 和路由规则,支持 Hysteria2、Shadowsocks、Trojan、VLESS、TUIC、WireGuard 等多种协议。 典型用途包括: 在自己的服务器和设备之间建立加密代理通道; 根据域名、IP、端口或规则集进行分流; 在 Android、iOS、macOS、Windows 和 Linux 上使用统一配置; 作为其他网关或代理软件的底层网络核心。 本文使用的 Hysteria2 基于 QUIC,主要承载在 UDP 上。在高延迟或存在一定丢包的网络中,它通常比传统 TCP 代理更容易保持吞吐量,但它并不能突破服务器带宽上限,也不能解决所有线路质量问题。 请仅在法律和服务条款允许的范围内使用。 三、本文部署结构 客户端 | | Hysteria2 + TLS + UDP/8443 v 云服务器公网 IP | v Docker(host 网络) | v sing-box hysteria2 inbound | v direct outbound -> 目标网站 使用 network_mode: host 后,容器直接监听宿主机 UDP 端口,不需要在 Compose 中编写 ports 映射。Hysteria2 是 QUIC/UDP 服务,所以防火墙必须放行 UDP,而不是只放行同端口的 TCP。 四、服务器准备 建议准备: 一台具有公网 IPv4 或 IPv6 的 Linux 服务器; Ubuntu 22.04 或更新版本; 一个解析到服务器公网 IP 的域名,例如 hy2.example.com; Docker Engine 与 Docker Compose Plugin; 云厂商安全组和系统防火墙均允许目标 UDP 端口。 安装 Docker 下面以 Ubuntu 为例。若服务器已经安装 Docker,可跳过这一节。 sudo apt-get update sudo apt-get install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg  -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc ​ echo  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" |  sudo tee /etc/apt/sources.list.d/docker.list >/dev/null ​ sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ​ sudo docker version sudo docker compose version 配置域名和防火墙 将 hy2.example.com 的 A 记录指向服务器公网 IPv4。若使用 IPv6,再添加 AAAA 记录。 使用 UFW 时可以这样放行: sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 8443/udp sudo ufw status 80/tcp 用于 Certbot 申请和续期证书。如果服务器上已有 Nginx 或 Caddy,应改用与现有 Web 服务兼容的 ACME 验证方式。云厂商安全组也需要单独放行 8443/udp。 申请可信 TLS 证书 sudo apt-get update sudo apt-get install -y certbot ​ sudo certbot certonly --standalone  -d hy2.example.com  --agree-tos  --email admin@example.com  --non-interactive 证书通常位于: /etc/letsencrypt/live/hy2.example.com/fullchain.pem /etc/letsencrypt/live/hy2.example.com/privkey.pem 生产环境推荐使用受信任 CA 签发的证书。自签名证书也能工作,但客户端必须安全地取得并信任该证书;不建议长期使用“跳过证书验证”。 五、创建服务端配置 创建部署目录: sudo install -d -m 700 /opt/sing-box-hy2 cd /opt/sing-box-hy2 生成两个彼此独立的随机密码: openssl rand -base64 32 openssl rand -base64 32 第一个作为用户认证密码,第二个作为 Salamander 混淆密码。不要把密码提交到 Git 仓库,也不要直接放进公开博客。 创建 /opt/sing-box-hy2/config.json: {  "log": {    "level": "info",    "timestamp": true },  "inbounds": [   {      "type": "hysteria2",      "tag": "hy2-in",      "listen": "::",      "listen_port": 8443,      "up_mbps": 100,      "down_mbps": 300,      "obfs": {        "type": "salamander",        "password": "请替换为混淆密码"     },      "users": [       {          "name": "user1",          "password": "请替换为认证密码"       }     ],      "tls": {        "enabled": true,        "server_name": "hy2.example.com",        "alpn": [          "h3"       ],        "certificate_path": "/etc/letsencrypt/live/hy2.example.com/fullchain.pem",        "key_path": "/etc/letsencrypt/live/hy2.example.com/privkey.pem"     }   } ],  "outbounds": [   {      "type": "direct",      "tag": "direct"   } ] } up_mbps 和 down_mbps 是 Hysteria2 拥塞控制使用的最大带宽参数,单位为 Mbps。应按照线路的实际可用带宽设置,不是填写得越大越好。服务端和客户端需要使用合理、匹配的带宽值。 限制配置文件权限: sudo chmod 600 /opt/sing-box-hy2/config.json 六、创建 Docker Compose 文件 创建 /opt/sing-box-hy2/docker-compose.yml: services: sing-box-hy2: image: ghcr.io/sagernet/sing-box:latest container_name: sing-box-hy2 restart: unless-stopped network_mode: host volumes: ./config.json:/etc/sing-box/config.json:ro /etc/letsencrypt:/etc/letsencrypt:ro command: -D /var/lib/sing-box -C /etc/sing-box run logging: driver: json-file options: max-size: 10m max-file: "3" 官方文档使用 ghcr.io/sagernet/sing-box 镜像。生产环境更稳妥的做法是先验证版本,再把 latest 改为经过测试的具体版本标签,避免上游更新后出现不兼容变化。 七、检查并启动服务 先让 sing-box 检查配置语法: cd /opt/sing-box-hy2 sudo docker compose run --rm sing-box-hy2 -C /etc/sing-box check 确认没有错误后启动: sudo docker compose pull sudo docker compose up -d sudo docker compose ps sudo docker compose logs --tail=100 sing-box-hy2 检查 UDP 端口: sudo ss -lunp | grep ':8443' Hysteria2 使用 UDP,不能用浏览器、curl 或普通 TCP 端口检测来判断它是否正常。最可靠的验证方式是查看服务日志并使用配置正确的 Hysteria2 客户端实际连接。 八、客户端怎么配置 官方图形客户端包括: Android:sing-box for Android iOS、macOS、Apple tvOS:sing-box for Apple platforms Windows、Linux:sing-box for Desktop 在图形客户端中新建 Hysteria2 节点时,各字段与服务端的对应关系如下: 客户端字段示例必须与服务端一致Server / 地址hy2.example.com是Port / 端口8443是Password / 认证密码服务端 users[].password是Obfs typesalamander是Obfs password服务端 obfs.password是TLS开启是Server name / SNIhy2.example.com必须匹配证书ALPNh3建议一致Allow insecure关闭生产环境应关闭 保存节点后,选择该配置并开启系统代理或 TUN/VPN 模式。只创建节点但没有启用系统代理或 VPN 模式时,系统流量不会自动经过该节点。 使用 sing-box JSON 客户端 下面是一份适合命令行或桌面端导入的最小客户端示例。它在本机 127.0.0.1:2080 提供 HTTP/SOCKS 混合代理: { "log": { "level": "info", "timestamp": true }, "inbounds": [ { "type": "mixed", "tag": "mixed-in", "listen": "127.0.0.1", "listen_port": 2080 } ], "outbounds": [ { "type": "hysteria2", "tag": "hy2-out", "server": "hy2.example.com", "server_port": 8443, "up_mbps": 20, "down_mbps": 100, "obfs": { "type": "salamander", "password": "请替换为混淆密码" }, "password": "请替换为认证密码", "tls": { "enabled": true, "server_name": "hy2.example.com", "alpn": [ "h3" ] } } ], "route": { "final": "hy2-out", "auto_detect_interface": true } } 启动客户端核心后,把浏览器或操作系统代理设置为: 地址:127.0.0.1 端口:2080 类型:HTTP 或 SOCKS5 如果服务端使用自签名证书 更安全的做法是通过可信渠道把服务端的公开证书 cert.pem 传给客户端,并在客户端 TLS 配置中加入: { "enabled": true, "server_name": "证书中的域名", "certificate_path": "/客户端上的安全路径/cert.pem", "alpn": [ "h3" ] } insecure: true 会接受任意服务器证书,容易受到中间人攻击,只适合短时间排障,不应作为正式配置。 九、日常维护 查看状态和日志: cd /opt/sing-box-hy2 sudo docker compose ps sudo docker compose logs --tail=200 sing-box-hy2 sudo docker stats sing-box-hy2 修改配置后,先检查再重启: sudo docker compose run --rm sing-box-hy2 -C /etc/sing-box check sudo docker compose restart sing-box-hy2 更新镜像: sudo docker compose pull sudo docker compose up -d sudo docker image prune 十、常见问题 客户端一直超时 重点检查云安全组和 UFW 是否放行 8443/udp,以及服务端是否真的监听 UDP 端口。只开放 8443/tcp 没有作用。 出现证书验证失败 检查客户端 server_name 是否与证书域名一致、域名是否解析到正确 IP、证书是否过期。自签名证书还需要客户端显式信任对应证书。 日志提示认证失败 客户端的认证密码必须与服务端 users[].password 完全一致。用户名只是服务端用于区分用户,sing-box 的 Hysteria2 认证核心字段是密码。 开启 Salamander 后无法连接 客户端和服务端的 obfs.type、obfs.password 必须同时一致。排查时可以在两端同时暂时移除 obfs,不能只改一端。 节点连接成功但应用没有流量 确认图形客户端已启用系统代理或 TUN/VPN 模式。若只使用本地 mixed 入站,还需要把应用代理指向 127.0.0.1:2080。 总结 sing-box 是一个通用代理平台,Hysteria2 只是它支持的协议之一。部署成功的关键点并不多:服务端开放正确的 UDP 端口、两端认证与混淆参数一致、TLS 域名匹配,并在客户端真正启用系统代理或 TUN/VPN 模式。生产环境还应固定经过验证的镜像版本、限制日志大小、保护配置权限,并建立证书续期后的重启机制。
  • 主包主包,为什么要单轮请求~

    开源求助
    1
    0赞同
    1帖子
    30浏览
    为啥要单轮请求,不需要上下文吗
  • 可以叫网安的朋友攻击一下吗

    学习路线
    3
    0赞同
    3帖子
    147浏览
    我觉得可以,给主播练技术了
  • 0赞同
    1帖子
    57浏览
    我发现是不是只要我一直发主题,然后再删了,就可以一直加五分
  • 如何两年内达到佬哥的水平,在线求解

    学习路线
    4
    0赞同
    4帖子
    111浏览
    @Hic丶 希望佬哥会喜欢我们的屁股