miniAVX 2.02.178 - DIY Arduino Mini Avionics Panel

Trying to research the possibilities:

I found these OLED displays which use the I2c bus.

https://www.amazon.com/MakerFocus-Display-SSD1306-3-3V-5V-Arduino/dp/B079BN2J8V?th=1

image

This will spare a lot of GPIO pins but I will need a multiplexer cause each device has a fixed i2c address. The device is 38 mm long (1.5 inches ) and can display 8 digits. It would be possible to put 4 of them side by side and that will only be 6 inches which means I could use a 7.87 inch long ABS enclosure.

I also read that the MAX7219 would be a cpu hog in complicated sketches with one commenter recommeding I2c devices instead.

Yes, I2C is generally much faster. Even better would be SPI.

Since I am using only one MAX it’s ok, did not notice that. The MAX can be serialized and hence save a lot of pins.

I don’t know what you mean about the MAX able to be serialized. Are you saying that several 7 segment 6 digit displays for example, can be daisy chained?

Found these from propwash which might cover up what would likely be horrible square holes that I will make.

image

I can’t find any 5 digit 7 segment LED though, so I am hoping a 6 digit 7 segment LED would fit (with one digit covered ).

Yes correct. The ones I used can be daisy chained and addressed.

Good to know.

The concept of the project is evolving. Now I am thinking I will try to modularize it. Perhaps build the Cessna radio panels first. The box will have a mega (receive) and a uno (send). Then I will leave some kind of input jack on the side that exposes the SDA,SCL and GND lines so when I build another box, it can be connected as a slave with just an uno (send).

So I might build a general purpose box for example that has all the common flight buttons. Then stack a slave box on top for perhaps the Cessna AP. Then stack another box on top for perhaps the A320 AP.

The lowest box will always be both send and receive.

Each slave will have a data struct that is also defined in the master. The slave will have a data byte whose bits indicate which data the slave is currently interested in. This changes in the slave for example when the slave buttons are pressed that change the LED display to displaying Altitude from Vertical Speed.

The master requests this byte from from the slave. Checks the bits and then polls SimConnect for the values requested. Then sends the data struct over to the slave. Or maybe sending an array of bytes over will be better because I don’t want to send a full data struct when only a few members are requested. So Both master and slave will have the same request byte and know exactly how many bytes to send on the master and read on the slave and in what order to reassemble. This cuts down on requests for SimConnect data and cuts down on bytes transferred via the I2c bus. 8 booleans could also be sent as a byte.

In the startup, the master needs to figure which slaves are connected to I2c. A slave could be removed from the I2c bus by moving a toggle switch which breaks continuity with I2c lines but without breaking the connection of other slaves above it.

I have started to add more functions to MiniAVX and it seems I have reached the limit of variables I can read at the same time. The disply now lags behind quite a bit, I would say it only updates like every 1/2 second.

I finally got my capacitors and the send and receive sketches are both working using just once instance of the connector app. Still on 0.8.3 for now. I had to make one simple change to the transmit code: the variable ‘message’ is now cleared after each case statement gets executed, instead of after the switch statement finishes. This ensures that each iteration of ‘message’ received is acted upon. Without the change, some button actions seemingly get ignored and the effect is a laggy response ( I have to turn the encoder several times before I see the effect. ) I think if the I2c receive interrupt gets triggered during the switch statement, message justs gets cleared in the original code.

I got the basics working now. Will upgrade to 0.8.4 and work on the i2c message code so that it can handle ints and bools in a byte. Hope my coding is up to the task.

How many variables could you read at the same time?

Chris

THAT IS A GREAT FIND! I have taken the liberty to forward this to the developer, he will be very keen to get this information.

Yes, I had that lagging too, it does seem some variables cause this. I had this on the feetAboveGround variable. Removing it somewhat stabled the code again. I have forwarded this issue and it will be adressed. Furthermore BitsAndDroids should support sets of varibales soon, so you can chosse which set is received by pressing a button.

This was an interessting read. I’m still working on 't sets like pontiac mentioned so they are comming up (no paywalling just progresswalled). Did you manage to get the light states working?

I am wracking my brain trying to figure out how to convert a 4 byte float to a 2 byte int. Most of the variables in bits and droids are Strings and that is a lot of bytes to send and receive via the serial line. I am thinking of this more for I2c communication. Most values in the Sim are less than 65535 ( Closest to this maybe altitude). Then there are values in the 200 range with decimals. If I sacrifice one bit, then I have a maximum of 15 bits or 32767 that can be represented as a uint16_t. I plan to use the last (or first) bit to signal whether the next 15 bits represent a 15 bit uint16_t or formatted as a 2 byte whole number ( 8 bits gives up to 255) and a fractional number with two decimal places ( 7 bits give 127 → only need 0-99 ).

If I ever put this together, then it might be a good idea to apply the same between BitsandDroids and Arduino serial communication. Send the connector packages that will fit in 2 bytes as uint16_t, then unpack on the Arduino.

It’s all pointless if reducing the number of bytes that travel the I2c bus does not change how fast the sketch runs. I think though that less bytes sent with each write means less time hogging the serial line.

Does any numerical sim variable go beyond 255.99 or 32767?

Yes, the capacitors did the trick.

Chris

Got this snippet that seems to be working for the goals I stated with the limitations stated. The numerical strings from the connector app can be converted to floats and then sent here to compress into two bytes, then sent to the I2c bus and unpacked on the other side. I think it will work. Better than sending a string of 254.12 ( 7 bytes).

uint16_t Encode(float fnumber) {
uint16_t inumber=abs(fnumber);
//Lose precision, always positive -> store as square root -> first bit will be 1 and next 7 bits 
approximate the nearest 1/27 value using numbers 100-127
if ((inumber > 16384) && (fnumber > 0)) { 
     float sroot=sqrt(fnumber); 
     //Extract whole number and set the first byte to its value
     inumber=(uint8_t) sroot;
     //Extract fractinal number set the second byte to its value
     //First Method, not very accurate but faster
     uint16_t ifract = (uint16_t) ((((sroot * 100) - (inumber*100)))/27)+100;  //Extract the fractional part as an int and divice by 27 and add to 100
     //second Method, more steps but better accuracy -->takes an extra 20 microseconds to finnish
     //uint16_t ifract = (uint16_t)  (round(((sroot * 100) - (inumber*100))/27))+100;
     
     inumber <<= 8;
     ifract <<=  1;
     inumber +=  ifract;
     inumber |= 1UL << 0;  //Set the first bit
     return inumber;
// Precise and with a sign if no fractional component -> store as 14 bit number with first bit 0 and second bit sign with 0 positive and 1 negative      
} else if ((inumber <= 16384 ) && ((int)fnumber == fnumber )) { 
    
    inumber <<=  2;         //Shift 2 bits to the left, first bit is automatically zero
    //Extract the sign of the float and put it in the second bit
    
    if ((int)fnumber <0)
        inumber |= 1UL << 1;
    return inumber;    
// Precise and with a fractional component, always positive      
} else if ((inumber < 256 ) && (fnumber > inumber)) {
  //The whole number is already inumber
  //Extract the fractional number
   uint16_t ifract=(uint16_t)  ((fnumber * 100)-(inumber*100));
   inumber <<= 8;
   if (ifract > 0) {
     ifract <<=  1;
     inumber +=  ifract;
   }  
   inumber |= 1UL << 0;  //Set the first bit
   return inumber;
}  else {
         //Error. Unhandled case
       return 0; //error condition
}
}



float Decode(uint16_t inumber) {
//check first bit
 uint8_t bit = inumber & 1U; 
if (bit) { // whole + fract
 //Extract whole number
 uint16_t whole= inumber >> 8;
 
 //Extract the fraction
 uint16_t fract = inumber & 0xFF;

 fract >>= 1; 
 if (fract > 99) { //Greater than 16384
    //Round away the inaccuracy and report values in thousands only -> takes twice as long to finnish
    return (round((sq(whole + ((fract - 100 ) * 27)/(float)100))/1000))*1000;
    //Don't round away the inaccuracy
    //return sq(whole + ((fract - 100 ) * 27)/(float)100);
 } else {
   whole *=  100;
   return ((float)whole + fract)/100;
 }  
} else {
 //Extract the sign from second bit
 uint8_t sign = inumber & 0x2;

 if (inumber & 0x2)
    return ((float)(inumber >> 2))*-1;
 else
    return (float)(inumber >> 2);
       
}
}  

EDIT:

Modified the code to handle negative values for certain conditions and to handle numbers up to 65537.

Performance:
Encode 65000.00 ==> 65231 ==> 65000.00 Encode time: 108 Decode time 112
Encode 44000.00 ==> 53709 ==> 44000.00 Encode time: 116 Decode time 112
Encode 44501.00 ==> 53967 ==> 44000.00 Encode time: 108 Decode time 112
Encode 15101.00 ==> 60404 ==> 15101.00 Encode time: 24 Decode time 8
Encode -5000.00 ==> 20002 ==> -5000.00 Encode time: 24 Decode time 8
Encode 250.00 ==> 1000 ==> 250.00 Encode time: 20 Decode time 8
Encode -25.00 ==> 102 ==> -25.00 Encode time: 24 Decode time 12
Encode -25.12 ==> 0 ==> 0.00 Encode time: 32 Decode time 4
Encode 108.45 ==> 27739 ==> 108.45 Encode time: 56 Decode time 44
Encode -100.21 ==> 0 ==> 0.00 Encode time: 28 Decode time 4
Encode -32000.00 ==> 0 ==> 0.00 Encode time: 12 Decode time 4

Chris

Djeez you’re onto something. There is definitely some improvement to be made in the current implementation. When I started creating the connector I had 0 knowledge of microcontrollers or c++. In other applications performance never was a real issue. Who cared that the app uses 0.01 seconds longer to log me in or something along those lines.
I definitely learned that this doesn’t apply to this field of programming. Looking back at the code there are many areas of improvement possible (managed to kill many ugly-looking monsters already). Your approach of breaking it down into bits is something I definitly want to explore.

Hey good to hear from you. I’ve been meaning to send you an email. I was wondering if you would keep the possibility of using two arduino’s ( one for send and one for receive ) as an option in the connector app going forward. I think I am partial to that solution because I think it will be faster.
Also, do you think having one master and multiple slave addons is a practical solution? I was thinking of building a master and slave combo in the first box and then later add a second box with an arduino as an i2c slave. Master arduino does all the data fetching, each slave will do their own sending of commands. Each slave will request the data they need from the master. If the master has spare IO pins for buttons or encoders, they can be “forwarded digitally” to the slave as in the example codes above.

As an aside, I think I have figured out a working algorithm for saving values above 32767 in two bytes by extending the concept in the code snippets above. You lose a little precision and there will be some computational overhead which might be minimal in the PC side if you decide to try to pack big things into smaller packages before sending them over.

For the case of values above 32767( if it is really needed, don’t know what sim values go above this number) :
My idea involves using the first byte to store the whole number part of the square root of the number. Then save the fractional part indirectly in the next 7 bits. Those 7 bits are being used already for values between 0-99 for fractional parts of numbers between 0 -255. The values of 100 - 127 are still available. The square root of 65535 is 255.99 for example. We can assign increasing multiples of 1/28 values in a lookup table corresponding to each number from 100 - 127. Record the number of 100 - 127 on the 7 bits representing the closest approximation of the fractional part. On decode, check the value of the 7 bits and if it is 100 or greater, then we know we are dealing with a number above 32767. Subtract 100 from this number and the remainder will be an index to the lookup table. So add the number derived from the first byte to the fractional value in the lookup table corresponding to the value between 100-127. Square the sum and you should have a number very close to the original that was encoded. At 50,000 feet altitude, I think plus or minus 50 feet is not really significant.

Chris

I fly almost VR. Is there (an option for) an acoustic feedback on the main knob as beep for Pos1, beep-beep for Pos2 and so on?

thank you

The connector itself does not generate sound, only inputs. You could add a speaker to the Arduino, but I think this is not feasible for VR as you use headphones surely?

In addition, my panel has far to many entries now to work with beeps. That would be 8 beeps for the last menu entry.

In this case I think it would be more feasible to use different knobs or switches.

1 Like

My first attempt at a SimConnect App.

Consumed my weekend. I think I will call it Form1. Or maybe droidsandbits (all lowercase). Where did I get that idea? :wink:

EDIT: Maybe I will call it Form1.
Chris

Good work! But there might be someone who beat you to it. :wink:

MobiFlight + Arduino + Your Favorite Flight Simulator = Your Home Cockpit!

Actually ( without researching how ), I think it is possible to have an audio Annunciation in VR mode but the work will have to be done on the PC side. The arduino just needs to issue a special serial request for example to BitsandDroids to request the annunciation per SimEvent trigger. I am sure there is a way to generate this audio on the PC side informing the user of the SimEvent. It will have to be written in to the code. It would be cool to hear “AutoPilot Engaged” in your headphones when you set that in the Arduino sketch. Just pretend it is your friendly copilot.
Or something like “Red Alert”, " She can’t take it anymore Captain", "Phasers online ", “Engaging Evasive Maneuver Alpha Gamma”, “We are being Hailed”, “We are being scanned”, “Resistance is futile”. :wink:

Or if vertical speed is -30000, “We are crashing captain. It has been an honor serving. Transporter, one to beam out”
Captain (you) : “Gyaaaahhhh!”

Chris

Yes, that sounds like a plan.

Wow, this is great! Thank you for sharing! I’ve been using Mobiflight, but looking for a more direct/custom solution that works practically directly with the sim. I assume one could make modules like this essentially plug and play? Does any software need to be running or does the Arduino talk directly to MSFS once the sketch is upload it to it?