Jump to content

Jittery output for Altimeter data


lesthegrngo

Recommended Posts

Disclaimer: I'm not sure that the sketch in the previous post is 100% working but I think so :-)

It wasn't. ;)

 

I've managed to get it to compile (with the DCSBIOS links removed), but the displayupdate() function causes it to freeze if it's run in the setup. Particularly if the 0X3D address 128x64 display is enabled.

 

I put a pin 13 LED blink in the loop() to determine if it's running.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "characters.h"

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define SCREEN_HEIGHT_A 64 // OLED display height, in pixels

#define AltAddress 0
#define HGAddress 1

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)

struct scrollDigit {
 int digit;
 int y;
};

struct disp {
 Adafruit_SSD1306 display;
 int address;
 int width;
 int numberOfDigits;
 scrollDigit digits[5];
};


disp displays[2] = 
{
 {Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT_A, &Wire, OLED_RESET), 0, 24, 5, {{0,0},{0,0},{0,0},{0,0},{0,0}}},
 {Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), 1, 16, 4, {{0,0},{0,0},{0,0},{0,0},{0,0}}}
};


void setup() {
 
     displays[1].display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Address 0x3C for 128x32

//   if(!displays[1].display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
//     for(;;); // Don't proceed, loop forever      
//   }
     displays[1].display.clearDisplay();
     displays[1].display.display();

    displays[0].display.begin(SSD1306_SWITCHCAPVCC, 0x3D);  // Address 0x3D for 128x64
   
//       if(!displays[0].display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) { // Address 0x3D for 128x64
//     for(;;); // Don't proceed, loop forever      
//   }

     displays[0].display.clearDisplay();
     displays[0].display.display();
     

//      UpdateDisplays();
}

void UpdateDisplays()
{
 for (int j = 0; j<2; j++)
 {
   displays[j].display.clearDisplay();
   for (int i = 0; i < displays[j].numberOfDigits; i++)
   {
     printScrollingDigit(displays[j].digits[i].digit, displays[j].width, displays[j].digits[i].y, i + 1, &displays[j]);
   }
   if (displays[j].width == 16)
   {
     displays[j].display.fillRect(0, 25, 128, 7, BLACK);
   }
   displays[j].display.display();
 }
}

void printScrollingDigit(int digit, int width, int y, int pos, disp *displaynum)
{
//  int width = 24;
 int x = (width * pos) - width + pos;

 if (width == 24)
 {
   switch (digit)
   {
     case -1: displaynum->display.drawBitmap(x, y, c24_Empty, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_1, 24, 32, 1); break;
     case 1: displaynum->display.drawBitmap(x, y, c24_1, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_2, 24, 32, 1); break;
     case 2: displaynum->display.drawBitmap(x, y, c24_2, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_3, 24, 32, 1); break;
     case 3: displaynum->display.drawBitmap(x, y, c24_3, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_4, 24, 32, 1); break;
     case 4: displaynum->display.drawBitmap(x, y, c24_4, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_5, 24, 32, 1); break;
     case 5: displaynum->display.drawBitmap(x, y, c24_5, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_6, 24, 32, 1); break;
     case 6: displaynum->display.drawBitmap(x, y, c24_6, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_7, 24, 32, 1); break;
     case 7: displaynum->display.drawBitmap(x, y, c24_7, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_8, 24, 32, 1); break;
     case 8: displaynum->display.drawBitmap(x, y, c24_8, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_9, 24, 32, 1); break;
     case 9: displaynum->display.drawBitmap(x, y, c24_9, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_0, 24, 32, 1); break;
     default: displaynum->display.drawBitmap(x, y, c24_0, 24, 32, 1); displaynum->display.drawBitmap(x, y+33, c24_1, 24, 32, 1); break;
   }
 }
 else
 {
   switch (digit)
   {
     case -1: displaynum->display.drawBitmap(x, y, c16_Empty, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_1, 16, 24, 1); break;
     case 1: displaynum->display.drawBitmap(x, y, c16_1, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_2, 16, 24, 1); break;
     case 2: displaynum->display.drawBitmap(x, y, c16_2, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_3, 16, 24, 1); break;
     case 3: displaynum->display.drawBitmap(x, y, c16_3, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_4, 16, 24, 1); break;
     case 4: displaynum->display.drawBitmap(x, y, c16_4, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_5, 16, 24, 1); break;
     case 5: displaynum->display.drawBitmap(x, y, c16_5, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_6, 16, 24, 1); break;
     case 6: displaynum->display.drawBitmap(x, y, c16_6, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_7, 16, 24, 1); break;
     case 7: displaynum->display.drawBitmap(x, y, c16_7, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_8, 16, 24, 1); break;
     case 8: displaynum->display.drawBitmap(x, y, c16_8, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_9, 16, 24, 1); break;
     case 9: displaynum->display.drawBitmap(x, y, c16_9, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_0, 16, 24, 1); break;
     default: displaynum->display.drawBitmap(x, y, c16_0, 16, 24, 1); displaynum->display.drawBitmap(x, y+25, c16_1, 16, 24, 1); break;
   }
 }
}

void onAlt10000FtChange(unsigned int newValue)
{

   unsigned int mappedValue = newValue / 6553;
   unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, -33);

   if (mappedValue == 0)
   {
     mappedValue = -1;
   }
   displays[AltAddress].digits[0].digit = mappedValue;
   displays[AltAddress].digits[0].y = y;
 
}

void onAlt1000FtChange(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6553;
 unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, -33);

 displays[AltAddress].digits[1].digit = mappedValue;
 displays[AltAddress].digits[1].y = y;
//  UpdateDisplays();
}

void onAlt100FtChange(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6553;
 unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, -33);

 displays[AltAddress].digits[2].digit = mappedValue;
 displays[AltAddress].digits[2].y = y;
 //UpdateDisplays();
}

void onBaro0Change(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6554;
 unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, -25);

 displays[HGAddress].digits[3].digit = mappedValue;
 displays[HGAddress].digits[3].y = y;
 //UpdateDisplays();
}

void onBaro1Change(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6554;
 unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, -25);

 displays[HGAddress].digits[2].digit = mappedValue;
 displays[HGAddress].digits[2].y = y;
//  UpdateDisplays();
}

void onBaro2Change(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6554;
 unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, -25);

 displays[HGAddress].digits[1].digit = mappedValue;
 displays[HGAddress].digits[1].y = y;
//  UpdateDisplays();
}

void onBaro3Change(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6554;
 unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, -25);

 displays[HGAddress].digits[0].digit = mappedValue;
 displays[HGAddress].digits[0].y = y;
//  UpdateDisplays();
}



int count = 0;
void loop() {

 count++;
 if (count % 10 == 0)
 {
   UpdateDisplays();
 }

digitalWrite(13,HIGH);
delay(500);
digitalWrite(13,LOW);
delay(500);

}

It blanks the display, then after 10 flashes of the LED, it puts 4 zeros on the 32-line display (0x3C).

Link to comment
Share on other sites

Here is a working sketch.

There where some copy/paste errors in the previous version and some other stuff that also was wrong.

Apparently I had lost the old working version but I managed to get it working again.

 

//Uncomment for testmode (No DCS needed)
//#define TEST

#ifndef TEST
 #define DCSBIOS_IRQ_SERIAL
 //#define DCSBIOS_RS485_SLAVE 2
 
 #include "DcsBios.h"
#endif

#define TXENABLE_PIN 2

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "characters.h"

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define TCAAddress 0x70
#define AltAddress 0
#define HGAddress 1

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)

int updateInterval = 100; //the number of milliseconds between updates

struct scrollDigit {
 int digit;
 int y;
};

struct disp {
 Adafruit_SSD1306 display;
 int address;
 int width;
 int numberOfDigits;
 scrollDigit digits[5];
};


disp displays[2] = 
{
 {Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), AltAddress, 24, 5, {{0,0},{0,0},{0,0},{0,0},{0,0}}},
 {Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), HGAddress, 16, 4, {{0,0},{0,0},{0,0},{0,0},{0,0}}}
};

void TCASelect(uint8_t addr) {
 if (addr > 7) return;

 Wire.beginTransmission(TCAAddress);
 Wire.write(1 << addr);
 Wire.endTransmission();  
}

void setup() {
 
 for (int i = 0; i<2; i++)
 {
   TCASelect(displays[i].address);
   if(!displays[i].display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
     for(;;); // Don't proceed, loop forever
   }
 }

 #ifdef TEST
   Serial.begin(9600);
 #else
   DcsBios::setup();
 #endif

 UpdateDisplays();
}

void UpdateDisplays()
{
 for (int j = 0; j<2; j++)
 {
   TCASelect(displays[j].address);
   displays[j].display.clearDisplay();
   for (int i = 0; i < displays[j].numberOfDigits; i++)
   {
     printScrollingDigit(displays[j].digits[i].digit, displays[j].width, displays[j].digits[i].y, i + 1, &displays[j]);
   }
   if (displays[j].width == 16)
   {
     displays[j].display.fillRect(0, 25, 128, 7, BLACK);
   }
   displays[j].display.display();
 }
}

void printScrollingDigit(int digit, int width, int y, int pos, disp *display)
{
//  int width = 24;
 int x = (width * pos) - width + pos;

 if (width == 24)
 {
   switch (digit)
   {
     case -1: display->display.drawBitmap(x, y, c24_Empty, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_1, 24, 32, 1); break;
     case 1: display->display.drawBitmap(x, y, c24_1, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_2, 24, 32, 1); break;
     case 2: display->display.drawBitmap(x, y, c24_2, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_3, 24, 32, 1); break;
     case 3: display->display.drawBitmap(x, y, c24_3, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_4, 24, 32, 1); break;
     case 4: display->display.drawBitmap(x, y, c24_4, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_5, 24, 32, 1); break;
     case 5: display->display.drawBitmap(x, y, c24_5, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_6, 24, 32, 1); break;
     case 6: display->display.drawBitmap(x, y, c24_6, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_7, 24, 32, 1); break;
     case 7: display->display.drawBitmap(x, y, c24_7, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_8, 24, 32, 1); break;
     case 8: display->display.drawBitmap(x, y, c24_8, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_9, 24, 32, 1); break;
     case 9: display->display.drawBitmap(x, y, c24_9, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_0, 24, 32, 1); break;
     default: display->display.drawBitmap(x, y, c24_0, 24, 32, 1); display->display.drawBitmap(x, y+33, c24_1, 24, 32, 1); break;
   }
 }
 else
 {
   switch (digit)
   {
     case -1: display->display.drawBitmap(x, y, c16_Empty, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_1, 16, 24, 1); break;
     case 1: display->display.drawBitmap(x, y, c16_1, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_2, 16, 24, 1); break;
     case 2: display->display.drawBitmap(x, y, c16_2, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_3, 16, 24, 1); break;
     case 3: display->display.drawBitmap(x, y, c16_3, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_4, 16, 24, 1); break;
     case 4: display->display.drawBitmap(x, y, c16_4, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_5, 16, 24, 1); break;
     case 5: display->display.drawBitmap(x, y, c16_5, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_6, 16, 24, 1); break;
     case 6: display->display.drawBitmap(x, y, c16_6, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_7, 16, 24, 1); break;
     case 7: display->display.drawBitmap(x, y, c16_7, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_8, 16, 24, 1); break;
     case 8: display->display.drawBitmap(x, y, c16_8, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_9, 16, 24, 1); break;
     case 9: display->display.drawBitmap(x, y, c16_9, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_0, 16, 24, 1); break;
     default: display->display.drawBitmap(x, y, c16_0, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_1, 16, 24, 1); break;
   }
 }
}


void onAlt10000FtChange(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6553;
 unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, -33);

 if (mappedValue == 0)
 {
   mappedValue = -1;
 }
 displays[AltAddress].digits[0].digit = mappedValue;
 displays[AltAddress].digits[0].y = y;
}

void onAlt1000FtChange(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6553;
 unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, -33);

 displays[AltAddress].digits[1].digit = mappedValue;
 displays[AltAddress].digits[1].y = y;
}

void onAlt100FtChange(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6553;
 unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, -33);

 displays[AltAddress].digits[2].digit = mappedValue;
 displays[AltAddress].digits[2].y = y;
}

void onBaro0Change(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6554;
 unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, -25);

 displays[HGAddress].digits[3].digit = mappedValue;
 displays[HGAddress].digits[3].y = y;
}

void onBaro1Change(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6554;
 unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, -25);

 displays[HGAddress].digits[2].digit = mappedValue;
 displays[HGAddress].digits[2].y = y;
}

void onBaro2Change(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6554;
 unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, -25);

 displays[HGAddress].digits[1].digit = mappedValue;
 displays[HGAddress].digits[1].y = y;
}

void onBaro3Change(unsigned int newValue)
{
 unsigned int mappedValue = newValue / 6554;
 unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, -25);

 displays[HGAddress].digits[0].digit = mappedValue;
 displays[HGAddress].digits[0].y = y;
}

#ifndef TEST
 DcsBios::IntegerBuffer Alt10000FtBuffer(0x1080, 0xffff, 0, onAlt10000FtChange);
 DcsBios::IntegerBuffer Alt1000FtBuffer(0x1082, 0xffff, 0, onAlt1000FtChange);
 DcsBios::IntegerBuffer Alt100FtBuffer(0x1084, 0xffff, 0, onAlt100FtChange);
 DcsBios::IntegerBuffer Baro0Buffer(0x1086, 0xffff, 0, onBaro0Change);
 DcsBios::IntegerBuffer Baro1Buffer(0x1088, 0xffff, 0, onBaro1Change);
 DcsBios::IntegerBuffer Baro2Buffer(0x108a, 0xffff, 0, onBaro2Change);
 DcsBios::IntegerBuffer Baro3Buffer(0x108c, 0xffff, 0, onBaro3Change);
#endif

unsigned long time = 0;

unsigned int i = 0;

void loop() {

 #ifndef TEST
   DcsBios::loop();
 #endif
 
 time = millis();
 if (time % updateInterval == 0)
 {
   #ifdef TEST
       onAlt10000FtChange(i);
       onAlt1000FtChange(i);
       onAlt100FtChange(i);
       onBaro0Change(i);
       onBaro1Change(i);
       onBaro2Change(i);
       onBaro3Change(i);
       i += 400;
   #endif
   UpdateDisplays();
 }
}

 

I tested it in DCS and everything works as it should.

IMG_0196.thumb.JPEG.2e21c5baa693cce98bca9e6188312ddc.JPEG

Link to comment
Share on other sites

Here is a working sketch.

There where some copy/paste errors in the previous version and some other stuff that also was wrong.

Apparently I had lost the old working version but I managed to get it working again.

I don't have a multiplexer to test with, but leaving that off makes the single display flick between the two.

I still can't make it work with the different addresses - it needs someone with more knowledge of the language and libraries than me.

 

On the other hand, I'm starting to feel like I'm re-inventing the wheel - If the multiplexer works reliably with different sized displays, that might be a better option than my sub-master/slave idea. It'll be one Nano instead of two, and still only one connection to DCS-BIOS for both displays, rather than two. Plus, changing the tiny address resistor on the 128x64 OLED is a pig with ordinary soldering tools.

 

 

In the meantime, I have a slightly better version of the altitude display for use on 64-line OLEDs and an altimeter with a long slot for the 100 ft digit (like the one in-game).

 

I've not had a chance to try these changes with a multiplexer, etc., but they work in the programs that only run one display (though the definition part would need to altered for that).

 

First, I altered the OLED definitions:

#define SCREEN_HEIGHT_A 64 // Altitude OLED display height, in pixels

disp displays[2] = 
{
 {Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT_A, &Wire, OLED_RESET), AltAddress, 24, 3, {{0,0},{0,0},{0,0},{0,0},{0,0}}},
 {Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), HGAddress, 16, 4, {{0,0},{0,0},{0,0},{0,0},{0,0}}}
};

This changes the screen height to 64 pixels and width to 3 characters for the altitude display.

 

Then replace the printScrollingDigit function with this:

void printScrollingDigit(int digit, int width, int y, int pos, disp *display)
{  

 pos = pos+2;  //  Move digits over a bit
 
 int x = (width * pos) - width + pos;

  
#ifdef ALTIMETER

    y=y+16;  //  Move to centre of display

   switch (digit)
   {
     case -1: display->display.drawBitmap(x, y-35, c24_9, 24, 32, 1); display->display.drawBitmap(x, y, c24_Empty, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_1, 24, 32, 1); break;
     case 1: display->display.drawBitmap(x, y-35, c24_0, 24, 32, 1); display->display.drawBitmap(x, y, c24_1, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_2, 24, 32, 1); break;
     case 2: display->display.drawBitmap(x, y-35, c24_1, 24, 32, 1); display->display.drawBitmap(x, y, c24_2, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_3, 24, 32, 1); break;
     case 3: display->display.drawBitmap(x, y-35, c24_2, 24, 32, 1); display->display.drawBitmap(x, y, c24_3, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_4, 24, 32, 1); break;
     case 4: display->display.drawBitmap(x, y-35, c24_3, 24, 32, 1); display->display.drawBitmap(x, y, c24_4, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_5, 24, 32, 1); break;
     case 5: display->display.drawBitmap(x, y-35, c24_4, 24, 32, 1); display->display.drawBitmap(x, y, c24_5, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_6, 24, 32, 1); break;
     case 6: display->display.drawBitmap(x, y-35, c24_5, 24, 32, 1); display->display.drawBitmap(x, y, c24_6, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_7, 24, 32, 1); break;
     case 7: display->display.drawBitmap(x, y-35, c24_6, 24, 32, 1); display->display.drawBitmap(x, y, c24_7, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_8, 24, 32, 1); break;
     case 8: display->display.drawBitmap(x, y-35, c24_7, 24, 32, 1); display->display.drawBitmap(x, y, c24_8, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_9, 24, 32, 1); break;
     case 9: display->display.drawBitmap(x, y-35, c24_8, 24, 32, 1);  display->display.drawBitmap(x, y, c24_9, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_0, 24, 32, 1); break;
     default: display->display.drawBitmap(x, y-35, c24_9, 24, 32, 1); display->display.drawBitmap(x, y, c24_0, 24, 32, 1); display->display.drawBitmap(x, y+35, c24_1, 24, 32, 1); break;
   }
#else
   switch (digit)
   {
     case -1: display->display.drawBitmap(x, y, c16_Empty, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_1, 16, 24, 1); break;
     case 1: display->display.drawBitmap(x, y, c16_1, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_2, 16, 24, 1); break;
     case 2: display->display.drawBitmap(x, y, c16_2, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_3, 16, 24, 1); break;
     case 3: display->display.drawBitmap(x, y, c16_3, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_4, 16, 24, 1); break;
     case 4: display->display.drawBitmap(x, y, c16_4, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_5, 16, 24, 1); break;
     case 5: display->display.drawBitmap(x, y, c16_5, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_6, 16, 24, 1); break;
     case 6: display->display.drawBitmap(x, y, c16_6, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_7, 16, 24, 1); break;
     case 7: display->display.drawBitmap(x, y, c16_7, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_8, 16, 24, 1); break;
     case 8: display->display.drawBitmap(x, y, c16_8, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_9, 16, 24, 1); break;
     case 9: display->display.drawBitmap(x, y, c16_9, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_0, 16, 24, 1); break;
     default: display->display.drawBitmap(x, y, c16_0, 16, 24, 1); display->display.drawBitmap(x, y+25, c16_1, 16, 24, 1); break;
   }
#endif
}

This moves the digits 2 character widths to the right, and shifts the "active" digits of the altitude display to the centre. That puts the 100ft digit right at the edge of the display, meaning the shaft for the needle can run past.

It also adds the characters above the "active" characters, so that you can see them in the display window - particularly if you have the long slot for 100ft.

Link to comment
Share on other sites

As I understand it, the multiplexer does not replace what you are doing. I am going to have to use a multiplexer on my TISL sketch simply because the addresses of the I2C 0.49" can't be changed and so if I want more than one running off a nano I need the multiplexer to split out the different OLEDs.

 

Whereas what you are doing (again if I understand correctly) is a pre-process / post process of a signal so that the work having to be done by the second one to determine what to show on the OLED is not so much it overwhelms the unit.

 

So I think what you are doing still adds value, especially if what it does is reduce the data stream so that heavy processing sketches like the scrolling OLED ones can be combined on one Nano, rather than having multiple nanos, one for each.

 

It also may make the processing of the scrolling sketchs a low enough work load that you can run a gauge or other function on the same nano without it slowing the gauge function down horribly (as it does at the moment)

 

If the static I2C addresses of the OLEDs used can't be changed, that's when the multiplexer comes in.

 

A question, the 'slave' nano, is it connected to USB for power only, or does it receive data direct from DCS Bios? If it is just power, that's also an advantage as you could just use a USB power source, and it means one less 'active' USB connection that can cause lag

 

Cheers

 

Les

Link to comment
Share on other sites

Whereas what you are doing (again if I understand correctly) is a pre-process / post process of a signal so that the work having to be done by the second one to determine what to show on the OLED is not so much it overwhelms the unit.
Yes.

My idea is that the Arduino interfacing with DCS-BIOS "hands-off" the job of driving the displays to a dedicated Nano.

 

So I think what you are doing still adds value, especially if what it does is reduce the data stream so that heavy processing sketches like the scrolling OLED ones can be combined on one Nano, rather than having multiple nanos, one for each.

The problem is I don't know how to make it drive the two displays on the same bus, in one program.

 

What makes it more annoying is that they've BOTH been electrically connected on my test setup all the time, but will only work one at a time through the code.

 

All I do is change the address in the single-display code, and the other one will work.

 

It HAS to be a code problem that I can't see. Maybe the Adafruit SSD1306 library isn't up to the task.

Another option is just use one Nano for the altitude and another for the pressure. Both could be connected to the same output from the transmitter as there is no handshaking. That wouldn't reduce the Arduino count, but it would work.

 

Another option is to use my transmitter/receiver code AND the multiplexer.

 

It also may make the processing of the scrolling sketchs a low enough work load that you can run a gauge or other function on the same nano without it slowing the gauge function down horribly (as it does at the moment)

I've tried this briefly using the pressure adjustment rotary encoder.

It was a bit glitchy at the low baud rates. I think it's the software serial transmitter causing the problem

It might need a faster baud rate, a different software serial transmitter or an arduino with a second hardware UART for the transmitter.

 

The latter option, if it's a Mega, for example, might allow more to be done because telling the transmitter what to send would take far less processor time than the software transmitter.

 

My current test transmitter is an Uno R3.

 

A question, the 'slave' nano, is it connected to USB for power only, or does it receive data direct from DCS Bios? If it is just power, that's also an advantage as you could just use a USB power source, and it means one less 'active' USB connection that can cause lag.

My slave currently receives its power from the master arduino's regulator.

I've attached a simple diagram.

DCSBIOS_ALT_TX_RX_Concept.thumb.png.58869d55ba727c78ccdc143ce34b0b11.png


Edited by No1sonuk
Link to comment
Share on other sites

Another option is just use one Nano for the altitude and another for the pressure. Both could be connected to the same output from the transmitter as there is no handshaking. That wouldn't reduce the Arduino count, but it would work.

 

If it cuts down on active USB connections I still see it as a benefit, as an RS485 network would need the same number of nanos

 

Cheers

 

Les

Link to comment
Share on other sites

I just made an attempt to use the U8G2 library as it looks easy enough to run 2 displays.

It wouldn't compile for my nano with a simple test program.

 

I'll rig something tomorrow with an Uno and 2 Nanos.


Edited by No1sonuk
Link to comment
Share on other sites

Update:

I've got the transmitter working on an Arduino Mega now so that the serial transmitter to the displays is a hardware transmitter and so it's automatic, with no processor load.

 

The Altimeter display still glitches occasionally when it's updating, so more work is required there.

 

Here's the transmitter code for a MEGA - it has a rotary encoder input as well for the adjuster.

I'm not convinced the glitching is in the transmitter, so it might be possible for the Mega to run the encoder, switch and needle stepper as well as the transmitter.

/*
 Tell DCS-BIOS to use a serial connection and use the default Arduino Serial
 library. This will work on the vast majority of Arduino-compatible boards,
 but you can get corrupted data if you have too many or too slow outputs
 (e.g. when you have multiple character displays), because the receive
 buffer can fill up if the sketch spends too much time updating them.
 
 If you can, use the IRQ Serial connection instead.
*/

/* 
This program transmits the 3 altitude counter and 4 barometric pressure digits for the
DCS World A-10C altimeter on a serial port so that external display drivers don't have
the DCS-BIOS overhead.
This code must be run on an Arduino MEGA as it uses a second hardware UART
Written by Tim Jacobs  ("No1sonuk" on DCS World forums)  May 2020
*/



#define DCSBIOS_IRQ_SERIAL   // DEFAULT works as well as IRQ

#include "DcsBios.h"

#define frameLength 8  // Number of data bytes in the frame, excluding the start and end bytes +1
unsigned int frameData[frameLength] = {0, 6553, 13106, 19660, 26214, 32767, 39321, 45875};  // Set up frame data array with test data


/* paste code snippets from the reference documentation here */

DcsBios::RotaryEncoder altSetPressure("ALT_SET_PRESSURE", "-3200", "+3200", 3, 2);  // Add rotary encoder for testing

void onAlt100ftCntChange(unsigned int newValue) {
   /* your code here */
   frameData[5] = newValue;
   sendDataSerial();
}
DcsBios::IntegerBuffer alt100ftCntBuffer(0x1084, 0xffff, 0, onAlt100ftCntChange);

void onAlt1000ftCntChange(unsigned int newValue) {
   /* your code here */
   frameData[6] = newValue;
   sendDataSerial();
}
DcsBios::IntegerBuffer alt1000ftCntBuffer(0x1082, 0xffff, 0, onAlt1000ftCntChange);

void onAlt10000ftCntChange(unsigned int newValue) {
   /* your code here */
   frameData[7] = newValue;
   sendDataSerial();
}
DcsBios::IntegerBuffer alt10000ftCntBuffer(0x1080, 0xffff, 0, onAlt10000ftCntChange);

void onAltPressure0Change(unsigned int newValue) {
   /* your code here */
   frameData[1] = newValue;
   sendDataSerial();
}
DcsBios::IntegerBuffer altPressure0Buffer(0x1086, 0xffff, 0, onAltPressure0Change);

void onAltPressure1Change(unsigned int newValue) {
   /* your code here */
   frameData[2] = newValue;
   sendDataSerial();
}
DcsBios::IntegerBuffer altPressure1Buffer(0x1088, 0xffff, 0, onAltPressure1Change);

void onAltPressure2Change(unsigned int newValue) {
   /* your code here */
   frameData[3] = newValue;
   sendDataSerial();
}
DcsBios::IntegerBuffer altPressure2Buffer(0x108a, 0xffff, 0, onAltPressure2Change);

void onAltPressure3Change(unsigned int newValue) {
   /* your code here */
   frameData[4] = newValue;
   sendDataSerial();
}
DcsBios::IntegerBuffer altPressure3Buffer(0x108c, 0xffff, 0, onAltPressure3Change);



void setup() {
 DcsBios::setup();

 Serial1.begin(115200);   
 sendDataSerial();  // Send initial numbers to displays
}

void loop() {
 DcsBios::loop();

 
}

void sendDataSerial (){

   Serial1.print("AAAA");  // Send frame start bytes

 for(int x= frameLength-1; x>0; x--){     // Send frame data bytes
   Serial1.write(highByte(frameData[x]));
   Serial1.write(lowByte(frameData[x]));
 }

 Serial1.print("ZZ");  // Send frame stop bytes  
 
}

 

This is the receiver so far. It uses Middlefart's characters.h file.

One arduino for each display is required, with the appropriate code commenting.

/* 
V 0.2  

This program receives the 3 altitude counter and 4 barometric pressure digits for the
DCS World A-10C altimeter on the hardware serial port so this display driver doesn't
have the DCS-BIOS overhead.
This code must be run in conjunction with a separate transmitter Arduino.
Only one of the two displays can be run on one device, so two of these are required, with 
the altitude definition switched.

Original display driver code written by "Middlefart" on DCS World forums.
Display code mods and serial receiver code written by Tim Jacobs ("No1sonuk" on DCS World forums)  May 2020
 Mods are: 
  Alt display set up for 64-line
  Alt display active digits moved to centre of display
  Alt display character drawing modified to allow wider windows, showing parts of numbers above and below
  Alt display changed to only the three active digits
  Both displays moved 2 characters to the right
  Character vertical spacing increased to make it look more like real drums


*/

// My 128x64 OLED has its address changed from default, so check these before uploading

#define AltimeterDisplayAddr 0x3D // OLED display address for altimeter digits
#define BarometricDisplayAddr 0x3C // OLED display address for barometric digits

#define AltimeterDisplayHeight 64 // OLED display height for altimeter digits
#define BarometricDisplayHeight 32 // OLED display height for barometric digits

// Comment for barometric pressure
//  #define ALTIMETER


//  Code from original display:
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "characters.h"            //  "Middlefart's" original font file

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // Defailt OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)


#ifdef ALTIMETER
 #define SCREEN_HEIGHT AltimeterDisplayHeight // Altimeter OLED display height, in pixels
#else
 #define SCREEN_HEIGHT BarometricDisplayHeight // Barometric OLED display height, in pixels
#endif

int updateInterval = 100; //the number of milliseconds between updates

struct scrollDigit {
 int digit; //The digit that is displayed
 int y; // The vertical position of the digit
};

struct disp {
 Adafruit_SSD1306 display;
 int width;
 int numberOfDigits;
 scrollDigit digits[5];
};

#ifdef ALTIMETER
 disp oled = {Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), 24, 3, {{0,0},{0,0},{0,0},{0,0},{0,0}}};
#else
 disp oled = {Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), 16, 4, {{0,0},{0,0},{0,0},{0,0},{0,0}}};
#endif


//  End of code from original display





unsigned int upperByte, lowerByte ;
int dataByte;
bool dataValid = false;

#define frameLength 8  // Number of data bytes in the frame, excluding the start and end bytes +1

unsigned int frameData[frameLength] = {0, 6553, 13106, 19660, 26214, 32767, 39321, 45875};  // Set up frame data array with test data

void setup() 
{
 Serial.begin(115200);
 
#ifdef ALTIMETER
   if(!oled.display.begin(SSD1306_SWITCHCAPVCC, AltimeterDisplayAddr, false)) { // Address 0x3C for 128x32 and 0x3D for 128 x 64
   for(;;); // Don't proceed, loop forever
   }
#else
   if(!oled.display.begin(SSD1306_SWITCHCAPVCC, BarometricDisplayAddr, false)) { // Address 0x3C for 128x32 and 0x3D for 128 x 64
   for(;;); // Don't proceed, loop forever
   }
#endif

   displayOutput();

} 
unsigned long time = 0;
void loop () 
{

 checkFrameStart();

 getFrameData();

// if (dataValid == true)  //  Check if a valid frame has been received (not yet implemented)
 {displayOutput();     //  Send data to the display if it's valid
 }

 //  Ignore the data if the frame isn't valid (not yet implemented)



 time = millis();
 if (time % updateInterval == 0)
 {
   displayOutput();
 }

}

void checkFrameStart(){


// Look for the frame start
 int FstartBytes = 0;

 while (FstartBytes <4){           // number of "A"s in frame start
   while (Serial.available()>=1)
   {
     if (Serial.read()=='A')
     {
       FstartBytes++;
     }
     else {
       FstartBytes = 0;
     }
   }
 }
}   

void getFrameData(){

 int byteCount = frameLength -1;   //  
 dataValid = false;

 while (byteCount>0){                           // Get data frame
   if (Serial.available()>1)
   {
     upperByte = Serial.read();
     lowerByte = Serial.read();

     frameData[byteCount] = (upperByte << 8) + lowerByte ;  // Read data into array. Byte [0] unused
     byteCount --;
   }
 }

 if (upperByte == 'Z' && lowerByte == 'Z' )   // Check end bytes (not yet implemented)
 { dataValid = true;                          // (not yet implemented)
 }
}

// ************************************
// *  Display stuff after this point  *
// ************************************

void displayOutput()
{
 // Data display here

 // Uses original OnChange functions to avoid breaking something that works
 
 // Transmitter needs to send in this order (top to bottom)
 #ifdef ALTIMETER
 {
   onAlt10000FtChange(frameData[7]);  // *..
   onAlt1000FtChange(frameData[6]);   // .*.
   onAlt100FtChange(frameData[5]);    // ..*
 }
 #else
 {
   onBaro3Change(frameData[4]);  // *...
   onBaro2Change(frameData[3]);  // .*..
   onBaro1Change(frameData[2]);  // ..*.
   onBaro0Change(frameData[1]);  // ...*
 }
 #endif

UpdateDisplay();
 
}

//  Code from original display:

void UpdateDisplay()
{
 oled.display.clearDisplay();
 for (int i = 0; i < oled.numberOfDigits; i++)
 {
   printScrollingDigit(oled.digits[i].digit, oled.width, oled.digits[i].y, i + 1, &oled);
 }
 //Clear the area below the the numbers if we are using the small font
 if (oled.width == 16)
 {
   oled.display.fillRect(0, 25, 67, 7, BLACK);
 }
 
 oled.display.display();
}

int YPos()
{
 return (oled.width + 9) * -1;
}

void printScrollingDigit(int digit, int width, int y, int pos, disp *oled)
{

   pos = pos+2;

 
 int x = (width * pos) - width + pos;



#ifdef ALTIMETER
 y=y+16;

   switch (digit)
   {
     case -1: oled->display.drawBitmap(x, y-35, c24_9, 24, 32, 1); oled->display.drawBitmap(x, y, c24_Empty, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_1, 24, 32, 1); break;
     case 1: oled->display.drawBitmap(x, y-35, c24_0, 24, 32, 1); oled->display.drawBitmap(x, y, c24_1, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_2, 24, 32, 1); break;
     case 2: oled->display.drawBitmap(x, y-35, c24_1, 24, 32, 1); oled->display.drawBitmap(x, y, c24_2, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_3, 24, 32, 1); break;
     case 3: oled->display.drawBitmap(x, y-35, c24_2, 24, 32, 1); oled->display.drawBitmap(x, y, c24_3, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_4, 24, 32, 1); break;
     case 4: oled->display.drawBitmap(x, y-35, c24_3, 24, 32, 1); oled->display.drawBitmap(x, y, c24_4, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_5, 24, 32, 1); break;
     case 5: oled->display.drawBitmap(x, y-35, c24_4, 24, 32, 1); oled->display.drawBitmap(x, y, c24_5, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_6, 24, 32, 1); break;
     case 6: oled->display.drawBitmap(x, y-35, c24_5, 24, 32, 1); oled->display.drawBitmap(x, y, c24_6, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_7, 24, 32, 1); break;
     case 7: oled->display.drawBitmap(x, y-35, c24_6, 24, 32, 1); oled->display.drawBitmap(x, y, c24_7, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_8, 24, 32, 1); break;
     case 8: oled->display.drawBitmap(x, y-35, c24_7, 24, 32, 1); oled->display.drawBitmap(x, y, c24_8, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_9, 24, 32, 1); break;
     case 9: oled->display.drawBitmap(x, y-35, c24_8, 24, 32, 1); oled->display.drawBitmap(x, y, c24_9, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_0, 24, 32, 1); break;
     default: oled->display.drawBitmap(x, y-35, c24_9, 24, 32, 1); oled->display.drawBitmap(x, y, c24_0, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_1, 24, 32, 1); break;
   }
#else
   switch (digit)
   {
     case -1: oled->display.drawBitmap(x, y, c16_Empty, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_1, 16, 24, 1); break;
     case 1: oled->display.drawBitmap(x, y, c16_1, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_2, 16, 24, 1); break;
     case 2: oled->display.drawBitmap(x, y, c16_2, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_3, 16, 24, 1); break;
     case 3: oled->display.drawBitmap(x, y, c16_3, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_4, 16, 24, 1); break;
     case 4: oled->display.drawBitmap(x, y, c16_4, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_5, 16, 24, 1); break;
     case 5: oled->display.drawBitmap(x, y, c16_5, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_6, 16, 24, 1); break;
     case 6: oled->display.drawBitmap(x, y, c16_6, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_7, 16, 24, 1); break;
     case 7: oled->display.drawBitmap(x, y, c16_7, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_8, 16, 24, 1); break;
     case 8: oled->display.drawBitmap(x, y, c16_8, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_9, 16, 24, 1); break;
     case 9: oled->display.drawBitmap(x, y, c16_9, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_0, 16, 24, 1); break;
     default: oled->display.drawBitmap(x, y, c16_0, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_1, 16, 24, 1); break;
   }
#endif
}

#ifdef ALTIMETER
 void onAlt10000FtChange(unsigned int newValue)
 {
   if (newValue == 0)
   {
     oled.digits[0].digit = -1;
     oled.digits[0].y = 0;
   }
   else
   {
   unsigned int mappedValue = newValue / 6553;
   unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, YPos());
 
   oled.digits[0].digit = mappedValue;
   oled.digits[0].y = y;
   }
 }
 
 void onAlt1000FtChange(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6553;
   unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, YPos());
 
   oled.digits[1].digit = mappedValue;
   oled.digits[1].y = y;
 }
 
 void onAlt100FtChange(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6553;
   unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, YPos());
 
   oled.digits[2].digit = mappedValue;
   oled.digits[2].y = y;
 }
 

#else
 void onBaro0Change(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6554;
   unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, YPos());
 
   oled.digits[3].digit = mappedValue;
   oled.digits[3].y = y;
 }
 
 void onBaro1Change(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6554;
   unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, YPos());
 
   oled.digits[2].digit = mappedValue;
   oled.digits[2].y = y;
 }
 
 void onBaro2Change(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6554;
   unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, YPos());
 
   oled.digits[1].digit = mappedValue;
   oled.digits[1].y = y;
 }
 
 void onBaro3Change(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6554;
   unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, YPos());
 
   oled.digits[0].digit = mappedValue;
   oled.digits[0].y = y;
 }

#endif

 

The Serial1 TX line from the Mega is connected to the RX line of each receiver.

 

Remember to disconnect the receivers' RX lines from the rest of the circuit before programming.

The Mega's TX1 can stay connected as it's not involved in programming.

 

I was thinking of using separate ATMEGA328 processors (the pDIP ones from the Uno) to cut the cost, and increase the performance a bit. but my Nanos are the 328 type anyway, and are a bit cheaper.

Link to comment
Share on other sites

I have spent the day making an Up Front Controller, so just seen this - will have to take a good long look tomorrow when I'm a bit fresher, but thanks for doing this!

 

If you need 64 height characters I think I provided a character file along with my 128 x 64 sketch in an earlier post.

 

Also if you need a modified character map, let me know to try and help

 

Cheers

 

Les

Link to comment
Share on other sites

I did some measuring and scaling of photos and it looks to me like Middlefart's altimeter characters are OK for size. The Barometric ones might be a bit big, though.

I don't have a 1:1 scale drawing to compare yet. It seems the altimeter is one of the gauges where people put it off and/or use a monitor behind the panel and include it with the ADI.

 

I'll likely never build a full pit, but I do like trying to solve problems like this one. :D

Link to comment
Share on other sites

New version of the receiver code.

I've done a paper mockup of the display face, and modified the display code to make the numbers fit better.

 

/* 
V 0.25  

This program receives the 3 altitude counter and 4 barometric pressure digits for the
DCS World A-10C altimeter on the hardware serial port so this display driver doesn't
have the DCS-BIOS overhead.
This code must be run in conjunction with a separate transmitter Arduino.
Only one of the two displays can be run on one device, so two of these are required, with 
the altitude definition switched.

Original display driver code written by "Middlefart" on DCS World forums.
Display code mods and serial receiver code written by Tim Jacobs ("No1sonuk" on DCS World forums)  May 2020
 Mods are: 
  Alt display set up for 64-line
  Alt display active digits moved to centre of display
  Alt display character drawing modified to allow wider windows, showing parts of numbers above and below
  Alt display changed to only the three active digits
  Alt display moved to the right
  Character vertical spacing increased to make it look more like real drums


*/

// My 128x64 OLED has its address changed from default, so check these before uploading

#define AltimeterDisplayAddr 0x3D // OLED display address for altimeter digits
#define BarometricDisplayAddr 0x3C // OLED display address for barometric digits

#define AltimeterDisplayHeight 64 // OLED display height for altimeter digits
#define BarometricDisplayHeight 32 // OLED display height for barometric digits

// Comment for barometric pressure
 #define ALTIMETER


//  Code from original display:
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "characters.h"            //  "Middlefart's" original font file

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // Defailt OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)


#ifdef ALTIMETER
 #define SCREEN_HEIGHT AltimeterDisplayHeight // Altimeter OLED display height, in pixels
#else
 #define SCREEN_HEIGHT BarometricDisplayHeight // Barometric OLED display height, in pixels
#endif

int updateInterval = 100; //the number of milliseconds between updates

struct scrollDigit {
 int digit; //The digit that is displayed
 int y; // The vertical position of the digit
};

struct disp {
 Adafruit_SSD1306 display;
 int width;
 int numberOfDigits;
 scrollDigit digits[5];
};

#ifdef ALTIMETER
 disp oled = {Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), 24, 3, {{0,0},{0,0},{0,0},{0,0},{0,0}}};
#else
 disp oled = {Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), 16, 4, {{0,0},{0,0},{0,0},{0,0},{0,0}}};
#endif


//  End of code from original display





unsigned int upperByte, lowerByte ;
int dataByte;
bool dataValid = false;

#define frameLength 8  // Number of data bytes in the frame, excluding the start and end bytes +1

unsigned int frameData[frameLength] = {0, 6553, 13106, 19660, 26214, 32767, 39321, 45875};  // Set up frame data array with test data

void setup() 
{
 Serial.begin(115200);
 
#ifdef ALTIMETER
   if(!oled.display.begin(SSD1306_SWITCHCAPVCC, AltimeterDisplayAddr, false)) { // Address 0x3C for 128x32 and 0x3D for 128 x 64
   for(;;); // Don't proceed, loop forever
   }
#else
   if(!oled.display.begin(SSD1306_SWITCHCAPVCC, BarometricDisplayAddr, false)) { // Address 0x3C for 128x32 and 0x3D for 128 x 64
   for(;;); // Don't proceed, loop forever
   }
#endif

   displayOutput();

} 
unsigned long time = 0;
void loop () 
{

 checkFrameStart();

 getFrameData();

// if (dataValid == true)  //  Check if a valid frame has been received (not yet implemented)
 {displayOutput();     //  Send data to the display if it's valid
 }

 //  Ignore the data if the frame isn't valid (not yet implemented)



 time = millis();
 if (time % updateInterval == 0)
 {
   displayOutput();
 }

}

void checkFrameStart(){


// Look for the frame start
 int FstartBytes = 0;

 while (FstartBytes <4){           // number of "A"s in frame start
   while (Serial.available()>=1)
   {
     if (Serial.read()=='A')
     {
       FstartBytes++;
     }
     else {
       FstartBytes = 0;
     }
   }
 }
}   

void getFrameData(){

 int byteCount = frameLength -1;   //  
 dataValid = false;

 while (byteCount>0){                           // Get data frame
   if (Serial.available()>1)
   {
     upperByte = Serial.read();
     lowerByte = Serial.read();

     frameData[byteCount] = (upperByte << 8) + lowerByte ;  // Read data into array. Byte [0] unused
     byteCount --;
   }
 }

 if (upperByte == 'Z' && lowerByte == 'Z' )   // Check end bytes (not yet implemented)
 { dataValid = true;                          // (not yet implemented)
 }
}

// ************************************
// *  Display stuff after this point  *
// ************************************

void displayOutput()
{
 // Data display here

 // Uses original OnChange functions to avoid breaking something that works
 
 // Transmitter needs to send in this order (top to bottom)
 #ifdef ALTIMETER
 {
   onAlt10000FtChange(frameData[7]);  // *..
   onAlt1000FtChange(frameData[6]);   // .*.
   onAlt100FtChange(frameData[5]);    // ..*
 }
 #else
 {
   onBaro3Change(frameData[4]);  // *...
   onBaro2Change(frameData[3]);  // .*..
   onBaro1Change(frameData[2]);  // ..*.
   onBaro0Change(frameData[1]);  // ...*
 }
 #endif

UpdateDisplay();
 
}

//  Code from original display:

void UpdateDisplay()
{
 oled.display.clearDisplay();
 for (int i = 0; i < oled.numberOfDigits; i++)
 {
   printScrollingDigit(oled.digits[i].digit, oled.width, oled.digits[i].y, i + 1, &oled);
 }
 //Clear the area below the the numbers if we are using the small font
 if (oled.width == 16)
 {
   oled.display.fillRect(0, 25, 67, 7, BLACK);
 }
 
 oled.display.display();
}

int YPos()
{
 return (oled.width + 9) * -1;
}

void printScrollingDigit(int digit, int width, int y, int pos, disp *oled)
{
  #ifdef ALTIMETER
   pos = pos+1;
   int x = (((width+10) * pos) - width + pos )-(width/2);
   
   #else
   int x = (width * pos) - width + pos;

  #endif
 

#ifdef ALTIMETER
 y=y+16;

   switch (digit)
   {
     case -1: oled->display.drawBitmap(x, y-35, c24_9, 24, 32, 1); oled->display.drawBitmap(x, y, c24_Empty, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_1, 24, 32, 1); break;
     case 1: oled->display.drawBitmap(x, y-35, c24_0, 24, 32, 1); oled->display.drawBitmap(x, y, c24_1, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_2, 24, 32, 1); break;
     case 2: oled->display.drawBitmap(x, y-35, c24_1, 24, 32, 1); oled->display.drawBitmap(x, y, c24_2, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_3, 24, 32, 1); break;
     case 3: oled->display.drawBitmap(x, y-35, c24_2, 24, 32, 1); oled->display.drawBitmap(x, y, c24_3, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_4, 24, 32, 1); break;
     case 4: oled->display.drawBitmap(x, y-35, c24_3, 24, 32, 1); oled->display.drawBitmap(x, y, c24_4, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_5, 24, 32, 1); break;
     case 5: oled->display.drawBitmap(x, y-35, c24_4, 24, 32, 1); oled->display.drawBitmap(x, y, c24_5, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_6, 24, 32, 1); break;
     case 6: oled->display.drawBitmap(x, y-35, c24_5, 24, 32, 1); oled->display.drawBitmap(x, y, c24_6, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_7, 24, 32, 1); break;
     case 7: oled->display.drawBitmap(x, y-35, c24_6, 24, 32, 1); oled->display.drawBitmap(x, y, c24_7, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_8, 24, 32, 1); break;
     case 8: oled->display.drawBitmap(x, y-35, c24_7, 24, 32, 1); oled->display.drawBitmap(x, y, c24_8, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_9, 24, 32, 1); break;
     case 9: oled->display.drawBitmap(x, y-35, c24_8, 24, 32, 1); oled->display.drawBitmap(x, y, c24_9, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_0, 24, 32, 1); break;
     default: oled->display.drawBitmap(x, y-35, c24_9, 24, 32, 1); oled->display.drawBitmap(x, y, c24_0, 24, 32, 1); oled->display.drawBitmap(x, y+35, c24_1, 24, 32, 1); break;
   }
#else
   switch (digit)
   {
     case -1: oled->display.drawBitmap(x, y, c16_Empty, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_1, 16, 24, 1); break;
     case 1: oled->display.drawBitmap(x, y, c16_1, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_2, 16, 24, 1); break;
     case 2: oled->display.drawBitmap(x, y, c16_2, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_3, 16, 24, 1); break;
     case 3: oled->display.drawBitmap(x, y, c16_3, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_4, 16, 24, 1); break;
     case 4: oled->display.drawBitmap(x, y, c16_4, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_5, 16, 24, 1); break;
     case 5: oled->display.drawBitmap(x, y, c16_5, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_6, 16, 24, 1); break;
     case 6: oled->display.drawBitmap(x, y, c16_6, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_7, 16, 24, 1); break;
     case 7: oled->display.drawBitmap(x, y, c16_7, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_8, 16, 24, 1); break;
     case 8: oled->display.drawBitmap(x, y, c16_8, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_9, 16, 24, 1); break;
     case 9: oled->display.drawBitmap(x, y, c16_9, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_0, 16, 24, 1); break;
     default: oled->display.drawBitmap(x, y, c16_0, 16, 24, 1); oled->display.drawBitmap(x, y+25, c16_1, 16, 24, 1); break;
   }
#endif
}

#ifdef ALTIMETER
 void onAlt10000FtChange(unsigned int newValue)
 {
   if (newValue == 0)
   {
     oled.digits[0].digit = -1;
     oled.digits[0].y = 0;
   }
   else
   {
   unsigned int mappedValue = newValue / 6553;
   unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, YPos());
 
   oled.digits[0].digit = mappedValue;
   oled.digits[0].y = y;
   }
 }
 
 void onAlt1000FtChange(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6553;
   unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, YPos());
 
   oled.digits[1].digit = mappedValue;
   oled.digits[1].y = y;
 }
 
 void onAlt100FtChange(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6553;
   unsigned int y = map(newValue, mappedValue * 6553, mappedValue * 6553 + 6553, 0, YPos());
 
   oled.digits[2].digit = mappedValue;
   oled.digits[2].y = y;
 }
 

#else
 void onBaro0Change(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6554;
   unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, YPos());
 
   oled.digits[3].digit = mappedValue;
   oled.digits[3].y = y;
 }
 
 void onBaro1Change(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6554;
   unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, YPos());
 
   oled.digits[2].digit = mappedValue;
   oled.digits[2].y = y;
 }
 
 void onBaro2Change(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6554;
   unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, YPos());
 
   oled.digits[1].digit = mappedValue;
   oled.digits[1].y = y;
 }
 
 void onBaro3Change(unsigned int newValue)
 {
   unsigned int mappedValue = newValue / 6554;
   unsigned int y = map(newValue, mappedValue * 6554, mappedValue * 6554 + 6554, 0, YPos());
 
   oled.digits[0].digit = mappedValue;
   oled.digits[0].y = y;
 }

#endif

a10_altimeter_mockup1.thumb.jpg.3e7870eb03c240daf4513d84cf010255.jpg

a10_altimeter_mockup2.thumb.jpg.4e85dad34ad9031f75f2ceec1782c2a6.jpg

Link to comment
Share on other sites

They are pretty small, aren't they? But I have switched over to them for any new items I make, as I find them so much more tolerant of issues. The easydrivers seem to have a high fail rate, and if you have a weak connection on any connections, particularly the power or stepper connections, they blow. The A4988's don't do that.

 

I will be playing around with the microstepping today as i am convinced it will make the gauge movement less noisy and smooth, will report back with findings

 

Cheers

 

Les

Link to comment
Share on other sites

  • 2 weeks later...

I think you asked a question in this thread earlier (which I don't seem to be able to find) about whether the barometric pressure set readout can be included in a sketch for the altimeter or similar, on the basis that it doesn't seem to be as processor hungry as the scrolling sketches.

 

The answer is that it can't be used in the same sketch, now I have the optical switch and have my altimeter working, when I tried it it slows the altimeter pointer down to a point where it can't keep up with what's happening in the game..

 

So, OLED displays on one arduino, gauges on another I'm sorry to say.

 

Next on the test list is whether I can use an I2c multiplexer and get all the OLED displays working off one Arduino, I'll let you know how I get on in due course

 

Les

Link to comment
Share on other sites

Next on the test list is whether I can use an I2c multiplexer and get all the OLED displays working off one Arduino, I'll let you know how I get on in due course

I looked at the prices. Depending on the sources, for 2 displays, the cost of one Nano and one multiplexer isn't all that different from two Nanos.

Link to comment
Share on other sites

Ah, you are forgetting my tribulations with RS485, basically if I can reduce the number of Arduinos (be them Nanos, Unos or Megas) I can do away with RS 485 and just use USB connections. That's how I ended up with 23 stepper motors being driven from one Mega.

 

As I can't get RS485 to work at all, I have to find an alternative solution, sadly

 

Les

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...