Project Goals

The main goal of this project is to supply an open source infrastructure for avionics development. Each aircraft has different budgets and requirements for aviation instruments; trying to address them all with a single module is unrealistic. A modular approach splits functionality into reusable components. This makes it easier to maintain, replace faulty units, and upgrade by adding modules instead of replacing the entire system. An example is shown below:

Another advantage of the modular approach is that the project can be completed in stages, with nice-to-have features being added later. As a pilot, having access to real-time, reliable and relevant information greatly assists in the decision making process. Adding additional instruments thus becomes another way of making aviation safer for everyone sharing the airspace. With the centralised design of the original prototype, I needed to remove the electronics from the aircraft every time I wanted to add functionality. This resulted in not being able to fly while features were developed (which usually took longer than expected), or consequently not developing features because I wanted to fly.

The end goal is to have a library of modules that can be mixed and matched to suit different pilot and aircraft requirements. Examples of modules include:

Module Features Status
Rotax 582 Engine RPM, EGTs, H2O, fuel level, fuel flow, relay and open drain outputs prototype
3.0" Graphic LCD
Nelytech NT-G128641A 128x64 monochrome (Black on White) prototype
Navigation Waypoints, GPS, IMU, altitude, air speed design
Computer Interface Allows PCs, tablets, smart phones and smart watches to be used as multi-functional displays (MFD) planning
7" TFT LCD
Single board computer (RasPi / BB) with sunlight readable TFT LCD screen planning
1.4" Graphic LCD Nokia5110 84x48 monochrome planning
Aircraft Interfaces Outputs for fuel pump, lights, flaps, trims, heater, gear, ... idea
VHF Transceiver Interface Remote interface for airband radio idea
Transponder Interface Remote interface for ATC transponders, altitude encoder for older Mode C types idea
Aircraft Intercom Annunciation, volumes, PAX idea
Situational Awareness SDR-based ADS-B receiver, air-band channel activity indicator idea
Air-to-air Communication Low-cost ISM-band position broadcasting (similar to ADS-B in and out) to keep track of other aircraft in your squadron idea
Black Box Logging to micro SD card idea
Power Alternator and battery management, voltage and current idea

Personal Goals and Current Priorities

My main goal is to develop an advanced avionics system for my weight shift controlled (WSC) microlight trike (see background post). Being an open cockpit, my requirements might not always align with other pilots expectations. For example, touchscreen interfaces are almost impossible to use in-air (e.g. typing, zooming, panning); especially when fighting bumpy air and with thick gloves. Physical buttons, knobs and rotary encoders work quite well though. Depending on the angle of the sun, most TFT LCDs are challenging to read. Transflective STN LCD panels on the other hand, are easily readable in direct sunlight.


System Design

Requirements drive design. Read that again but slower. This project has three simple requirements:

On the display modules, this modularity is achieved by allowing the user to add widgets onto the screen, and linking them with sensor parameters as shown in the diagram below.

This approach applies to all display modules; whether small 2x16 character LCDs, monochrome graphical displays, Smart-phones, tablets or large colour TFT LCD screens. Widgets can be simple text, graphical items like gauges and bars, graphs that plot parameters (over time or against other parameters), attitude reference lines, 2D moving maps, or full 3D components such as synthetic vision (using topological maps and taking position and attitude sensor inputs).

For sensor modules, modularity is realized with a remote menu that can be accessed by a display module. This remote console can be used for configuration, alarm setup, viewing status, editing the value broadcast rate and any other options relating to the sensor.

This also greatly simplifies the communication protocol. There is no need to define message types for calibration or any other sensor-specific data. Similarly, the display modules and rest of the system need no prior knowledge of how to handle custom user data. User inputs can be simple up/down/ok/... buttons, rotary encoder inputs, or any characters from a keyboard.


Modules and Parameters

Modules provide an interface between the system bus and various inputs/outputs (usually sensor inputs and control outputs). This interface is abstracted to parameters which have an identifier (a unique 16-bit ID) and a name (text characters). Parameter IDs have default values for each module, but are not predefined by the protocol and must be unique on the bus. As an example, consider the block diagram for an abstracted Engine Module below.

On the hardware level, the 'RPM Sensor' consists of two resistors and two diodes connected to an interrupt capable input pin on the micro-controller. After some counting and scaling, this sensor produces a parameter called "RPM" which is periodically broadcast onto the CAN bus at 10 Hz. This sensor is also used to detect if the engine is running, and as such produces additional parameters such as total engine hours (i.e. Hobbs meter, "ENG HRS"), engine on-time ("ENG ON"), and service / maintenance reminders ("MAINTAIN"). Thus modules with inputs feature a bunch of sensors, each of which produces one or more parameters, which are in turn broadcast and/or remotely accessed via the CAN bus.

Each module is required to have a remote configuration menu (i.e. console), through which its parameter IDs, names, broadcast rates and other settings can be modified. This makes it easy to add features, maintain and manage complexity. For a two-engine aircraft, the same engine module and firmware could thus be used, and configured with different parameter IDs and names.

Module outputs are also mapped into the parameter space. They are set with a RemoteCMD.SetValue message, or are configured (via their remote console) to trigger by other parameters. They can be virtual (e.g. "QNH" setting for the pressure altitude), or physical (e.g. "RELAY", "BUZZER", or "ALT ENC").

Message Types

This section describes the message types (i.e. application layer) for the ModAir bus, with the CAN standard defining the transfer (CAN 2.0B) and physical (ISO 11898-2:2003) layers. The CAN-bus was chosen for it's robustness, automatic message arbitration and widespread use in the aerospace and automotive industries.

For the ModAir protocol, extended CAN messages are used at a fixed data rate of 1 Mbps. An extended CAN message consists of a 29-bit identifier (CAN-ID) and a maximum payload of 8 data bytes. The 8 lower bits of the 11-bit standard identifier are used to specify the Message Type (MT). The 18-bit extended identifier is split into a 16-bit Source Parameter ID (PID) and a 2-bit Frame Type (FT) as shown below.

Depending on the message type, the data bytes are either broadcast to all other nodes, or address a specific destination parameter ID with a command and remote data. Message types are defined in the table below.

Message Type (MT_) Hex Value Comments
ALARM 0x01 Alarm broadcasts
REMOTE_CMD 0x02 Addresses a remote node with a command and data
VALUE 0x03 Value broadcasts (e.g. periodic sensor readings)
MSG_ERROR 0x04 Error message text
MSG_WARNING 0x05 Warning message text
MSG_INFO 0x06 Information message text
NAME 0x07 Name broadcast
CONSOLE_TEXT 0x20 Terminal text / console
REMOTE_ACK 0x2E Acknowledges a remote command
DATA_CHANNEL 0x2F Data channel for configuration upload and download

The priority of a CAN message is given by its CAN-ID; lower numbers have higher priority. The MT defines are thus carefully chosen to represent the priority of the particular message type (with the data channel having the lowest priority). Thus the contents, priority and the source node are uniquely identified by the CAN-ID.

The frame type definitions are given in the table below. Here the transmit order is important, hence end-of-frame has the lowest priority. Continued frames should not interrupt MT or PIDs of higher priority, making the FT defines the two least significant bits of the CAN-ID.

Frame Type (FT_) Hex Value Comments
PKT_SINGLE 0x0 We only expect a single data packet
PKT_START 0x1 Start of Frame data, expect continued data next
PKT_CONT 0x2 Continued data
PKT_END 0x3 End of Frame data: this was the last data packet

The full protocol is given in the C header file below:

#ifndef MODAIR_BUS_H
#define MODAIR_BUS_H
// MODAIR CAN-ID defines:
#define MA_PKT_TYPE_MASK            0x00000003 // Packet Frame Type (see FT_* defines)
#define MA_PARAM_ID_MASK            0x0003FFFC // Source Parameter ID: uniquely identifies the module, sensor, or output
#define MA_MSGTYPE_MASK             0x0FF00000 // Message Type (see MT_* defines)
// FRAME defines:
#define FT_PKT_SINGLE               0x0  // We only expect a single data packet
#define FT_PKT_START                0x1  // Start of Frame data, expect continued data next
#define FT_PKT_CONT                 0x2  // Continued data
#define FT_PKT_END                  0x3  // End of Frame data: this was the last data packet
// MSGTYPE defines: Message Types
#define MT_BROADCAST_ALARM          0x01 // Sensor alarm broadcasts
#define MT_REMOTE_CMD               0x02 // D<0:1> DESTINATION PARAM_ID (or DPI_* defines), D<2> CMD_CODE (see RC_* defines)
#define MT_BROADCAST_VALUE          0x03 // Sensor broadcasts (periodic, requested, or as a response to SET_VALUE)
#define MT_BROADCAST_MSG_ERROR      0x04 // Error message
#define MT_BROADCAST_MSG_WARNING    0x05 // Warning message
#define MT_BROADCAST_MSG_INFO       0x06 // Information message
#define MT_BROADCAST_NAME           0x07 // Broadcast name (after powerup or requested)
#define MT_CONSOLE_TEXT             0x20 // Text terminal / console / menu
#define MT_REMOTE_ACK               0x2E // D<0:1> DESTINATION PARAM_ID (or DPI_* defines), D<2> ACK_CODE (see AK_* defines)
#define MT_DATA_CHANNEL             0x2F // Data channel for Firmware / Configuration upload and download
// DESTINATION PARAM_ID (unique ID for each module, sensor, and output), or one of these defines:
#define DPI_ALL_PARAMETERS          0xFFFF // Addresses all parameters (virtual nodes hosted on a physical node)
#define DPI_ALL_MODULES             0xFFFE // Addresses all modules (physical node)
// MT_REMOTE_CMD defines: Remote Commands Codes (CMD_CODE)
#define RC_GET_VALUE                0x00
#define RC_GET_NAME                 0x01
#define RC_SET_VALUE                0x02 // D<4:7> new value
#define RC_CONSOLE_KEY              0x03 // Remote terminal / console: <D3> KeyPress (see KP_* defines)
#define RC_GET_VALUE_DTYPE          0x04 // Data type: s8,u8,s16,u16,s32,u32,float,double,time,date,char
#define RC_GET_VALUE_RMAX           0x05 // Range
#define RC_GET_VALUE_RMIN           0x06
#define RC_GET_VALUE_WMAX           0x07 // Warning
#define RC_GET_VALUE_WMIN           0x08
#define RC_GET_VALUE_AMAX           0x09 // Alarm
#define RC_GET_VALUE_AMIN           0x0A
#define RC_REBOOT                   0x10 // Reboot module
#define RC_FACTORY_RESET            0x11 // Restore default settings for module
#define RC_FIRMWARE_READ            0x12 // Read current firmware
#define RC_FIRMWARE_WRITE           0x13 // Program new firmware
#define RC_CONFIG_READ              0x14 // Read current configuration data
#define RC_CONFIG_WRITE             0x15 // Write new configuration data
#define RC_LOG_READ                 0x16
#define RC_LOG_CLEAR                0x17
// MT_REMOTE_ACK defines: Remote Acknowledge Codes (ACK_CODE)
#define AK_OKAY                     0x01
#define AK_DONE                     0x02
#define AK_ERROR                    0xFF
// RC_CONSOLE_KEY defines: Key Press codes
#define KP_ASCII                    0x00 // 0x00 to 0x7F: standard ASCII chars
#define KP_ROT_INC                  0x81
#define KP_ROT_DEC                  0x82
#define KP_ROT_HOLD_INC             0x83
#define KP_ROT_HOLD_DEC             0x84
#define KP_ROT_PUSH                 0x85
#define KP_ROT_HOLD                 0x86
#define KP_ROT_LONGHOLD             0x87
#define KP_ROT_EXTRALONGHOLD        0x88
#endif