Caution panel – tech post

Now that we have seen the Caution panel at work,
Let’s take a step back and have a look at the technical aspect of it.

This post will have some code samples and inner workings – so feel free to move on if that doesn’t interest you.

Let’s start by looking at the SharedMem objects. I say Objects because unfortunately he have no “caution panel” we have a bunch on bits spread around all the light bits. I’ve only left the relevant bits, but as you can see they are all over the place.

enum LightBits
        EQUIP_HOT = 0x8,   // Caution light; repurposed for cooling fault (was: not used)

        // Caution Lights
        FltControlSys = 0x40000,
        LEFlaps       = 0x80000,
        EngineFault   = 0x100000,
        Overheat      = 0x200000,
        FuelLow       = 0x400000,
        Avionics      = 0x800000,
        RadarAlt      = 0x1000000,
        IFF           = 0x2000000,
        ECM           = 0x4000000,
        Hook          = 0x8000000,
        NWSFail       = 0x10000000,
        CabinPress    = 0x20000000,


    enum LightBits2
        // Caution Lights
        FwdFuelLow = 0x40000,
        AftFuelLow = 0x80000,

        // Caution panel
        SEC          = 0x400000,
        OXY_LOW      = 0x800000,
        PROBEHEAT    = 0x1000000,
        SEAT_ARM     = 0x2000000,
        BUC          = 0x4000000,
        FUEL_OIL_HOT = 0x8000000,
        ANTI_SKID    = 0x10000000,

    enum LightBits3
        // Caution panel
        Elec_Fault = 0x400,
        Lef_Fault  = 0x800,


As we have seen in one of the previous posts, there are two types for Caution panels, pre-blk40 and blk40 and up, they differ in the order of the lamps.
so we need to accommodate for that in software by allowing the user to select the desired version. But we’ll leave that for now, and go focus on the Arduino side

We’ll start from the most basic thing. storing the lamp data, the first instinct would be to look at the caution panel and say “We have 32 lights, we need them on or off, that is a bool, lets make a bool arrary and reg done with it”. That is a valid option, but it is very memory consuming and a bool takes up a full byte. so choosing to use a 32byte bool array will take up a big chunk of memory on the Arduino, in addition to the fact that the data needs to be transmitted, and 32 bytes is possible (we’re doing 125 bytes for the DED) it will slow us down.

for true or false we don’t need a byte we need a bit – just like it is originally stored. so a uint32_t would do the trick – it’s a four byte integer (32 bits) in C# it’s “int”, on Arduino it’s “long” but that allows us store and transmit only 4 bytes. a lot quicker compared to 32 byte. but because we are transmitting byte by byte – I figured that saving a 4 byte array would allow me some more flexibility in the whole endianess thing. as I can flip the order inside a byte very easily but between bytes might be a bit more complex. this way I just call the for loop backwards if needed.

Endianess is the way computers handle bits in bytes. there are two types, little endians and big endians. In the little endians the END is little – i.e Most Significant Bit First (MSB). the Big Endians are LSB, the Least significant bit is first. in humane terms, let’s sat we want to write the number “hundred twenty three”. the little endian version would be the normal “123”, the Big endian way of writing this would be “321”. Intel processors (x86 and x64) are Little endians. AMD for the home users (Athelon, phantom, etc.) are also Little endians.

As I’ve mentioned before, I’ve chose to use shift registers for their ease of use and the ability to chain them together.
how simple are they in Arduino? Very.. this is the ENTIRE code that deals with the Caution panel on the arduino side.

byte CautionPanel[4]; // Define global var for caution panel

void readCautionPanel() {
  Serial.print('C'); // Request Caution panel from PC
  Serial.readBytes(CautionPanel, 4); //Read 4 bytes into the global var

void lightCautionPanel() {
  digitalWrite(CpLatchPin, LOW); // pull caution panel CS low
  for (short i = 0; i < 4; i++) { // go over the bytes
    SPI.transfer(CautionPanel[i]); // transfer out to shift registers
  digitalWrite(CpLatchPin, HIGH); // light 'em up

I only had to initiallize the SPI properly first (a global definition)


On the PC side of things the heavy lifting is made.
I’ve also badly implemented a way to respond to the “test” button being pressed – I will need to revise it so that it will actually work..

        private byte[] MakeCautionPanel(string version="new")
         * this function takes one string argument "new" or "old" and returns an integer (4 bytes) of light bits according to the selected layout of the Caution panel
            //if (!LightCheck) // if we are not in lightcheck - run logic
            if (BMSdata.lightBits != -1073741841) //  check if all the lamp bits on LB1 are up. pretty much will only happen when you check lights.
            { //if "false" we are not in lightcheck - run logic
                BitArray mapping = new BitArray(32, false);
                switch (version)
                    case "new":
                        #region newCautionPanel
                      /// left row (bottom to top)
                        mapping[31] = lightBits2[19]; // AFT FUEL LOW
                        mapping[30] = lightBits2[18]; // FWD FUEL LOW
                        mapping[29] = false; // ATF NOT ENGAGED
                        mapping[28] = lightBits[6]; // STORES CONFIG
                        mapping[27] = lightBits3[22]; // CADC
                        mapping[26] = lightBits2[24]; // PROBE HEAT
                        mapping[25] = lightBits3[10]; // ELEC SYS
                        mapping[24] = lightBits[18]; // FLCS FAULT
                      /// mid left row (bottom to top)
                        mapping[23] = false; //blank
                        mapping[22] = lightBits2[26]; //BUC
                        mapping[21] = false; // EEC
                        mapping[20] = lightBits[21]; // OVERHEAT
                        mapping[19] = false; // INLET ICING
                        mapping[18] = lightBits2[27]; // FUEL OIL HOT
                        mapping[17] = lightBits2[22]; // SEC
                        mapping[16] = lightBits[20]; // ENGINE FAULT
                      /// mid right row (bottom to top)
                        mapping[15] = false;  //blank
                        mapping[14] = false;  //blank
                        mapping[13] = false; //blank
                        mapping[12] = false; // nuclear
                        mapping[11] = lightBits[25]; // IFF
                        mapping[10] = lightBits[24]; // Radar ALT
                        mapping[9] = lightBits[3]; // EQUIP HOT
                        mapping[8] = lightBits[23]; // Avionics Fault
                      /// right row (bottom to top)
                        mapping[7] = false; //blank
                        mapping[6] = false; //blank
                        mapping[5] = lightBits[29]; // Cabin Press
                        mapping[4] = lightBits2[23]; // Oxy_Low
                        mapping[3] = lightBits[27]; // hook
                        mapping[2] = lightBits2[28]; // anti-skid
                        mapping[1] = lightBits[28]; // NWS fail
                        mapping[0] = lightBits2[25]; // Seat not armed
                    case "old":
                        #region oldCautionPanel
                    /// left row (bottom to top)
                        mapping[31] = lightBits2[22]; //SEC
                        mapping[30] = lightBits[20]; // ENGINE FAULT
                        mapping[29] = false; // INLET ICING
                        mapping[28] = lightBits3[10]; // ELEC SYS
                        mapping[27] = lightBits3[22]; // CADC
                        mapping[26] = lightBits3[11]; // LE FLAPS
                        mapping[25] = false; // ADC
                        mapping[24] = lightBits[18]; // FLT CONT SYS
                    /// mid left row (bottom to top)
                        mapping[23] = false; //blank
                        mapping[22] = lightBits2[25]; // SEAT NOT ARMED
                        mapping[21] = lightBits2[27]; // FUEL OIL HOT
                        mapping[20] = lightBits2[26]; // BUC
                        mapping[19] = false; // EEC
                        mapping[18] = lightBits[21]; // OVERHEAT
                        mapping[17] = lightBits2[19];// AFT FUEL LOW
                        mapping[16] = lightBits2[18]; // FWD FUEL LOW
                    /// mid right row (bottom to top)
                        mapping[15] = false; //blank
                        mapping[14] = lightBits[6]; ; // STORES CONFIG
                        mapping[15] = lightBits[26]; // ECM
                        mapping[13] = lightBits[25]; // IFF
                        mapping[11] = lightBits[3]; // EQUIP HOT
                        mapping[10] = lightBits[24]; // RADAR ALT
                        mapping[9] = false; // ATF NOT ENGAGED
                        mapping[8] = lightBits[23]; // AVIONICS
                    /// right row (bottom to top)
                        mapping[7] = false; //blank
                        mapping[6] = lightBits2[24]; // PROBE HEAT
                        mapping[5] = false; // NUCLEAR
                        mapping[4] = lightBits2[23]; // OXY_LOW
                        mapping[3] = lightBits[29]; // CABIN PRESS
                        mapping[2] = lightBits[28]; // NWS FAILT
                        mapping[1] = lightBits[27]; // HOOK
                        mapping[0] = lightBits2[28]; // ANTI SKID

                byte[] result = new byte[mapping.Length];
                mapping.CopyTo(result, 0);
                return result;
            else //We are at lightcheck
                return BitConverter.GetBytes( uint.MaxValue ); // return all On.


And that’s pretty much the entire caution panel work proccess.

Leave a Reply