Skip to content

Commit

Permalink
feat: add bluetooth controlled robot operations
Browse files Browse the repository at this point in the history
  • Loading branch information
mediocre9 committed Feb 6, 2023
1 parent 682284b commit 5184c63
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 94 deletions.
63 changes: 26 additions & 37 deletions lib/screens/remote/cubit/remote_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ignore_for_file: constant_identifier_names

import 'dart:convert';
import 'package:bloc/bloc.dart';
import 'package:flutter/foundation.dart';
Expand All @@ -6,52 +8,39 @@ import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
part 'remote_state.dart';

class RemoteCubit extends Cubit<RemoteState> {
RemoteCubit() : super(OffSignal());
RemoteCubit() : super(Initial(status: false, message: 'WAITING'));

static const String FORWARD = 'F';
static const String BACKWARD = 'B';
static const String LEFT = 'L';
static const String RIGHT = 'R';

BluetoothDevice? device;
BluetoothConnection? _connection;

Future<void> onMessage(BluetoothDevice device) async {
Future<void> connect(BluetoothDevice device) async {
emit(Loading());
_connection = await BluetoothConnection.toAddress(device.address);
emit(Initial(status: _connection!.isConnected, message: 'WAITING'));
}

void moveForward() {
_connection!.output.add(Uint8List.fromList(utf8.encode(FORWARD)));
emit(Initial(status: _connection!.isConnected, message: FORWARD));
}

/**
* Wrote test cases, have tested
* on real devices. Wrote an external server script to
* this library and even did some changes to library's
* java source code for testing purpose. Still no luck... :(
*
* Culprit code is below that establishes connection
* between devices...
*/
BluetoothConnection con =
await BluetoothConnection.toAddress(device.address);



con.output.add(Uint8List.fromList(utf8.encode('Hello')));
con.dispose();
listenResponse(device);
emit(OnSignal());
void moveBackward() {
_connection!.output.add(Uint8List.fromList(utf8.encode(BACKWARD)));
emit(Initial(status: _connection!.isConnected, message: BACKWARD));
}

Future<void> offMessage(BluetoothDevice device) async {
emit(Loading());
BluetoothConnection con =
await BluetoothConnection.toAddress(device.address);
con.output.add(Uint8List.fromList(utf8.encode('World')));
con.dispose();
emit(OffSignal());
void moveLeft() {
_connection!.output.add(Uint8List.fromList(utf8.encode(LEFT)));
emit(Initial(status: _connection!.isConnected, message: LEFT));
}

Future listenResponse(BluetoothDevice device) async {
emit(Loading());
BluetoothConnection con =
await BluetoothConnection.toAddress(device.address);
con.input!.listen((event) {
ascii.decode(event);
}).onDone(() {
con.dispose();
emit(OffSignal());
});
void moveRight() {
_connection!.output.add(Uint8List.fromList(utf8.encode(RIGHT)));
emit(Initial(status: _connection!.isConnected, message: RIGHT));
}
}
16 changes: 5 additions & 11 deletions lib/screens/remote/cubit/remote_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,10 @@ part of 'remote_cubit.dart';

abstract class RemoteState {}

class Initial extends RemoteState {}
class Initial extends RemoteState {
final bool? status;
final String? message;
Initial({this.status, this.message});
}

class Loading extends RemoteState {}

class OnSignal extends RemoteState {}

class OffSignal extends RemoteState {}

class ListenResponse extends RemoteState {
final String message;

ListenResponse(this.message);
}
136 changes: 90 additions & 46 deletions lib/screens/remote/remote_screen.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:remo_tooth/screens/remote/cubit/remote_cubit.dart';

class RemoteScreen extends StatelessWidget {
class RemoteScreen extends StatefulWidget {
final BluetoothDevice device;
const RemoteScreen({super.key, required this.device});

@override
State<RemoteScreen> createState() => _RemoteScreenState();
}

class _RemoteScreenState extends State<RemoteScreen> {
@override
void initState() {
() async {
await BlocProvider.of<RemoteCubit>(context).connect(widget.device);
}();
super.initState();
}

@override
Widget build(BuildContext context) {
var event = BlocProvider.of<RemoteCubit>(context);
var mediaQuery = MediaQuery.of(context);
var theme = Theme.of(context);
var message = '0';

return Scaffold(
appBar: AppBar(
title: Text(device.name!),
title: Text(widget.device.name!),
actions: [
PopupMenuButton(
itemBuilder: (context) {
Expand All @@ -35,35 +48,50 @@ class RemoteScreen extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
BlocConsumer<RemoteCubit, RemoteState>(
listener: (context, state) {
if (state is ListenResponse) {
message = state.message;
}
},
builder: (context, state) {
if (state is Loading) {
return FloatingActionButton.large(
child: const CircularProgressIndicator(),
onPressed: () => event.onMessage(device),
);
} else if (state is OnSignal) {
return FloatingActionButton.large(
foregroundColor:
const Color.fromARGB(255, 131, 78, 255),
child: const Icon(Icons.power_settings_new_rounded),
onPressed: () => event.offMessage(device),
);
} else if (state is OffSignal) {
return FloatingActionButton.large(
child: const Icon(Icons.power_settings_new_rounded),
onPressed: () => event.onMessage(device),
);
} else {
return Container();
}
},
)
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
OutlinedButton(
style: ButtonStyle(
overlayColor: MaterialStateProperty.all(Colors.blue),
),
child: const Icon(Icons.arrow_circle_up_rounded),
onPressed: () async {
event.moveForward();
await HapticFeedback.heavyImpact();
},
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
OutlinedButton(
style: ButtonStyle(
overlayColor:
MaterialStateProperty.all(Colors.blue),
),
child: const Icon(Icons.arrow_circle_left_outlined),
onPressed: () => event.moveLeft(),
),
OutlinedButton(
style: ButtonStyle(
overlayColor:
MaterialStateProperty.all(Colors.blue),
),
child:
const Icon(Icons.arrow_circle_right_outlined),
onPressed: () => event.moveRight(),
),
],
),
OutlinedButton(
style: ButtonStyle(
overlayColor: MaterialStateProperty.all(Colors.blue),
),
child: const Icon(Icons.arrow_circle_down_rounded),
onPressed: () => event.moveBackward(),
),
],
),
],
),
),
Expand All @@ -82,20 +110,36 @@ class RemoteScreen extends StatelessWidget {
),
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Console Log:', style: theme.textTheme.labelLarge),
SizedBox(height: mediaQuery.size.height / 150),
Text('Device: ${device.name}',
style: theme.textTheme.labelMedium),
Text('Connected: ${device.isConnected}',
style: theme.textTheme.labelMedium),
Text('MAC: ${device.address}',
style: theme.textTheme.labelMedium),
Text('Signal Status: $message',
style: theme.textTheme.labelSmall),
],
child: BlocBuilder<RemoteCubit, RemoteState>(
builder: (context, state) {
if (state is Loading) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
LinearProgressIndicator(),
],
);
} else if (state is Initial) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Console Log:',
style: theme.textTheme.labelLarge),
SizedBox(height: mediaQuery.size.height / 150),
Text('Device: ${widget.device.name}',
style: theme.textTheme.labelMedium),
Text('Connected: ${state.status}',
style: theme.textTheme.labelMedium),
Text('MAC: ${widget.device.address}',
style: theme.textTheme.labelMedium),
Text('Operation Status: ${state.message}',
style: theme.textTheme.labelSmall),
],
);
}

return Container();
},
),
),
),
Expand Down

0 comments on commit 5184c63

Please sign in to comment.