Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Bug fix in lastSpeed reporting; added support for TOML file in cwd; added colors for some debug events #31

Merged
merged 1 commit into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,31 +59,33 @@ For my own indoor cycling configuration, I use a Performance Travel Trac 3 train

### Software Components

- The open source, cross-platform [mpv media player](https://mpv.io/), installed and operational
- The open source, cross-platform [mpv media player](https://mpv.io/), installed (e.g., `sudo apt-get install mpv`) and operational
- The `libmpv2` library, installed (e.g., `sudo apt-get install libmpv2`)
- In order to compile the executable for this project, an operational [Go language](https://go.dev/) environment is required (this release was developed using Go 1.23.2). Once the **BLE Sync Cycle** application is compiled into an executable, it can be run without the dependencies on the Go language environment
- A local video file for playback using mpv, preferably a first-person view cycling video. Check out [YouTube with this query: "first person cycling"](https://www.youtube.com/results?search_query=first+person+cycling) for some ideas
- This application. While **BLE Sync Cycle** has been written and tested using Ubuntu 24.04 (LTS) on an Intel processor (amd64), it should work across any recent Unix-like platform and architecture
- In order to compile the executable for this project, an operational [Go language](https://go.dev/) environment is required (this release was developed using Go 1.23.2)

While **BLE Sync Cycle** has been written and tested using Ubuntu 24.04 (LTS) on an Intel processor (amd64), it should work across any recent comparable Unix-like platform and architecture.

## Installation

### Building the Application

1. Clone the repository:

```bash
```console
git clone https://github.com/richbl/go-ble-sync-cycle
cd go-ble-sync-cycle
```

2. Install dependencies:

```bash
```console
go mod download
```

3. Build the application:

```bash
```console
go build -o ble-sync-cycle cmd/main.go
```

Expand All @@ -95,7 +97,7 @@ Edit the `config.toml` file found in the `internal/configuration` directory. The

```toml
# BLE Sync Cycle TOML configuration
# 0.6.1
# 0.6.2

[app]
logging_level = "debug" # Log messages to see during execution: "debug", "info", "warn", "error"
Expand Down Expand Up @@ -177,22 +179,22 @@ At a high level, **BLE Sync Cycle** will perform the following:

To run the application, execute the following command:

```bash
```console
./ble-sync-cycle
```

Or, if the application hasn't yet been built using the `go build` command, you can execute the following command:

```bash
```console
go run cmd/main.go
```

> Be sure that your Bluetooth devices are enabled and in range before running this command. On a computer or similar, you should have your Bluetooth radio turned on. On a BLE sensor, you typically "wake it up" by moving or shaking the device

At this point, you should see the following output:

```bash
2024/12/11 22:02:28 Starting BLE Sync Cycle 0.6.1
```console
2024/12/11 22:02:28 Starting BLE Sync Cycle 0.6.2
2024/12/11 22:02:28 INFO [BLE] Created new BLE central controller
2024/12/11 22:02:28 INFO [BLE] Now scanning the ether for BLE peripheral UUID of F1:42:D8:DE:35:16...
2024/12/11 22:02:42 INFO [BLE] Found BLE peripheral F1:42:D8:DE:35:16
Expand All @@ -206,8 +208,8 @@ At this point, you should see the following output:

In this first example, while the application was able to find the BLE peripheral, it failed to discover the CSC services and characteristics before timing out. Depending on the BLE peripheral, it may take some time before a BLE peripheral advertises both its device services and characteristics. If the peripheral is not responding, you may need to increase the timeout in the `config.toml` file.

```bash
2024/12/11 22:03:59 Starting BLE Sync Cycle 0.6.1
```console
2024/12/11 22:03:59 Starting BLE Sync Cycle 0.6.2
2024/12/11 22:03:59 INFO [BLE] Created new BLE central controller
2024/12/11 22:03:59 INFO [BLE] Now scanning the ether for BLE peripheral UUID of F1:42:D8:DE:35:16...
2024/12/11 22:03:59 INFO [BLE] Found BLE peripheral F1:42:D8:DE:35:16
Expand All @@ -229,7 +231,7 @@ In this first example, while the application was able to find the BLE peripheral

In the example above, the application is now running in a loop, periodically querying the BLE peripheral for speed data. The application will also update the video player to match the speed of the sensor. Here, since the video has just begun, its speed is set to 0.0 (paused).

```bash
```console
...
024/12/11 22:05:08 INFO [SPEED] BLE sensor speed: 8.54 mph
2024/12/11 22:05:09 INFO [VIDEO] Sensor speed buffer: [9.06 8.28 8.11 8.20 8.54]
Expand Down Expand Up @@ -265,7 +267,7 @@ In this last example, **BLE Sync Cycle** is coordinating with both the BLE perip

**To quit the application, press `Ctrl+C`.**

```bash
```console
...
2024/12/11 22:05:11 INFO [SPEED] BLE sensor speed: 0.00 mph
2024/12/11 22:05:12 INFO [VIDEO] Sensor speed buffer: [8.44 7.84 6.56 6.65 0.00]
Expand All @@ -287,7 +289,6 @@ In this last example, **BLE Sync Cycle** is coordinating with both the BLE perip
2024/12/11 22:05:14 INFO [APP] Shutdown signal received
2024/12/11 22:05:14 INFO [VIDEO] Context cancelled. Shutting down video player component
2024/12/11 22:05:14 INFO [APP] Application shutdown complete. Goodbye!

```

## FAQ
Expand Down
14 changes: 2 additions & 12 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ type appControllers struct {
}

func main() {

log.Println("Starting BLE Sync Cycle 0.6.1")
log.Println("Starting BLE Sync Cycle 0.6.2")

// Load configuration
cfg, err := config.LoadFile("internal/configuration/config.toml")
cfg, err := config.LoadFile("config.toml")
if err != nil {
log.Fatal("FATAL - Failed to load TOML configuration: " + err.Error())
}
Expand All @@ -55,7 +54,6 @@ func main() {

// Shutdown the application... buh bye!
logger.Info("[APP] Application shutdown complete. Goodbye!")

}

// startAppControllers is responsible for starting and managing the component controllers
Expand Down Expand Up @@ -136,7 +134,6 @@ func setupAppControllers(cfg config.Config) (appControllers, error) {
videoPlayer: videoPlayer,
bleController: bleController,
}, nil

}

// scanForBLESpeedCharacteristic scans for the BLE CSC speed characteristic
Expand All @@ -149,15 +146,13 @@ func scanForBLESpeedCharacteristic(ctx context.Context, controllers appControlle
// Scan for the BLE CSC speed characteristic
go func() {
characteristic, err := controllers.bleController.GetBLECharacteristic(ctx, controllers.speedController)

if err != nil {
errChan <- err
return
}

// Return the characteristic
results <- characteristic

}()

// Wait for the characteristic or an error
Expand All @@ -169,19 +164,14 @@ func scanForBLESpeedCharacteristic(ctx context.Context, controllers appControlle
case characteristic := <-results:
return characteristic, nil
}

}

// monitorBLESpeed monitors the BLE speed characteristic
func monitorBLESpeed(ctx context.Context, controllers appControllers, bleSpeedCharacter *bluetooth.DeviceCharacteristic) error {

return controllers.bleController.GetBLEUpdates(ctx, controllers.speedController, bleSpeedCharacter)

}

// playVideo starts the video player
func playVideo(ctx context.Context, controllers appControllers) error {

return controllers.videoPlayer.Start(ctx, controllers.speedController)

}
35 changes: 10 additions & 25 deletions internal/ble/sensor_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const (
// NewBLEController creates a new BLE central controller for accessing a BLE peripheral
func NewBLEController(bleConfig config.BLEConfig, speedConfig config.SpeedConfig) (*BLEController, error) {

// Enable BLE adapter
bleAdapter := bluetooth.DefaultAdapter
if err := bleAdapter.Enable(); err != nil {
return nil, err
Expand All @@ -55,7 +56,6 @@ func NewBLEController(bleConfig config.BLEConfig, speedConfig config.SpeedConfig
speedConfig: speedConfig,
bleAdapter: *bleAdapter,
}, nil

}

// GetBLECharacteristic scans for the BLE peripheral and returns CSC services/characteristics
Expand All @@ -67,7 +67,7 @@ func (m *BLEController) GetBLECharacteristic(ctx context.Context, speedControlle
return nil, err
}

logger.Info("[BLE] Connecting to BLE peripheral device " + result.Address.String())
logger.Debug("[BLE] Connecting to BLE peripheral device " + result.Address.String())

// Connect to BLE peripheral device
var device bluetooth.Device
Expand All @@ -76,7 +76,7 @@ func (m *BLEController) GetBLECharacteristic(ctx context.Context, speedControlle
}

logger.Info("[BLE] BLE peripheral device connected")
logger.Info("[BLE] Discovering CSC services " + bluetooth.New16BitUUID(0x1816).String())
logger.Debug("[BLE] Discovering CSC services " + bluetooth.New16BitUUID(0x1816).String())

// Find CSC service and characteristic
svc, err := device.DiscoverServices([]bluetooth.UUID{bluetooth.New16BitUUID(0x1816)})
Expand All @@ -85,25 +85,23 @@ func (m *BLEController) GetBLECharacteristic(ctx context.Context, speedControlle
return nil, err
}

logger.Info("[BLE] Found CSC service " + svc[0].UUID().String())
logger.Info("[BLE] Discovering CSC characteristics " + bluetooth.New16BitUUID(0x2A5B).String())
logger.Debug("[BLE] Found CSC service " + svc[0].UUID().String())
logger.Debug("[BLE] Discovering CSC characteristics " + bluetooth.New16BitUUID(0x2A5B).String())

char, err := svc[0].DiscoverCharacteristics([]bluetooth.UUID{bluetooth.New16BitUUID(0x2A5B)})
if err != nil {
logger.Warn("[BLE] CSC characteristics discovery failed: " + err.Error())
return nil, err
}

logger.Info("[BLE] Found CSC characteristic " + char[0].UUID().String())

logger.Debug("[BLE] Found CSC characteristic " + char[0].UUID().String())
return &char[0], nil

}

// GetBLEUpdates enables BLE peripheral monitoring to report real-time sensor data
func (m *BLEController) GetBLEUpdates(ctx context.Context, speedController *speed.SpeedController, char *bluetooth.DeviceCharacteristic) error {

logger.Info("[BLE] Starting real-time monitoring of BLE sensor notifications...")
logger.Debug("[BLE] Starting real-time monitoring of BLE sensor notifications...")

// Subscribe to live BLE sensor notifications
if err := char.EnableNotifications(func(buf []byte) {
Expand All @@ -115,7 +113,6 @@ func (m *BLEController) GetBLEUpdates(ctx context.Context, speedController *spee

<-ctx.Done()
return nil

}

// ScanForBLEPeripheral scans for a BLE peripheral with the specified UUID
Expand All @@ -128,30 +125,24 @@ func (m *BLEController) ScanForBLEPeripheral(ctx context.Context) (bluetooth.Sca
errChan := make(chan error, 1)

go func() {

logger.Info("[BLE] Now scanning the ether for BLE peripheral UUID of " + m.bleConfig.SensorUUID + "...")

if err := m.startScanning(found); err != nil {
errChan <- err
}

}()

// Wait for device discovery or timeout
select {

case result := <-found:
logger.Info("[BLE] Found BLE peripheral " + result.Address.String())
logger.Debug("[BLE] Found BLE peripheral " + result.Address.String())
return result, nil

case err := <-errChan:
return bluetooth.ScanResult{}, err

case <-scanCtx.Done():
if err := m.bleAdapter.StopScan(); err != nil {
logger.Error("[BLE] Failed to stop scan: " + err.Error())
}

return bluetooth.ScanResult{}, errors.New("scanning time limit reached")
}
}
Expand All @@ -161,6 +152,7 @@ func (m *BLEController) startScanning(found chan<- bluetooth.ScanResult) error {

err := m.bleAdapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) {

// Check if the target peripheral was found
if result.Address.String() == m.bleConfig.SensorUUID {

// Stop scanning
Expand All @@ -170,17 +162,13 @@ func (m *BLEController) startScanning(found chan<- bluetooth.ScanResult) error {

// Found the target peripheral
found <- result

}

})

if err != nil {
logger.Error("[BLE] Scan error: " + err.Error())
}

return nil

}

// ProcessBLESpeed processes the raw speed data from the BLE peripheral
Expand All @@ -195,10 +183,9 @@ func (m *BLEController) ProcessBLESpeed(data []byte) float64 {

// Calculate speed from parsed data
speed := m.calculateSpeed(newSpeedData)
logger.Info("[SPEED] BLE sensor speed: " + strconv.FormatFloat(speed, 'f', 2, 64) + " " + m.speedConfig.SpeedUnits)
logger.Info("[SPEED] " + logger.Blue + " BLE sensor speed: " + strconv.FormatFloat(speed, 'f', 2, 64) + " " + m.speedConfig.SpeedUnits)

return speed

}

// calculateSpeed calculates the current speed based on the sensor data
Expand Down Expand Up @@ -232,7 +219,6 @@ func (m *BLEController) calculateSpeed(sm SpeedMeasurement) float64 {
lastWheelTime = sm.wheelTime

return speed

}

// parseSpeedData parses the raw speed data from the BLE peripheral
Expand All @@ -253,5 +239,4 @@ func (m *BLEController) parseSpeedData(data []byte) (SpeedMeasurement, error) {
wheelRevs: binary.LittleEndian.Uint32(data[1:]),
wheelTime: binary.LittleEndian.Uint16(data[5:]),
}, nil

}
Loading
Loading