MobileApp/lib/boxes/compass.dart

185 lines
5.8 KiB
Dart

import 'dart:async';
import 'dart:ffi';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:ju_rc_app/lib/boxes.dart';
import 'package:ju_rc_app/lib/sensors.dart';
import 'package:ju_rc_app/lib/serial.dart';
// c-structs
base class GnssLocData extends Struct {
@Uint8()
external int type;
@Uint8()
external int command;
@Int32()
external int X;
@Int32()
external int Y;
@Int32()
external int Z;
@Int32()
external int angle;
}
// end c-structs
class Compass extends JuBox {
final bool small;
const Compass(this.small, {super.key});
@override
// ignore: no_logic_in_create_state
State<StatefulWidget> createState() => _CompassState(small);
}
class _CompassState extends State<Compass> {
final bool small;
_CompassState(this.small) : super();
USerial serial = getSerial();
Timer? timer;
int _magX = 0, _magY = 0, _magZ = 0, _magAngle = 0;
bool available = true;
@override
void initState() {
super.initState();
SensorReader.listen(serialListen);
if (small) {
timer = Timer.periodic(const Duration(milliseconds: 500), (_) async {
if (available) serial.sprintln("<rfSystemSensor>2200", system: true);
});
}
}
@override
void dispose() {
SensorReader.removeListen(serialListen);
timer?.cancel();
super.dispose();
}
void serialListen(int type, int command, Pointer<ArrayCStruct> ptr) {
if (type == 0x22 && command == 0xff) {
setState(() {
available = false;
});
return;
}
if (type != 0x22 || command != 0x00) return;
var loc = ptr as Pointer<GnssLocData>;
setState(() {
_magX = loc.ref.X;
_magY = loc.ref.Y;
_magZ = loc.ref.Z;
int newAngle = loc.ref.angle;
if (newAngle - _magAngle > 200) newAngle -= 360;
if (newAngle - _magAngle < -200) newAngle += 360;
_magAngle = newAngle;
});
}
@override
Widget build(BuildContext context) => small
? GestureDetector(
onTap: () {
if (available) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const Compass(false)),
);
} else {
available = true;
}
},
child: buildLoc(context))
: Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text(
"Compass",
style: TextStyle(
fontSize: 16,
),
),
toolbarHeight: 40,
),
body: Container(
padding: const EdgeInsets.all(0.3 * 2 * 20),
child: buildLoc(context)),
);
Widget buildLoc(BuildContext context) => !available
? const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [Text("Compass is not available!"), Icon(Icons.block)]))
: Stack(
children: [
Center(child: SvgPicture.asset('assets/compass.svg')),
Center(
child: AnimatedRotation(
turns: _magAngle / 360.0,
duration: const Duration(milliseconds: 500),
alignment: Alignment.center, // Rotate around the center
child: SvgPicture.asset('assets/needle.svg'))),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"x: ${(_magX * 8.0 / 32768.0 * 100.0).toStringAsFixed(2)}µT"),
Text(
"y: ${(_magY * 8.0 / 32768.0 * 100.0).toStringAsFixed(2)}µT"),
Text(
"z: ${(_magZ * 8.0 / 32768.0 * 100.0).toStringAsFixed(2)}µT"),
if (!small)
Expanded(
child: Align(
alignment: Alignment.bottomLeft,
child: FilledButton(
child: const Icon(Icons.calculate),
onPressed: () {
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Callibrate Compass'),
content: const Text(
'When clicking okay the compass will be calibrated, \ncompletely blocking all radio communication for 10 sec. \nWhile calibrating rotate the sensor in ever direction.'),
actions: <Widget>[
TextButton(
onPressed: () =>
Navigator.pop(context, 'Cancel'),
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
serial.sprintln("<rfSystemSensor>2201",
system: true);
Navigator.pop(context, 'OK');
},
child: const Text('OK'),
),
],
),
);
//
},
),
),
),
],
),
Align(
alignment: Alignment.topRight,
child: Text(
"t: ${(sqrt(_magX * _magX + _magY * _magY + _magZ * _magZ) * 8.0 / 32768.0 * 100.0).toStringAsFixed(2)}µT"),
)
],
);
}