185 lines
5.8 KiB
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"),
|
|
)
|
|
],
|
|
);
|
|
}
|