dredger-iot 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +124 -1
- data/lib/dredger/iot/sensors/adxl345.rb +28 -0
- data/lib/dredger/iot/sensors/adxl345_provider.rb +103 -0
- data/lib/dredger/iot/sensors/neo6m.rb +35 -0
- data/lib/dredger/iot/sensors/neo6m_provider.rb +140 -0
- data/lib/dredger/iot/sensors/scd30.rb +28 -0
- data/lib/dredger/iot/sensors/scd30_provider.rb +142 -0
- data/lib/dredger/iot/sensors/yf_s201.rb +32 -0
- data/lib/dredger/iot/sensors/yf_s201_provider.rb +88 -0
- data/lib/dredger/iot/sensors.rb +8 -0
- data/lib/dredger/iot/version.rb +1 -1
- metadata +10 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 701ad99dd1f271df310ba3dffbb3295084b54e64689f4d3ed77f9b6ab9e31df5
|
|
4
|
+
data.tar.gz: e9b5ba95c4d2af83b529e6f66af5c81fb83c43f3f679e89e894209661651f93e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5362ae41f8f92d403809728857c139b00e3d8141e8f83725a3cc302eb7e7ace2c32b8bd7306fba5e83c2ee197e42c633b7beaeb9a84b9b77f1d95299e80aad1d
|
|
7
|
+
data.tar.gz: 220b5834cd302cef375c3922211c055788855d4b516b7260aa952d13fc312a3f9c028248863906e93a38badbd32839d2083783bb1431ba5fd1ada0b4df92bca5
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
## [0.3.0] - 2025-10-06
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- New sensors for industrial IoT applications:
|
|
15
|
+
- **ADXL345** - I2C 3-axis accelerometer for vibration monitoring (±2g, ±4g, ±8g, ±16g ranges)
|
|
16
|
+
- **SCD30** - I2C NDIR CO2 sensor with integrated temperature and humidity (400-10,000 ppm range)
|
|
17
|
+
- **YF-S201** - GPIO hall effect flow meter for liquid flow measurement (1-30 L/min range)
|
|
18
|
+
- **NEO-6M** - UART/Serial GPS module with NMEA 0183 parsing for location tracking
|
|
19
|
+
- README: comprehensive usage examples for all new sensors
|
|
20
|
+
- README: organized sensor list by category (Environmental, Light & Motion, Industrial)
|
|
21
|
+
|
|
10
22
|
## [0.2.1] - 2025-10-05
|
|
11
23
|
|
|
12
24
|
### Added
|
|
@@ -70,7 +82,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
70
82
|
- RuboCop configuration and compliance
|
|
71
83
|
- Comprehensive documentation and usage examples
|
|
72
84
|
|
|
73
|
-
[Unreleased]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.
|
|
85
|
+
[Unreleased]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.3.0...HEAD
|
|
86
|
+
[0.3.0]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.2.1...v0.3.0
|
|
74
87
|
[0.2.1]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.2.0...v0.2.1
|
|
75
88
|
[0.2.0]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.1.2...v0.2.0
|
|
76
89
|
[0.1.2]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.1.1...v0.1.2
|
data/README.md
CHANGED
|
@@ -113,15 +113,24 @@ If you run on a development host (no /dev/gpiochip0), Auto will default to the s
|
|
|
113
113
|
|
|
114
114
|
Dredger-IoT includes drivers for popular embedded sensors:
|
|
115
115
|
|
|
116
|
+
**Environmental Sensors:**
|
|
116
117
|
- **DHT22** - GPIO humidity/temperature sensor
|
|
117
118
|
- **BME280** - I2C temperature/humidity/pressure sensor
|
|
118
|
-
- **DS18B20** - 1-Wire digital temperature sensor
|
|
119
119
|
- **BMP180** - I2C barometric pressure/temperature sensor
|
|
120
120
|
- **MCP9808** - I2C high-accuracy temperature sensor
|
|
121
121
|
- **SHT31** - I2C temperature/humidity sensor
|
|
122
|
+
- **DS18B20** - 1-Wire digital temperature sensor
|
|
123
|
+
- **SCD30** - I2C NDIR CO2/temperature/humidity sensor (NEW)
|
|
124
|
+
|
|
125
|
+
**Light & Motion Sensors:**
|
|
122
126
|
- **BH1750** - I2C ambient light sensor (lux)
|
|
123
127
|
- **TSL2561** - I2C ambient light sensor (lux)
|
|
128
|
+
- **ADXL345** - I2C 3-axis accelerometer (vibration monitoring) (NEW)
|
|
129
|
+
|
|
130
|
+
**Industrial Sensors:**
|
|
124
131
|
- **INA219** - I2C bus voltage/current monitor
|
|
132
|
+
- **YF-S201** - GPIO hall effect flow meter (liquid flow) (NEW)
|
|
133
|
+
- **NEO-6M** - UART/Serial GPS module (location tracking) (NEW)
|
|
125
134
|
|
|
126
135
|
Sensors use a provider pattern for testability and hardware abstraction.
|
|
127
136
|
|
|
@@ -245,6 +254,101 @@ temp = sensor.readings.first
|
|
|
245
254
|
puts "#{temp.value}°C"
|
|
246
255
|
```
|
|
247
256
|
|
|
257
|
+
### ADXL345 Accelerometer (Vibration Monitoring)
|
|
258
|
+
|
|
259
|
+
```ruby path=null start=null
|
|
260
|
+
require 'dredger/iot'
|
|
261
|
+
|
|
262
|
+
# Set up I2C bus and ADXL345 provider
|
|
263
|
+
i2c = Dredger::IoT::Bus::Auto.i2c
|
|
264
|
+
provider = Dredger::IoT::Sensors::ADXL345Provider.new(i2c_bus: i2c, range: 2) # ±2g
|
|
265
|
+
|
|
266
|
+
# Create sensor instance (default I2C address 0x53)
|
|
267
|
+
sensor = Dredger::IoT::Sensors::ADXL345.new(
|
|
268
|
+
i2c_addr: 0x53,
|
|
269
|
+
provider: provider,
|
|
270
|
+
metadata: { location: 'motor_mount' }
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
readings = sensor.readings
|
|
274
|
+
readings.each { |r| puts "#{r.sensor_type}: #{r.value} #{r.unit}" }
|
|
275
|
+
# => acceleration_x: 0.024 g
|
|
276
|
+
# => acceleration_y: -0.012 g
|
|
277
|
+
# => acceleration_z: 0.980 g
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### SCD30 CO2 Sensor
|
|
281
|
+
|
|
282
|
+
```ruby path=null start=null
|
|
283
|
+
require 'dredger/iot'
|
|
284
|
+
|
|
285
|
+
# Set up I2C bus and SCD30 provider
|
|
286
|
+
i2c = Dredger::IoT::Bus::Auto.i2c
|
|
287
|
+
provider = Dredger::IoT::Sensors::SCD30Provider.new(i2c_bus: i2c, interval: 2)
|
|
288
|
+
|
|
289
|
+
# Create sensor instance (default I2C address 0x61)
|
|
290
|
+
sensor = Dredger::IoT::Sensors::SCD30.new(
|
|
291
|
+
i2c_addr: 0x61,
|
|
292
|
+
provider: provider,
|
|
293
|
+
metadata: { location: 'greenhouse' }
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
readings = sensor.readings
|
|
297
|
+
readings.each { |r| puts "#{r.sensor_type}: #{r.value} #{r.unit}" }
|
|
298
|
+
# => co2: 412.5 ppm
|
|
299
|
+
# => temperature: 23.45 celsius
|
|
300
|
+
# => humidity: 45.2 %
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### YF-S201 Flow Meter
|
|
304
|
+
|
|
305
|
+
```ruby path=null start=null
|
|
306
|
+
require 'dredger/iot'
|
|
307
|
+
|
|
308
|
+
# Set up GPIO bus and flow meter provider
|
|
309
|
+
gpio = Dredger::IoT::Bus::Auto.gpio
|
|
310
|
+
provider = Dredger::IoT::Sensors::YFS201Provider.new(gpio_bus: gpio, calibration_factor: 7.5)
|
|
311
|
+
|
|
312
|
+
# Create sensor instance
|
|
313
|
+
sensor = Dredger::IoT::Sensors::YFS201.new(
|
|
314
|
+
pin_label: 'P9_12',
|
|
315
|
+
provider: provider,
|
|
316
|
+
sample_duration: 1.0, # Count pulses for 1 second
|
|
317
|
+
metadata: { location: 'main_line' }
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
readings = sensor.readings
|
|
321
|
+
flow = readings.first
|
|
322
|
+
puts "Flow: #{flow.value} #{flow.unit}"
|
|
323
|
+
puts "Pulses: #{flow.metadata[:pulses]}"
|
|
324
|
+
# => Flow: 5.25 L/min
|
|
325
|
+
# => Pulses: 656
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### NEO-6M GPS Module
|
|
329
|
+
|
|
330
|
+
```ruby path=null start=null
|
|
331
|
+
require 'dredger/iot'
|
|
332
|
+
|
|
333
|
+
# Set up GPS provider
|
|
334
|
+
provider = Dredger::IoT::Sensors::NEO6MProvider.new(baud_rate: 9600, timeout: 5)
|
|
335
|
+
|
|
336
|
+
# Create sensor instance
|
|
337
|
+
sensor = Dredger::IoT::Sensors::NEO6M.new(
|
|
338
|
+
device: '/dev/ttyAMA0', # Serial device
|
|
339
|
+
provider: provider,
|
|
340
|
+
metadata: { vehicle: 'truck_1' }
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
readings = sensor.readings
|
|
344
|
+
readings.each { |r| puts "#{r.sensor_type}: #{r.value} #{r.unit}" }
|
|
345
|
+
# => latitude: 37.774929 degrees
|
|
346
|
+
# => longitude: -122.419418 degrees
|
|
347
|
+
# => altitude: 52.4 m
|
|
348
|
+
# => speed: 12.5 km/h
|
|
349
|
+
# => gps_quality: 8 satellites
|
|
350
|
+
```
|
|
351
|
+
|
|
248
352
|
### Multiple Sensors with Scheduled Polling
|
|
249
353
|
|
|
250
354
|
```ruby path=null start=null
|
|
@@ -596,6 +700,25 @@ reading.timestamp # Time object when reading was taken
|
|
|
596
700
|
dredger read ina219 0x40 --shunt 0.1
|
|
597
701
|
```
|
|
598
702
|
|
|
703
|
+
- **`ADXL345`** - 3-axis accelerometer (I2C)
|
|
704
|
+
- Parameters: `i2c_addr` (default: `0x53`), `provider`
|
|
705
|
+
- Returns: acceleration_x (g), acceleration_y (g), acceleration_z (g)
|
|
706
|
+
- Ranges: ±2g, ±4g, ±8g, ±16g (configurable in provider)
|
|
707
|
+
|
|
708
|
+
- **`SCD30`** - NDIR CO2 sensor (I2C)
|
|
709
|
+
- Parameters: `i2c_addr` (default: `0x61`), `provider`
|
|
710
|
+
- Returns: co2 (ppm), temperature (celsius), humidity (%)
|
|
711
|
+
- Range: 400-10,000 ppm CO2
|
|
712
|
+
|
|
713
|
+
- **`YFS201`** - Hall effect flow meter (GPIO)
|
|
714
|
+
- Parameters: `pin_label`, `provider`, `sample_duration` (default: `1.0`)
|
|
715
|
+
- Returns: flow_rate (L/min)
|
|
716
|
+
- Range: 1-30 L/min
|
|
717
|
+
|
|
718
|
+
- **`NEO6M`** - GPS module (UART/Serial)
|
|
719
|
+
- Parameters: `device` (default: `'/dev/ttyAMA0'`), `provider`
|
|
720
|
+
- Returns: latitude (degrees), longitude (degrees), altitude (m), speed (km/h), gps_quality (satellites)
|
|
721
|
+
|
|
599
722
|
### Scheduling
|
|
600
723
|
|
|
601
724
|
#### `Dredger::IoT::Scheduler.periodic_with_jitter`
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# ADXL345 3-axis digital accelerometer sensor (I2C/SPI)
|
|
7
|
+
# Measures acceleration/vibration in x, y, z axes
|
|
8
|
+
# Uses a provider interface to allow simulation in tests and hardware backends in production.
|
|
9
|
+
class ADXL345 < BaseSensor
|
|
10
|
+
# provider must respond to :read_measurements(i2c_addr) -> { x_g:, y_g:, z_g: }
|
|
11
|
+
def initialize(i2c_addr: 0x53, provider:, metadata: {})
|
|
12
|
+
super(metadata: metadata)
|
|
13
|
+
@i2c_addr = i2c_addr
|
|
14
|
+
@provider = provider
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def readings
|
|
18
|
+
sample = @provider.read_measurements(@i2c_addr)
|
|
19
|
+
[
|
|
20
|
+
reading(sensor_type: 'acceleration_x', value: sample[:x_g], unit: 'g'),
|
|
21
|
+
reading(sensor_type: 'acceleration_y', value: sample[:y_g], unit: 'g'),
|
|
22
|
+
reading(sensor_type: 'acceleration_z', value: sample[:z_g], unit: 'g')
|
|
23
|
+
]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# Hardware provider for ADXL345 3-axis accelerometer over I2C.
|
|
7
|
+
# Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
|
|
8
|
+
#
|
|
9
|
+
# Key features:
|
|
10
|
+
# - ±2g, ±4g, ±8g, ±16g selectable measurement ranges
|
|
11
|
+
# - 10-bit to 13-bit resolution
|
|
12
|
+
# - I2C (up to 400 kHz) or SPI interface
|
|
13
|
+
# - Default I2C address: 0x53 (ALT address: 0x1D if SDO/ALT pulled high)
|
|
14
|
+
#
|
|
15
|
+
# Key registers:
|
|
16
|
+
# - 0x00: DEVID (should be 0xE5)
|
|
17
|
+
# - 0x2D: POWER_CTL (power modes)
|
|
18
|
+
# - 0x31: DATA_FORMAT (range and resolution)
|
|
19
|
+
# - 0x32-0x37: DATAX0, DATAX1, DATAY0, DATAY1, DATAZ0, DATAZ1 (acceleration data)
|
|
20
|
+
class ADXL345Provider
|
|
21
|
+
DEVID_REG = 0x00
|
|
22
|
+
DEVID_EXPECTED = 0xE5
|
|
23
|
+
POWER_CTL_REG = 0x2D
|
|
24
|
+
DATA_FORMAT_REG = 0x31
|
|
25
|
+
DATAX0_REG = 0x32
|
|
26
|
+
|
|
27
|
+
# Measurement mode bit (POWER_CTL register)
|
|
28
|
+
MEASURE_BIT = 0x08
|
|
29
|
+
|
|
30
|
+
# i2c_bus: an I2C bus interface (e.g., Dredger::IoT::Bus::Auto.i2c)
|
|
31
|
+
# range: measurement range in g (2, 4, 8, or 16)
|
|
32
|
+
def initialize(i2c_bus:, range: 2)
|
|
33
|
+
@i2c = i2c_bus
|
|
34
|
+
@range = range
|
|
35
|
+
@scale_factor = calculate_scale_factor(range)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Read acceleration measurements from the ADXL345 at the given I2C address.
|
|
39
|
+
# Returns { x_g: Float, y_g: Float, z_g: Float }
|
|
40
|
+
def read_measurements(addr)
|
|
41
|
+
# Verify device ID
|
|
42
|
+
dev_id = @i2c.read(addr, 1, register: DEVID_REG).first
|
|
43
|
+
raise IOError, "ADXL345 not found (devid=0x#{dev_id.to_s(16)})" unless dev_id == DEVID_EXPECTED
|
|
44
|
+
|
|
45
|
+
# Configure sensor (one-time setup)
|
|
46
|
+
configure_sensor(addr)
|
|
47
|
+
|
|
48
|
+
# Read 6 bytes of acceleration data (X, Y, Z as 16-bit signed integers)
|
|
49
|
+
raw = @i2c.read(addr, 6, register: DATAX0_REG)
|
|
50
|
+
|
|
51
|
+
# Parse 16-bit signed values (little-endian)
|
|
52
|
+
x_raw = to_signed16(raw[0] | (raw[1] << 8))
|
|
53
|
+
y_raw = to_signed16(raw[2] | (raw[3] << 8))
|
|
54
|
+
z_raw = to_signed16(raw[4] | (raw[5] << 8))
|
|
55
|
+
|
|
56
|
+
# Convert to g units using scale factor
|
|
57
|
+
{
|
|
58
|
+
x_g: (x_raw * @scale_factor).round(3),
|
|
59
|
+
y_g: (y_raw * @scale_factor).round(3),
|
|
60
|
+
z_g: (z_raw * @scale_factor).round(3)
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# Configure sensor for measurement mode with specified range
|
|
67
|
+
def configure_sensor(addr)
|
|
68
|
+
# Set data format: range bits [1:0]
|
|
69
|
+
# ±2g: 0b00, ±4g: 0b01, ±8g: 0b10, ±16g: 0b11
|
|
70
|
+
range_bits = case @range
|
|
71
|
+
when 2 then 0b00
|
|
72
|
+
when 4 then 0b01
|
|
73
|
+
when 8 then 0b10
|
|
74
|
+
when 16 then 0b11
|
|
75
|
+
else raise ArgumentError, "Invalid range: #{@range}g (must be 2, 4, 8, or 16)"
|
|
76
|
+
end
|
|
77
|
+
@i2c.write(addr, [range_bits], register: DATA_FORMAT_REG)
|
|
78
|
+
|
|
79
|
+
# Enable measurement mode
|
|
80
|
+
@i2c.write(addr, [MEASURE_BIT], register: POWER_CTL_REG)
|
|
81
|
+
|
|
82
|
+
# Wait for sensor to stabilize
|
|
83
|
+
sleep(0.01)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Calculate scale factor for converting raw values to g
|
|
87
|
+
# ADXL345 uses 10-bit resolution in full resolution mode
|
|
88
|
+
# Scale factor: range / 512 (for 10-bit)
|
|
89
|
+
def calculate_scale_factor(range)
|
|
90
|
+
# In full resolution mode, scale is ~3.9 mg/LSB regardless of range
|
|
91
|
+
# In fixed 10-bit mode, scale depends on range
|
|
92
|
+
# Using simplified calculation: range / 512
|
|
93
|
+
range / 512.0
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Convert unsigned 16-bit to signed 16-bit
|
|
97
|
+
def to_signed16(val)
|
|
98
|
+
val > 32_767 ? val - 65_536 : val
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# NEO-6M GPS module (UART/Serial)
|
|
7
|
+
# Provides location, altitude, speed, and satellite information
|
|
8
|
+
# Uses a provider interface to allow simulation in tests and hardware backends in production.
|
|
9
|
+
class NEO6M < BaseSensor
|
|
10
|
+
# provider must respond to :read_position(device) -> { latitude:, longitude:, altitude:, speed:, satellites: }
|
|
11
|
+
def initialize(device: '/dev/ttyAMA0', provider:, metadata: {})
|
|
12
|
+
super(metadata: metadata)
|
|
13
|
+
@device = device
|
|
14
|
+
@provider = provider
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def readings
|
|
18
|
+
sample = @provider.read_position(@device)
|
|
19
|
+
[
|
|
20
|
+
reading(sensor_type: 'latitude', value: sample[:latitude], unit: 'degrees'),
|
|
21
|
+
reading(sensor_type: 'longitude', value: sample[:longitude], unit: 'degrees'),
|
|
22
|
+
reading(sensor_type: 'altitude', value: sample[:altitude], unit: 'm'),
|
|
23
|
+
reading(sensor_type: 'speed', value: sample[:speed], unit: 'km/h'),
|
|
24
|
+
reading(
|
|
25
|
+
sensor_type: 'gps_quality',
|
|
26
|
+
value: sample[:satellites],
|
|
27
|
+
unit: 'satellites',
|
|
28
|
+
metadata: { fix_quality: sample[:fix_quality] }
|
|
29
|
+
)
|
|
30
|
+
]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'io/wait'
|
|
4
|
+
|
|
5
|
+
module Dredger
|
|
6
|
+
module IoT
|
|
7
|
+
module Sensors
|
|
8
|
+
# Hardware provider for NEO-6M GPS module via UART/Serial with NMEA parsing.
|
|
9
|
+
# Datasheet: https://www.u-blox.com/sites/default/files/products/documents/NEO-6_DataSheet_(GPS.G6-HW-09005).pdf
|
|
10
|
+
#
|
|
11
|
+
# Key features:
|
|
12
|
+
# - 50-channel GPS receiver
|
|
13
|
+
# - UART interface (default: 9600 baud, 8N1)
|
|
14
|
+
# - NMEA 0183 protocol output
|
|
15
|
+
# - Update rate: 1-5 Hz (default: 1 Hz)
|
|
16
|
+
# - Cold start: ~27s, Warm start: ~1s
|
|
17
|
+
#
|
|
18
|
+
# NMEA Sentences:
|
|
19
|
+
# - $GPGGA: Global Positioning System Fix Data (position, altitude, satellites)
|
|
20
|
+
# - $GPRMC: Recommended Minimum Navigation Information (position, speed, date/time)
|
|
21
|
+
# - $GPGSA: GPS DOP and Active Satellites
|
|
22
|
+
# - $GPGSV: GPS Satellites in View
|
|
23
|
+
class NEO6MProvider
|
|
24
|
+
# baud_rate: serial baud rate (default: 9600)
|
|
25
|
+
# timeout: read timeout in seconds (default: 5)
|
|
26
|
+
def initialize(baud_rate: 9600, timeout: 5)
|
|
27
|
+
@baud_rate = baud_rate
|
|
28
|
+
@timeout = timeout
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Read GPS position by parsing NMEA sentences from the serial device.
|
|
32
|
+
# Returns { latitude: Float, longitude: Float, altitude: Float, speed: Float, satellites: Integer, fix_quality: Integer }
|
|
33
|
+
#
|
|
34
|
+
# @param device [String] Serial device path (e.g., '/dev/ttyAMA0', '/dev/ttyUSB0')
|
|
35
|
+
def read_position(device)
|
|
36
|
+
File.open(device, 'r+') do |serial|
|
|
37
|
+
configure_serial(serial)
|
|
38
|
+
|
|
39
|
+
# Read NMEA sentences until we have both GGA and RMC (or timeout)
|
|
40
|
+
gga_data = nil
|
|
41
|
+
rmc_data = nil
|
|
42
|
+
start_time = Time.now
|
|
43
|
+
|
|
44
|
+
while (gga_data.nil? || rmc_data.nil?) && (Time.now - start_time < @timeout)
|
|
45
|
+
line = read_line(serial, @timeout)
|
|
46
|
+
next unless line
|
|
47
|
+
|
|
48
|
+
if line.start_with?('$GPGGA') || line.start_with?('$GNGGA')
|
|
49
|
+
gga_data = parse_gga(line)
|
|
50
|
+
elsif line.start_with?('$GPRMC') || line.start_with?('$GNRMC')
|
|
51
|
+
rmc_data = parse_rmc(line)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
raise IOError, 'GPS timeout: no valid NMEA sentences received' if gga_data.nil? && rmc_data.nil?
|
|
56
|
+
|
|
57
|
+
# Merge data from both sentences (GGA has altitude/satellites, RMC has speed)
|
|
58
|
+
{
|
|
59
|
+
latitude: gga_data&.dig(:latitude) || rmc_data&.dig(:latitude) || 0.0,
|
|
60
|
+
longitude: gga_data&.dig(:longitude) || rmc_data&.dig(:longitude) || 0.0,
|
|
61
|
+
altitude: gga_data&.dig(:altitude) || 0.0,
|
|
62
|
+
speed: rmc_data&.dig(:speed) || 0.0,
|
|
63
|
+
satellites: gga_data&.dig(:satellites) || 0,
|
|
64
|
+
fix_quality: gga_data&.dig(:fix_quality) || 0
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# Configure serial port (Linux termios settings)
|
|
72
|
+
def configure_serial(serial)
|
|
73
|
+
# Set raw mode, no echo, baud rate
|
|
74
|
+
# This would typically use `stty` or `termios` gem
|
|
75
|
+
# For simplicity, assuming device is already configured
|
|
76
|
+
# In production, use: system("stty -F #{device} #{@baud_rate} raw -echo")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Read a line from serial with timeout
|
|
80
|
+
def read_line(serial, timeout)
|
|
81
|
+
return nil unless serial.wait_readable(timeout)
|
|
82
|
+
|
|
83
|
+
serial.gets&.chomp
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Parse $GPGGA sentence: Global Positioning System Fix Data
|
|
87
|
+
# Format: $GPGGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh
|
|
88
|
+
# Example: $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
|
|
89
|
+
def parse_gga(sentence)
|
|
90
|
+
parts = sentence.split(',')
|
|
91
|
+
return nil if parts.size < 15 || parts[6].to_i.zero? # Check fix quality
|
|
92
|
+
|
|
93
|
+
{
|
|
94
|
+
latitude: parse_coordinate(parts[2], parts[3]),
|
|
95
|
+
longitude: parse_coordinate(parts[4], parts[5]),
|
|
96
|
+
fix_quality: parts[6].to_i,
|
|
97
|
+
satellites: parts[7].to_i,
|
|
98
|
+
altitude: parts[9].to_f
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Parse $GPRMC sentence: Recommended Minimum Navigation Information
|
|
103
|
+
# Format: $GPRMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,ddmmyy,x.x,a*hh
|
|
104
|
+
# Example: $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
|
|
105
|
+
def parse_rmc(sentence)
|
|
106
|
+
parts = sentence.split(',')
|
|
107
|
+
return nil if parts.size < 12 || parts[2] != 'A' # Check if data is valid
|
|
108
|
+
|
|
109
|
+
{
|
|
110
|
+
latitude: parse_coordinate(parts[3], parts[4]),
|
|
111
|
+
longitude: parse_coordinate(parts[5], parts[6]),
|
|
112
|
+
speed: parts[7].to_f * 1.852 # Convert knots to km/h
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Parse NMEA coordinate format (ddmm.mmmm) to decimal degrees
|
|
117
|
+
# @param coord_str [String] Coordinate string (e.g., "4807.038")
|
|
118
|
+
# @param direction [String] Direction (N/S for latitude, E/W for longitude)
|
|
119
|
+
def parse_coordinate(coord_str, direction)
|
|
120
|
+
return 0.0 if coord_str.nil? || coord_str.empty?
|
|
121
|
+
|
|
122
|
+
# Determine if latitude or longitude based on length
|
|
123
|
+
# Latitude: ddmm.mmmm (2 digit degrees)
|
|
124
|
+
# Longitude: dddmm.mmmm (3 digit degrees)
|
|
125
|
+
degree_digits = coord_str.length >= 5 && coord_str[4] == '.' ? 2 : 3
|
|
126
|
+
|
|
127
|
+
degrees = coord_str[0, degree_digits].to_f
|
|
128
|
+
minutes = coord_str[degree_digits..-1].to_f
|
|
129
|
+
|
|
130
|
+
decimal_degrees = degrees + (minutes / 60.0)
|
|
131
|
+
|
|
132
|
+
# Apply direction (negative for South and West)
|
|
133
|
+
decimal_degrees *= -1 if %w[S W].include?(direction)
|
|
134
|
+
|
|
135
|
+
decimal_degrees.round(6)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# SCD30 NDIR CO2 sensor with integrated temperature and humidity sensor
|
|
7
|
+
# Measures CO2 concentration, temperature, and humidity
|
|
8
|
+
# Uses a provider interface to allow simulation in tests and hardware backends in production.
|
|
9
|
+
class SCD30 < BaseSensor
|
|
10
|
+
# provider must respond to :read_measurements(i2c_addr) -> { co2_ppm:, temperature_c:, humidity: }
|
|
11
|
+
def initialize(i2c_addr: 0x61, provider:, metadata: {})
|
|
12
|
+
super(metadata: metadata)
|
|
13
|
+
@i2c_addr = i2c_addr
|
|
14
|
+
@provider = provider
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def readings
|
|
18
|
+
sample = @provider.read_measurements(@i2c_addr)
|
|
19
|
+
[
|
|
20
|
+
reading(sensor_type: 'co2', value: sample[:co2_ppm], unit: 'ppm'),
|
|
21
|
+
reading(sensor_type: 'temperature', value: sample[:temperature_c], unit: 'celsius'),
|
|
22
|
+
reading(sensor_type: 'humidity', value: sample[:humidity], unit: '%')
|
|
23
|
+
]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# Hardware provider for SCD30 CO2, temperature, and humidity sensor over I2C.
|
|
7
|
+
# Datasheet: https://sensirion.com/media/documents/4EAF6AF8/61652C3C/Sensirion_CO2_Sensors_SCD30_Datasheet.pdf
|
|
8
|
+
#
|
|
9
|
+
# Key features:
|
|
10
|
+
# - NDIR CO2 sensor (400-10,000 ppm range)
|
|
11
|
+
# - Integrated SHT31 temperature and humidity sensor
|
|
12
|
+
# - I2C interface (default address: 0x61)
|
|
13
|
+
# - Automatic self-calibration
|
|
14
|
+
# - Measurement interval: 2-1800 seconds
|
|
15
|
+
#
|
|
16
|
+
# Key commands:
|
|
17
|
+
# - 0x0010: Start continuous measurement
|
|
18
|
+
# - 0x0104: Stop continuous measurement
|
|
19
|
+
# - 0x0202: Set measurement interval
|
|
20
|
+
# - 0x0300: Data ready status
|
|
21
|
+
# - 0x0027: Read measurement (18 bytes: CO2, temp, humidity)
|
|
22
|
+
class SCD30Provider
|
|
23
|
+
CMD_START_MEASUREMENT = 0x0010
|
|
24
|
+
CMD_STOP_MEASUREMENT = 0x0104
|
|
25
|
+
CMD_SET_INTERVAL = 0x0202
|
|
26
|
+
CMD_DATA_READY = 0x0300
|
|
27
|
+
CMD_READ_MEASUREMENT = 0x0027
|
|
28
|
+
|
|
29
|
+
# i2c_bus: an I2C bus interface (e.g., Dredger::IoT::Bus::Auto.i2c)
|
|
30
|
+
# interval: measurement interval in seconds (2-1800)
|
|
31
|
+
# ambient_pressure: ambient pressure compensation in mBar (700-1400, 0=disable)
|
|
32
|
+
def initialize(i2c_bus:, interval: 2, ambient_pressure: 0)
|
|
33
|
+
@i2c = i2c_bus
|
|
34
|
+
@interval = interval
|
|
35
|
+
@ambient_pressure = ambient_pressure
|
|
36
|
+
@initialized = false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Read measurements from the SCD30 at the given I2C address.
|
|
40
|
+
# Returns { co2_ppm: Float, temperature_c: Float, humidity: Float }
|
|
41
|
+
def read_measurements(addr)
|
|
42
|
+
# Initialize sensor on first read
|
|
43
|
+
initialize_sensor(addr) unless @initialized
|
|
44
|
+
|
|
45
|
+
# Wait for data ready
|
|
46
|
+
wait_for_data_ready(addr)
|
|
47
|
+
|
|
48
|
+
# Read 18 bytes of measurement data
|
|
49
|
+
# Format: [CO2_MSB, CO2_LSB, CO2_CRC] [CO2_MSB, CO2_LSB, CO2_CRC] [T_MSB, T_LSB, T_CRC] [T_MSB, T_LSB, T_CRC] [H_MSB, H_LSB, H_CRC] [H_MSB, H_LSB, H_CRC]
|
|
50
|
+
# Actually: 3 float32 values (4 bytes each + CRC after every 2 bytes = 6 bytes per value)
|
|
51
|
+
@i2c.write(addr, [CMD_READ_MEASUREMENT >> 8, CMD_READ_MEASUREMENT & 0xFF])
|
|
52
|
+
sleep(0.01) # Wait for response
|
|
53
|
+
raw = @i2c.read(addr, 18)
|
|
54
|
+
|
|
55
|
+
# Parse float32 values with CRC validation
|
|
56
|
+
co2_ppm = parse_float32_with_crc(raw, 0)
|
|
57
|
+
temp_c = parse_float32_with_crc(raw, 6)
|
|
58
|
+
humidity = parse_float32_with_crc(raw, 12)
|
|
59
|
+
|
|
60
|
+
{
|
|
61
|
+
co2_ppm: co2_ppm.round(1),
|
|
62
|
+
temperature_c: temp_c.round(2),
|
|
63
|
+
humidity: humidity.round(1)
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# Initialize sensor with measurement interval and start continuous measurement
|
|
70
|
+
def initialize_sensor(addr)
|
|
71
|
+
# Set measurement interval
|
|
72
|
+
write_command_with_arg(addr, CMD_SET_INTERVAL, @interval)
|
|
73
|
+
sleep(0.01)
|
|
74
|
+
|
|
75
|
+
# Start continuous measurement with optional pressure compensation
|
|
76
|
+
write_command_with_arg(addr, CMD_START_MEASUREMENT, @ambient_pressure)
|
|
77
|
+
sleep(0.02)
|
|
78
|
+
|
|
79
|
+
@initialized = true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Wait for data to be ready (poll data ready status)
|
|
83
|
+
def wait_for_data_ready(addr, timeout: 2.0)
|
|
84
|
+
start_time = Time.now
|
|
85
|
+
loop do
|
|
86
|
+
@i2c.write(addr, [CMD_DATA_READY >> 8, CMD_DATA_READY & 0xFF])
|
|
87
|
+
sleep(0.01)
|
|
88
|
+
status = @i2c.read(addr, 3)
|
|
89
|
+
# Data ready when bit 0 of word is 1
|
|
90
|
+
data_ready = (status[1] & 0x01) == 1
|
|
91
|
+
return if data_ready
|
|
92
|
+
|
|
93
|
+
raise IOError, 'SCD30 data ready timeout' if Time.now - start_time > timeout
|
|
94
|
+
|
|
95
|
+
sleep(0.1)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Write a command with a 16-bit argument and CRC
|
|
100
|
+
def write_command_with_arg(addr, command, arg)
|
|
101
|
+
cmd_msb = command >> 8
|
|
102
|
+
cmd_lsb = command & 0xFF
|
|
103
|
+
arg_msb = arg >> 8
|
|
104
|
+
arg_lsb = arg & 0xFF
|
|
105
|
+
crc = calculate_crc8([arg_msb, arg_lsb])
|
|
106
|
+
@i2c.write(addr, [cmd_msb, cmd_lsb, arg_msb, arg_lsb, crc])
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Parse a float32 value from 6 bytes (4 data + 2 CRC)
|
|
110
|
+
# Format: [MSB0, LSB0, CRC0, MSB1, LSB1, CRC1]
|
|
111
|
+
def parse_float32_with_crc(data, offset)
|
|
112
|
+
# Verify CRCs
|
|
113
|
+
crc0 = calculate_crc8([data[offset], data[offset + 1]])
|
|
114
|
+
crc1 = calculate_crc8([data[offset + 3], data[offset + 4]])
|
|
115
|
+
raise IOError, 'SCD30 CRC error' unless crc0 == data[offset + 2] && crc1 == data[offset + 5]
|
|
116
|
+
|
|
117
|
+
# Combine bytes into uint32 and convert to float32
|
|
118
|
+
bytes = [
|
|
119
|
+
data[offset],
|
|
120
|
+
data[offset + 1],
|
|
121
|
+
data[offset + 3],
|
|
122
|
+
data[offset + 4]
|
|
123
|
+
].pack('C4').unpack1('N')
|
|
124
|
+
[bytes].pack('L>').unpack1('g')
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Calculate CRC-8 checksum (polynomial: 0x31, init: 0xFF)
|
|
128
|
+
def calculate_crc8(data)
|
|
129
|
+
crc = 0xFF
|
|
130
|
+
data.each do |byte|
|
|
131
|
+
crc ^= byte
|
|
132
|
+
8.times do
|
|
133
|
+
crc = (crc & 0x80) != 0 ? ((crc << 1) ^ 0x31) : (crc << 1)
|
|
134
|
+
crc &= 0xFF
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
crc
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# YF-S201 water flow sensor (hall effect pulse counter)
|
|
7
|
+
# Measures liquid flow rate by counting pulses from hall effect sensor
|
|
8
|
+
# Uses a provider interface to allow simulation in tests and hardware backends in production.
|
|
9
|
+
class YFS201 < BaseSensor
|
|
10
|
+
# provider must respond to :read_flow_rate(pin_label, duration) -> { flow_rate_lpm: Float, pulses: Integer }
|
|
11
|
+
def initialize(pin_label:, provider:, sample_duration: 1.0, metadata: {})
|
|
12
|
+
super(metadata: metadata)
|
|
13
|
+
@pin_label = pin_label
|
|
14
|
+
@provider = provider
|
|
15
|
+
@sample_duration = sample_duration
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def readings
|
|
19
|
+
sample = @provider.read_flow_rate(@pin_label, @sample_duration)
|
|
20
|
+
[
|
|
21
|
+
reading(
|
|
22
|
+
sensor_type: 'flow_rate',
|
|
23
|
+
value: sample[:flow_rate_lpm],
|
|
24
|
+
unit: 'L/min',
|
|
25
|
+
metadata: { pulses: sample[:pulses], duration: @sample_duration }
|
|
26
|
+
)
|
|
27
|
+
]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dredger
|
|
4
|
+
module IoT
|
|
5
|
+
module Sensors
|
|
6
|
+
# Hardware provider for YF-S201 water flow sensor via GPIO pulse counting.
|
|
7
|
+
# Datasheet: https://www.hobbytronics.co.uk/download/YF-S201.pdf
|
|
8
|
+
#
|
|
9
|
+
# Key features:
|
|
10
|
+
# - Hall effect sensor with digital output
|
|
11
|
+
# - Flow rate range: 1-30 L/min
|
|
12
|
+
# - Pulse frequency: ~4.5 * flow_rate (L/min)
|
|
13
|
+
# - Working voltage: 5V DC
|
|
14
|
+
# - Thread size: G1/2" (DN15)
|
|
15
|
+
#
|
|
16
|
+
# Calibration:
|
|
17
|
+
# - Frequency (Hz) = 7.5 * flow rate (L/min) (official spec)
|
|
18
|
+
# - In practice: F = K * Q where K ≈ 4.5-7.5 depending on unit
|
|
19
|
+
# - Default calibration factor: 7.5 (pulses per liter)
|
|
20
|
+
class YFS201Provider
|
|
21
|
+
# gpio_bus: a GPIO bus interface (e.g., Dredger::IoT::Bus::Auto.gpio)
|
|
22
|
+
# calibration_factor: pulses per liter (default: 7.5 for YF-S201)
|
|
23
|
+
def initialize(gpio_bus:, calibration_factor: 7.5)
|
|
24
|
+
@gpio = gpio_bus
|
|
25
|
+
@calibration_factor = calibration_factor
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Read flow rate by counting pulses over the specified duration.
|
|
29
|
+
# Returns { flow_rate_lpm: Float, pulses: Integer }
|
|
30
|
+
#
|
|
31
|
+
# @param pin_label [String] GPIO pin label (e.g., 'P9_12', 'GPIO17')
|
|
32
|
+
# @param duration [Float] Sampling duration in seconds
|
|
33
|
+
def read_flow_rate(pin_label, duration)
|
|
34
|
+
# Configure pin as input
|
|
35
|
+
@gpio.set_direction(pin_label, :in)
|
|
36
|
+
|
|
37
|
+
# Count rising edge pulses over the duration
|
|
38
|
+
pulses = count_pulses(pin_label, duration)
|
|
39
|
+
|
|
40
|
+
# Calculate flow rate in L/min
|
|
41
|
+
# pulses_per_second = pulses / duration
|
|
42
|
+
# liters_per_second = pulses_per_second / calibration_factor
|
|
43
|
+
# liters_per_minute = liters_per_second * 60
|
|
44
|
+
flow_rate_lpm = (pulses / duration / @calibration_factor * 60.0).round(2)
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
flow_rate_lpm: flow_rate_lpm,
|
|
48
|
+
pulses: pulses
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# Count rising edge transitions on the pin over the specified duration.
|
|
55
|
+
# This is a simplified implementation - production code should use:
|
|
56
|
+
# - Hardware interrupts (kernel module or pigpio daemon)
|
|
57
|
+
# - Edge detection via sysfs GPIO (epoll)
|
|
58
|
+
# - High-priority thread for accurate timing
|
|
59
|
+
def count_pulses(pin_label, duration)
|
|
60
|
+
pulses = 0
|
|
61
|
+
last_state = @gpio.read(pin_label)
|
|
62
|
+
start_time = Time.now
|
|
63
|
+
|
|
64
|
+
# Poll GPIO at high frequency to detect edges
|
|
65
|
+
# Note: This is CPU-intensive and timing-dependent
|
|
66
|
+
# For production, use hardware interrupts or kernel GPIO edge detection
|
|
67
|
+
while Time.now - start_time < duration
|
|
68
|
+
current_state = @gpio.read(pin_label)
|
|
69
|
+
|
|
70
|
+
# Detect rising edge (0 -> 1 transition)
|
|
71
|
+
if current_state == 1 && last_state == 0
|
|
72
|
+
pulses += 1
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
last_state = current_state
|
|
76
|
+
|
|
77
|
+
# Small sleep to reduce CPU usage
|
|
78
|
+
# Trade-off: Higher sleep = lower CPU, but may miss pulses
|
|
79
|
+
# For accurate counting, use interrupts instead
|
|
80
|
+
sleep(0.0001) # 0.1ms
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
pulses
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
data/lib/dredger/iot/sensors.rb
CHANGED
|
@@ -17,3 +17,11 @@ require_relative 'sensors/tsl2561'
|
|
|
17
17
|
require_relative 'sensors/tsl2561_provider'
|
|
18
18
|
require_relative 'sensors/ina219'
|
|
19
19
|
require_relative 'sensors/ina219_provider'
|
|
20
|
+
require_relative 'sensors/adxl345'
|
|
21
|
+
require_relative 'sensors/adxl345_provider'
|
|
22
|
+
require_relative 'sensors/scd30'
|
|
23
|
+
require_relative 'sensors/scd30_provider'
|
|
24
|
+
require_relative 'sensors/yf_s201'
|
|
25
|
+
require_relative 'sensors/yf_s201_provider'
|
|
26
|
+
require_relative 'sensors/neo6m'
|
|
27
|
+
require_relative 'sensors/neo6m_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.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The Mad Botter INC
|
|
@@ -53,6 +53,8 @@ files:
|
|
|
53
53
|
- lib/dredger/iot/reading.rb
|
|
54
54
|
- lib/dredger/iot/scheduler.rb
|
|
55
55
|
- lib/dredger/iot/sensors.rb
|
|
56
|
+
- lib/dredger/iot/sensors/adxl345.rb
|
|
57
|
+
- lib/dredger/iot/sensors/adxl345_provider.rb
|
|
56
58
|
- lib/dredger/iot/sensors/base_sensor.rb
|
|
57
59
|
- lib/dredger/iot/sensors/bh1750.rb
|
|
58
60
|
- lib/dredger/iot/sensors/bh1750_provider.rb
|
|
@@ -66,10 +68,16 @@ files:
|
|
|
66
68
|
- lib/dredger/iot/sensors/ina219.rb
|
|
67
69
|
- lib/dredger/iot/sensors/ina219_provider.rb
|
|
68
70
|
- lib/dredger/iot/sensors/mcp9808.rb
|
|
71
|
+
- lib/dredger/iot/sensors/neo6m.rb
|
|
72
|
+
- lib/dredger/iot/sensors/neo6m_provider.rb
|
|
73
|
+
- lib/dredger/iot/sensors/scd30.rb
|
|
74
|
+
- lib/dredger/iot/sensors/scd30_provider.rb
|
|
69
75
|
- lib/dredger/iot/sensors/sht31.rb
|
|
70
76
|
- lib/dredger/iot/sensors/sht31_provider.rb
|
|
71
77
|
- lib/dredger/iot/sensors/tsl2561.rb
|
|
72
78
|
- lib/dredger/iot/sensors/tsl2561_provider.rb
|
|
79
|
+
- lib/dredger/iot/sensors/yf_s201.rb
|
|
80
|
+
- lib/dredger/iot/sensors/yf_s201_provider.rb
|
|
73
81
|
- lib/dredger/iot/version.rb
|
|
74
82
|
homepage: https://github.com/TheMadBotterINC/dredger-iot
|
|
75
83
|
licenses:
|
|
@@ -98,7 +106,7 @@ post_install_message: "\n ═════════════════
|
|
|
98
106
|
\ | \n |_____________________________| \n
|
|
99
107
|
\ ~~~ \\ // ~~~ \n \\_______________//
|
|
100
108
|
\ \n ═══════════════════════════════════════════════════════════════════\nHardware
|
|
101
|
-
Integration for Embedded Linux v0.
|
|
109
|
+
Integration for Embedded Linux v0.3.0\n ═══════════════════════════════════════════════════════════════════\n\n
|
|
102
110
|
\ \U0001F389 Thanks for installing!\n\n \U0001F4DA Hardware Setup (kernel modules
|
|
103
111
|
& permissions):\n https://github.com/TheMadBotterINC/dredger-iot#hardware-setup\n\n
|
|
104
112
|
\ \U0001F680 Quick Start:\n require 'dredger/iot'\n gpio = Dredger::IoT::Bus::Auto.gpio\n
|