A Ruby gem providing enterprise-grade, cross-platform Bluetooth Low Energy (BLE) functionality through Ruby bindings for the SimpleBLE C++ library. Delivers production-ready BLE scanning, device connection, and GATT operations.
gem install simpleblerequire 'simpleble'
# Check if Bluetooth is available
SimpleBLE.bluetooth_enabled?  # => true
# Get available Bluetooth adapters
adapters = SimpleBLE.adapters
adapter = adapters.first
puts adapter.identifier       # => "Default Adapter [uuid]"
# Quick scan for BLE devices (5 second timeout)
devices = SimpleBLE.scan(5000)
puts "Found #{devices.length} BLE devices!"
# Advanced scanning with adapter control
adapter.scan_for(3000)       # Scan for 3 seconds
peripherals = adapter.scan_results
peripherals.each do |device|
  puts "Device: #{device.identifier} (#{device.address})"
end- macOS - CoreBluetooth framework integration
- Linux - DBus/BlueZ backend support
- Windows - WinRT Bluetooth APIs (ready for testing)
- β Adapter discovery & identifiers
- β Bluetooth enabled check
- β Device scanning (blocking scan_for + continuous start/stop)
- β Peripheral information (identifier, address, RSSI, TX power, MTU, address_type)
- β Connection lifecycle (connect, disconnect, paired?, unpair)
- β Paired peripherals access
- β GATT service & characteristic enumeration with capabilities
- β Characteristic read/write operations (request & command modes)
- β Descriptor read/write operations
- β Manufacturer data & advertisement parsing
- π§ Notifications/Indications (callback support planned)
- β Core C extension foundation & memory management for adapters/peripherals
- β Exception hierarchy & native error mapping (scan/connection/characteristic errors)
- β Cross-platform build scripts (macOS/Linux/Windows all working)
- β Comprehensive adapter & peripheral API implemented
- β GATT operations layer with service/characteristic/descriptor access
- β Ruby-friendly API with helper methods and convenience features
- π§ Expanded test coverage (integration tests gated by hardware)
- π§ Notification/indication callback support
macOS:
- Xcode command line tools: xcode-select --install
Linux:
# Ubuntu/Debian
sudo apt-get install build-essential libdbus-1-dev cmake
# Red Hat/CentOS
sudo yum install gcc-c++ dbus-devel cmakeWindows:
- Visual Studio Build Tools
- Windows SDK
- CMake
gem install simplebleOr add to your Gemfile:
gem 'simpleble'# Check Bluetooth availability
SimpleBLE.bluetooth_enabled?  # => true/false
# Get all adapters (convenience method)
SimpleBLE.adapters            # => [Adapter, ...]
# Quick scan with first available adapter
SimpleBLE.scan(timeout_ms)    # => [Peripheral, ...]# Get all available Bluetooth adapters
adapters = SimpleBLE::Adapter.get_adapters
adapter = adapters.first
# Adapter information
adapter.identifier            # => "Default Adapter [uuid]"
adapter.address              # => "XX:XX:XX:XX:XX:XX" (or UUID on macOS)
# Scanning operations
adapter.scan_start           # Start continuous scan
adapter.scan_stop            # Stop scanning
adapter.scan_for(timeout_ms) # Scan for specific duration
adapter.scan_active?         # => true/false
adapter.scan_results         # => [Peripheral, ...]
adapter.paired_peripherals   # => [Peripheral, ...] - Previously paired devicesdevices = SimpleBLE.scan(5000)
device = devices.first
# Basic information
device.identifier            # Device name or identifier
device.address              # MAC address or UUID
device.rssi                 # Signal strength in dBm
device.tx_power             # Advertised TX power in dBm
device.mtu                  # Maximum transmission unit
device.address_type         # Address type (public/random/unspecified)
# Connection management
device.connectable?         # Whether device accepts connections
device.connected?           # Current connection status
device.paired?              # Whether device is paired
device.connect              # Establish connection
device.disconnect           # Close connection
device.unpair               # Remove pairing
# GATT operations (requires connection)
services = device.services  # => [{"uuid" => "...", "characteristics" => [...]}]
data = device.read_characteristic(service_uuid, char_uuid)
device.write_characteristic_request(service_uuid, char_uuid, data)
device.write_characteristic_command(service_uuid, char_uuid, data)
# Descriptor operations
desc_data = device.read_descriptor(service_uuid, char_uuid, desc_uuid)
device.write_descriptor(service_uuid, char_uuid, desc_uuid, data)
# Advertisement data
mfg_data = device.manufacturer_data  # => [{"manufacturer_id" => 123, "data" => "..."}]
# Helper methods
device.name                 # Friendly name (identifier or address)
device.to_s                 # "Name (address)"
device.has_data?            # Check if device has valid data
device.rssi_s               # "-67 dBm"
device.address_type_s       # "Public" / "Random" / "Unspecified"Launch an interactive Ruby session with SimpleBLE loaded:
ruby -rsimpleble -e "
adapters = SimpleBLE.adapters
adapter = adapters.first
puts 'Try: SimpleBLE.scan(3000)'
require 'irb'; IRB.start
"# Clone repository with submodules
git clone --recurse-submodules https://github.com/twilightcoders/ruby-simpleble.git
cd ruby-simpleble
# Or if you already cloned without submodules:
git submodule update --init --recursive
# Install dependencies
bundle install
# Compile the C extension
rake compile
# Run tests
rake test# Update to latest SimpleBLE upstream
git submodule update --remote vendor/simpleble
# Verify the update
git submodule status# Run all tests
bundle exec rspec
# Run with coverage
bundle exec rspec --format documentation
# Test specific functionality
bundle exec rspec spec/simpleble_spec.rbSimpleBLE Ruby uses a layered architecture:
Ruby Application
       β
SimpleBLE Ruby Wrapper (lib/simpleble.rb)
       β
C Extension Layer (ext/simpleble/)
       β
SimpleBLE C++ Library (vendor/simpleble/)
       β
Platform BLE APIs (CoreBluetooth/BlueZ/WinRT)
- Ruby API Layer: Clean, idiomatic Ruby interface
- C Extension: Memory-safe Ruby β C++ bridge
- C++ Wrapper: Type-safe interface to SimpleBLE library
- SimpleBLE Library: Cross-platform BLE abstraction
- Platform Backends: OS-specific BLE implementations
| Area | Implemented | Notes | 
|---|---|---|
| Adapter enumeration | β | identifier, address | 
| Scanning (start/stop/for) | β | Timed & continuous | 
| Scan results retrieval | β | Returns Peripheral objects | 
| Peripheral basic info | β | identifier, address, RSSI, TX power, MTU, address_type | 
| Connection lifecycle | β | connect, disconnect, paired?, unpair | 
| Paired peripherals | β | Access to previously paired devices | 
| Services/Characteristics | β | Full enumeration with capabilities | 
| Characteristic I/O | β | Read/write with request & command modes | 
| Descriptor I/O | β | Read/write operations | | Manufacturer data | β | Advertisement parsing | | Windows support | β | All platforms working | | Test coverage | π§ | Expanding beyond placeholders | | Documentation accuracy | β | Reflects current API |
- Notification/indication callbacks with GC-safe storage
- Hardware-gated integration test suite expansion
- Performance optimizations and memory usage analysis
- Precompiled native gem variants (later)
| Platform | Status | Backend | Notes | 
|---|---|---|---|
| macOS | β Working | CoreBluetooth | Full support, production tested | 
| Linux | β Working | BlueZ/DBus | CI passing, production ready | 
| Windows | β Working | WinRT | CI passing, production ready | 
Building the SimpleBLE core for every Ruby version slows the matrix. Two knobs:
- 
SIMPLEBLE_PREBUILT_LIBβ point to a prebuilt static library (and headers) so only the Ruby bridge compiles.
- 
SIMPLEBLE_REUSE_OBJECTS=1β skipmake cleanand reuse previously cached.ofiles.
Create a warmup job that builds once, packages tmp_flat/*.o (or a consolidated libsimpleble_core.a you produce via ar), uploads as an artifact, then matrix jobs download and set:
env:
       SIMPLEBLE_PREBUILT_LIB: path/to/libsimpleble_core.a
extconf.rb detects this and links only the Ruby layer.
Use actions/cache keyed on OS + hash of vendor/simpleble/**:
- uses: actions/cache@v4
       with:
              path: ext/simpleble/tmp_flat
              key: simpleble-obj-${{ runner.os }}-${{ hashFiles('vendor/simpleble/**') }}
Then set SIMPLEBLE_REUSE_OBJECTS: 1 so the compile task skips cleaning.
| Var | Purpose | 
|---|---|
| SIMPLEBLE_PREBUILT_LIB | Path to prebuilt SimpleBLE static lib to link instead of compiling sources | 
| SIMPLEBLE_REUSE_OBJECTS | If 1, do not runmake clean; rely on cached object files | 
Regenerate caches whenever the SimpleBLE submodule changes.
- Fork the repository
- Create your feature branch (git checkout -b feature/amazing-feature)
- Run tests: bundle exec rspec
- Commit your changes (git commit -am 'Add amazing feature')
- Push to the branch (git push origin feature/amazing-feature)
- Open a Pull Request
This project is licensed under the MIT License - see the LICENSE.txt file for details.
- SimpleBLE Library - The excellent cross-platform BLE library this gem wraps
- OpenBluetoothToolbox - For creating and maintaining SimpleBLE