@bradtech/sensor-lorawan 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HOWTO.md +99 -0
- package/LICENSE +620 -0
- package/README.md +27 -0
- package/dist/BradOSCodec.d.ts +68 -0
- package/dist/BradOSCodec.d.ts.map +1 -0
- package/dist/BradOSCodec.js +123 -0
- package/dist/BradOSCodec.js.map +1 -0
- package/dist/LoRaWanPipeline.d.ts +142 -0
- package/dist/LoRaWanPipeline.d.ts.map +1 -0
- package/dist/LoRaWanPipeline.js +280 -0
- package/dist/LoRaWanPipeline.js.map +1 -0
- package/dist/defaultAdapters.d.ts +49 -0
- package/dist/defaultAdapters.d.ts.map +1 -0
- package/dist/defaultAdapters.js +70 -0
- package/dist/defaultAdapters.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
- package/src/BradOSCodec.ts +165 -0
- package/src/LoRaWanPipeline.ts +443 -0
- package/src/defaultAdapters.ts +82 -0
- package/src/index.ts +3 -0
package/HOWTO.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# HOWTO: Using `@bradtech/sensor-lorawan`
|
|
2
|
+
|
|
3
|
+
This guide explains how to process ChirpStack LoRaWAN uplinks into calibrated DataPoints with `LoRaWanPipeline`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🏛️ Architecture: Decoupling Radio Frame from Agronomic Soil Models
|
|
8
|
+
|
|
9
|
+
> [!IMPORTANT]
|
|
10
|
+
> **Payload Agnosticism**: A LoRaWAN uplink packet is strictly hardware and radio-centric (devEUI, FPort, frame counter, RSSI, and raw IEEE 754 Float32 sensor readings). A physical probe **never** knows what parcel or soil type it is installed in.
|
|
11
|
+
>
|
|
12
|
+
> **Backoffice Agronomic Context**: The Backoffice database assigns a physical probe (`devEUI`) to an agricultural parcel (`Plot`), and stores the plot's soil profile (e.g. `clay`, `sand`, `loam`) along with optional laboratory calibration models ($y = a \cdot x + b$).
|
|
13
|
+
>
|
|
14
|
+
> When ingesting an uplink, the Telemetry Worker retrieves the plot's `AgronomicPlotContext` from the database/cache and injects it into `LoRaWanPipeline.process(uplink, agronomicContext)`.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Processing a ChirpStack Uplink Message into DataPoints
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import {
|
|
22
|
+
LoRaWanPipeline,
|
|
23
|
+
type PipelineUplinkInput,
|
|
24
|
+
type AgronomicPlotContext,
|
|
25
|
+
} from '@bradtech/sensor-lorawan'
|
|
26
|
+
|
|
27
|
+
// 1. Raw JSON uplink payload received via MQTT from ChirpStack
|
|
28
|
+
const uplinkMessage: PipelineUplinkInput = {
|
|
29
|
+
deviceInfo: {
|
|
30
|
+
deviceName: 'b25s004',
|
|
31
|
+
devEui: '8c1f640000000004',
|
|
32
|
+
},
|
|
33
|
+
fPort: 12, // Soil Moisture @ 10cm depth (Raw Capacitance / Dielectric value)
|
|
34
|
+
fCnt: 142,
|
|
35
|
+
data: 'AAAAQEF', // Base64 encoded Float32 LE (e.g. 25.4)
|
|
36
|
+
rxInfo: [
|
|
37
|
+
{
|
|
38
|
+
gatewayId: '0016c001f1122334',
|
|
39
|
+
rssi: -82,
|
|
40
|
+
snr: 9.2,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
txInfo: {
|
|
44
|
+
frequency: 868100000,
|
|
45
|
+
dataRate: 0, // SF12 / 125kHz
|
|
46
|
+
},
|
|
47
|
+
publishedAt: '2026-08-30T18:00:00Z',
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. Agronomic Plot Context resolved from Backoffice / MDM database
|
|
51
|
+
const agronomicContext: AgronomicPlotContext = {
|
|
52
|
+
plot: 'plots/parcelle-saint-emilion-01',
|
|
53
|
+
company: 'companies/chateau-alpha',
|
|
54
|
+
soilTexture: 'clay',
|
|
55
|
+
soilLinearRegression: {
|
|
56
|
+
slope: 1.05,
|
|
57
|
+
intercept: -0.8,
|
|
58
|
+
modelLabel: 'Lab-Pedo-2026',
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 3. Ingest and transform
|
|
63
|
+
const dataPoints = LoRaWanPipeline.process(uplinkMessage, agronomicContext)
|
|
64
|
+
|
|
65
|
+
for (const dp of dataPoints) {
|
|
66
|
+
console.log(`[${dp.kind.toUpperCase()}] ${dp.metric}: ${dp.value} ${dp.unit}`)
|
|
67
|
+
console.log(` Device: ${dp.device}, Plot: ${dp.plot}`)
|
|
68
|
+
console.log(` Metadata:`, dp.metadata)
|
|
69
|
+
}
|
|
70
|
+
// Outputs:
|
|
71
|
+
// [MEASURED] okf:radio/lorawan/rssi: -82 dBm
|
|
72
|
+
// [MEASURED] okf:radio/lorawan/snr: 9.2 dB
|
|
73
|
+
// [MEASURED] okf:soil/moisture/10cm: 25.87 % (sensorSource: 'Brad soil sensor', converterClass: 'SoilMoistureConverter', soilTexture: 'clay')
|
|
74
|
+
// [COMPUTED] okf:soil/potential/pf/10cm: 2.82 pF (Derived water retention index based on plot soil texture)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 2. Using Pre-Instantiated Default Adapters Directly
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { defaultSensorAdapters, registerDefaultSensorAdapters } from '@bradtech/sensor-lorawan'
|
|
83
|
+
import { Sensor } from '@bradtech/sensor'
|
|
84
|
+
|
|
85
|
+
// 1. Direct access without new allocations
|
|
86
|
+
const airConverter = defaultSensorAdapters.canopyAir
|
|
87
|
+
const tempResults = airConverter.convert({ temperatureCelsius: 21.0, relativeHumidityPercent: 70 })
|
|
88
|
+
|
|
89
|
+
// 2. Or register all domain adapters into the global Quatrain Sensor facade in one line
|
|
90
|
+
registerDefaultSensorAdapters()
|
|
91
|
+
|
|
92
|
+
const soilAdapter = Sensor.getAdapter('soilMoisture')
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 📄 License & Copyright
|
|
98
|
+
|
|
99
|
+
GNU AGPL-v3 — Copyright (C) 2026 Olivier Lépine <olivier@lepine.fr>
|