package main import ( "fmt" "log" "math/rand" "time" "ble_simulator/internal/ble" "ble_simulator/internal/device" "tinygo.org/x/bluetooth" ) func main() { log.Println("[INFO] BLE Simulator v1.0") adapter := bluetooth.DefaultAdapter must("enable BLE", adapter.Enable()) // Connection handler adapter.SetConnectHandler(func(dev bluetooth.Device, connected bool) { if connected { log.Printf("[CONN] Client Connected: %s", dev.Address.String()) } else { log.Println("[CONN] Client Disconnected") } }) // Setup device state and service state := device.NewDeviceState() notifyChar, err := ble.SetupService(adapter, state) must("add service", err) // Configure advertising adv := adapter.DefaultAdvertisement() must("configure adv", adv.Configure(bluetooth.AdvertisementOptions{ LocalName: "GO_SIMULATOR", ServiceUUIDs: []bluetooth.UUID{ble.ServiceUUID}, })) must("start advertising", adv.Start()) log.Println("[INFO] Advertising as GO_SIMULATOR...") log.Println("[INFO] Using Nordic UART Service (NUS) UUIDs") log.Println("[INFO] Service UUID: 6e400001-b5a3-f393-e0a9-e50e24dcca9e") log.Println("[INFO] Command Char: 6e400002-b5a3-f393-e0a9-e50e24dcca9e (Write/RX)") log.Println("[INFO] Notify Char: 6e400003-b5a3-f393-e0a9-e50e24dcca9e (Notify/TX)") log.Println("[INFO] Waiting for connections...") // Start notification loop (500ms heartbeat with sensor data) go notificationLoop(notifyChar, state) // Block forever select {} } func notificationLoop(char *bluetooth.Characteristic, state *device.DeviceState) { ticker := time.NewTicker(500 * time.Millisecond) for range ticker.C { value := state.GetSensorValue() // Add small jitter for realism (-10 to +10) jitter := rand.Intn(21) - 10 msg := fmt.Sprintf("SENSOR:%d", value+jitter) _, err := char.Write([]byte(msg + "\n")) if err != nil { // Silently ignore write errors (no subscribers) continue } log.Printf("[TX] Sending: %s", msg) } } func must(action string, err error) { if err != nil { log.Fatalf("Failed to %s: %v", action, err) } }