Meh Belly Lint Collection

That awful moment when you realize,
THIS is YOUR circus and THOSE are YOUR monkeys.

User Tools

Site Tools


server_status_oled_display

This is an old revision of the document!


Server Status OLED Display

Displayinator v1.0

This is a device you can shove into a spare USB connector on say a server rack system to display info like IP/Hostname/etc.

This is a old school project using a ATMEGA32U4.

  • The board is a clone of an Arduino Micro, and I2C is connnected to the OLED (SSD1315) and the Accelerometer (MMA8451).
  • The OLED's I2C address 0x3C, and I'm using adafruit's 1306 driver, mainly for display rotation support.
  • The Accelerometer's address is 0x1C and the adafruit MMA8541 driver is basic but functional. Note that the more advanced features (e.g. tap to click) built into the chip are unavailable with this library.

Loading the Arduino Bootloader

I'm using a cheap chinese USBasp (with avrdude v8.0)

avrdude will complain about older firmware but my recommendation is to ignore it. The TLDR; is the chinese firmware is modified and does some automagic stuff and in general works more reliably than the publicly available open source code version.

Use zadig to make it (USBasp) use libusb-win32

Copy avrdude to the arduino avrdude location. (You can “Show verbose output during [X] compile [X] upload” in File→Preferences to try to upload to see where it is)

Then from Arduino, select USBasp in Tools→Programmer and burn using Tools→Burn Bootloader

Flashing the code

Choose “Arduino Micro” in Tools → Board → Arduino AVR Micros Select the right COM port in Tools → Port I use Serial Port Notifier from Helm as a helper function. Select “Arduino as ISP” in Tools → Programmer

Then download the following code and upload it.

Arduino Code: oledusbdisplayaccx1.zip

Controlling the device

When the device boots, it checks the EEPROM for stored key-value pairs and displays them on screen. I uses the accelerometer to detect orientation so it should be right side up.

The USB serial is nominally 115200, and the commands it takes are

!LIST which displays the stored key-value pairs

!LIST
Key-Value Pairs:
Hostname = DESKTOP-PRYPYAT
IP1 = 10.10.22.102
IP2 = 10.10.22.61
GW = 10.10.22.1
WAN = 99.29.30.10

!VER reports the devicename and version of firmware

!VER
Displayinator v1.0

!NUKE clears all stored key-value pairs from memory and eeprom

!NUKE
All key-value pairs cleared.

!ORIENT [0..3] overrides the display orientation, but hasn't been implemented yet

!ORIENT 0
Orientation argument extracted: '0'
Orientation set to 0

Everything else is checked to see if it is a Key-Value pair. A pair is just two space delimited alphanumeric words.

HAPPY GILMORE
Space index found at: 5
Key extracted: 'HAPPY', Value extracted: 'GILMORE'
Added HAPPY = GILMORE

Python Code: displayinatorhostip.zip

Code Tips

  • The 32u4 is sloooow by modern standards, so after you send a command, I wait up to half a second before checking for a response.
  • It also likes to reset when you connect via serial so I wait 3 to 5 seconds for it to boot up before poking it.
  • Python raises DTR, and for whatever reason it breaks things, so I set ser.dtr = False.

Code Dump

Arduino:

#include <Wire.h>
#include <Adafruit_MMA8451.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Fonts/TomThumb.h> // TomThumb
//#include <Fonts/Org_01.h> // Org_01
//#include <Fonts/Picopixel.h> // Picopixel
#include <EEPROM.h>
 
class MySSD1306 : public Adafruit_SSD1306 {
public:
  MySSD1306(int16_t w, int16_t h, TwoWire *twi, int8_t rst_pin)
    : Adafruit_SSD1306(w, h, twi, rst_pin) {}
 
  int16_t getCursorX() { return cursor_x; }
  int16_t getCursorY() { return cursor_y; }
};
 
// ----- EEPROM Layout Settings -----
#define MAX_KEY_LENGTH 16       // Maximum characters for a key
#define MAX_VALUE_LENGTH 16     // Maximum characters for a value
#define PAIR_SIZE (MAX_KEY_LENGTH + MAX_VALUE_LENGTH)
#define EEPROM_KEY_COUNT_OFFSET 0
#define EEPROM_KEY_VALUE_OFFSET 1  // Data begins here
// -----------------------------------
 
// Use a renamed enum to avoid potential conflicts
enum Fuckme {
  UNKNOWN,
  FACE_UP,
  FACE_DOWN,
  LANDSCAPE_RIGHT,
  LANDSCAPE_LEFT,
  PORTRAIT_UP,
  PORTRAIT_DOWN
};
 
// CLI Code
#define MAX_KEYS 10  // Maximum number of key-value pairs
struct KeyValue {
  String key;
  String value;
};
KeyValue keyValueStore[MAX_KEYS];  // Storage for key-value pairs
int keyCount = 0;                  // Current number of keys stored
int orient = 0;                    // Stores Display Orientation (0-3)
String inputString = "";           // Buffer for incoming serial data
bool stringComplete = false;       // Indicates if a full line has been received
int orientChanged = 0;
 
// PROGMEM constant command strings
const char cmd_list[] PROGMEM   = "!LIST";
const char cmd_nuke[] PROGMEM   = "!NUKE";
const char cmd_ver[] PROGMEM    = "!VER";
const char cmd_orient[] PROGMEM = "!ORIENT";
 
int landscape = -1;
const float THRESHOLD = 7.0;         // Minimum m/s² value to consider an axis "dominant"
const unsigned long DEBOUNCE_DELAY = 250;  // Time in milliseconds to wait before confirming a change
Fuckme lastStableOrientation = UNKNOWN;
unsigned long lastChangeTime = 0;
 
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define SCREEN_ADDRESS 0x3C // I2C Addr, Commonly 0x3D or 0x3C
#define OLED_RESET     -1 
MySSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
 
#define ACCEL_ADDRESS 0x1C 
Adafruit_MMA8451 mma = Adafruit_MMA8451();
sensors_event_t event; 
 
//-------------------------------------------------- EEPROM Functions ---------------------------------------
void saveKeyValuesToEEPROM() {
  // Save the number of key-value pairs at address 0.
  EEPROM.update(EEPROM_KEY_COUNT_OFFSET, keyCount);
  for (int i = 0; i < keyCount; i++) {
    int base = EEPROM_KEY_VALUE_OFFSET + i * PAIR_SIZE;
    // Save key as fixed-length char array
    for (int j = 0; j < MAX_KEY_LENGTH; j++) {
      char c = (j < keyValueStore[i].key.length()) ? keyValueStore[i].key.charAt(j) : '\0';
      EEPROM.update(base + j, c);
    }
    // Save value as fixed-length char array
    for (int j = 0; j < MAX_VALUE_LENGTH; j++) {
      char c = (j < keyValueStore[i].value.length()) ? keyValueStore[i].value.charAt(j) : '\0';
      EEPROM.update(base + MAX_KEY_LENGTH + j, c);
    }
  }
}
 
void loadKeyValuesFromEEPROM() {
  keyCount = EEPROM.read(EEPROM_KEY_COUNT_OFFSET);
  // Validate keyCount (if invalid, reset to 0)
  if (keyCount < 0 || keyCount > MAX_KEYS) {
    keyCount = 0;
  }
  for (int i = 0; i < keyCount; i++) {
    int base = EEPROM_KEY_VALUE_OFFSET + i * PAIR_SIZE;
    char keyBuffer[MAX_KEY_LENGTH + 1];
    char valueBuffer[MAX_VALUE_LENGTH + 1];
    for (int j = 0; j < MAX_KEY_LENGTH; j++) {
      keyBuffer[j] = EEPROM.read(base + j);
    }
    keyBuffer[MAX_KEY_LENGTH] = '\0';
    for (int j = 0; j < MAX_VALUE_LENGTH; j++) {
      valueBuffer[j] = EEPROM.read(base + MAX_KEY_LENGTH + j);
    }
    valueBuffer[MAX_VALUE_LENGTH] = '\0';
    keyValueStore[i].key = String(keyBuffer);
    keyValueStore[i].value = String(valueBuffer);
  }
}
//-----------------------------------------------------------------------------------------------------
 
const void* activeFont = NULL;
 
// customPrintln() prints text and only repositions if the font changes.
// If the string length exceeds 'threshold', we use the TomThumb font; otherwise, the default.
void customPrintln(const String &str, size_t threshold) {
  // Determine which font we want based on the string length.
  const void* desiredFont = (str.length() > threshold) ? (void*)&TomThumb : NULL;
 
  //Serial.print("Y:");
  //Serial.println(display.getCursorY());
 
  int currY = display.getCursorY();
  if ((currY == 0) && (!landscape)) { // weird bug
    display.setCursor(0,7);
  }
 
  // Only change font (and reposition) if it differs from the active font.
  if (activeFont != desiredFont) {
    display.setFont(desiredFont);
    activeFont = desiredFont;
 
    // Reposition cursor when the font changes cause reasons.
    // For example, when switching to TomThumb, move up by 1 pixel;
    // when switching back to default, move down by 1 pixel.
    if (desiredFont == (void*)&TomThumb) {
      //Serial.println(display.getCursorY());
      display.setCursor(0, display.getCursorY() - 1);
    } else {
      display.setCursor(0, display.getCursorY() + 1);
    }
  }
 
  // Print the string (this also moves the cursor downward).
  display.println(str);
}
 
 
//-------------------------------------------------- SETUP -------------------------------------------
void setup() {
  Serial.begin(115200);
  //while (!Serial) delay(10);     // will pause until serial console opens
 
  //Serial.println("Displayinator Boot");
 
  // Load key-value pairs from EEPROM
  loadKeyValuesFromEEPROM();
 
  if (!mma.begin(ACCEL_ADDRESS)) {
    Serial.println("MMA8451 FAIL!");
    for(;;); // Don't proceed, loop forever
  }  
  Serial.println("MMA8451 OK.");
 
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 FAIL"));
    for(;;); // Don't proceed, loop forever
  }
  Serial.println("SSD1306 OK.");
 
 
  display.clearDisplay();
  display.display();
 
  mma.getEvent(&event);
  float ax = event.acceleration.x; // acceleration is measured in m/s^2
  float ay = event.acceleration.y;
  float az = event.acceleration.z;
  updateOrientation(ax, ay, az);
 
  //display.setFont(&Picopixel);
  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  customPrintln(F("Displayinator"),10);
 
/*  customPrintln(F("Displayinator"),10);
  customPrintln(F("123456789012345678901234567890"),10);
  customPrintln(F("123.456.789.012"),10);
  customPrintln(F("COW"),10);
*/
  display.display();
  inputString.reserve(20);     // Reserve memory for the input string
 
 
 
  mma.setRange(MMA8451_RANGE_2_G);
 
  //Serial.print("ACCRANGE = ");  Serial.print(2 << mma.getRange());  Serial.println("G");
}
 
//-------------------------------------------------- LOOP --------------------------------------------
void loop() {
  /* Get a new sensor event */ 
 
  mma.getEvent(&event);
  float ax = event.acceleration.x; // acceleration is measured in m/s^2
  float ay = event.acceleration.y;
  float az = event.acceleration.z;
  updateOrientation(ax, ay, az);
 
  updateDisplay();
 
  while (Serial.available()) { // Nonblocking serial input: read available characters one at a time
    char inChar = (char)Serial.read();
    //Serial.print(inChar);
    if (inChar == '\n') {  // Newline signals end of command
      //Serial.print("[LF]");
      stringComplete = true;
      break; // Process this command after breaking out
    } else if (inChar != '\r') { // Ignore carriage return
      inputString += inChar;
    } else {
      //Serial.print("[CR]");
    }
  }
 
  // If a complete command was received, process it
  if (stringComplete) {
    inputString.trim();
    if (inputString.length() > 0) {
      //Serial.println(inputString);
      processCommand(inputString);
    }
    // Reset buffer for the next command
    inputString = "";
    stringComplete = false;
  }
  delay(250);
}
 
//-------------------------------------------------- DISPLAY ---------------------------------------
unsigned long lastDisplayUpdate = 0;         // Tracks the last display update time
const unsigned long DISPLAY_UPDATE_INTERVAL = 1000; // Update interval in milliseconds
 
void updateDisplay() {
  unsigned long now = millis();
  int sWidth = 10;
  if (landscape) {
    sWidth = 21;
  } else {
    sWidth = 10;
  }
 
  if (now - lastDisplayUpdate >= DISPLAY_UPDATE_INTERVAL) {
    lastDisplayUpdate = now;
 
    display.clearDisplay();
    display.setCursor(0, 0);
 
    // Display key-value pairs
    if (keyCount == 0) {
      customPrintln(F("No KV data"),sWidth);
    } else {
      for (int i = 0; i < keyCount; i++) {
        if ((keyValueStore[i].key.length() + keyValueStore[i].value.length() + 1) <= sWidth) {
          String result = keyValueStore[i].key + ":" + keyValueStore[i].value;
          customPrintln(result,sWidth);
        } else {
          customPrintln(keyValueStore[i].key,sWidth);
          customPrintln(keyValueStore[i].value,sWidth);
        }
      }
    }
 
    display.display();
  }
}
 
//-------------------------------------------------- ORIENTATION ---------------------------------------
Fuckme detectOrientation(float ax, float ay, float az) {
  if (az > THRESHOLD) return FACE_UP;
  if (az < -THRESHOLD) return FACE_DOWN;
  if (ax > THRESHOLD) return LANDSCAPE_RIGHT;
  if (ax < -THRESHOLD) return LANDSCAPE_LEFT;
  if (ay > THRESHOLD) return PORTRAIT_UP;
  if (ay < -THRESHOLD) return PORTRAIT_DOWN;
  return UNKNOWN;
}
 
void updateOrientation(float ax, float ay, float az) {
  Fuckme current = detectOrientation(ax, ay, az);
  unsigned long now = millis();
 
  if (current != lastStableOrientation) {
    if (now - lastChangeTime >= DEBOUNCE_DELAY) {
      lastStableOrientation = current;
      orientChanged = 1;
      Serial.print("REORIENT: ");
      switch (current) {
        case FACE_UP:         
          Serial.println("FACE_UP"); 
          break;
        case FACE_DOWN:       
          Serial.println("FACE_DOWN"); 
          break;
        case LANDSCAPE_RIGHT: 
          Serial.println("LANDSCAPE_RIGHT"); 
          display.setRotation(0);
          landscape = 1;
          break;
        case LANDSCAPE_LEFT:  
          Serial.println("LANDSCAPE_LEFT"); 
          display.setRotation(2);
          landscape = 1;
          break;
        case PORTRAIT_UP:     
          Serial.println("PORTRAIT_UP"); 
          display.setRotation(3);
          landscape = 0;
          break;
        case PORTRAIT_DOWN:   
          Serial.println("PORTRAIT_DOWN"); 
          display.setRotation(1);
          landscape = 0;
          break;
        default:
          Serial.println("UNKNOWN"); 
          break;
      }
    }
  } else {
    lastChangeTime = now;
  }
}
 
//-------------------------------------------------- COMMAND PROCESSING ---------------------------------------
void processCommand(String command) {
  // Check if command is special (starts with '!')
  if (command.startsWith("!")) {
    if (startsWithProgmem(command, cmd_list)) {
      listKeyValues();
    } else if (startsWithProgmem(command, cmd_nuke)) {
      nukeKeyValues();
    } else if (startsWithProgmem(command, cmd_ver)) {
      printVersion();
    } else if (startsWithProgmem(command, cmd_orient)) {
      processOrientCommand(command);
    } else {
      Serial.println(F("Error: Unknown command"));
    }
  } else {
    // Assume key-value pair in the form "KEY VALUE"
    int spaceIndex = command.indexOf(' ');
    Serial.print(F("Space index found at: "));
    Serial.println(spaceIndex);
    if (spaceIndex == -1) {
      Serial.println(F("Error: Command format incorrect. Expected 'KEY VALUE'"));
    } else {
      String key = command.substring(0, spaceIndex);
      String value = command.substring(spaceIndex + 1);
      key.trim();
      value.trim();
      Serial.print(F("Key extracted: '"));
      Serial.print(key);
      Serial.print(F("', Value extracted: '"));
      Serial.print(value);
      Serial.println(F("'"));
      setKeyValue(key, value);
    }
  }
}
 
void setKeyValue(String key, String value) {
  // Update if key exists
  for (int i = 0; i < keyCount; i++) {
    if (keyValueStore[i].key == key) {
      keyValueStore[i].value = value;
      Serial.print(F("Updated "));
      Serial.print(key);
      Serial.print(F(" = "));
      Serial.println(value);
      saveKeyValuesToEEPROM();
      return;
    }
  }
  // Add new key-value pair if space is available
  if (keyCount < MAX_KEYS) {
    keyValueStore[keyCount].key = key;
    keyValueStore[keyCount].value = value;
    keyCount++;
    Serial.print(F("Added "));
    Serial.print(key);
    Serial.print(F(" = "));
    Serial.println(value);
    saveKeyValuesToEEPROM();
  } else {
    Serial.println(F("Error: Key store is full"));
  }
}
 
void listKeyValues() {
  Serial.println(F("Key-Value Pairs:"));
  for (int i = 0; i < keyCount; i++) {
    Serial.print(keyValueStore[i].key);
    Serial.print(F(" = "));
    Serial.println(keyValueStore[i].value);
  }
}
 
void nukeKeyValues() {
  keyCount = 0;  // Clear the store by resetting the count
  Serial.println(F("All key-value pairs cleared."));
  saveKeyValuesToEEPROM();
}
 
void printVersion() {
  Serial.println(F("Displayinator v1.0"));
}
 
void processOrientCommand(String command) {
  int spaceIndex = command.indexOf(' ');
  if (spaceIndex == -1) {
    Serial.println(F("Error: !ORIENT requires an argument (0-3)"));
    return;
  }
  String arg = command.substring(spaceIndex + 1);
  arg.trim();
  Serial.print(F("Orientation argument extracted: '"));
  Serial.print(arg);
  Serial.println(F("'"));
  int newOrient = arg.toInt();
  if (newOrient < 0 || newOrient > 3) {
    Serial.println(F("Error: Orientation must be between 0 and 3."));
  } else {
    orient = newOrient;
    Serial.print(F("Orientation set to "));
    Serial.println(orient);
  }
}
 
// Helper function: compare a String to a command stored in PROGMEM.
bool startsWithProgmem(const String &s, const char *progmemStr) {
  char buffer[20]; // Adjust size if commands grow longer
  strcpy_P(buffer, progmemStr);
  return s.startsWith(buffer);
}
 
void displayKeyValuePairs() {
  if (keyCount == 0) {
    display.println(F("No KV data"));
  } else {
    for (int i = 0; i < keyCount; i++) {
      display.print(keyValueStore[i].key);
      display.print(F(" = "));
      display.println(keyValueStore[i].value);
    }
  }
}

Python:

import serial
import time
import socket
import re
import sys
import netifaces
import argparse
import platform
import urllib.request
import json
 
"""
pip install pyserial netifaces
"""
 
def send_command(ser, command):
    """
    Sends a command to the serial device and returns its response.
    Clears the input buffer, sends the command with a newline,
    waits briefly, then reads available lines.
    """
    try:
        ser.reset_input_buffer()
        # Write command (append newline)
        ser.write((command + "\n").encode('utf-8'))
        time.sleep(0.5)
    except Exception as e:
        print(f"Error writing command '{command}' to port: {e}")
        return ""
 
    response = ""
    try:
        while ser.in_waiting > 0:
            line = ser.readline().decode('utf-8', errors='replace')
            response += line
    except Exception as e:
        print(f"Warning: error reading response for command '{command}': {e}")
    return response.strip()
 
def get_ips_with_gateway():
    """
    Returns a list of IPv4 addresses from interfaces that have an associated gateway.
    Requires the netifaces module.
    """
    gateways = netifaces.gateways()
    interfaces_with_gateway = set()
 
    if netifaces.AF_INET in gateways:
        for gw_info in gateways[netifaces.AF_INET]:
            if isinstance(gw_info, tuple) and len(gw_info) >= 2:
                interface = gw_info[1]
                interfaces_with_gateway.add(interface)
 
    ip_list = []
    for interface in interfaces_with_gateway:
        addrs = netifaces.ifaddresses(interface)
        if netifaces.AF_INET in addrs:
            for link in addrs[netifaces.AF_INET]:
                ip = link.get('addr')
                if ip and ip != "127.0.0.1":
                    ip_list.append(ip)
    return ip_list
 
def get_default_gateways():
    """
    Returns a deduplicated list of default IPv4 gateway addresses.
    """
    gateways = netifaces.gateways()
    gw_set = set()
    if netifaces.AF_INET in gateways:
        for gw_info in gateways[netifaces.AF_INET]:
            if isinstance(gw_info, tuple):
                gw_set.add(gw_info[0])
    return list(gw_set)
 
def get_wan_ip():
    """
    Retrieves the WAN IP address using the ipify API.
    """
    url = "https://api.ipify.org?format=json"
    try:
        with urllib.request.urlopen(url) as response:
            data = response.read().decode('utf-8')
            ip_info = json.loads(data)
            return ip_info.get("ip", "Unknown")
    except Exception as e:
        return f"Error retrieving IP: {e}"
 
def parse_key_value_response(response):
    """
    Parses a !LIST response into a dictionary.
    Expected response format:
      Key-Value Pairs:
      Hostname = SOMETHING
      IP1 = X.X.X.X
      ...
    """
    kv_pairs = {}
    lines = response.splitlines()
    start_parsing = False
    for line in lines:
        if "Key-Value Pairs:" in line:
            start_parsing = True
            continue
        if start_parsing and line.strip():
            match = re.match(r'^\s*(\S+)\s*=\s*(\S+)\s*$', line)
            if match:
                key = match.group(1)
                value = match.group(2)
                kv_pairs[key] = value
    return kv_pairs
 
def needs_nuke(stored_kv, local_hostname, local_ips):
    """
    Compares the stored key-value pairs against the local hostname and IP addresses.
    Returns True if a difference is detected.
    """
    # Check hostname difference
    if stored_kv.get("Hostname", "") != local_hostname:
        print(f"Hostname difference detected: stored='{stored_kv.get('Hostname', None)}' vs local='{local_hostname}'")
        return True
 
    # Gather stored IPs in order: IP1, IP2, ...
    stored_ips = []
    i = 1
    while True:
        key = f"IP{i}"
        if key in stored_kv:
            stored_ips.append(stored_kv[key])
            i += 1
        else:
            break
 
    # Compare lists: if number or order of IPs is different, we consider it a difference.
    if stored_ips != local_ips:
        print(f"IP address difference detected: stored={stored_ips} vs local={local_ips}")
        return True
 
    return False
 
def main():
    # Determine default port based on the OS
    if platform.system() == "Linux":
        default_port = "/dev/ttyUSB0"
    else:
        default_port = "COM70"
 
    # Parse command-line arguments to get the serial port
    parser = argparse.ArgumentParser(description="Serial updater for Displayinator")
    parser.add_argument("--port", type=str, default=default_port,
                        help="Serial port to connect to (e.g., /dev/ttyUSB0 on Linux or COM70 on Windows)")
    args = parser.parse_args()
 
    com_port = args.port
    baud_rate = 115200
 
    # Open and configure the serial port
    try:
        ser = serial.Serial(
            port=com_port,
            baudrate=baud_rate,
            bytesize=serial.EIGHTBITS,
            parity=serial.PARITY_NONE,
            stopbits=serial.STOPBITS_ONE,
            timeout=2,
            write_timeout=2
        )
        ser.dtr = False
        # Allow time for Arduino reset upon opening the port
        time.sleep(5)
    except Exception as e:
        print(f"Failed to open port {com_port}. Error details: {e}")
        sys.exit(1)
 
    # Query version
    response_ver = send_command(ser, "!VER")
    print(f"Response from !VER: {response_ver}")
    if "Displayinator v1.0" not in response_ver:
        print(f"Error: Version mismatch or no valid response from device. Expected 'Displayinator v1.0' but received: '{response_ver}'")
        ser.close()
        sys.exit(1)
 
    # Query key-value pairs with !LIST
    response_list = send_command(ser, "!LIST")
    print("Response from !LIST:")
    print(response_list)
    stored_kv_pairs = parse_key_value_response(response_list)
 
    # Get the machine's hostname and IP addresses (only those with a gateway)
    local_hostname = socket.gethostname()
    local_ips = get_ips_with_gateway()
    print(f"Machine Hostname: {local_hostname}")
    print(f"Machine IP Addresses (with gateway): {', '.join(local_ips)}")
 
    # Determine if stored values differ from local values
    if needs_nuke(stored_kv_pairs, local_hostname, local_ips):
        print("Differences detected. Issuing !NUKE command to clear stored key-value pairs...")
        nuke_response = send_command(ser, "!NUKE")
        print(f"Response from !NUKE: {nuke_response}")
    else:
        print("Stored key-value pairs are up-to-date. No need to issue !NUKE.")
 
    # Update hostname regardless; after nuke it should be empty
    cmd_hostname = f"Hostname {local_hostname}"
    print(f"Updating hostname: {cmd_hostname}")
    update_response = send_command(ser, cmd_hostname)
    print(f"Response: {update_response}")
 
    # Update IP addresses, assigning them as IP1, IP2, etc.
    for index, ip in enumerate(local_ips, start=1):
        key = f"IP{index}"
        cmd_ip = f"{key} {ip}"
        print(f"Updating IP with command: {cmd_ip}")
        update_response = send_command(ser, cmd_ip)
        print(f"Response: {update_response}")
 
    # Update default gateways:
    default_gateways = get_default_gateways()
    print(f"Default Gateways: {', '.join(default_gateways)}")
    if len(default_gateways) == 1:
        # Only one gateway; use key "GW"
        cmd_gw = f"GW {default_gateways[0]}"
        print(f"Updating gateway with command: {cmd_gw}")
        update_response = send_command(ser, cmd_gw)
        print(f"Response: {update_response}")
    else:
        # Multiple gateways; use keys GW1, GW2, etc.
        for index, gw in enumerate(default_gateways, start=1):
            key = f"GW{index}"
            cmd_gw = f"{key} {gw}"
            print(f"Updating gateway with command: {cmd_gw}")
            update_response = send_command(ser, cmd_gw)
            print(f"Response: {update_response}")
 
    # Retrieve and update WAN IP address using key "WAN"
    wan_ip = get_wan_ip()
    cmd_wan = f"WAN {wan_ip}"
    print(f"Updating WAN IP with command: {cmd_wan}")
    update_response = send_command(ser, cmd_wan)
    print(f"Response: {update_response}")
 
    # Close the serial port
    ser.close()
 
if __name__ == '__main__':
    main()
server_status_oled_display.1742543023.txt.gz · Last modified: 2025/03/21 07:43 by kenson

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki