youtube demo ---- https://www.youtube.com/watch?v=y8UL7Ijpvgc // // flutter program simple dcc ble // uses: // ble - hm19 module // pico - rp2040 core a // booster - cytron md13s // dcc station - github.com/pico-cs/firmware // // bluetooth module connections: // hm19 ground - pico ground // hm19 vcc - pico +5v // hm19 rxd - pico gpio0 // hm19 txd - pico gpio1 // // booster connections: // md13s ground - pico ground // md13s pwm - pico +5v // md13s dir - pico gpio2 // // use nrf connect to find ble mac address // if having problem with ble on android, give app location permision // import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; // needed for exit import 'package:get/get.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:wheel_chooser/wheel_chooser.dart'; import 'dart:async'; import 'dart:convert'; // needed for utf8.encode final buttonStyle = ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.teal[100])); void main() {WidgetsFlutterBinding.ensureInitialized(); // without this, error tx has not been initialized runApp(MaterialApp(home:Home()));} class Home extends StatelessWidget{ final ble=FlutterReactiveBle(); late QualifiedCharacteristic tx; dynamic trackPower=false.obs; var status='connect to ble'.obs; int currentSpeed = 0; void send(val) async { List data=utf8.encode(val); await ble.writeCharacteristicWithoutResponse(tx,value:data);} void ble_connect() async{ status.value='connecting...'; late StreamSubscription c; c=ble.connectToDevice(id:'A4:DA:32:55:06:1E').listen((s){ if (s.connectionState == DeviceConnectionState.connected){ status.value='connected'; tx=QualifiedCharacteristic(serviceId:Uuid.parse('0000ffe0-0000-1000-8000-00805f9b34fb'), characteristicId:Uuid.parse('0000ffe1-0000-1000-8000-00805f9b34fb'), deviceId:'A4:DA:32:55:06:1E');}});} @override Widget build(BuildContext c){ return Scaffold(appBar:AppBar(title:Text('simple dcc bluetooth')), body:Column(children:[ SizedBox(height:30), ElevatedButton(onPressed:(){ble_connect();}, child: Obx(() =>Text('${status}')), style:buttonStyle), SizedBox(height:30), Padding(padding:EdgeInsets.all(16.0),child: Obx(() => Row(children:[Text('track power on: '), (Switch(value:trackPower.value, activeColor:Colors.green, onChanged:(bool v){send('+mte t\r'); trackPower.value=v;}))]))), Expanded(child:WheelChooser.integer( onValueChanged: (s) => currentSpeed = s, maxValue: 20, minValue: 1, initValue: 1, horizontal: true, unSelectTextStyle: TextStyle(color: Colors.grey))), ElevatedButton(child:Text('set speed'), onPressed:(){send('+ls 15 ' + currentSpeed.toString() + '\r');}, style:buttonStyle), SizedBox(height:30), ElevatedButton(child:Text('toggle direction'), onPressed:(){send('+ld 15 ~\r');}, style:buttonStyle), SizedBox(height:40), //on exit turn off track power ElevatedButton(child:Text('Exit'), onPressed:(){ send('+mte f\r'); SystemNavigator.pop();})]));}}