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.