dredger-iot 0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: be57692da679526bf1757f3f76cda63f472b0cbe73767b31d75bb2b1d6b063ca
4
+ data.tar.gz: 2a888f1d324b99fc306c9fc4fa27d27f3d8b0ef799703faae48843230459ec0c
5
+ SHA512:
6
+ metadata.gz: 0f257ec33e61e47a21f03e741068909f04cc2c701a6a4dc6d1c06e5268071cdb1887dd2b72183c96178bc3ebb0f80de00b8e62dd02493c3cbb04d4335db86825
7
+ data.tar.gz: eb57aad72c0629d2bf05ec953b1cc8951e6e51a9e3f80f3af50681015f0439a1f592e5f572b66a5f2c00449b72562c2725b0e8b513c9d4f90b1b44c757fb0d48
data/CHANGELOG.md ADDED
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2025-10-03
11
+
12
+ ### Added
13
+ - Initial release of dredger-iot gem
14
+ - GPIO access via libgpiod FFI backend with simulation fallback
15
+ - I2C access via Linux i2c-dev FFI backend with simulation fallback
16
+ - Auto-selection API for automatic backend detection
17
+ - Beaglebone Black P9_XX pin label mapping for libgpiod
18
+ - Sensor drivers:
19
+ - DHT22 (GPIO-based temperature/humidity sensor)
20
+ - BME280 (I2C temperature/humidity/pressure sensor)
21
+ - DS18B20 (1-Wire temperature sensor via kernel w1-gpio)
22
+ - BMP180 (I2C barometric pressure/temperature sensor)
23
+ - MCP9808 (I2C high-accuracy temperature sensor)
24
+ - Provider pattern for sensor abstraction and testability
25
+ - Scheduling utilities:
26
+ - `periodic_with_jitter` for distributed polling
27
+ - `exponential_backoff` for retry logic
28
+ - Full RSpec test suite with 100% coverage (excluding hardware providers)
29
+ - RuboCop configuration and compliance
30
+ - Comprehensive documentation and usage examples
31
+
32
+ [Unreleased]: https://github.com/TheMadBotterINC/dredger-iot/compare/v0.1.0...HEAD
33
+ [0.1.0]: https://github.com/TheMadBotterINC/dredger-iot/releases/tag/v0.1.0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 The Mad Botter INC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,521 @@
1
+ ```
2
+ _______________
3
+ | DREDGER-IoT |
4
+ |_______________|
5
+ /| ___ ___ |\
6
+ / | |___| |___|| \
7
+ / |______________| \
8
+ ====|========================|====
9
+ | | |-----------| | |
10
+ | |____| |_____| |
11
+ ___|____| |____|___
12
+ ~~~~{________|_________________________|________}~~~~~~~
13
+ ~~ | \\ // | ~~~
14
+ | \\___________________// |
15
+ |_____________________________|
16
+ ~~~ \\ // ~~~
17
+ \\_______________//
18
+
19
+ Hardware Integration for Embedded Linux
20
+ ```
21
+
22
+ [![CI](https://github.com/TheMadBotterINC/dredger-iot/workflows/CI/badge.svg)](https://github.com/TheMadBotterINC/dredger-iot/actions/workflows/ci.yml)
23
+ [![Gem Version](https://badge.fury.io/rb/dredger-iot.svg)](https://badge.fury.io/rb/dredger-iot)
24
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
25
+ [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.1-ruby.svg)](https://www.ruby-lang.org/)
26
+
27
+ A small, FOSS Ruby library for hardware access on embedded Linux (Beaglebone Black, etc.).
28
+
29
+ Goals:
30
+ - Generic, device-agnostic hardware integration (no proprietary references)
31
+ - Clean abstractions for GPIO and I2C
32
+ - Pluggable sensor drivers (DHT22, BME280 as starters)
33
+ - Simple scheduling/retry helpers
34
+ - Config-driven pin mapping (Beaglebone P9_XX labels)
35
+
36
+ License: MIT (c) The Mad Botter INC
37
+
38
+ ## Installation
39
+
40
+ Add to your Gemfile:
41
+
42
+ ```ruby path=null start=null
43
+ gem 'dredger-iot', git: 'https://github.com/TheMadBotterINC/dredger-iot'
44
+ ```
45
+
46
+ ## Backends
47
+
48
+ Dredger-IoT supports multiple backends per bus:
49
+ - GPIO: Simulation (default), libgpiod (via FFI)
50
+ - I2C: Simulation (default), Linux i2c-dev (via FFI + ioctl)
51
+
52
+ Backends are not auto-required for portability. Use the Auto API to choose an appropriate backend at runtime.
53
+
54
+ ## Auto-selection API
55
+
56
+ ```ruby path=null start=null
57
+ require 'dredger/iot'
58
+
59
+ # GPIO
60
+ gpio = Dredger::IoT::Bus::Auto.gpio
61
+ # I2C
62
+ i2c = Dredger::IoT::Bus::Auto.i2c
63
+ ```
64
+
65
+ Auto rules:
66
+ - GPIO: if /dev/gpiochip0 exists and libgpiod is available → libgpiod; else simulation
67
+ - I2C: if bus path exists (default /dev/i2c-1) and backend is available → linux; else simulation
68
+
69
+ Environment overrides:
70
+ - `DREDGER_IOT_GPIO_BACKEND`: `simulation` | `libgpiod`
71
+ - `DREDGER_IOT_I2C_BACKEND`: `simulation` | `linux`
72
+
73
+ ## Beaglebone P9_XX label mapping
74
+
75
+ 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.
76
+
77
+ Example:
78
+
79
+ ```ruby path=null start=null
80
+ require 'dredger/iot'
81
+
82
+ gpio = Dredger::IoT::Bus::Auto.gpio # picks libgpiod on BBB, otherwise simulation
83
+
84
+ # Works with labels; on BBB this is translated to the correct chip:line
85
+ gpio.set_direction('P9_12', :out)
86
+ gpio.write('P9_12', 1)
87
+ value = gpio.read('P9_12')
88
+ ```
89
+
90
+ If you run on a development host (no /dev/gpiochip0), Auto will default to the simulation backend and still accept labels without error, though they have no hardware effect.
91
+
92
+ ## Sensors
93
+
94
+ Dredger-IoT includes drivers for popular embedded sensors:
95
+
96
+ - **DHT22** - GPIO humidity/temperature sensor
97
+ - **BME280** - I2C temperature/humidity/pressure sensor
98
+ - **DS18B20** - 1-Wire digital temperature sensor
99
+ - **BMP180** - I2C barometric pressure/temperature sensor
100
+ - **MCP9808** - I2C high-accuracy temperature sensor
101
+
102
+ Sensors use a provider pattern for testability and hardware abstraction.
103
+
104
+ ## Usage Examples
105
+
106
+ ### DHT22 Temperature/Humidity Sensor
107
+
108
+ ```ruby path=null start=null
109
+ require 'dredger/iot'
110
+
111
+ # Set up GPIO bus and DHT22 provider
112
+ gpio = Dredger::IoT::Bus::Auto.gpio
113
+ provider = Dredger::IoT::Sensors::DHT22Provider.new(gpio_bus: gpio)
114
+
115
+ # Create sensor instance
116
+ sensor = Dredger::IoT::Sensors::DHT22.new(
117
+ pin_label: 'P9_12',
118
+ provider: provider,
119
+ metadata: { location: 'greenhouse' }
120
+ )
121
+
122
+ # Read measurements
123
+ readings = sensor.readings
124
+ readings.each do |r|
125
+ puts "#{r.sensor_type}: #{r.value} #{r.unit}"
126
+ end
127
+ # => humidity: 65.2 %
128
+ # => temperature: 22.3 celsius
129
+ ```
130
+
131
+ ### BME280 Environmental Sensor
132
+
133
+ ```ruby path=null start=null
134
+ require 'dredger/iot'
135
+
136
+ # Set up I2C bus and BME280 provider
137
+ i2c = Dredger::IoT::Bus::Auto.i2c
138
+ provider = Dredger::IoT::Sensors::BME280Provider.new(i2c_bus: i2c)
139
+
140
+ # Create sensor instance (default I2C address 0x76)
141
+ sensor = Dredger::IoT::Sensors::BME280.new(
142
+ i2c_addr: 0x76,
143
+ provider: provider,
144
+ metadata: { location: 'weather_station' }
145
+ )
146
+
147
+ # Read all measurements
148
+ readings = sensor.readings
149
+ readings.each { |r| puts "#{r.sensor_type}: #{r.value} #{r.unit}" }
150
+ # => temperature: 24.1 celsius
151
+ # => humidity: 40.2 %
152
+ # => pressure: 101.4 kPa
153
+ ```
154
+
155
+ ### DS18B20 Waterproof Temperature Sensor
156
+
157
+ ```ruby path=null start=null
158
+ require 'dredger/iot'
159
+
160
+ # Uses Linux kernel w1-gpio module (no FFI needed)
161
+ provider = Dredger::IoT::Sensors::DS18B20Provider.new
162
+
163
+ # List available devices
164
+ devices = provider.list_devices
165
+ puts "Found devices: #{devices.inspect}"
166
+ # => ["28-0000056789ab", "28-0000056789cd"]
167
+
168
+ # Read from specific device
169
+ sensor = Dredger::IoT::Sensors::DS18B20.new(
170
+ device_id: '28-0000056789ab',
171
+ provider: provider,
172
+ metadata: { location: 'tank_a' }
173
+ )
174
+
175
+ temp = sensor.readings.first
176
+ puts "#{temp.value}°C"
177
+ ```
178
+
179
+ ### Multiple Sensors with Scheduled Polling
180
+
181
+ ```ruby path=null start=null
182
+ require 'dredger/iot'
183
+
184
+ # Set up buses
185
+ gpio = Dredger::IoT::Bus::Auto.gpio
186
+ i2c = Dredger::IoT::Bus::Auto.i2c
187
+
188
+ # Create multiple sensors
189
+ sensors = [
190
+ Dredger::IoT::Sensors::DHT22.new(
191
+ pin_label: 'P9_12',
192
+ provider: Dredger::IoT::Sensors::DHT22Provider.new(gpio_bus: gpio),
193
+ metadata: { zone: 'indoor' }
194
+ ),
195
+ Dredger::IoT::Sensors::BME280.new(
196
+ i2c_addr: 0x76,
197
+ provider: Dredger::IoT::Sensors::BME280Provider.new(i2c_bus: i2c),
198
+ metadata: { zone: 'outdoor' }
199
+ )
200
+ ]
201
+
202
+ # Poll sensors with jitter to avoid harmonics
203
+ scheduler = Dredger::IoT::Scheduler.periodic_with_jitter(
204
+ base_interval: 60.0,
205
+ jitter_ratio: 0.1
206
+ )
207
+
208
+ scheduler.each do |interval|
209
+ sleep interval
210
+
211
+ sensors.each do |sensor|
212
+ readings = sensor.readings
213
+ timestamp = Time.now.iso8601
214
+
215
+ readings.each do |r|
216
+ puts "#{timestamp} [#{r.metadata[:zone]}] #{r.sensor_type}: #{r.value} #{r.unit}"
217
+ end
218
+ end
219
+ end
220
+ ```
221
+
222
+ ## Scheduling Utilities
223
+
224
+ ### Periodic with Jitter
225
+
226
+ Avoid harmonic spikes by adding randomized jitter to intervals:
227
+
228
+ ```ruby path=null start=null
229
+ enum = Dredger::IoT::Scheduler.periodic_with_jitter(
230
+ base_interval: 10.0,
231
+ jitter_ratio: 0.2
232
+ )
233
+ sleeps = enum.take(3) # => [8.2, 10.4, 11.7] etc.
234
+ ```
235
+
236
+ ### Exponential Backoff
237
+
238
+ Retry failed operations with increasing delays:
239
+
240
+ ```ruby path=null start=null
241
+ backoff = Dredger::IoT::Scheduler.exponential_backoff(
242
+ base: 1.0,
243
+ max: 30.0,
244
+ attempts: 5
245
+ )
246
+
247
+ backoff.each do |delay|
248
+ sleep delay
249
+ break if try_operation # retry until success or attempts exhausted
250
+ end
251
+ ```
252
+
253
+ ## Hardware Setup
254
+
255
+ ### Prerequisites
256
+
257
+ On embedded Linux hardware (Beaglebone, Raspberry Pi, etc.), certain kernel modules and permissions are required.
258
+
259
+ #### GPIO (libgpiod)
260
+
261
+ ```bash path=null start=null
262
+ # Ensure gpiod tools are installed (optional but useful for testing)
263
+ sudo apt-get install gpiod
264
+
265
+ # Verify GPIO chips are available
266
+ ls /dev/gpiochip*
267
+
268
+ # Add user to gpio group for permissions
269
+ sudo usermod -a -G gpio $USER
270
+ # Log out and back in for group changes to take effect
271
+ ```
272
+
273
+ #### I2C
274
+
275
+ ```bash path=null start=null
276
+ # Enable I2C kernel module
277
+ sudo modprobe i2c-dev
278
+
279
+ # Make it permanent
280
+ echo 'i2c-dev' | sudo tee -a /etc/modules
281
+
282
+ # Add user to i2c group
283
+ sudo usermod -a -G i2c $USER
284
+
285
+ # Set permissions (if group doesn't exist)
286
+ sudo chmod 666 /dev/i2c-*
287
+ ```
288
+
289
+ #### DS18B20 (1-Wire)
290
+
291
+ The DS18B20 sensor requires the kernel w1-gpio module:
292
+
293
+ ```bash path=null start=null
294
+ # Load 1-Wire kernel modules
295
+ sudo modprobe w1-gpio
296
+ sudo modprobe w1-therm
297
+
298
+ # Make it permanent
299
+ echo 'w1-gpio' | sudo tee -a /etc/modules
300
+ echo 'w1-therm' | sudo tee -a /etc/modules
301
+
302
+ # Verify devices are detected
303
+ ls /sys/bus/w1/devices/
304
+ # Should show devices like: 28-00000xxxxxx
305
+ ```
306
+
307
+ #### Beaglebone Black Device Tree
308
+
309
+ For Beaglebone Black, you may need to enable device tree overlays:
310
+
311
+ ```bash path=null start=null
312
+ # Edit /boot/uEnv.txt and enable overlays:
313
+ # For I2C:
314
+ uboot_overlay_addr4=/lib/firmware/BB-I2C1-00A0.dtbo
315
+
316
+ # For GPIO/1-Wire (if using P9_12 for example):
317
+ uboot_overlay_addr5=/lib/firmware/BB-W1-P9.12-00A0.dtbo
318
+
319
+ # Reboot after changes
320
+ sudo reboot
321
+ ```
322
+
323
+ ## Troubleshooting
324
+
325
+ ### Permission Denied Errors
326
+
327
+ **Problem:** `Errno::EACCES: Permission denied - /dev/gpiochip0` or `/dev/i2c-1`
328
+
329
+ **Solution:**
330
+ ```bash path=null start=null
331
+ # Add your user to the appropriate group
332
+ sudo usermod -a -G gpio $USER
333
+ sudo usermod -a -G i2c $USER
334
+
335
+ # Or temporarily change permissions (not recommended for production)
336
+ sudo chmod 666 /dev/gpiochip*
337
+ sudo chmod 666 /dev/i2c-*
338
+
339
+ # Log out and back in for group changes
340
+ ```
341
+
342
+ ### DS18B20 Not Found
343
+
344
+ **Problem:** `No DS18B20 devices found` or empty `/sys/bus/w1/devices/`
345
+
346
+ **Solution:**
347
+ ```bash path=null start=null
348
+ # Verify modules are loaded
349
+ lsmod | grep w1
350
+
351
+ # Load if missing
352
+ sudo modprobe w1-gpio
353
+ sudo modprobe w1-therm
354
+
355
+ # Check dmesg for errors
356
+ sudo dmesg | grep w1
357
+
358
+ # Verify wiring: DS18B20 requires 4.7kΩ pull-up resistor on data line
359
+ ```
360
+
361
+ ### FFI Library Not Found
362
+
363
+ **Problem:** `LoadError: cannot load such file -- ffi`
364
+
365
+ **Solution:**
366
+ ```bash path=null start=null
367
+ # Install FFI gem
368
+ gem install ffi
369
+
370
+ # Or ensure it's in your Gemfile
371
+ bundle install
372
+ ```
373
+
374
+ ### Sensor Reading Returns Nil or Errors
375
+
376
+ **Problem:** Sensor readings fail or return nil values
377
+
378
+ **Solutions:**
379
+ - **Verify wiring:** Double-check sensor connections (VCC, GND, data lines)
380
+ - **Check I2C address:** Use `i2cdetect -y 1` to scan for devices
381
+ - **Timing issues:** DHT22 requires ~2 second intervals between reads
382
+ - **Power supply:** Ensure adequate power for sensors (some require 3.3V, others 5V)
383
+ - **Enable simulation backend** for testing without hardware:
384
+ ```bash path=null start=null
385
+ export DREDGER_IOT_GPIO_BACKEND=simulation
386
+ export DREDGER_IOT_I2C_BACKEND=simulation
387
+ ```
388
+
389
+ ### I2C Device Not Detected
390
+
391
+ **Problem:** I2C sensor not responding
392
+
393
+ **Solution:**
394
+ ```bash path=null start=null
395
+ # Install i2c-tools
396
+ sudo apt-get install i2c-tools
397
+
398
+ # Scan I2C bus (bus 1 is common, try 0 if not found)
399
+ i2cdetect -y 1
400
+
401
+ # Should show device at its address (e.g., 0x76 for BME280)
402
+ # If not detected:
403
+ # - Check wiring and pull-up resistors (typically 4.7kΩ on SDA/SCL)
404
+ # - Verify I2C is enabled in device tree
405
+ # - Check for bus conflicts
406
+ ```
407
+
408
+ ## API Reference
409
+
410
+ ### Core Modules
411
+
412
+ #### `Dredger::IoT::Bus::Auto`
413
+
414
+ Automatic backend selection for GPIO and I2C buses.
415
+
416
+ ```ruby path=null start=null
417
+ gpio = Dredger::IoT::Bus::Auto.gpio # Returns GPIO backend
418
+ i2c = Dredger::IoT::Bus::Auto.i2c # Returns I2C backend
419
+ ```
420
+
421
+ #### `Dredger::IoT::Bus::GPIOLibgpiod`
422
+
423
+ FFI-based GPIO access using libgpiod.
424
+
425
+ **Methods:**
426
+ - `set_direction(pin, direction)` - Set pin as `:in` or `:out`
427
+ - `write(pin, value)` - Write `0` or `1` to output pin
428
+ - `read(pin)` - Read current value from pin (returns `0` or `1`)
429
+ - `close` - Release GPIO resources
430
+
431
+ #### `Dredger::IoT::Bus::I2CLinux`
432
+
433
+ FFI-based I2C access using Linux i2c-dev.
434
+
435
+ **Methods:**
436
+ - `write(addr, data)` - Write bytes to I2C device at address
437
+ - `read(addr, count)` - Read bytes from I2C device
438
+ - `write_read(addr, write_data, read_count)` - Combined write then read
439
+ - `close` - Release I2C bus
440
+
441
+ ### Sensors
442
+
443
+ All sensors follow a common interface:
444
+
445
+ ```ruby path=null start=null
446
+ sensor = Dredger::IoT::Sensors::SensorClass.new(
447
+ # Sensor-specific parameters
448
+ provider: provider_instance,
449
+ metadata: { custom: 'data' } # Optional metadata
450
+ )
451
+
452
+ readings = sensor.readings # Returns array of Reading objects
453
+ ```
454
+
455
+ #### `Reading` Object
456
+
457
+ ```ruby path=null start=null
458
+ reading.sensor_type # e.g., 'temperature', 'humidity', 'pressure'
459
+ reading.value # Numeric value
460
+ reading.unit # Unit string (e.g., 'celsius', '%', 'kPa')
461
+ reading.metadata # Hash of custom metadata
462
+ reading.timestamp # Time object when reading was taken
463
+ ```
464
+
465
+ #### Available Sensors
466
+
467
+ - **`DHT22`** - Temperature/humidity (GPIO)
468
+ - Parameters: `pin_label`, `provider`
469
+ - Returns: temperature (celsius), humidity (%)
470
+
471
+ - **`BME280`** - Environmental sensor (I2C)
472
+ - Parameters: `i2c_addr` (default: `0x76`), `provider`
473
+ - Returns: temperature (celsius), humidity (%), pressure (kPa)
474
+
475
+ - **`DS18B20`** - Waterproof temperature (1-Wire)
476
+ - Parameters: `device_id`, `provider`
477
+ - Returns: temperature (celsius)
478
+
479
+ - **`BMP180`** - Barometric pressure (I2C)
480
+ - Parameters: `i2c_addr` (default: `0x77`), `provider`
481
+ - Returns: temperature (celsius), pressure (kPa)
482
+
483
+ - **`MCP9808`** - High-accuracy temperature (I2C)
484
+ - Parameters: `i2c_addr` (default: `0x18`), `provider`
485
+ - Returns: temperature (celsius)
486
+
487
+ ### Scheduling
488
+
489
+ #### `Dredger::IoT::Scheduler.periodic_with_jitter`
490
+
491
+ Generates intervals with randomized jitter to avoid harmonic patterns.
492
+
493
+ ```ruby path=null start=null
494
+ scheduler = Dredger::IoT::Scheduler.periodic_with_jitter(
495
+ base_interval: 60.0, # Base interval in seconds
496
+ jitter_ratio: 0.1 # ±10% jitter
497
+ )
498
+
499
+ scheduler.each { |interval| sleep interval; poll_sensors }
500
+ ```
501
+
502
+ #### `Dredger::IoT::Scheduler.exponential_backoff`
503
+
504
+ Generates exponentially increasing delays for retry logic.
505
+
506
+ ```ruby path=null start=null
507
+ backoff = Dredger::IoT::Scheduler.exponential_backoff(
508
+ base: 1.0, # Initial delay
509
+ max: 30.0, # Maximum delay
510
+ attempts: 5 # Number of attempts
511
+ )
512
+
513
+ backoff.each { |delay| sleep delay; break if retry_operation }
514
+ ```
515
+
516
+ ## Notes
517
+
518
+ - libgpiod and i2c-dev backends are optional and only required on hardware.
519
+ - You can explicitly `require 'dredger/iot/bus/gpio_libgpiod'` or `'dredger/iot/bus/i2c_linux'` when running on target devices.
520
+ - The Auto API will attempt to load these backends if it detects the corresponding device nodes are present.
521
+ - Simulation backends are perfect for development and testing without hardware access.
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dredger
4
+ module IoT
5
+ module Bus
6
+ module Auto
7
+ module_function
8
+
9
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
10
+ def gpio(prefer: :auto, chip: 'gpiochip0', active_low: false)
11
+ pref = prefer_from_env(ENV.fetch('DREDGER_IOT_GPIO_BACKEND', nil), prefer)
12
+ case pref
13
+ when :simulation
14
+ Dredger::IoT::Bus::GPIO.new
15
+ when :libgpiod
16
+ begin
17
+ safe_require('dredger/iot/bus/gpio_libgpiod')
18
+ safe_require('dredger/iot/bus/gpio_label_adapter')
19
+ raw = Dredger::IoT::Bus::GpioLibgpiod.new(chip: chip, active_low: active_low)
20
+ backend = Dredger::IoT::Bus::GPIOLabelAdapter.new(backend: raw)
21
+ Dredger::IoT::Bus::GPIO.new(backend: backend)
22
+ rescue LoadError
23
+ Dredger::IoT::Bus::GPIO.new
24
+ end
25
+ else # :auto
26
+ if File.exist?('/dev/gpiochip0')
27
+ begin
28
+ safe_require('dredger/iot/bus/gpio_libgpiod')
29
+ safe_require('dredger/iot/bus/gpio_label_adapter')
30
+ raw = Dredger::IoT::Bus::GpioLibgpiod.new(chip: chip, active_low: active_low)
31
+ backend = Dredger::IoT::Bus::GPIOLabelAdapter.new(backend: raw)
32
+ return Dredger::IoT::Bus::GPIO.new(backend: backend)
33
+ rescue LoadError
34
+ # fallthrough
35
+ end
36
+ end
37
+ Dredger::IoT::Bus::GPIO.new
38
+ end
39
+ end
40
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
41
+
42
+ # rubocop:disable Metrics/MethodLength
43
+ def i2c(prefer: :auto, bus_path: '/dev/i2c-1')
44
+ pref = prefer_from_env(ENV.fetch('DREDGER_IOT_I2C_BACKEND', nil), prefer)
45
+ case pref
46
+ when :simulation
47
+ Dredger::IoT::Bus::I2C.new
48
+ when :linux
49
+ begin
50
+ safe_require('dredger/iot/bus/i2c_linux')
51
+ backend = Dredger::IoT::Bus::I2cLinux.new(bus_path: bus_path)
52
+ Dredger::IoT::Bus::I2C.new(backend: backend)
53
+ rescue LoadError
54
+ Dredger::IoT::Bus::I2C.new
55
+ end
56
+ else # :auto
57
+ if File.exist?(bus_path)
58
+ begin
59
+ safe_require('dredger/iot/bus/i2c_linux')
60
+ backend = Dredger::IoT::Bus::I2cLinux.new(bus_path: bus_path)
61
+ return Dredger::IoT::Bus::I2C.new(backend: backend)
62
+ rescue LoadError
63
+ # fallthrough
64
+ end
65
+ end
66
+ Dredger::IoT::Bus::I2C.new
67
+ end
68
+ end
69
+ # rubocop:enable Metrics/MethodLength
70
+
71
+ def prefer_from_env(env_value, default)
72
+ case env_value&.downcase
73
+ when 'simulation' then :simulation
74
+ when 'libgpiod' then :libgpiod
75
+ when 'linux' then :linux
76
+ else default
77
+ end
78
+ end
79
+
80
+ def safe_require(path)
81
+ require path
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ # EOF