From 32586349e755ed2d00f91b7f725df3f86b1be7ef Mon Sep 17 00:00:00 2001 From: Mplan Date: Thu, 18 Jun 2026 22:13:52 +0800 Subject: [PATCH] first commit --- .claude/settings.local.json | 1 + README.md | 0 .../.claude/settings.local.json | 7 + weather-station-receive/receive.sh | 154 ++++++++++++++++++ 4 files changed, 162 insertions(+) create mode 100644 .claude/settings.local.json create mode 100644 README.md create mode 100644 weather-station-receive/.claude/settings.local.json create mode 100755 weather-station-receive/receive.sh diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1 @@ +{} diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/weather-station-receive/.claude/settings.local.json b/weather-station-receive/.claude/settings.local.json new file mode 100644 index 0000000..ec39487 --- /dev/null +++ b/weather-station-receive/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(chmod +x *)" + ] + } +} diff --git a/weather-station-receive/receive.sh b/weather-station-receive/receive.sh new file mode 100755 index 0000000..ac04fb4 --- /dev/null +++ b/weather-station-receive/receive.sh @@ -0,0 +1,154 @@ +#!/bin/bash +#=============================================================================== +# 气象站串口数据接收脚本 (Shell 版本) +# 功能等价于原 Python 脚本,读取串口数据 -> CRC 校验 -> 解析 -> 上传 +#=============================================================================== +set -uo pipefail + +#------------------------------------------------------------------------------- +# 配置(修改这里即可,不需要 TOML 文件) +#------------------------------------------------------------------------------- +URL="http://127.0.0.1:7763/v1/upload" +TTY="/dev/ttyUSB0" +BAUD=9600 +WHOLE_LEN=11 +DATA_LEN=7 +CRC_LEN=4 + +echo "========================================" +echo " 气象站串口接收器 (Shell)" +echo "========================================" +echo "串口: $TTY" +echo "波特率: $BAUD" +echo "数据长度: $WHOLE_LEN 字节 (payload=$DATA_LEN, crc=$CRC_LEN)" +echo "服务器: $URL" +echo "========================================" + +#------------------------------------------------------------------------------- +# CRC32 计算(调用 python3 单行 —— 与 binascii.crc32 完全一致) +# 输入: 通过管道传入原始二进制字节 +# 输出: 十进制无符号 CRC32 值 +#------------------------------------------------------------------------------- +crc32() { + python3 -c "import binascii,sys; print(binascii.crc32(sys.stdin.buffer.read()) & 0xFFFFFFFF)" +} + +#------------------------------------------------------------------------------- +# 符号扩展:把 16 位无符号值转为有符号 +#------------------------------------------------------------------------------- +sign_extend16() { + local v=$1 + if (( v >= 32768 )); then + echo $(( v - 65536 )) + else + echo $v + fi +} + +#------------------------------------------------------------------------------- +# 刷新串口输入缓冲区(等价于 Python 的 ser.reset_input_buffer()) +#------------------------------------------------------------------------------- +flush_serial() { + # 临时切换到非阻塞模式,排空所有已缓冲数据 + stty -F "$TTY" -icanon min 0 time 0 2>/dev/null || true + dd if="$TTY" bs=4096 count=1 of=/dev/null 2>/dev/null || true + # 恢复阻塞模式 + stty -F "$TTY" -icanon min 1 time 0 2>/dev/null || true +} + +#------------------------------------------------------------------------------- +# 配置串口 +#------------------------------------------------------------------------------- +stty -F "$TTY" "$BAUD" cs8 -cstopb -parenb raw -echo +echo "串口已配置,开始监听..." + +# 初始清空缓冲区 +flush_serial + +#------------------------------------------------------------------------------- +# 主循环:读帧 → 校验 → 解析 → 上传 +#------------------------------------------------------------------------------- +pkt_count=0 +err_count=0 + +while true; do + # --- 1. 从串口读取完整一帧(阻塞直到凑齐 WHOLE_LEN 字节)--- + tmpfile=$(mktemp) + # dd bs=1 逐字节读取,确保不提前返回(raw 模式下 min=1, time=0) + dd if="$TTY" bs=1 count="$WHOLE_LEN" of="$tmpfile" 2>/dev/null + + # 校验实际读到的字节数 + actual_len=$(stat -c%s "$tmpfile" 2>/dev/null || echo 0) + if (( actual_len != WHOLE_LEN )); then + rm -f "$tmpfile" + continue + fi + + # --- 2. 转为十六进制便于解析 --- + hex=$(od -An -tx1 -v "$tmpfile" | tr -d ' \n') + rm -f "$tmpfile" + + # 安全检查:hex 长度应该等于 WHOLE_LEN * 2 + if (( ${#hex} != WHOLE_LEN * 2 )); then + continue + fi + + # --- 3. 拆分 payload 和 CRC --- + payload_hex="${hex:0:$((DATA_LEN * 2))}" + remote_crc_hex="${hex:$((DATA_LEN * 2)):$((CRC_LEN * 2))}" + remote_crc=$((16#$remote_crc_hex)) + + # --- 4. 计算本地 CRC32 并与远端比对 --- + local_crc=$(echo "$payload_hex" | xxd -r -p 2>/dev/null | crc32) + + if (( remote_crc != local_crc )); then + ((err_count++)) + echo "[错误] CRC 校验失败 #${err_count} — 刷新缓冲区..." + flush_serial + continue + fi + + # --- 5. 解析 payload: >Bhhh (大端: uint8, int16, int16, int16) --- + station_id=$((16#${payload_hex:0:2})) + t_raw=$(( 16#${payload_hex:2:4})) + p_raw=$(( 16#${payload_hex:6:4})) + rh_raw=$(( 16#${payload_hex:10:4})) + + t=$(sign_extend16 $t_raw) + p=$(sign_extend16 $p_raw) + rh=$(sign_extend16 $rh_raw) + + # 定点小数转换(原始值 / 10,保留 1 位小数) + t_val=$(awk "BEGIN {printf \"%.1f\", $t/10}") + p_val=$(awk "BEGIN {printf \"%.1f\", $p/10}") + rh_val=$(awk "BEGIN {printf \"%.1f\", $rh/10}") + + # --- 6. 站台名称 --- + if (( station_id == 1 )); then + station_name="AAU" + else + station_name="unknown" + fi + + ((pkt_count++)) + echo "----------------------------------------" + echo "[数据包 #${pkt_count}] station_id=$station_id ($station_name)" + echo " 温度: $t_val °C | 气压: $p_val hPa | 湿度: $rh_val %" + + # --- 7. HTTP POST 上传 --- + json=$(printf '{"station_name":"%s","temperature":%s,"pressure":%s,"relative_humidity":%s}' \ + "$station_name" "$t_val" "$p_val" "$rh_val") + + http_code=$(curl -s -o /dev/null -w "%{http_code}" \ + -X POST "$URL" \ + -H "Content-Type: application/json" \ + -d "$json" 2>/dev/null || echo "000") + + if [[ "$http_code" == "200" ]] || [[ "$http_code" == "201" ]]; then + echo " 上传成功 (HTTP $http_code)" + else + echo " 上传失败 (HTTP $http_code)" + echo " 数据: $json" + echo " 等待服务器连接..." + fi +done