Files
2026-06-18 22:13:52 +08:00

155 lines
5.5 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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