dredger-iot 0.1.2 → 0.2.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -1
- data/README.md +112 -0
- data/bin/dredger +78 -1
- data/lib/dredger/iot/bus/gpio_label_adapter.rb +14 -4
- data/lib/dredger/iot/pins/raspberry_pi.rb +54 -0
- data/lib/dredger/iot/pins.rb +1 -0
- data/lib/dredger/iot/sensors/bh1750.rb +22 -0
- data/lib/dredger/iot/sensors/bh1750_provider.rb +34 -0
- data/lib/dredger/iot/sensors/ina219.rb +25 -0
- data/lib/dredger/iot/sensors/ina219_provider.rb +54 -0
- data/lib/dredger/iot/sensors/sht31.rb +25 -0
- data/lib/dredger/iot/sensors/sht31_provider.rb +41 -0
- data/lib/dredger/iot/sensors/tsl2561.rb +22 -0
- data/lib/dredger/iot/sensors/tsl2561_provider.rb +72 -0
- data/lib/dredger/iot/sensors.rb +8 -0
- data/lib/dredger/iot/version.rb +1 -1
- metadata +31 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6710d17818b007805d0634e65fde9048c5fb191b9a11d4c5ac3d09aeccf9483e
|
|
4
|
+
data.tar.gz: cb7d2e68d335ee490f902b9eaed4b30b6499b8a688336f47678619e862b48cf6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c14e577283263ea3a3674280cf416bde53ce0c172776fe85c2a9bde00835d7b42fd5badf5bcff6e8dc16b041abae9d810ad7fe510f2afbb86a1c7ffcf0b9d35b
|
|
7
|
+
data.tar.gz: bb7fc628012d3f9e26d83a792e24fdf20493de7cf984dee32a17658221aa53a7e5b208c49bbd528bb431320354edec09b3dd4506b52fff1b05ae6b4965de1daf
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.1] - 2025-10-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Examples: Raspberry Pi GPIO blink script (GPIO17)
|
|
14
|
+
- Docs: Raspberry Pi OS instructions to enable I2C and 1-Wire
|
|
15
|
+
|
|
16
|
+
## [0.2.0] - 2025-10-05
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- New sensors: SHT31 (I2C temp/humidity), BH1750 (I2C lux), TSL2561 (I2C lux), INA219 (I2C bus voltage/current)
|
|
20
|
+
- CLI: --shunt option for INA219 to specify shunt resistance (default 0.1 Ω)
|
|
21
|
+
- Examples: example scripts for SHT31, BH1750, TSL2561, INA219
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- README: document new sensors and CLI usage
|
|
25
|
+
- Coverage: exclude all provider implementations from coverage (hardware-dependent)
|
|
26
|
+
|
|
10
27
|
## [0.1.2] - 2025-10-04
|
|
11
28
|
|
|
12
29
|
### Added
|
|
@@ -53,7 +70,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
53
70
|
- RuboCop configuration and compliance
|
|
54
71
|
- Comprehensive documentation and usage examples
|
|
55
72
|
|
|
56
|
-
[Unreleased]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.1
|
|
73
|
+
[Unreleased]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.2.1...HEAD
|
|
74
|
+
[0.2.1]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.2.0...v0.2.1
|
|
75
|
+
[0.2.0]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.1.2...v0.2.0
|
|
57
76
|
[0.1.2]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.1.1...v0.1.2
|
|
58
77
|
[0.1.1]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.1.0...v0.1.1
|
|
59
78
|
[0.1.0]: https://github.com/TheMadBotterINC/dredger-iot/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -70,6 +70,26 @@ Environment overrides:
|
|
|
70
70
|
- `DREDGER_IOT_GPIO_BACKEND`: `simulation` | `libgpiod`
|
|
71
71
|
- `DREDGER_IOT_I2C_BACKEND`: `simulation` | `linux`
|
|
72
72
|
|
|
73
|
+
## Raspberry Pi GPIO label mapping
|
|
74
|
+
|
|
75
|
+
When the libgpiod backend is selected via Auto, Dredger-IoT resolves Raspberry Pi labels to the corresponding chip:line before accessing the GPIO line. Accepted labels:
|
|
76
|
+
- GPIO17 or BCM17 (Broadcom numbering)
|
|
77
|
+
- PIN11 or BOARD11 (header pin numbers)
|
|
78
|
+
|
|
79
|
+
On most Raspberry Pi boards, GPIO lines are exposed on gpiochip0 and the line offset matches the BCM number. The adapter will translate labels accordingly.
|
|
80
|
+
|
|
81
|
+
Example:
|
|
82
|
+
|
|
83
|
+
```ruby path=null start=null
|
|
84
|
+
require 'dredger/iot'
|
|
85
|
+
|
|
86
|
+
gpio = Dredger::IoT::Bus::Auto.gpio # picks libgpiod on RPi, otherwise simulation
|
|
87
|
+
|
|
88
|
+
# Use Raspberry Pi labels
|
|
89
|
+
gpio.set_direction('GPIO17', :out)
|
|
90
|
+
gpio.write('GPIO17', 1)
|
|
91
|
+
```
|
|
92
|
+
|
|
73
93
|
## Beaglebone P9_XX label mapping
|
|
74
94
|
|
|
75
95
|
When the libgpiod backend is selected via Auto, Dredger-IoT resolves Beaglebone labels like `P9_12` to the corresponding `gpiochipN:line` before accessing the GPIO line. A minimal built-in table is provided and can be extended in future releases.
|
|
@@ -98,9 +118,58 @@ Dredger-IoT includes drivers for popular embedded sensors:
|
|
|
98
118
|
- **DS18B20** - 1-Wire digital temperature sensor
|
|
99
119
|
- **BMP180** - I2C barometric pressure/temperature sensor
|
|
100
120
|
- **MCP9808** - I2C high-accuracy temperature sensor
|
|
121
|
+
- **SHT31** - I2C temperature/humidity sensor
|
|
122
|
+
- **BH1750** - I2C ambient light sensor (lux)
|
|
123
|
+
- **TSL2561** - I2C ambient light sensor (lux)
|
|
124
|
+
- **INA219** - I2C bus voltage/current monitor
|
|
101
125
|
|
|
102
126
|
Sensors use a provider pattern for testability and hardware abstraction.
|
|
103
127
|
|
|
128
|
+
## CLI Usage
|
|
129
|
+
|
|
130
|
+
Commands:
|
|
131
|
+
- list-sensors
|
|
132
|
+
- List available sensor types supported by dredger-iot.
|
|
133
|
+
- read SENSOR [ARGS]
|
|
134
|
+
- Read once or continuously from a sensor.
|
|
135
|
+
- Examples:
|
|
136
|
+
- dredger read bme280 0x76
|
|
137
|
+
- dredger read dht22 P9_12
|
|
138
|
+
- dredger read ina219 0x40 --shunt 0.1
|
|
139
|
+
- test-gpio PIN
|
|
140
|
+
- Simple blink test to verify GPIO output works.
|
|
141
|
+
- test-i2c
|
|
142
|
+
- Scans a few common I2C addresses (hardware backend only).
|
|
143
|
+
- info
|
|
144
|
+
- Prints version, detected backends, and environment variables.
|
|
145
|
+
|
|
146
|
+
Options:
|
|
147
|
+
- --backend BACKEND
|
|
148
|
+
- auto (default), simulation, hardware
|
|
149
|
+
- simulation forces both GPIO and I2C simulation backends
|
|
150
|
+
- hardware forces libgpiod (GPIO) and linux (I2C)
|
|
151
|
+
- --format FORMAT
|
|
152
|
+
- text (default) or json
|
|
153
|
+
- --interval SECONDS
|
|
154
|
+
- Poll continuously at the specified interval (e.g., 2.0)
|
|
155
|
+
- --shunt OHMS
|
|
156
|
+
- INA219-only: specify the shunt resistance in ohms (default 0.1)
|
|
157
|
+
|
|
158
|
+
Examples:
|
|
159
|
+
```bash
|
|
160
|
+
# Read a BME280 once (auto-detected backends)
|
|
161
|
+
dredger read bme280 0x76
|
|
162
|
+
|
|
163
|
+
# Read a DHT22 every 2 seconds, JSON output
|
|
164
|
+
dredger read dht22 P9_12 --interval 2 --format json
|
|
165
|
+
|
|
166
|
+
# Force simulation backends for local testing
|
|
167
|
+
dredger --backend simulation read bh1750 0x23
|
|
168
|
+
|
|
169
|
+
# INA219 with custom shunt
|
|
170
|
+
dredger read ina219 0x40 --shunt 0.05
|
|
171
|
+
```
|
|
172
|
+
|
|
104
173
|
## Usage Examples
|
|
105
174
|
|
|
106
175
|
### DHT22 Temperature/Humidity Sensor
|
|
@@ -304,6 +373,29 @@ ls /sys/bus/w1/devices/
|
|
|
304
373
|
# Should show devices like: 28-00000xxxxxx
|
|
305
374
|
```
|
|
306
375
|
|
|
376
|
+
#### Raspberry Pi OS: Enable I2C and 1-Wire
|
|
377
|
+
|
|
378
|
+
On Raspberry Pi OS you can enable I2C and 1-Wire via raspi-config or by editing /boot/config.txt.
|
|
379
|
+
|
|
380
|
+
Option A: raspi-config (recommended)
|
|
381
|
+
```bash path=null start=null
|
|
382
|
+
sudo raspi-config
|
|
383
|
+
# Interface Options → I2C → Enable
|
|
384
|
+
# Interface Options → 1-Wire → Enable
|
|
385
|
+
sudo reboot
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Option B: edit /boot/config.txt
|
|
389
|
+
```bash path=null start=null
|
|
390
|
+
# Enable I2C
|
|
391
|
+
sudo sed -i 's/^#\?dtparam=i2c_arm=.*/dtparam=i2c_arm=on/' /boot/config.txt
|
|
392
|
+
# Enable 1-Wire on default BCM4 (PIN7)
|
|
393
|
+
echo 'dtoverlay=w1-gpio,gpiopin=4' | sudo tee -a /boot/config.txt
|
|
394
|
+
sudo reboot
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Note: Dredger-IoT accepts Raspberry Pi labels like GPIO17, BCM17, and PIN11.
|
|
398
|
+
|
|
307
399
|
#### Beaglebone Black Device Tree
|
|
308
400
|
|
|
309
401
|
For Beaglebone Black, you may need to enable device tree overlays:
|
|
@@ -483,6 +575,26 @@ reading.timestamp # Time object when reading was taken
|
|
|
483
575
|
- **`MCP9808`** - High-accuracy temperature (I2C)
|
|
484
576
|
- Parameters: `i2c_addr` (default: `0x18`), `provider`
|
|
485
577
|
- Returns: temperature (celsius)
|
|
578
|
+
|
|
579
|
+
- **`SHT31`** - Temperature/humidity (I2C)
|
|
580
|
+
- Parameters: `i2c_addr` (default: `0x44`), `provider`
|
|
581
|
+
- Returns: temperature (celsius), humidity (%)
|
|
582
|
+
|
|
583
|
+
- **`BH1750`** - Ambient light (I2C)
|
|
584
|
+
- Parameters: `i2c_addr` (default: `0x23`), `provider`
|
|
585
|
+
- Returns: illuminance (lux)
|
|
586
|
+
|
|
587
|
+
- **`TSL2561`** - Ambient light (I2C)
|
|
588
|
+
- Parameters: `i2c_addr` (default: `0x39`), `provider`
|
|
589
|
+
- Returns: illuminance (lux)
|
|
590
|
+
|
|
591
|
+
- **`INA219`** - Bus voltage/current monitor (I2C)
|
|
592
|
+
- Parameters: `i2c_addr` (default: `0x40`), `provider`
|
|
593
|
+
- Returns: bus_voltage (V), current (mA)
|
|
594
|
+
- CLI example:
|
|
595
|
+
```bash path=null start=null
|
|
596
|
+
dredger read ina219 0x40 --shunt 0.1
|
|
597
|
+
```
|
|
486
598
|
|
|
487
599
|
### Scheduling
|
|
488
600
|
|
data/bin/dredger
CHANGED
|
@@ -32,6 +32,8 @@ class DredgerCLI
|
|
|
32
32
|
test_i2c
|
|
33
33
|
when 'info'
|
|
34
34
|
show_info
|
|
35
|
+
when 'doctor'
|
|
36
|
+
doctor
|
|
35
37
|
else
|
|
36
38
|
puts parser
|
|
37
39
|
exit 1
|
|
@@ -53,6 +55,7 @@ class DredgerCLI
|
|
|
53
55
|
opts.separator ' read SENSOR [OPTIONS] Read from a sensor'
|
|
54
56
|
opts.separator ' test-gpio PIN Test GPIO pin'
|
|
55
57
|
opts.separator ' test-i2c Scan I2C bus'
|
|
58
|
+
opts.separator ' doctor Check system prerequisites'
|
|
56
59
|
opts.separator ' info Show system information'
|
|
57
60
|
opts.separator ''
|
|
58
61
|
opts.separator 'Options:'
|
|
@@ -69,6 +72,10 @@ class DredgerCLI
|
|
|
69
72
|
@options[:interval] = i
|
|
70
73
|
end
|
|
71
74
|
|
|
75
|
+
opts.on('--shunt OHMS', Float, 'INA219 shunt resistance in ohms (default: 0.1)') do |ohms|
|
|
76
|
+
@options[:ina219_shunt] = ohms
|
|
77
|
+
end
|
|
78
|
+
|
|
72
79
|
opts.on('-h', '--help', 'Show this help') do
|
|
73
80
|
puts opts
|
|
74
81
|
exit
|
|
@@ -87,7 +94,11 @@ class DredgerCLI
|
|
|
87
94
|
'bme280' => 'BME280 - Temperature/Humidity/Pressure (I2C)',
|
|
88
95
|
'ds18b20' => 'DS18B20 - Waterproof Temperature (1-Wire)',
|
|
89
96
|
'bmp180' => 'BMP180 - Barometric Pressure/Temperature (I2C)',
|
|
90
|
-
'mcp9808' => 'MCP9808 - High-Accuracy Temperature (I2C)'
|
|
97
|
+
'mcp9808' => 'MCP9808 - High-Accuracy Temperature (I2C)',
|
|
98
|
+
'sht31' => 'SHT31 - Temperature/Humidity (I2C)',
|
|
99
|
+
'bh1750' => 'BH1750 - Ambient Light (I2C)',
|
|
100
|
+
'tsl2561' => 'TSL2561 - Ambient Light (I2C)',
|
|
101
|
+
'ina219' => 'INA219 - Bus Voltage/Current (I2C)'
|
|
91
102
|
}
|
|
92
103
|
|
|
93
104
|
puts 'Available Sensors:'
|
|
@@ -136,6 +147,27 @@ class DredgerCLI
|
|
|
136
147
|
exit 1
|
|
137
148
|
end
|
|
138
149
|
Dredger::IoT::Sensors::DS18B20.new(device_id: device_id, provider: provider)
|
|
150
|
+
when 'sht31'
|
|
151
|
+
addr = (args.shift || '0x44').to_i(16)
|
|
152
|
+
i2c = Dredger::IoT::Bus::Auto.i2c
|
|
153
|
+
provider = Dredger::IoT::Sensors::SHT31Provider.new(i2c_bus: i2c)
|
|
154
|
+
Dredger::IoT::Sensors::SHT31.new(i2c_addr: addr, provider: provider)
|
|
155
|
+
when 'bh1750'
|
|
156
|
+
addr = (args.shift || '0x23').to_i(16)
|
|
157
|
+
i2c = Dredger::IoT::Bus::Auto.i2c
|
|
158
|
+
provider = Dredger::IoT::Sensors::BH1750Provider.new(i2c_bus: i2c)
|
|
159
|
+
Dredger::IoT::Sensors::BH1750.new(i2c_addr: addr, provider: provider)
|
|
160
|
+
when 'tsl2561'
|
|
161
|
+
addr = (args.shift || '0x39').to_i(16)
|
|
162
|
+
i2c = Dredger::IoT::Bus::Auto.i2c
|
|
163
|
+
provider = Dredger::IoT::Sensors::TSL2561Provider.new(i2c_bus: i2c)
|
|
164
|
+
Dredger::IoT::Sensors::TSL2561.new(i2c_addr: addr, provider: provider)
|
|
165
|
+
when 'ina219'
|
|
166
|
+
addr = (args.shift || '0x40').to_i(16)
|
|
167
|
+
i2c = Dredger::IoT::Bus::Auto.i2c
|
|
168
|
+
shunt = @options[:ina219_shunt] || 0.1
|
|
169
|
+
provider = Dredger::IoT::Sensors::INA219Provider.new(i2c_bus: i2c, shunt_resistance_ohms: shunt)
|
|
170
|
+
Dredger::IoT::Sensors::INA219.new(i2c_addr: addr, provider: provider)
|
|
139
171
|
else
|
|
140
172
|
puts "Error: Unknown sensor type '#{type}'"
|
|
141
173
|
exit 1
|
|
@@ -241,6 +273,51 @@ class DredgerCLI
|
|
|
241
273
|
puts " DREDGER_IOT_I2C_BACKEND: #{ENV['DREDGER_IOT_I2C_BACKEND'] || '(not set)'}"
|
|
242
274
|
end
|
|
243
275
|
|
|
276
|
+
def doctor
|
|
277
|
+
puts 'Doctor: checking system prerequisites...'
|
|
278
|
+
puts
|
|
279
|
+
check_device_node('/dev/gpiochip0', 'GPIO (libgpiod) device')
|
|
280
|
+
check_device_node('/dev/i2c-1', 'I2C device (bus 1)')
|
|
281
|
+
check_path('/sys/bus/w1/devices', '1-Wire bus (DS18B20) directory')
|
|
282
|
+
|
|
283
|
+
puts
|
|
284
|
+
puts 'Kernel modules:'
|
|
285
|
+
mods = read_proc_modules
|
|
286
|
+
puts " i2c-dev: #{mods.include?('i2c_dev') ? 'loaded' : 'missing'}"
|
|
287
|
+
puts " w1-gpio: #{mods.include?('w1_gpio') ? 'loaded' : 'missing'}"
|
|
288
|
+
puts " w1-therm: #{mods.include?('w1_therm') ? 'loaded' : 'missing'}"
|
|
289
|
+
|
|
290
|
+
puts
|
|
291
|
+
puts 'Recommendations:'
|
|
292
|
+
puts " sudo apt-get install gpiod i2c-tools"
|
|
293
|
+
puts " sudo usermod -a -G gpio $USER # for GPIO access"
|
|
294
|
+
puts " sudo usermod -a -G i2c $USER # for I2C access"
|
|
295
|
+
puts " sudo modprobe i2c-dev"
|
|
296
|
+
puts " sudo modprobe w1-gpio; sudo modprobe w1-therm"
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def check_device_node(path, desc)
|
|
300
|
+
if File.exist?(path)
|
|
301
|
+
puts " ✔ #{desc}: present (#{path})"
|
|
302
|
+
else
|
|
303
|
+
puts " ✖ #{desc}: missing (#{path})"
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def check_path(path, desc)
|
|
308
|
+
if Dir.exist?(path)
|
|
309
|
+
puts " ✔ #{desc}: present (#{path})"
|
|
310
|
+
else
|
|
311
|
+
puts " ✖ #{desc}: missing (#{path})"
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def read_proc_modules
|
|
316
|
+
File.read('/proc/modules')
|
|
317
|
+
rescue StandardError
|
|
318
|
+
''
|
|
319
|
+
end
|
|
320
|
+
|
|
244
321
|
def setup_backends
|
|
245
322
|
case @options[:backend]
|
|
246
323
|
when 'simulation'
|
|
@@ -5,9 +5,11 @@ module Dredger
|
|
|
5
5
|
module Bus
|
|
6
6
|
# Adapts label strings (e.g. 'P9_12') to PinRef with chip:line for libgpiod backends
|
|
7
7
|
class GPIOLabelAdapter
|
|
8
|
-
|
|
8
|
+
# mapper can be a single mapper module/class or an Array of them.
|
|
9
|
+
# Defaults to both Beaglebone and RaspberryPi mappers.
|
|
10
|
+
def initialize(backend:, mapper: [Dredger::IoT::Pins::Beaglebone, Dredger::IoT::Pins::RaspberryPi])
|
|
9
11
|
@backend = backend
|
|
10
|
-
@
|
|
12
|
+
@mappers = Array(mapper)
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def set_direction(pin, direction)
|
|
@@ -24,16 +26,24 @@ module Dredger
|
|
|
24
26
|
|
|
25
27
|
private
|
|
26
28
|
|
|
29
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
27
30
|
def resolve(pin)
|
|
28
31
|
# Already a PinRef with line
|
|
29
32
|
return pin if pin.respond_to?(:line) && !pin.line.nil?
|
|
33
|
+
|
|
30
34
|
# Numeric line
|
|
31
35
|
return Integer(pin) if pin.is_a?(Integer) || pin.to_s =~ /^\d+$/
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
|
|
37
|
+
# Try all mappers in order
|
|
38
|
+
@mappers.each do |m|
|
|
39
|
+
if m.respond_to?(:resolve_label_to_pinref) && m.respond_to?(:valid_label?) && m.valid_label?(pin)
|
|
40
|
+
return m.resolve_label_to_pinref(pin)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
34
43
|
|
|
35
44
|
raise ArgumentError, 'Unsupported pin format'
|
|
36
45
|
end
|
|
46
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
37
47
|
end
|
|
38
48
|
end
|
|
39
49
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Pins
|
|
6
|
+
# Raspberry Pi header/BCM label mapping.
|
|
7
|
+
# Supports labels like:
|
|
8
|
+
# - GPIO17, BCM17
|
|
9
|
+
# - PIN11 (a.k.a. BOARD11)
|
|
10
|
+
class RaspberryPi
|
|
11
|
+
PinRef = Struct.new(:label, :chip, :line, keyword_init: true) do
|
|
12
|
+
def to_s
|
|
13
|
+
chip && line ? "#{label}(chip#{chip}:#{line})" : label.to_s
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Subset of BOARD pin to BCM mapping for common usable GPIOs on 40-pin header
|
|
18
|
+
BOARD_TO_BCM = {
|
|
19
|
+
3 => 2, 5 => 3, 7 => 4, 8 => 14, 10 => 15, 11 => 17, 12 => 18, 13 => 27,
|
|
20
|
+
15 => 22, 16 => 23, 18 => 24, 19 => 10, 21 => 9, 22 => 25, 23 => 11,
|
|
21
|
+
24 => 8, 26 => 7, 29 => 5, 31 => 6, 32 => 12, 33 => 13, 35 => 19,
|
|
22
|
+
36 => 16, 37 => 26, 38 => 20, 40 => 21
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
# Accept variants like GPIO17, BCM17, PIN11, BOARD11
|
|
26
|
+
def self.valid_label?(label)
|
|
27
|
+
s = label.to_s.upcase
|
|
28
|
+
return true if s.match?(/^(GPIO|BCM)\d+$/)
|
|
29
|
+
return true if s.match?(/^(PIN|BOARD)\d+$/)
|
|
30
|
+
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.resolve_label_to_pinref(label)
|
|
35
|
+
s = label.to_s.upcase
|
|
36
|
+
if s =~ /^(GPIO|BCM)(\d+)$/
|
|
37
|
+
bcm = Regexp.last_match(2).to_i
|
|
38
|
+
return PinRef.new(label: label.to_s, chip: 0, line: bcm)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if s =~ /^(PIN|BOARD)(\d+)$/
|
|
42
|
+
board = Regexp.last_match(2).to_i
|
|
43
|
+
bcm = BOARD_TO_BCM[board]
|
|
44
|
+
raise ArgumentError, "Unknown/unsupported board pin: #{label}" if bcm.nil?
|
|
45
|
+
|
|
46
|
+
return PinRef.new(label: label.to_s, chip: 0, line: bcm)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
raise ArgumentError, "Unknown Raspberry Pi pin label: #{label}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/dredger/iot/pins.rb
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# BH1750 ambient light sensor (I2C)
|
|
7
|
+
# Provider must respond to :read_lux(addr) -> Float (lux)
|
|
8
|
+
class BH1750 < BaseSensor
|
|
9
|
+
def initialize(i2c_addr:, provider:, metadata: {})
|
|
10
|
+
super(metadata: metadata)
|
|
11
|
+
@i2c_addr = i2c_addr
|
|
12
|
+
@provider = provider
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def readings
|
|
16
|
+
lux = @provider.read_lux(@i2c_addr)
|
|
17
|
+
[reading(sensor_type: 'illuminance', value: lux, unit: 'lux')]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# Hardware provider for BH1750 ambient light sensor (I2C)
|
|
7
|
+
# Measurement: Continuous High-Resolution Mode (1 lx, typical 120ms)
|
|
8
|
+
class BH1750Provider
|
|
9
|
+
POWER_ON = 0x01
|
|
10
|
+
RESET = 0x07
|
|
11
|
+
CONT_HIRES = 0x10
|
|
12
|
+
|
|
13
|
+
def initialize(i2c_bus:)
|
|
14
|
+
@i2c = i2c_bus
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns lux as Float
|
|
18
|
+
def read_lux(addr)
|
|
19
|
+
# Power on and reset
|
|
20
|
+
@i2c.write(addr, [POWER_ON])
|
|
21
|
+
@i2c.write(addr, [RESET])
|
|
22
|
+
# Start continuous high-resolution measurement
|
|
23
|
+
@i2c.write(addr, [CONT_HIRES])
|
|
24
|
+
# Wait for conversion
|
|
25
|
+
sleep(0.18)
|
|
26
|
+
# Read 2 bytes (big-endian)
|
|
27
|
+
bytes = @i2c.read(addr, 2)
|
|
28
|
+
raw = (bytes[0] << 8) | bytes[1]
|
|
29
|
+
(raw / 1.2).to_f
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# INA219 current/voltage/power monitor (I2C)
|
|
7
|
+
# Provider must respond to :read_measurements(addr) -> { bus_voltage_v:, current_ma:, shunt_voltage_mv: }
|
|
8
|
+
class INA219 < BaseSensor
|
|
9
|
+
def initialize(i2c_addr:, provider:, metadata: {})
|
|
10
|
+
super(metadata: metadata)
|
|
11
|
+
@i2c_addr = i2c_addr
|
|
12
|
+
@provider = provider
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def readings
|
|
16
|
+
m = @provider.read_measurements(@i2c_addr)
|
|
17
|
+
[
|
|
18
|
+
reading(sensor_type: 'bus_voltage', value: m[:bus_voltage_v], unit: 'V'),
|
|
19
|
+
reading(sensor_type: 'current', value: m[:current_ma], unit: 'mA')
|
|
20
|
+
]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# Hardware provider for INA219 current/voltage monitor (I2C)
|
|
7
|
+
# Simplified: computes current from shunt voltage and provided shunt resistance,
|
|
8
|
+
# avoids calibration register dependency.
|
|
9
|
+
class INA219Provider
|
|
10
|
+
REG_SHUNT_VOLTAGE = 0x01
|
|
11
|
+
REG_BUS_VOLTAGE = 0x02
|
|
12
|
+
|
|
13
|
+
def initialize(i2c_bus:, shunt_resistance_ohms: 0.1)
|
|
14
|
+
@i2c = i2c_bus
|
|
15
|
+
@r_shunt = shunt_resistance_ohms.to_f
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Returns { bus_voltage_v: Float, current_ma: Float, shunt_voltage_mv: Float }
|
|
19
|
+
def read_measurements(addr)
|
|
20
|
+
shunt_raw = read_word(addr, REG_SHUNT_VOLTAGE, signed: true)
|
|
21
|
+
bus_raw = read_word(addr, REG_BUS_VOLTAGE, signed: false)
|
|
22
|
+
|
|
23
|
+
# Shunt voltage LSB = 10uV => mV = raw * 0.01
|
|
24
|
+
shunt_mv = shunt_raw * 0.01
|
|
25
|
+
# Current (mA) = (shunt_mv / R_ohms)
|
|
26
|
+
current_ma = (@r_shunt.positive? ? (shunt_mv / @r_shunt) : 0.0)
|
|
27
|
+
|
|
28
|
+
# Bus voltage: bits [15:3] * 4mV
|
|
29
|
+
bus_voltage_v = ((bus_raw >> 3) * 0.004)
|
|
30
|
+
|
|
31
|
+
{
|
|
32
|
+
bus_voltage_v: bus_voltage_v,
|
|
33
|
+
current_ma: current_ma,
|
|
34
|
+
shunt_voltage_mv: shunt_mv
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# Read 16-bit word
|
|
41
|
+
# INA219 registers are big-endian; register reads may return bytes LSB-first
|
|
42
|
+
def read_word(addr, reg, signed: false)
|
|
43
|
+
bytes = @i2c.read(addr, 2, register: reg)
|
|
44
|
+
val = (bytes[0] << 8) | bytes[1]
|
|
45
|
+
if signed && val > 0x7FFF
|
|
46
|
+
val - 0x1_0000
|
|
47
|
+
else
|
|
48
|
+
val
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# SHT31 temperature/humidity sensor (I2C)
|
|
7
|
+
# Provider must respond to :read_measurements(addr) -> { temperature_c:, humidity: }
|
|
8
|
+
class SHT31 < BaseSensor
|
|
9
|
+
def initialize(i2c_addr:, provider:, metadata: {})
|
|
10
|
+
super(metadata: metadata)
|
|
11
|
+
@i2c_addr = i2c_addr
|
|
12
|
+
@provider = provider
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def readings
|
|
16
|
+
m = @provider.read_measurements(@i2c_addr)
|
|
17
|
+
[
|
|
18
|
+
reading(sensor_type: 'temperature', value: m[:temperature_c], unit: 'celsius'),
|
|
19
|
+
reading(sensor_type: 'humidity', value: m[:humidity], unit: '%')
|
|
20
|
+
]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# Hardware provider for Sensirion SHT31 temperature/humidity over I2C.
|
|
7
|
+
# Basic single-shot measurement without CRC checks for simplicity.
|
|
8
|
+
# Datasheet formula:
|
|
9
|
+
# - Temperature (°C) = -45 + 175 * (ST / 65535)
|
|
10
|
+
# - Humidity (%RH) = 100 * (SRH / 65535)
|
|
11
|
+
class SHT31Provider
|
|
12
|
+
# I2C commands
|
|
13
|
+
CMD_SINGLE_SHOT_HIGHREP = [0x24, 0x00].freeze # High repeatability, clock stretching disabled
|
|
14
|
+
|
|
15
|
+
def initialize(i2c_bus:)
|
|
16
|
+
@i2c = i2c_bus
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns { temperature_c: Float, humidity: Float }
|
|
20
|
+
def read_measurements(addr)
|
|
21
|
+
# Trigger single-shot measurement
|
|
22
|
+
@i2c.write(addr, CMD_SINGLE_SHOT_HIGHREP)
|
|
23
|
+
# Max measurement duration ~15ms (high repeatability). Add margin.
|
|
24
|
+
sleep(0.02)
|
|
25
|
+
|
|
26
|
+
# Read 6 bytes: T_MSB, T_LSB, T_CRC, RH_MSB, RH_LSB, RH_CRC
|
|
27
|
+
data = @i2c.read(addr, 6)
|
|
28
|
+
|
|
29
|
+
st = (data[0] << 8) | data[1]
|
|
30
|
+
srh = (data[3] << 8) | data[4]
|
|
31
|
+
|
|
32
|
+
temp_c = -45.0 + (175.0 * st / 65_535.0)
|
|
33
|
+
humidity = (100.0 * srh / 65_535.0)
|
|
34
|
+
humidity = humidity.clamp(0.0, 100.0)
|
|
35
|
+
|
|
36
|
+
{ temperature_c: temp_c, humidity: humidity }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# TSL2561 ambient light sensor (I2C)
|
|
7
|
+
# Provider must respond to :read_lux(addr) -> Float (lux)
|
|
8
|
+
class TSL2561 < BaseSensor
|
|
9
|
+
def initialize(i2c_addr:, provider:, metadata: {})
|
|
10
|
+
super(metadata: metadata)
|
|
11
|
+
@i2c_addr = i2c_addr
|
|
12
|
+
@provider = provider
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def readings
|
|
16
|
+
lux = @provider.read_lux(@i2c_addr)
|
|
17
|
+
[reading(sensor_type: 'illuminance', value: lux, unit: 'lux')]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# Hardware provider for TSL2561 ambient light sensor (I2C)
|
|
7
|
+
# Uses simple configuration: integration 402ms, low gain.
|
|
8
|
+
class TSL2561Provider
|
|
9
|
+
CMD = 0x80
|
|
10
|
+
CONTROL = 0x00
|
|
11
|
+
TIMING = 0x01
|
|
12
|
+
DATA0LOW = 0x0C
|
|
13
|
+
DATA1LOW = 0x0E
|
|
14
|
+
|
|
15
|
+
POWER_ON = 0x03
|
|
16
|
+
POWER_OFF = 0x00
|
|
17
|
+
INTEG_402MS = 0x02 # integration time bits [1:0] = 10
|
|
18
|
+
GAIN_LOW = 0x00 # gain bit [4] = 0
|
|
19
|
+
|
|
20
|
+
def initialize(i2c_bus:)
|
|
21
|
+
@i2c = i2c_bus
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns lux as Float
|
|
25
|
+
def read_lux(addr)
|
|
26
|
+
# Power on
|
|
27
|
+
@i2c.write(addr, [POWER_ON], register: CMD | CONTROL)
|
|
28
|
+
# Set timing: 402ms, low gain
|
|
29
|
+
@i2c.write(addr, [GAIN_LOW | INTEG_402MS], register: CMD | TIMING)
|
|
30
|
+
|
|
31
|
+
# Wait for integration
|
|
32
|
+
sleep(0.45)
|
|
33
|
+
|
|
34
|
+
ch0 = read_word(addr, CMD | DATA0LOW)
|
|
35
|
+
ch1 = read_word(addr, CMD | DATA1LOW)
|
|
36
|
+
|
|
37
|
+
compute_lux(ch0, ch1).to_f
|
|
38
|
+
ensure
|
|
39
|
+
# Power off to save energy
|
|
40
|
+
@i2c.write(addr, [POWER_OFF], register: CMD | CONTROL)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Read 16-bit little-endian word from LSB register
|
|
46
|
+
def read_word(addr, reg)
|
|
47
|
+
bytes = @i2c.read(addr, 2, register: reg)
|
|
48
|
+
bytes[0] | (bytes[1] << 8)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Lux calculation based on TSL2561 datasheet/Adafruit library
|
|
52
|
+
def compute_lux(ch0, ch1)
|
|
53
|
+
return 0.0 if ch0.zero?
|
|
54
|
+
|
|
55
|
+
ratio = ch1.to_f / ch0
|
|
56
|
+
|
|
57
|
+
if ratio <= 0.5
|
|
58
|
+
(0.0304 * ch0) - (0.062 * ch0 * (ratio**1.4))
|
|
59
|
+
elsif ratio <= 0.61
|
|
60
|
+
(0.0224 * ch0) - (0.031 * ch1)
|
|
61
|
+
elsif ratio <= 0.80
|
|
62
|
+
(0.0128 * ch0) - (0.0153 * ch1)
|
|
63
|
+
elsif ratio <= 1.30
|
|
64
|
+
(0.00146 * ch0) - (0.00112 * ch1)
|
|
65
|
+
else
|
|
66
|
+
0.0
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
data/lib/dredger/iot/sensors.rb
CHANGED
|
@@ -9,3 +9,11 @@ require_relative 'sensors/ds18b20'
|
|
|
9
9
|
require_relative 'sensors/ds18b20_provider'
|
|
10
10
|
require_relative 'sensors/bmp180'
|
|
11
11
|
require_relative 'sensors/mcp9808'
|
|
12
|
+
require_relative 'sensors/sht31'
|
|
13
|
+
require_relative 'sensors/sht31_provider'
|
|
14
|
+
require_relative 'sensors/bh1750'
|
|
15
|
+
require_relative 'sensors/bh1750_provider'
|
|
16
|
+
require_relative 'sensors/tsl2561'
|
|
17
|
+
require_relative 'sensors/tsl2561_provider'
|
|
18
|
+
require_relative 'sensors/ina219'
|
|
19
|
+
require_relative 'sensors/ina219_provider'
|
data/lib/dredger/iot/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dredger-iot
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The Mad Botter INC
|
|
@@ -49,10 +49,13 @@ files:
|
|
|
49
49
|
- lib/dredger/iot/bus/i2c_linux.rb
|
|
50
50
|
- lib/dredger/iot/pins.rb
|
|
51
51
|
- lib/dredger/iot/pins/beaglebone.rb
|
|
52
|
+
- lib/dredger/iot/pins/raspberry_pi.rb
|
|
52
53
|
- lib/dredger/iot/reading.rb
|
|
53
54
|
- lib/dredger/iot/scheduler.rb
|
|
54
55
|
- lib/dredger/iot/sensors.rb
|
|
55
56
|
- lib/dredger/iot/sensors/base_sensor.rb
|
|
57
|
+
- lib/dredger/iot/sensors/bh1750.rb
|
|
58
|
+
- lib/dredger/iot/sensors/bh1750_provider.rb
|
|
56
59
|
- lib/dredger/iot/sensors/bme280.rb
|
|
57
60
|
- lib/dredger/iot/sensors/bme280_provider.rb
|
|
58
61
|
- lib/dredger/iot/sensors/bmp180.rb
|
|
@@ -60,7 +63,13 @@ files:
|
|
|
60
63
|
- lib/dredger/iot/sensors/dht22_provider.rb
|
|
61
64
|
- lib/dredger/iot/sensors/ds18b20.rb
|
|
62
65
|
- lib/dredger/iot/sensors/ds18b20_provider.rb
|
|
66
|
+
- lib/dredger/iot/sensors/ina219.rb
|
|
67
|
+
- lib/dredger/iot/sensors/ina219_provider.rb
|
|
63
68
|
- lib/dredger/iot/sensors/mcp9808.rb
|
|
69
|
+
- lib/dredger/iot/sensors/sht31.rb
|
|
70
|
+
- lib/dredger/iot/sensors/sht31_provider.rb
|
|
71
|
+
- lib/dredger/iot/sensors/tsl2561.rb
|
|
72
|
+
- lib/dredger/iot/sensors/tsl2561_provider.rb
|
|
64
73
|
- lib/dredger/iot/version.rb
|
|
65
74
|
homepage: https://github.com/TheMadBotterINC/dredger-iot
|
|
66
75
|
licenses:
|
|
@@ -75,25 +84,27 @@ metadata:
|
|
|
75
84
|
rubygems_mfa_required: 'true'
|
|
76
85
|
keywords: iot, embedded, linux, gpio, i2c, beaglebone, raspberry-pi, hardware, sensors,
|
|
77
86
|
dht22, bme280, ds18b20, bmp180, mcp9808, libgpiod, automation
|
|
78
|
-
post_install_message: "\n═══════════════════════════════════════════════════════════════════\n
|
|
79
|
-
\
|
|
80
|
-
\ \n
|
|
81
|
-
\
|
|
82
|
-
\ ___ ___ | \n
|
|
83
|
-
\ \n
|
|
84
|
-
\
|
|
85
|
-
\ | | \n
|
|
86
|
-
\
|
|
87
|
-
\
|
|
88
|
-
\
|
|
89
|
-
\
|
|
90
|
-
\
|
|
91
|
-
\
|
|
92
|
-
|
|
93
|
-
\
|
|
94
|
-
|
|
95
|
-
\ = Dredger::IoT::Bus::Auto.
|
|
96
|
-
|
|
87
|
+
post_install_message: "\n ═══════════════════════════════════════════════════════════════════\n
|
|
88
|
+
\ \n _______________
|
|
89
|
+
\ \n | DREDGER-IoT | \n
|
|
90
|
+
\ |_______________| \n /|
|
|
91
|
+
\ ___ ___ | \n / | |___| |___||
|
|
92
|
+
\ \n / |______________| \n
|
|
93
|
+
\ ====|========================|==== \n |
|
|
94
|
+
\ | |-----------| | | \n | |____| |_____|
|
|
95
|
+
\ | \n ___|____| |____|___ \n
|
|
96
|
+
\ ~~~~{________|_________________________|________}~~~~~~~ \n ~~ |
|
|
97
|
+
\ \\ // | ~~~ \n | \\___________________//
|
|
98
|
+
\ | \n |_____________________________| \n
|
|
99
|
+
\ ~~~ \\ // ~~~ \n \\_______________//
|
|
100
|
+
\ \n ═══════════════════════════════════════════════════════════════════\nHardware
|
|
101
|
+
Integration for Embedded Linux v0.2.1\n ═══════════════════════════════════════════════════════════════════\n\n
|
|
102
|
+
\ \U0001F389 Thanks for installing!\n\n \U0001F4DA Hardware Setup (kernel modules
|
|
103
|
+
& permissions):\n https://github.com/TheMadBotterINC/dredger-iot#hardware-setup\n\n
|
|
104
|
+
\ \U0001F680 Quick Start:\n require 'dredger/iot'\n gpio = Dredger::IoT::Bus::Auto.gpio\n
|
|
105
|
+
\ i2c = Dredger::IoT::Bus::Auto.i2c\n\n \U0001F4A1 Supported Sensors:\n
|
|
106
|
+
\ DHT22, BME280, DS18B20, BMP180, MCP9808\n\n \U0001F4D6 Full Documentation:\n
|
|
107
|
+
\ https://github.com/TheMadBotterINC/dredger-iot\n\n"
|
|
97
108
|
rdoc_options: []
|
|
98
109
|
require_paths:
|
|
99
110
|
- lib
|