IMACS 2.0 Architecture

Overview

The Integrated Monitoring and Command Station (IMACS) is WARG’s custom ground control station.

Data Telemetry

The data telemetry system is written in the Dart programming language and uses the Dart MAVlink library to parse and send incoming MAVLink messages from a Serial or TCP port. More detail on MAVLink here: MAVLink

Graphical User Interface

The Graphical User Interface (GUI) is built using the Dart programming language and the Flutter framework.

Classes

MAVLink Communication

Task:

  • Reads the Serial or TCP port and converts the bytestream into MAVLink frames

  • Serializes MAVLink frames and send them to the drone

class MavlinkCommunication { MavlinkDialect dialect; MavlinkParser parser; private final StreamController<int> yawStreamController = StreamController<int>(); MavlinkCommunication(bool shouldReadFromTCP, String connectionAddress) { dialect = MavlinkDialectCommon(); parser = MavlinkParser(dialect); if (shouldReadFromTCP) { startupTcpPort(connectionAddress); } else { startupSerialPort(connectionAddress); } parseMavlinkMessages(); } startupTcpPort(String connectionAddress) { // Connect to the socket } startupSerialPort(String connectionAddress) { // Connect to the serial port } parseMavlinkMessage() { parser.stream.listen( (MavlinkFrame frame) { if (frame.message is Attitude) { // Append data to appropriate stream var attitude = frame.message as Attitude yawStreamController.add(attitude.yaw) }) } } // Getter function to corresponding stream Stream<int> getYawStream() async* { return yawStreamController.stream; } // Send MAVLink messages // Refer to the link below to see how MAVLink frames are sent // https://github.com/nus/dart_mavlink/blob/main/example/parameter.dart void write(MavlinkFrame frame) { if (shouldReadFromTCP) { writeToTcpPort(frame); } else { writeToSerialPort(frame); } } }

Input:

  • Boolean parameter in constructor to parse from either TCP port or Serial port

  • String parameter in constructor containing the address for the TCP or Serial port

Output:

  • Getter functions to retrieve streams for raw data (e.g. stream for yaw, stream for pitch, stream for roll)

Widgets

Widgets are the building blocks for Flutter applications. Each element on a screen of a Flutter app is a widget. The view of the screen completely depends upon the choice and sequence of the widgets used to build the apps. The structure of the code of an apps is a tree of widgets. Below is an example of widget that is updates its Text from the latest stream value.

void main => runApp(const ExamplePage()); class ExamplePage extends StatelessWidget { const ExamplePage({Key? key}) : super(key: key); MavlinkCommunication comms = MavlinkCommunication(true, "127.0.0.1:14550") @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Integrated Monitoring and Command Station"), ), body: Center( child: StreamBuilder( stream: comms.getYawStream(), builder: (context, AsyncSnapshot<int> snapshot) { return Text( snapshot.data!, ) } ) ) } }

Drone Information Widget

Input:

  • String parameter to display the name of the metric that is being updated in the GUI (corresponds to “Name” in the image above)

  • Stream parameter of type int to display the updates of the metric in the textbox

Map Widget

Input:

  • Stream parameter of type tuple<int, int> to display the drone’s location

NOTE: When choosing a map library for the GUI make sure that library does not require a network connection to work.