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:
- Open source: design files and source code (for hardware, firmware and software) are hosted on GitHub under the GPLv3 license. This means that the contributions to this project may not be used in any closed source projects. Anyone is free to use the code for private and/or commercial use, but needs to publish any modifications under the same open source license.
- Physically modular: modules (consisting of sensors, control outputs, interfaces, and/or displays) plug into a shared bus. Each module provides a specific set of features (also see overall feature wishlist) based on its attached peripherals. A module should be less than $100; if a set of peripherals exceeds this, the functionality should be split into smaller modules.
- Modularity in terms of software: no firmware updates should be needed if a new module is added onto the bus. All the functionality related to the new module (which might be unknown at this point in time) needs to be self-contained. Having access to all other sensor readings on each module allows for some interesting data fusion and new parameters. For example, the module that measures the fuel level can also calculate the fuel endurance and range with the current tank, based on the ground speed and average fuel consumption.
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