Search This Blog

Monday, 9 March 2026

Header Tank... changes

I foolishly used two Beelite ultrasonic level sensors on my header tank.  When I started running the engine - or more specifically the fuel pumps - the top sensor would alarm due to the return line being too close to the sensor.  This is my fault.

When the lower sensor failed, it was time to change them out.

I used two Madison sensors (which ironically is the same as beelite is now using in their 'new' sensor).

My panel was made to fit the old Beelite sensor, but I am NOT buying anything from them again.  So I am going to create my own unit for displaying the fuel level.

Employing a Arduino Nano Every board.  I've done some basic Arduino programming before, but not for 15 years.  Figured I would give ChatGPT a try.  OMG... WOW.  I did not expect that level of detail... now to see if the code it spit out works.

Removed the tank... had 3 new bungs welded in.  The first two are for the two new Madison magnetic sensors (sourced from Amazon... $27 each).  And since I was at it, move the return line from the center of the tank to the forward corner.

WOW.  One day and its up and running / burning in.  Going to let it run on the bench for a couple days and see if there are any problems.  ChatGPT sure made this simpler.

As soon as I told it this was going in an aircraft it hardened the code and added a bunch more watchdog timers.  I already had some of this included, but it added more.  There was some minor glitches in the code, and I explained what it was doing or not doing, it fixed it.  Unreal.

Code below:
/*
  Aircraft Header Tank Advisory Indicator
  Arduino Nano Every (ATmega4809)
  Industrial / Aviation Version
*/

#include <avr/io.h>
#include <avr/wdt.h>

// ================= PIN DEFINITIONS =================
const uint8_t level1Pin = 2;
const uint8_t level2Pin = 3;

const uint8_t green1Pin = 5;
const uint8_t red1Pin   = 6;

const uint8_t green2Pin = 9;
const uint8_t red2Pin   = 10;

// ================= TIMING =================
const unsigned long debounceTime      = 3000;
const unsigned long redFlashInterval  = 250;
const unsigned long heartbeatInterval = 10000;
const unsigned long heartbeatPulse    = 100;
const unsigned long startupDuration   = 1000;

// ================= STATE =================
bool level1State = false;
bool level2State = false;

bool lastReading1 = false;
bool lastReading2 = false;

unsigned long lastDebounce1 = 0;
unsigned long lastDebounce2 = 0;

unsigned long lastRedFlash = 0;
bool redFlashState = false;

unsigned long lastHeartbeat = 0;
bool heartbeatActive = false;
unsigned long heartbeatStart = 0;

unsigned long startupStart;
bool startupActive = true;

// ===================================================
// WATCHDOG
// ===================================================
void enableWatchdog()
{
  RSTCTRL.RSTFR = RSTCTRL_WDRF_bm;
  CCP = CCP_IOREG_gc;
  WDT.CTRLA = WDT_PERIOD_8KCLK_gc;   // ~8 seconds
}

void resetWatchdog()
{
  __asm__ __volatile__("wdr");
}

// ===================================================

void setup()
{
  // Safe startup state
  digitalWrite(green1Pin, LOW);
  digitalWrite(red1Pin, LOW);
  digitalWrite(green2Pin, LOW);
  digitalWrite(red2Pin, LOW);

  pinMode(green1Pin, OUTPUT);
  pinMode(red1Pin, OUTPUT);
  pinMode(green2Pin, OUTPUT);
  pinMode(red2Pin, OUTPUT);

  pinMode(level1Pin, INPUT_PULLUP);
  pinMode(level2Pin, INPUT_PULLUP);

  Serial.begin(115200);
  delay(200);

  logResetCause();

  startupStart = millis();
  enableWatchdog();
}

// ===================================================

void loop()
{
  unsigned long now = millis();

  if (startupActive)
  {
    handleStartup(now);
    resetWatchdog();
    return;
  }

  handleDebounce(level1Pin, level1State, lastReading1, lastDebounce1, 1);
  handleDebounce(level2Pin, level2State, lastReading2, lastDebounce2, 2);

  handleRedFlash(now);
  handleHeartbeat(now);

  updateOutputs();

  resetWatchdog();
}

// ===================================================

void handleDebounce(uint8_t pin, bool &state,
                    bool &lastReading,
                    unsigned long &lastDebounce,
                    uint8_t channel)
{
  bool reading = (digitalRead(pin) == LOW);

  if (reading != lastReading)
    lastDebounce = millis();

  if ((millis() - lastDebounce) >= debounceTime)
  {
    if (state != reading)
    {
      state = reading;

      Serial.print("Level ");
      Serial.print(channel);
      Serial.print(": ");
      Serial.println(state ? "FLUID PRESENT" : "NO FLUID");
    }
  }

  lastReading = reading;
}

// ===================================================

void handleRedFlash(unsigned long now)
{
  if (now - lastRedFlash >= redFlashInterval)
  {
    lastRedFlash = now;
    redFlashState = !redFlashState;
  }
}

// ===================================================

void handleHeartbeat(unsigned long now)
{
  if (now - lastHeartbeat >= heartbeatInterval)
  {
    heartbeatActive = true;
    heartbeatStart = now;
    lastHeartbeat = now;
    Serial.println("Heartbeat OK");
  }

  if (heartbeatActive &&
      (now - heartbeatStart > heartbeatPulse))
  {
    heartbeatActive = false;
  }
}

// ===================================================
// *** FIXED OUTPUT LOGIC ***
void updateOutputs()
{
  // LEVEL 1
  bool green1 = level1State;
  bool red1   = (!level1State) ? redFlashState : LOW;

  // LEVEL 2
  bool green2 = level2State;
  bool red2   = (!level2State) ? redFlashState : LOW;

  // Heartbeat overlays green LEDs
  if (heartbeatActive)
  {
    green1 = !green1;
    green2 = !green2;
  }

  digitalWrite(green1Pin, green1);
  digitalWrite(red1Pin, red1);

  digitalWrite(green2Pin, green2);
  digitalWrite(red2Pin, red2);
}

// ===================================================

void handleStartup(unsigned long now)
{
  if (now - startupStart >= startupDuration)
  {
    startupActive = false;
    Serial.println("Startup Complete");
    return;
  }

  if ((now / 250) % 2 == 0)
  {
    digitalWrite(green1Pin, HIGH);
    digitalWrite(green2Pin, HIGH);
    digitalWrite(red1Pin, LOW);
    digitalWrite(red2Pin, LOW);
  }
  else
  {
    digitalWrite(green1Pin, LOW);
    digitalWrite(green2Pin, LOW);
    digitalWrite(red1Pin, HIGH);
    digitalWrite(red2Pin, HIGH);
  }
}

// ===================================================

void logResetCause()
{
  uint8_t flags = RSTCTRL.RSTFR;

  Serial.println("---- RESET DETECTED ----");

  if (flags & RSTCTRL_PORF_bm) Serial.println("Power-On Reset");
  if (flags & RSTCTRL_BORF_bm) Serial.println("Brown-Out Reset");
  if (flags & RSTCTRL_EXTRF_bm) Serial.println("External Reset");
  if (flags & RSTCTRL_WDRF_bm) Serial.println("Watchdog Reset");
  if (flags & RSTCTRL_SWRF_bm) Serial.println("Software Reset");

  Serial.println("------------------------");

  RSTCTRL.RSTFR = flags;
}

I tested everything and it worked great.  Then tested it while running the engine at full power.  Still great.  This was a worthwhile change.

Header tank after welding... refinishing before installation.







No comments:

Post a Comment