728x90
1. 프로젝트 환경 설정 하기(안드로이드,IOS)
1. 구글 클라우드 접속
2. API 키 발급받기
3. 안드로이드 설정 하기
4. IOS 설정하기
5. IOS 권한 요청 메시지 설정하기
2. 레이아웃 구상하기
1. App bar
타이틀 작성
2. Body
지도
3. Footer
운전 시작하기 버튼
1. main.dart
import 'package:flutter/material.dart';
import 'package:flutter_naver/screen/home_screen.dart';
void main() {
//HomeScreen을 홈 위젯으로 설정
runApp(const MaterialApp(
home: HomeScreen(),
));
}
2. home_screen.dart
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Text('Home Screen'),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
import 'package:geolocator/geolocator.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// 앱의 메인 환경을 설정합니다. 여기서는 MaterialApp 위젯을 사용합니다.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter BLE Demo', // 앱의 제목 설정
theme: ThemeData(
primarySwatch: Colors.blue, // 기본 색상 테마 설정
),
home: const BluetoothScreen(), // 시작 화면으로 BluetoothScreen을 사용
);
}
}
// 메인 화면의 StatefulWidget 선언
class BluetoothScreen extends StatefulWidget {
const BluetoothScreen({Key? key}) : super(key: key);
@override
_BluetoothScreenState createState() => _BluetoothScreenState();
}
class _BluetoothScreenState extends State<BluetoothScreen> {
List<BluetoothDevice> devices = []; // 발견된 BLE 장치 목록
Map<String, String> deviceValues = {}; // 장치 이름과 값을 저장하는 맵
double currentSpeed = 0.0; // 현재 속도 저장 변수
Timer? timer; // 주기적 업데이트를 위한 타이머
bool isMeasuring = false; // 운행 측정 상태
bool isCalibrating = false; // 캘리브레이션 상태
@override
void initState() {
super.initState();
startScan(); // 컴포넌트 로드 시 스캔 시작
}
@override
void dispose() {
timer?.cancel(); // 컴포넌트 해제 시 타이머 취소
super.dispose();
}
// BLE 장치를 스캔하는 함수
void startScan() async {
FlutterBluePlus.startScan(timeout: const Duration(seconds: 4)); // 4초 동안 스캔
FlutterBluePlus.scanResults.listen((results) {
for (ScanResult result in results) {
if (result.device.advName == 'ESP32 Force Sensor A' ||
result.device.advName == 'ESP32 Force Sensor B') {
if (!devices.contains(result.device)) {
setState(() {
devices.add(result.device);
});
connectToDevice(result.device); // 장치 연결 시도
}
}
}
});
}
// 특정 BLE 장치에 연결 시도
void connectToDevice(BluetoothDevice device) async {
try {
await device.connect(); // 장치 연결
discoverServices(device); // 서비스 발견
} catch (e) {
print('Error connecting to device: $e');
}
}
// 연결된 장치의 서비스를 탐색
void discoverServices(BluetoothDevice device) async {
List<BluetoothService> services = await device.discoverServices();
for (var service in services) {
for (var characteristic in service.characteristics) {
if (characteristic.properties.notify) {
characteristic.setNotifyValue(true);
characteristic.lastValueStream.listen((value) {
if (isMeasuring || isCalibrating) {
// 측정 또는 캘리브레이션 중일 때만 데이터 처리
setState(() {
String deviceValue = String.fromCharCodes(value);
deviceValues[device.name] = deviceValue; // 데이터 저장
});
}
});
}
}
}
}
// 서버로 데이터 전송
void sendDataToServer(
String deviceName, String value, String operation) async {
if (isMeasuring || isCalibrating) {
// 측정 또는 캘리브레이션 상태일 때만 전송
final url = Uri.parse('http://localhost:8080/api/test');
final response = await http.post(
url,
headers: <String, String>{
'Content-Type': 'application/json',
},
body: jsonEncode(
<String, dynamic>{
'deviceName': deviceName,
'value': value,
'speed': currentSpeed.toStringAsFixed(1), // 속도는 소수점 한 자리까지
'operation': operation,
},
),
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('서버로 값을 보냅니다'),
duration: Duration(seconds: 1),
),
);
if (response.statusCode == 200) {
print('Data sent successfully');
} else {
print('Failed to send data. Status code: ${response.statusCode}');
}
}
}
// 운행 시작/종료 토글
void toggleMeasurement() {
if (isMeasuring) {
timer?.cancel();
isMeasuring = false;
deviceValues.forEach((deviceName, value) {
sendDataToServer(deviceName, value, "운행 종료");
});
} else {
timer = Timer.periodic(const Duration(seconds: 1), (timer) async {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
setState(() {
currentSpeed = position.speed * 3.6; // Convert m/s to km/h
});
deviceValues.forEach((deviceName, value) {
sendDataToServer(deviceName, value, "운행 시작");
});
});
isMeasuring = true;
}
}
// 캘리브레이션 시작/종료 토글
void toggleCalibration() {
if (isCalibrating) {
isCalibrating = false;
deviceValues.forEach((deviceName, value) {
sendDataToServer(deviceName, value, "캘리브레이션 종료");
});
} else {
isCalibrating = true;
deviceValues.forEach((deviceName, value) {
sendDataToServer(deviceName, value, "캘리브레이션 시작");
});
}
}
// UI 구성
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bluetooth Devices'), // 앱 바 타이틀
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isMeasuring || isCalibrating)
...deviceValues.entries
.map((e) => Text('${e.key}\nValue: ${e.value}'))
.toList(),
Text('내 기기 속도: ${currentSpeed.toStringAsFixed(1)} km/h'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: toggleMeasurement,
child: Text(isMeasuring ? '운행 종료' : '운행 시작'),
),
ElevatedButton(
onPressed: toggleCalibration,
child: Text(isCalibrating ? '캘리브레이션 종료' : '캘리브레이션 시작'),
),
ElevatedButton(
onPressed: () {
timer?.cancel(); // 타이머 중지
setState(() {
devices.clear();
deviceValues.clear();
isMeasuring = false;
isCalibrating = false;
startScan(); // 장치 재스캔
});
},
child: const Text('재스캔'),
),
],
),
),
);
}
}
728x90