Skip to content

toddheasley/p-func

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PFunc

Control LEGO® Powered Up motors, lights and sensors from an @Observable Swift interface

PFunc talks to LEGO® Powered Up hubs over Bluetooth Low Energy (BLE). Core Bluetooth does the heavy lifting, managing connections and writing instructions to the hubs.

PFunc implements just enough of the LEGO® Wireless Protocol to replace the 88010 Remote Control and drive the current generation of Powered Up attachments from the 2- and 4-port consumer hubs.

Supported Hubs

88012 Technic™ Hub 88009 Hub

Supported Attachments

88011 Train Motor 88013 Technic™ Large Motor
45303 Motor 88005 Light

Supported Platforms

Written in Swift 6.1 for Apple stuff:

Build with Xcode 16.3 or newer.

Instructions

Apps using PFunc are using Core Bluetooth. Your app will crash if its Info.plist doesn't include NSBluetoothAlwaysUsageDescription privacy description.

Additionally, app entitlements need to enable Bluetooth:

macOS iOS, visionOS

Add p-func package to your Xcode project, then add PFunc library to the app target(s).

Connect Hubs

Add @Observable PFunc object to the SwiftUI app environment; connect nearby hubs when Bluetooth is enabled:

import SwiftUI
import PFunc

@main
struct App: SwiftUI.App {
    @State private var pFunc: PFunc = PFunc()
    
    // MARK: App
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(pFunc)
                .onChange(of: pFunc.state) {
                    if pFunc.state == .poweredOn {
                        pFunc.connect()
                    }
                }
        }
    }
}

All hub property updates are published:

  • Advertising name (14-character ASCII string)
  • Battery voltage (0-100%)
  • Bluetooth signal strength (poor/fair/good w/ relative dbm) and connection status (CBPeripheralState)
  • Built-in RGB light color (10 named presets or custom RGB 0-255)
  • Ports and attached devices (automatically detect/init known Device types)

Control Attached Devices

Detect when a device is attached to a port and operate functions:

import PFunc
import SwiftUI

struct RemoteControl: View {
    init(hub id: UUID) {
        self.id = id
    }
    
    @Environment(PFunc.self) private var pFunc: PFunc
    private let id: UUID
    
    private var device: Device? { pFunc.hub(id)?.device(at: .external(.a)) }
    
    // MARK: View
    var body: some View {
        Button(action: {
            if let light: LEDLight = device as? LEDLight {
                light.intensity = light.intensity == .off ? .percent(50) : .off
            } else if let motor: Motor = device as? Motor {
                motor.ramp(to: motor.power == .float ? .forward(50) : .float)
            }
        }) {
            Text("Toggle Device Function")
        }
        .disabled(device == nil)
    }
}

Change Hub Name and RGB Light

Both advertising name and RGB light color are settable and resettable:

pFunc.hub(id)?.resetName("New Hub Name")
pFunc.hub(id)?.rgbLightColor = .red

Name changes are persisted on the hub across connections, until changed or reset. RGB light color always starts at hub default on connection. (To remember which hubs were which color last time connected, your app can depend on the Core Bluetooth peripheral CBUUID being the same across connections.)

Thanks

I had a little help from the Internet: