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 createState() => _CompassState(small); } class _CompassState extends State { 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("2200", system: true); }); } } @override void dispose() { SensorReader.removeListen(serialListen); timer?.cancel(); super.dispose(); } void serialListen(int type, int command, Pointer ptr) { if (type == 0x22 && command == 0xff) { setState(() { available = false; }); return; } if (type != 0x22 || command != 0x00) return; var loc = ptr as Pointer; 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( 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: [ TextButton( onPressed: () => Navigator.pop(context, 'Cancel'), child: const Text('Cancel'), ), TextButton( onPressed: () { serial.sprintln("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"), ) ], ); }