Skip to content

Commit

Permalink
built mvp app
Browse files Browse the repository at this point in the history
  • Loading branch information
zrezke committed Sep 21, 2022
1 parent 7d7fd58 commit d23592a
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 91 deletions.
4 changes: 3 additions & 1 deletion app/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ android {
applicationId "com.example.app"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion flutter.minSdkVersion
minSdkVersion 19
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand All @@ -58,6 +58,8 @@ android {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
shrinkResources false
minifyEnabled false
}
}
}
Expand Down
14 changes: 11 additions & 3 deletions app/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.app">
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

<!-- Needed only if your app makes the device discoverable to Bluetooth
devices. -->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!-- Needed only if your app communicates with already-paired Bluetooth
devices. -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<application android:label="app" android:name="${applicationName}" android:icon="@mipmap/ic_launcher">
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
Expand All @@ -14,8 +25,5 @@
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data android:name="flutterEmbedding" android:value="2" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
</application>
</manifest>
169 changes: 169 additions & 0 deletions app/lib/HomePage.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:fluttertoast/fluttertoast.dart';

class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
FlutterBluePlus flutterBlue = FlutterBluePlus.instance;
final availableDevices = <BluetoothDevice>[];
BluetoothDevice? anemometer = null;
BluetoothService? windSpeedService = null;
BluetoothCharacteristic? windSpeedCharcteristic = null;
Timer? periodicTimerHandle = null;
double windSpeed = 0;

@override
void initState() {
// TODO: implement initState
super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Anemometer"),
),
body: FutureBuilder(
future: bodyWidget(),
builder: (context, AsyncSnapshot<Widget> snapshot) {
if (snapshot.hasData) {
return snapshot.data!;
}
return const Center(child: Text("Shomething went wrong"));
}));
}

Widget connectToADeviceWidget() {
return Center(
child: RefreshIndicator(
onRefresh: startDiscovery,
child: ListView.builder(
itemBuilder: ((context, index) => ListTile(
title: Text(availableDevices[index].name),
onTap: () => connectToDevice(index),
)),
itemCount: availableDevices.length,
)));
}

Future<void> connectToDevice(int deviceIndex) async {
await availableDevices[deviceIndex].connect();
List<BluetoothDevice> connectedDevices = await flutterBlue.connectedDevices;
if (connectedDevices.contains(availableDevices[deviceIndex])) {
List<BluetoothService> services =
await availableDevices[deviceIndex].discoverServices();
services.forEach((service) {
if (service.isPrimary) {
windSpeedService = service;
for (BluetoothCharacteristic characteristic
in service.characteristics) {
print("UUID: ${characteristic.serviceUuid}");
if (characteristic.serviceUuid.toString() ==
"00000000-0000-0000-0000-00000000702a") {
windSpeedCharcteristic = characteristic;
}
}
}
});
setState(() {
anemometer = availableDevices[deviceIndex];
});
}
Fluttertoast.showToast(
msg: "Connection successful!",
toastLength: Toast.LENGTH_SHORT,
backgroundColor: Colors.green);
if (periodicTimerHandle == null) {
periodicTimerHandle = Timer.periodic(Duration(seconds: 1), (timer) async {
await fetchWindSpeed();
});
}
}

Future<void> startDiscovery() async {
setState(() => availableDevices.removeWhere((element) => true));
await flutterBlue.startScan(timeout: Duration(seconds: 4));
flutterBlue.scanResults.listen((result) {
for (var r in result) {
if (!availableDevices.contains(r.device) &&
r.device.name == "Anemometer V1") {
print("FOUND Anemometer: ${r.device.name}");
setState(() => availableDevices.add(r.device));
}
}
});
flutterBlue.stopScan();
}

Widget turnOnBtWidget() {
return RefreshIndicator(
child: Center(child: Text("Turn on bluetooth")),
onRefresh: () async => (await flutterBlue.isOn) ? setState(() {}) : null,
);
}

Widget windSpeedWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("Wind Speed", style: TextStyle(
fontSize: 26, fontWeight: FontWeight.w300
)),
Text("${windSpeed.toStringAsFixed(2)} m/s",
style: TextStyle(fontSize: 21, fontWeight: FontWeight.bold))
]));
}

Future<void> fetchWindSpeed() async {
List<int> readData = await windSpeedCharcteristic!.read();
setState(() {
windSpeed =
ByteData.sublistView(Uint8List.fromList(readData.reversed.toList()))
.getFloat32(0);
});
}

Future<Widget> bodyWidget() async {
if (await flutterBlue.isOn) {
try {
List<BluetoothDevice> connectedDevices =
await flutterBlue.connectedDevices;

if (anemometer == null) {
connectedDevices
.firstWhere((element) => element.name == "Anemometer V1")
.disconnect();
return connectToADeviceWidget();
}
} on StateError catch (_, e) {
return connectToADeviceWidget();
}
if (windSpeedCharcteristic == null) {
Fluttertoast.showToast(
msg: "Connection failed",
toastLength: Toast.LENGTH_SHORT,
backgroundColor: Colors.red);
setState(() async {
await anemometer!.disconnect();
anemometer = null;
windSpeedCharcteristic = null;
windSpeedService = null;
});
}
return windSpeedWidget();
} else {
return turnOnBtWidget();
}
}
}
91 changes: 4 additions & 87 deletions app/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'HomePage.dart';

void main() {
runApp(const MyApp());
Expand All @@ -11,7 +13,7 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
title: 'Anemometer',
theme: ThemeData(
// This is the theme of your application.
//
Expand All @@ -24,92 +26,7 @@ class MyApp extends StatelessWidget {
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);

// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.

// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".

final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}

@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
home: HomePage()
);
}
}
Loading

0 comments on commit d23592a

Please sign in to comment.