diff --git a/.secrets.baseline b/.secrets.baseline index 46edd02..42f6749 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1400,12 +1400,19 @@ "line_number": 108, "is_secret": false }, + { + "type": "Hex High Entropy String", + "filename": "pkg/decoder/tagxl/v1/decoder_test.go", + "hashed_secret": "bd38e013e088fd839d5d8f17d0ffd959632e83b1", + "is_verified": false, + "line_number": 126 + }, { "type": "Hex High Entropy String", "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "555917d2175ab1600c6cd926d255df6ad502febc", "is_verified": false, - "line_number": 221, + "line_number": 228, "is_secret": false }, { @@ -1413,7 +1420,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "6c45c0c1b339a7891ba24bf33ef5b40b040a86a0", "is_verified": false, - "line_number": 241, + "line_number": 248, "is_secret": false }, { @@ -1421,7 +1428,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "32179884041e9ddc27e1c5e0e45ccc6e81637d65", "is_verified": false, - "line_number": 366, + "line_number": 373, "is_secret": false }, { @@ -1429,7 +1436,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "10e8fe5b6a5342c5ead45cffec2d001a28e0c1bb", "is_verified": false, - "line_number": 379, + "line_number": 386, "is_secret": false }, { @@ -1437,7 +1444,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "b0771e36dfb55414a423ca9c0ceb087b03ea3cfc", "is_verified": false, - "line_number": 384, + "line_number": 391, "is_secret": false }, { @@ -1445,7 +1452,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "5cbb5bf20d8b56d849c06d5e0474a3cd42e6bc16", "is_verified": false, - "line_number": 389, + "line_number": 396, "is_secret": false }, { @@ -1453,7 +1460,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "632ee27a7e117b634cb0fb234f7f7d199db5d5d1", "is_verified": false, - "line_number": 394, + "line_number": 401, "is_secret": false }, { @@ -1461,7 +1468,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "9e1335b0e4c10057a072e6aa67e5cfb9d0e5d324", "is_verified": false, - "line_number": 399, + "line_number": 406, "is_secret": false }, { @@ -1469,7 +1476,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "55472b68d8a8560add30831739dd3552e63d5b33", "is_verified": false, - "line_number": 442, + "line_number": 449, "is_secret": false }, { @@ -1477,7 +1484,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "463609dcc13b7b90fcf29ca237191ad5bf977c46", "is_verified": false, - "line_number": 453, + "line_number": 460, "is_secret": false }, { @@ -1485,7 +1492,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "4c35a80282e5237761aeb3b9b2c8d422b16df653", "is_verified": false, - "line_number": 465, + "line_number": 472, "is_secret": false }, { @@ -1493,7 +1500,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "5bcf7c2f08e382a84f0a78f1c6aa91f711806aa8", "is_verified": false, - "line_number": 478, + "line_number": 485, "is_secret": false }, { @@ -1501,7 +1508,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "0560cb6af09786d2305b91018ca587c93c0d7dbd", "is_verified": false, - "line_number": 492, + "line_number": 499, "is_secret": false }, { @@ -1509,7 +1516,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "66cd8eba7181b16377a615d793be286a3aacb087", "is_verified": false, - "line_number": 502, + "line_number": 509, "is_secret": false }, { @@ -1517,7 +1524,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "a6c92fb0cd83e9a6f6f2bd5bfdb1a297dfe7a502", "is_verified": false, - "line_number": 514, + "line_number": 521, "is_secret": false }, { @@ -1525,7 +1532,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "c0e35b955de71e6fe09016adf1216ed73f1d7a8b", "is_verified": false, - "line_number": 528, + "line_number": 535, "is_secret": false }, { @@ -1533,7 +1540,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "5a0861255e90d61193afbc62ee5b7924739d1b54", "is_verified": false, - "line_number": 544, + "line_number": 551, "is_secret": false }, { @@ -1541,7 +1548,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "1aa68aee442b8b1c5c9fdca3fc2e18ed2f84a637", "is_verified": false, - "line_number": 562, + "line_number": 569, "is_secret": false }, { @@ -1549,7 +1556,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "bb265a97223c679953c85c60d61907ee7683468e", "is_verified": false, - "line_number": 716, + "line_number": 723, "is_secret": false }, { @@ -1557,7 +1564,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "4df89cb03f258ca60c13bf53e3442d60826bacf7", "is_verified": false, - "line_number": 722, + "line_number": 729, "is_secret": false }, { @@ -1565,7 +1572,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "f5188ea01f60dd0e30b8ff8126123c81f38ba425", "is_verified": false, - "line_number": 733, + "line_number": 740, "is_secret": false }, { @@ -1573,7 +1580,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "164c11e5bb3bdbb53a3682942846936da8006274", "is_verified": false, - "line_number": 745, + "line_number": 752, "is_secret": false }, { @@ -1581,7 +1588,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "39e57284237493c8386cbfebd10364b4f25b86bd", "is_verified": false, - "line_number": 758, + "line_number": 765, "is_secret": false }, { @@ -1589,7 +1596,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "d4fc2a168f60a698eef5c40e42f7147798791b70", "is_verified": false, - "line_number": 772, + "line_number": 779, "is_secret": false }, { @@ -1597,7 +1604,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "0c24951224219592f4f044aa8c1a43cd87d14bae", "is_verified": false, - "line_number": 787, + "line_number": 794, "is_secret": false }, { @@ -1605,7 +1612,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "67dfa780930cf12323bf6d3a2737f8be7168d2e7", "is_verified": false, - "line_number": 798, + "line_number": 805, "is_secret": false }, { @@ -1613,7 +1620,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "b821604371f934e1ce969c042520adc0f69859bf", "is_verified": false, - "line_number": 811, + "line_number": 818, "is_secret": false }, { @@ -1621,7 +1628,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "737544481bcf878548b5d3cef6898ebaaa307e35", "is_verified": false, - "line_number": 826, + "line_number": 833, "is_secret": false }, { @@ -1629,7 +1636,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "56bdd17763f2ca6b25584e70ca4888acd267da77", "is_verified": false, - "line_number": 843, + "line_number": 850, "is_secret": false }, { @@ -1637,7 +1644,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "56f27e2c927e138a36b3cb7d07b942da7667b8f2", "is_verified": false, - "line_number": 868, + "line_number": 875, "is_secret": false }, { @@ -1645,7 +1652,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "516dead2735f9bcd1eced3f678aa6dbb0ed87c86", "is_verified": false, - "line_number": 1198, + "line_number": 1205, "is_secret": false }, { @@ -1653,7 +1660,7 @@ "filename": "pkg/decoder/tagxl/v1/decoder_test.go", "hashed_secret": "884f72cf02528dcd37a031e2af8575273d4394e2", "is_verified": false, - "line_number": 1397, + "line_number": 1404, "is_secret": false } ], @@ -2228,5 +2235,5 @@ } ] }, - "generated_at": "2026-02-23T16:22:43Z" + "generated_at": "2026-04-28T18:24:36Z" } diff --git a/pkg/decoder/tagxl/v1/decoder.go b/pkg/decoder/tagxl/v1/decoder.go index ab5db0c..8674396 100644 --- a/pkg/decoder/tagxl/v1/decoder.go +++ b/pkg/decoder/tagxl/v1/decoder.go @@ -144,7 +144,7 @@ func (t TagXLv1Decoder) getConfig(port uint8, payload []byte) (common.PayloadCon }}, }, TargetType: reflect.TypeOf(Port151Payload{}), - Features: []decoder.Feature{decoder.FeatureDataRate}, + Features: []decoder.Feature{decoder.FeatureDataRate, decoder.FeatureResetReason}, }, nil case 152: if len(payload) < 1 { diff --git a/pkg/decoder/tagxl/v1/decoder_test.go b/pkg/decoder/tagxl/v1/decoder_test.go index 9fc7e42..6acad35 100644 --- a/pkg/decoder/tagxl/v1/decoder_test.go +++ b/pkg/decoder/tagxl/v1/decoder_test.go @@ -1562,6 +1562,14 @@ func TestFeatures(t *testing.T) { // call function to check if it panics sequenceNumber.GetSequenceNumber() } + if decodedPayload.Is(decoder.FeatureResetReason) { + resetReason, ok := decodedPayload.Data.(decoder.UplinkFeatureResetReason) + if !ok { + t.Fatalf("expected UplinkFeatureResetReason, got %T", decodedPayload) + } + // call function to check if it panics + resetReason.GetResetReason() + } }) } } @@ -1646,3 +1654,60 @@ func TestNewTagXLv1DecoderSolver(t *testing.T) { } } } + +func TestPort151ResetReason(t *testing.T) { + tests := []struct { + name string + cause *uint32 + expected decoder.ResetReason + }{ + {name: "nil", cause: nil, expected: decoder.ResetReasonUnknown}, + {name: "zero", cause: helpers.Uint32Ptr(0x00000000), expected: decoder.ResetReasonUnknown}, + {name: "VREGIN-only filtered", cause: helpers.Uint32Ptr(0x80000000), expected: decoder.ResetReasonUnknown}, + {name: "POR", cause: helpers.Uint32Ptr(0x00000001), expected: decoder.ResetReasonPowerReset}, + {name: "PIN", cause: helpers.Uint32Ptr(0x00000002), expected: decoder.ResetReasonPinReset}, + {name: "WDOG0", cause: helpers.Uint32Ptr(0x00000008), expected: decoder.ResetReasonWatchdog}, + {name: "SYSREQ", cause: helpers.Uint32Ptr(0x00000040), expected: decoder.ResetReasonSystemReset}, + {name: "LOCKUP unmapped bit", cause: helpers.Uint32Ptr(0x00000020), expected: decoder.ResetReasonOtherReset}, + {name: "POR+PIN priority", cause: helpers.Uint32Ptr(0x00000003), expected: decoder.ResetReasonPowerReset}, + {name: "PIN+WDOG0 priority", cause: helpers.Uint32Ptr(0x0000000a), expected: decoder.ResetReasonPinReset}, + {name: "WDOG0+VREGIN", cause: helpers.Uint32Ptr(0x80000008), expected: decoder.ResetReasonWatchdog}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := Port151Payload{ResetCause: tc.cause}.GetResetReason() + if got != tc.expected { + t.Errorf("expected %v, got %v", tc.expected, got) + } + }) + } +} + +func TestPort151ResetReasonFromDecode(t *testing.T) { + d := NewTagXLv1Decoder(context.TODO(), solver.MockSolverV1{ + Data: decoder.NewDecodedUplink([]decoder.Feature{decoder.FeatureWiFi}, Port197Payload{}), + }, zap.NewExample()) + + // Real-world payload taken from the existing TestDecode fixtures: contains + // 4a 04 00 00 00 02 (PIN reset). + const payload = "4c2a0940010f4104012c1c204204012c05dc43010644011e45020d4e4604f6c7d8104902000a4a0400000002" + + decodedUplink, err := d.Decode(context.TODO(), payload, 151) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if !decodedUplink.Is(decoder.FeatureResetReason) { + t.Fatalf("expected FeatureResetReason to be set") + } + + resetReason, ok := decodedUplink.Data.(decoder.UplinkFeatureResetReason) + if !ok { + t.Fatalf("expected UplinkFeatureResetReason, got %T", decodedUplink.Data) + } + + if got := resetReason.GetResetReason(); got != decoder.ResetReasonPinReset { + t.Errorf("expected %v, got %v", decoder.ResetReasonPinReset, got) + } +} diff --git a/pkg/decoder/tagxl/v1/port151.go b/pkg/decoder/tagxl/v1/port151.go index f9af34e..fcce31f 100644 --- a/pkg/decoder/tagxl/v1/port151.go +++ b/pkg/decoder/tagxl/v1/port151.go @@ -66,6 +66,7 @@ type Port151Payload struct { var _ decoder.UplinkFeatureBattery = &Port151Payload{} var _ decoder.UplinkFeatureConfig = &Port151Payload{} var _ decoder.UplinkFeatureFirmwareVersion = &Port151Payload{} +var _ decoder.UplinkFeatureResetReason = &Port151Payload{} func (p Port151Payload) GetBatteryVoltage() float64 { if p.Battery == nil { @@ -178,6 +179,42 @@ func (p Port151Payload) GetFirmwareVersion() *string { return nil } +// EFR32BG22 EMU_RSTCAUSE bit masks reported by the Tag XL firmware. +// See tag_xl/board/trackit/BSP/util/util.c::print_reset_cause and the +// EFR32xG22 reference manual. Bit 31 (VREGIN) is filtered out by the +// firmware before transmission; we mask defensively in case of replay. +const ( + tagXLResetCauseMask uint32 = 0x7FFFFFFF + tagXLResetCausePOR uint32 = 0x00000001 + tagXLResetCausePIN uint32 = 0x00000002 + tagXLResetCauseWDOG0 uint32 = 0x00000008 + tagXLResetCauseSYSREQ uint32 = 0x00000040 +) + +func (p Port151Payload) GetResetReason() decoder.ResetReason { + if p.ResetCause == nil { + return decoder.ResetReasonUnknown + } + + cause := *p.ResetCause & tagXLResetCauseMask + if cause == 0 { + return decoder.ResetReasonUnknown + } + + switch { + case cause&tagXLResetCausePOR != 0: + return decoder.ResetReasonPowerReset + case cause&tagXLResetCausePIN != 0: + return decoder.ResetReasonPinReset + case cause&tagXLResetCauseWDOG0 != 0: + return decoder.ResetReasonWatchdog + case cause&tagXLResetCauseSYSREQ != 0: + return decoder.ResetReasonSystemReset + default: + return decoder.ResetReasonOtherReset + } +} + // DataRateFromUint8 converts a uint8 data rate value to the corresponding TagXL DataRate enum. func DataRateFromUint8(value uint8) decoder.DataRate { switch value {