weather-station/model/protocol.go

815 lines
21 KiB
Go
Raw 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.

package model
import (
"fmt"
)
// Protocol 定义协议结构
type Protocol struct {
RawData []byte
}
// NewProtocol 创建新的协议实例
func NewProtocol(data []byte) *Protocol {
return &Protocol{
RawData: data,
}
}
// IdentifyTxType 解析传输类型第一个字节bit 0-7
func (p *Protocol) IdentifyTxType() (binary string, hexVal string, decVal uint8) {
if len(p.RawData) == 0 {
return "", "", 0
}
// 获取第一个字节
firstByte := p.RawData[0]
// 转换为二进制字符串8位
binary = fmt.Sprintf("%08b", firstByte)
// 转换为十六进制字符串
hexVal = fmt.Sprintf("%02X", firstByte)
// 十进制值
decVal = firstByte
return binary, hexVal, decVal
}
// IDParts 存储ID的三个部分
type IDParts struct {
HSB struct {
Binary string
Hex string
Dec uint8
}
MSB struct {
Binary string
Hex string
Dec uint8
}
LSB struct {
Binary string
Hex string
Dec uint8
}
Complete struct {
Binary string
Hex string
Dec uint32
}
}
// GetCompleteID 获取完整的24-bit ID code
// HSB: bit 168-175 (索引21)
// MSB: bit 176-183 (索引22)
// LSB: bit 8-15 (索引1)
func (p *Protocol) GetCompleteID() (*IDParts, error) {
if len(p.RawData) < 23 { // 确保有足够的数据
return nil, fmt.Errorf("insufficient data length")
}
result := &IDParts{}
// 处理 HSB (bit 168-175, 索引21)
hsbByte := p.RawData[21]
result.HSB.Binary = fmt.Sprintf("%08b", hsbByte)
result.HSB.Hex = fmt.Sprintf("%02X", hsbByte)
result.HSB.Dec = hsbByte
// 处理 MSB (bit 176-183, 索引22)
msbByte := p.RawData[22]
result.MSB.Binary = fmt.Sprintf("%08b", msbByte)
result.MSB.Hex = fmt.Sprintf("%02X", msbByte)
result.MSB.Dec = msbByte
// 处理 LSB (bit 8-15, 索引1)
lsbByte := p.RawData[1]
result.LSB.Binary = fmt.Sprintf("%08b", lsbByte)
result.LSB.Hex = fmt.Sprintf("%02X", lsbByte)
result.LSB.Dec = lsbByte
// 组合完整的24位ID
completeID := uint32(hsbByte)<<16 | uint32(msbByte)<<8 | uint32(lsbByte)
result.Complete.Binary = fmt.Sprintf("%024b", completeID)
result.Complete.Hex = fmt.Sprintf("%06X", completeID)
result.Complete.Dec = completeID
return result, nil
}
// WindDirection 存储风向的三个部分
type WindDirection struct {
DirH struct {
Binary string // 4位二进制格式为"000X"其中X是bit24
Value uint8 // 实际值
}
DirM struct {
Binary string // bit 16-19
Value uint8
}
DirL struct {
Binary string // bit 20-23
Value uint8
}
Complete struct {
Binary string // 完整的12位二进制
Value uint16 // 完整值 (Range: 0°- 359°, Invalid: 0x1FF)
Degree float64 // 角度值
IsValid bool // 是否有效
}
}
// GetWindDirection 解析风向数据
// DIR_H: 4位 (000 + bit24)
// DIR_M: bit 16-19
// DIR_L: bit 20-23
// Value in hex (Range: 0°- 359°)
// If invalid fill with 0x1FF
func (p *Protocol) GetWindDirection() (*WindDirection, error) {
if len(p.RawData) < 4 { // 确保有足够的数据
return nil, fmt.Errorf("insufficient data length")
}
result := &WindDirection{}
// 获取包含bit24的字节索引3
byte3 := p.RawData[3]
// 获取bit24字节的最低位
bit24 := byte3 & 0x01
// 构造DIR_H: "000" + bit24
result.DirH.Binary = fmt.Sprintf("000%d", bit24)
result.DirH.Value = bit24
// 获取包含bit16-23的字节索引2
byte2 := p.RawData[2]
// 获取DIR_M (bit 16-19)
dirM := (byte2 >> 4) & 0x0F
result.DirM.Binary = fmt.Sprintf("%04b", dirM)
result.DirM.Value = dirM
// 获取DIR_L (bit 20-23)
dirL := byte2 & 0x0F
result.DirL.Binary = fmt.Sprintf("%04b", dirL)
result.DirL.Value = dirL
// 组合完整的风向值12位
completeDir := (uint16(bit24) << 8) | (uint16(dirM) << 4) | uint16(dirL)
result.Complete.Binary = fmt.Sprintf("%012b", completeDir)
result.Complete.Value = completeDir
// 检查值是否在有效范围内 (0-359)
if completeDir > 359 {
result.Complete.Value = 0x1FF
result.Complete.IsValid = false
result.Complete.Degree = 0
} else {
result.Complete.IsValid = true
result.Complete.Degree = float64(completeDir)
}
return result, nil
}
// WindSpeed 存储风速的各个部分
type WindSpeed struct {
WspFlag struct {
Binary string // bit 25
Value bool // true = 9bit模式, false = 10bit模式
}
Extend struct {
Binary string // 9bit模式: 000 + bit27
// 10bit模式: 00 + bit26 + bit27
Value uint8
}
WspH struct {
Binary string // bit 48-51
Value uint8
}
WspL struct {
Binary string // bit 52-55
Value uint8
}
Complete struct {
Binary string // 完整的9位或10位二进制
RawValue uint16 // 原始值
Value float64 // 实际风速值 (计算公式: RawValue/8*0.51)
}
}
// GetWindSpeed 解析风速数据
// WSP_FLAG: bit 25
// WIND_EXTEND:
// - 当WSP_FLAG=1时000 + bit279bit模式
// - 当WSP_FLAG=0时0 + bit136 + bit26 + bit2710bit模式
//
// WIND_H: bit 48-51
// WIND_L: bit 52-55
// 实际风速计算公式: value/8*0.51
func (p *Protocol) GetWindSpeed() (*WindSpeed, error) {
if len(p.RawData) < 18 { // 确保有足够的数据需要读取到bit136
return nil, fmt.Errorf("insufficient data length")
}
result := &WindSpeed{}
// 解析 WSP_FLAG (bit 25)
byte3 := p.RawData[3]
wspFlag := (byte3 >> 1) & 0x01 // bit 25
result.WspFlag.Binary = fmt.Sprintf("%d", wspFlag)
result.WspFlag.Value = wspFlag == 1
// 获取bit26和bit27
bit26 := (byte3 >> 2) & 0x01
bit27 := (byte3 >> 3) & 0x01
// 获取bit136在第17个字节的最高位
byte17 := p.RawData[17]
bit136 := (byte17 >> 7) & 0x01
// 解析 WIND_H 和 WIND_L (byte6)
byte6 := p.RawData[6]
windH := (byte6 >> 4) & 0x0F
windL := byte6 & 0x0F
result.WspH.Binary = fmt.Sprintf("%04b", windH)
result.WspH.Value = windH
result.WspL.Binary = fmt.Sprintf("%04b", windL)
result.WspL.Value = windL
// 组合完整的风速值
var rawValue uint16
if result.WspFlag.Value {
// 9bit模式000 + bit27 + WIND_H + WIND_L
rawValue = (uint16(bit27) << 8) | (uint16(windH) << 4) | uint16(windL)
result.Complete.Binary = fmt.Sprintf("%09b", rawValue)
} else {
// 10bit模式0 + bit136 + bit26 + bit27 + WIND_H + WIND_L
extendBits := (uint16(0) << 3) | (uint16(bit136) << 2) | (uint16(bit26) << 1) | uint16(bit27)
rawValue = (uint16(extendBits) << 8) | (uint16(windH) << 4) | uint16(windL)
result.Complete.Binary = fmt.Sprintf("%010b", rawValue)
}
result.Complete.RawValue = rawValue
// 计算实际风速值value/8*0.51
result.Complete.Value = float64(rawValue) / 8.0 * 0.51
return result, nil
}
// Temperature 存储温度的三个部分
type Temperature struct {
TmpH struct {
Binary string // 3位二进制格式为"0XXX"其中XXX是bit29-31
Value uint8
}
TmpM struct {
Binary string // bit 32-35
Value uint8
}
TmpL struct {
Binary string // bit 36-39
Value uint8
}
Complete struct {
Binary string // 完整的11位二进制
RawValue uint16 // 原始值包含400的偏移
Value float64 // 实际温度值 (Range: -40.0°C -> 60.0°C)
IsValid bool // 是否有效
}
}
// GetTemperature 解析温度数据
// TMP_H: 4位 (0 + bit29-31)
// TMP_M: bit 32-35
// TMP_L: bit 36-39
// 温度计算公式:(RawValue-400)/10
// 示例:
// 10.5°C = 0x1F9 (505-400)/10 = 10.5
// -10.5°C = 0x127 (295-400)/10 = -10.5
// 范围:-40.0°C -> 60.0°C
// 无效值0x7FF
func (p *Protocol) GetTemperature() (*Temperature, error) {
if len(p.RawData) < 5 { // 确保有足够的数据
return nil, fmt.Errorf("insufficient data length")
}
result := &Temperature{}
// 获取包含bit29-31的字节索引3
byte3 := p.RawData[3]
// 直接获取bit29-31 (010)
tmpHBits := byte3 & 0x07
// TMP_H 是 "0" + bit29-31
result.TmpH.Binary = fmt.Sprintf("0%03b", tmpHBits)
result.TmpH.Value = tmpHBits
// 获取包含bit32-39的字节索引4
byte4 := p.RawData[4]
// 获取TMP_M (bit 32-35)在byte4的高4位
tmpM := (byte4 >> 4) & 0x0F
result.TmpM.Binary = fmt.Sprintf("%04b", tmpM)
result.TmpM.Value = tmpM
// 获取TMP_L (bit 36-39)在byte4的低4位
tmpL := byte4 & 0x0F
result.TmpL.Binary = fmt.Sprintf("%04b", tmpL)
result.TmpL.Value = tmpL
// 组合完整的温度值
// 1. TMP_H (0 + bit29-31) 放在最高位
// 2. TMP_M (bit 32-35) 放在中间
// 3. TMP_L (bit 36-39) 放在最低位
completeTemp := uint16(0)
completeTemp |= uint16(tmpHBits) << 8 // TMP_H 移到高8位
completeTemp |= uint16(tmpM) << 4 // TMP_M 移到中间4位
completeTemp |= uint16(tmpL) // TMP_L 在最低4位
result.Complete.Binary = fmt.Sprintf("%012b", completeTemp)
result.Complete.RawValue = completeTemp
// 检查温度是否在有效范围内
// 有效范围计算:
// -40°C = (-40 * 10 + 400) = 0
// 60°C = (60 * 10 + 400) = 1000
if completeTemp > 1000 { // 超出范围
result.Complete.RawValue = 0x7FF // 无效值
result.Complete.Value = 0
result.Complete.IsValid = false
} else {
// 温度计算:(RawValue-400)/10
result.Complete.Value = (float64(completeTemp) - 400) / 10
result.Complete.IsValid = true
}
return result, nil
}
// Humidity 存储湿度的两个部分
type Humidity struct {
HmH struct {
Binary string // bit 40-43
Value uint8
}
HmL struct {
Binary string // bit 44-47
Value uint8
}
Complete struct {
Binary string // 完整的8位二进制
RawValue uint8 // 原始值(十六进制)
Value uint8 // 实际湿度值 (Range: 1% - 99%)
IsValid bool // 是否有效
}
}
// GetHumidity 解析湿度数据
// HM_H: bit 40-43
// HM_L: bit 44-47
// Range: 1% - 99%
// If invalid fill with 0xFF
// 示例0x37 = 55% (3*16 + 7 = 55)
func (p *Protocol) GetHumidity() (*Humidity, error) {
if len(p.RawData) < 6 { // 确保有足够的数据bit 47 在第6个字节内
return nil, fmt.Errorf("insufficient data length")
}
result := &Humidity{}
// 获取包含bit40-47的字节索引5
byte5 := p.RawData[5]
// 获取HM_H (bit 40-43)
hmH := (byte5 >> 4) & 0x0F
result.HmH.Binary = fmt.Sprintf("%04b", hmH)
result.HmH.Value = hmH
// 获取HM_L (bit 44-47)
hmL := byte5 & 0x0F
result.HmL.Binary = fmt.Sprintf("%04b", hmL)
result.HmL.Value = hmL
// 原始十六进制值
result.Complete.Binary = fmt.Sprintf("%08b", byte5)
result.Complete.RawValue = byte5
// 直接使用十六进制值转换为十进制作为湿度值
decimalValue := byte5
// 检查湿度是否在有效范围内 (1-99)
if decimalValue < 1 || decimalValue > 99 {
result.Complete.RawValue = 0xFF // 无效值
result.Complete.Value = 0
result.Complete.IsValid = false
} else {
result.Complete.Value = decimalValue
result.Complete.IsValid = true
}
return result, nil
}
// GustSpeed 存储阵风速度数据
type GustSpeed struct {
GustH struct {
Binary string // bit 56-59
Value uint8
}
GustL struct {
Binary string // bit 60-63
Value uint8
}
Complete struct {
Binary string // 完整的8位二进制
RawValue uint8 // 原始值
Value float64 // 实际阵风速度值 (计算公式: RawValue*0.51)
}
}
// GetGustSpeed 解析阵风速度数据
// GUST_H: bit 56-59
// GUST_L: bit 60-63
// 实际阵风速度计算公式: value*0.51
func (p *Protocol) GetGustSpeed() (*GustSpeed, error) {
if len(p.RawData) < 8 { // 确保有足够的数据bit 63 在第8个字节内
return nil, fmt.Errorf("insufficient data length")
}
result := &GustSpeed{}
// 解析 GUST_H (bit 56-59) 和 GUST_L (bit 60-63)
byte7 := p.RawData[7]
gustH := (byte7 >> 4) & 0x0F
gustL := byte7 & 0x0F
result.GustH.Binary = fmt.Sprintf("%04b", gustH)
result.GustH.Value = gustH
result.GustL.Binary = fmt.Sprintf("%04b", gustL)
result.GustL.Value = gustL
// 组合完整的阵风速度值
rawValue := byte7
result.Complete.Binary = fmt.Sprintf("%08b", rawValue)
result.Complete.RawValue = rawValue
// 计算实际阵风速度值value*0.51
result.Complete.Value = float64(rawValue) * 0.51
return result, nil
}
// Rainfall 存储降雨量数据
type Rainfall struct {
RainHH struct {
Binary string // bit 64-67
Value uint8
}
RainHL struct {
Binary string // bit 68-71
Value uint8
}
RainLH struct {
Binary string // bit 72-75
Value uint8
}
RainLL struct {
Binary string // bit 76-79
Value uint8
}
Complete struct {
Binary string // 完整的16位二进制
RawValue uint16 // 原始值
Value float64 // 实际降雨量值 (计算公式: RawValue*0.254)
}
}
// GetRainfall 解析降雨量数据
// RAIN_HH: bit 64-67
// RAIN_HL: bit 68-71
// RAIN_LH: bit 72-75
// RAIN_LL: bit 76-79
// 实际降雨量计算公式: value*0.254
func (p *Protocol) GetRainfall() (*Rainfall, error) {
if len(p.RawData) < 10 { // 确保有足够的数据bit 79 在第10个字节内
return nil, fmt.Errorf("insufficient data length")
}
result := &Rainfall{}
// 解析 RAIN_HH 和 RAIN_HL (byte8)
byte8 := p.RawData[8]
rainHH := (byte8 >> 4) & 0x0F
rainHL := byte8 & 0x0F
result.RainHH.Binary = fmt.Sprintf("%04b", rainHH)
result.RainHH.Value = rainHH
result.RainHL.Binary = fmt.Sprintf("%04b", rainHL)
result.RainHL.Value = rainHL
// 解析 RAIN_LH 和 RAIN_LL (byte9)
byte9 := p.RawData[9]
rainLH := (byte9 >> 4) & 0x0F
rainLL := byte9 & 0x0F
result.RainLH.Binary = fmt.Sprintf("%04b", rainLH)
result.RainLH.Value = rainLH
result.RainLL.Binary = fmt.Sprintf("%04b", rainLL)
result.RainLL.Value = rainLL
// 组合完整的降雨量值
rawValue := (uint16(rainHH) << 12) | (uint16(rainHL) << 8) | (uint16(rainLH) << 4) | uint16(rainLL)
result.Complete.Binary = fmt.Sprintf("%016b", rawValue)
result.Complete.RawValue = rawValue
// 计算实际降雨量值value*0.254
result.Complete.Value = float64(rawValue) * 0.254
return result, nil
}
// UVIndex 存储紫外线指数数据
type UVIndex struct {
UviHH struct {
Binary string // bit 80-83
Value uint8
}
UviHL struct {
Binary string // bit 84-87
Value uint8
}
UviLH struct {
Binary string // bit 88-91
Value uint8
}
UviLL struct {
Binary string // bit 92-95
Value uint8
}
Complete struct {
Binary string // 完整的16位二进制
RawValue uint16 // 原始值
Value float64 // 实际紫外线值 (单位: uW/c㎡)
IsValid bool // 是否有效
}
}
// GetUVIndex 解析紫外线指数数据
// UVI_HH: bit 80-83
// UVI_HL: bit 84-87
// UVI_LH: bit 88-91
// UVI_LL: bit 92-95
// Range: 0 uW/c㎡ to 20000 uW/c㎡
// If invalid fill with 0xFFFF
func (p *Protocol) GetUVIndex() (*UVIndex, error) {
if len(p.RawData) < 12 { // 确保有足够的数据bit 95 在第12个字节内
return nil, fmt.Errorf("insufficient data length")
}
result := &UVIndex{}
// 解析 UVI_HH 和 UVI_HL (byte10)
byte10 := p.RawData[10]
uviHH := (byte10 >> 4) & 0x0F
uviHL := byte10 & 0x0F
result.UviHH.Binary = fmt.Sprintf("%04b", uviHH)
result.UviHH.Value = uviHH
result.UviHL.Binary = fmt.Sprintf("%04b", uviHL)
result.UviHL.Value = uviHL
// 解析 UVI_LH 和 UVI_LL (byte11)
byte11 := p.RawData[11]
uviLH := (byte11 >> 4) & 0x0F
uviLL := byte11 & 0x0F
result.UviLH.Binary = fmt.Sprintf("%04b", uviLH)
result.UviLH.Value = uviLH
result.UviLL.Binary = fmt.Sprintf("%04b", uviLL)
result.UviLL.Value = uviLL
// 组合完整的紫外线值
rawValue := (uint16(uviHH) << 12) | (uint16(uviHL) << 8) | (uint16(uviLH) << 4) | uint16(uviLL)
result.Complete.Binary = fmt.Sprintf("%016b", rawValue)
result.Complete.RawValue = rawValue
// 检查是否在有效范围内 (0-20000)
if rawValue > 20000 {
result.Complete.RawValue = 0xFFFF
result.Complete.Value = 0
result.Complete.IsValid = false
} else {
result.Complete.Value = float64(rawValue)
result.Complete.IsValid = true
}
return result, nil
}
// Light 存储光照数据
type Light struct {
LightHH struct {
Binary string // bit 96-99
Value uint8
}
LightHL struct {
Binary string // bit 100-103
Value uint8
}
LightMH struct {
Binary string // bit 104-107
Value uint8
}
LightML struct {
Binary string // bit 108-111
Value uint8
}
LightLH struct {
Binary string // bit 112-115
Value uint8
}
LightLL struct {
Binary string // bit 116-119
Value uint8
}
Complete struct {
Binary string // 完整的24位二进制
RawValue uint32 // 原始值
Value float64 // 实际光照值 (计算公式: RawValue/10) (单位: lux)
IsValid bool // 是否有效
}
}
// GetLight 解析光照数据
// bit 96-119 (byte12-14: 00 04 9C)
// 实际光照计算公式: value/10
// Range: 0.0 lux -> 300,000.0 lux
// If invalid fill with 0xFFFFFF
func (p *Protocol) GetLight() (*Light, error) {
if len(p.RawData) < 15 { // 确保有足够的数据
return nil, fmt.Errorf("insufficient data length")
}
result := &Light{}
// 获取三个字节 (00 04 9C)
byte1 := p.RawData[12] // 00
byte2 := p.RawData[13] // 04
byte3 := p.RawData[14] // 9C
// 组合完整的光照值 (00 04 9C)
rawValue := (uint32(byte1) << 16) | (uint32(byte2) << 8) | uint32(byte3)
result.Complete.Binary = fmt.Sprintf("%024b", rawValue)
result.Complete.RawValue = rawValue
// 检查是否在有效范围内 (0-3000000, 因为实际值要除以10)
if rawValue > 3000000 {
result.Complete.RawValue = 0xFFFFFF
result.Complete.Value = 0
result.Complete.IsValid = false
} else {
result.Complete.Value = float64(rawValue) / 10.0
result.Complete.IsValid = true
}
return result, nil
}
// Pressure 存储大气压数据
type Pressure struct {
PressureH struct {
Binary string // bit 143-144 (补前导000)
Value uint8
}
PressureM struct {
Binary string // bit 145-151
Value uint8
}
PressureL struct {
Binary string // bit 152-159
Value uint8
}
Complete struct {
Binary string // 完整的17位二进制
RawValue uint32 // 原始值
Value float64 // 实际气压值 (计算公式: RawValue/100) (单位: hPa)
IsValid bool // 是否有效
}
}
// GetPressure 解析大气压数据
// 17位值组成000 + bit143 + bit144-159
// 实际气压计算公式: value/100
// Range: 300.00 hPa -> 1200.00 hPa
// If invalid fill with 0x1FFFF
// Example: 0x018A9E = 1010.22 hPa
func (p *Protocol) GetPressure() (*Pressure, error) {
if len(p.RawData) < 20 { // 确保有足够的数据
return nil, fmt.Errorf("insufficient data length")
}
result := &Pressure{}
// 获取三个字节 (01 88 F5)
byte1 := p.RawData[17] // 01
byte2 := p.RawData[18] // 88
byte3 := p.RawData[19] // F5
// 组合完整的气压值
rawValue := (uint32(byte1) << 16) | (uint32(byte2) << 8) | uint32(byte3)
result.Complete.Binary = fmt.Sprintf("%024b", rawValue)
result.Complete.RawValue = rawValue
// 检查是否在有效范围内 (30000-120000因为实际值要除以100)
if rawValue < 30000 || rawValue > 120000 {
result.Complete.RawValue = 0x1FFFF
result.Complete.Value = 0
result.Complete.IsValid = false
} else {
result.Complete.Value = float64(rawValue) / 100.0
result.Complete.IsValid = true
}
return result, nil
}
// RS485Protocol 定义RS485协议结构
type RS485Protocol struct {
RawData []byte
}
// NewRS485Protocol 创建新的RS485协议实例
func NewRS485Protocol(data []byte) *RS485Protocol {
return &RS485Protocol{
RawData: data,
}
}
// ValidateRS485Data 验证RS485数据是否有效
func ValidateRS485Data(data []byte) bool {
// 检查数据长度是否为25字节
if len(data) != 25 {
return false
}
// 检查起始字节是否为0x24
if data[0] != 0x24 {
return false
}
return true
}
// RS485WeatherData 存储RS485气象数据
type RS485WeatherData struct {
Temperature float64 // 温度
Humidity float64 // 湿度
WindSpeed float64 // 风速
WindDirection float64 // 风向
Rainfall float64 // 雨量
Light float64 // 光照
UV float64 // 紫外线
Pressure float64 // 气压
}
// ParseRS485Data 解析RS485数据
func (p *RS485Protocol) ParseRS485Data() (*RS485WeatherData, error) {
if !ValidateRS485Data(p.RawData) {
return nil, fmt.Errorf("无效的RS485数据格式")
}
data := &RS485WeatherData{}
// 解析温度 (索引5-6)
tempRaw := int16(p.RawData[5])<<8 | int16(p.RawData[6])
data.Temperature = float64(tempRaw) / 10.0
// 解析湿度 (索引7-8)
humRaw := int16(p.RawData[7])<<8 | int16(p.RawData[8])
data.Humidity = float64(humRaw) / 10.0
// 解析风速 (索引9-10)
windSpeedRaw := int16(p.RawData[9])<<8 | int16(p.RawData[10])
data.WindSpeed = float64(windSpeedRaw) / 10.0
// 解析风向 (索引11-12)
windDirRaw := int16(p.RawData[11])<<8 | int16(p.RawData[12])
data.WindDirection = float64(windDirRaw)
// 解析雨量 (索引13-14)
rainRaw := int16(p.RawData[13])<<8 | int16(p.RawData[14])
data.Rainfall = float64(rainRaw) / 10.0
// 解析光照 (索引15-16)
lightRaw := int16(p.RawData[15])<<8 | int16(p.RawData[16])
data.Light = float64(lightRaw)
// 解析紫外线 (索引17-18)
uvRaw := int16(p.RawData[17])<<8 | int16(p.RawData[18])
data.UV = float64(uvRaw) / 10.0
// 解析气压 (索引19-20)
pressureRaw := int16(p.RawData[19])<<8 | int16(p.RawData[20])
data.Pressure = float64(pressureRaw) / 10.0
return data, nil
}