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)
SPI.begin(); SPI.setBitOrder(LSBFIRST); SPI.setClockDivider(SPI_CLOCK_DIV2);
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 #endregion break; 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 #endregion break; } 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.