Mastodon Politics, Power, and Science: Mini USB PC Stats Monitor

Friday, June 12, 2026

Mini USB PC Stats Monitor

with an ESP32 and an SSD1306 OLED display.
Since microcontrollers cannot read PC data natively through a standard USB port, you need a simple script on the computer to read the performance stats and stream them over the USB cord to the ESP32.

Step 1: The Concept & Communication
The architecture for this setup relies on a clean python stream:
  1. On the PC: A lightweight Python script runs in the background. It reads CPU, GPU, and Laptop Battery status using standard Python libraries, bundles the data into a text string, and shoots it down the USB Serial port (COM port) once every second.
  2. On the ESP32: The ESP32 stays plugged into a computer USB port. It listens to its Serial buffer, breaks the text string apart, and paints the layout onto the OLED screen.

Step 2: The PC Side (Python Script)
You will need to install two libraries on your computer via your command prompt/terminal:
bash
pip install psutil pyserial GPUtil

Create a file named pc_stats.py and run it on your computer while your ESP32 is plugged in. Make sure to change "COM3" to whatever port your ESP32 is using.
python
import time
import psutil
import serial
import GPUtil

# Configure this to match your ESP32's COM port
SERIAL_PORT = "COM3"  
BAUD_RATE = 115200

try:
    ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
    print(f"Streaming data to {SERIAL_PORT}...")
except Exception as e:
    print(f"Error opening port: {e}")
    exit()

while True:
    try:
        # 1. Get CPU Usage
        cpu = int(psutil.cpu_percent())

        # 2. Get GPU Usage (Accounts for integrated vs dedicated cards)
        gpus = GPUtil.getGPUs()
        gpu = int(gpus[0].load * 100) if gpus else 0

        # 3. Get Battery Stats
        battery = psutil.sensors_battery()
        bat_percent = int(battery.percent) if battery else 100
        is_charging = 1 if (battery and battery.power_plugged) else 0

        # Package data into a clean comma-separated string: "CPU,GPU,BAT,CHARGING"
        # Example: "42,65,80,1"
        data_packet = f"{cpu},{gpu},{bat_percent},{is_charging}\n"
        
        # Send data across the USB cable
        ser.write(data_packet.encode('utf-8'))
        
        time.sleep(1) # Send update once per second
        
    except KeyboardInterrupt:
        ser.close()
        break
    except Exception as e:
        print(f"Connection lost: {e}")
        time.sleep(2)
Step 3: The ESP32 Code
This code handles parsing the text coming from the PC and formatting it onto your screen. Connect your SSD1306 display's SCL to GPIO 22 and SDA to GPIO 21.
cpp
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
  // Start serial communications at the same speed as the python script
  Serial.begin(115200); 
  
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    for(;;);
  }
  
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 25);
  display.println("Waiting for PC...");
  display.display();
}

void loop() {
  // Check if the Python script has pushed a new data packet down the wire
  if (Serial.available() > 0) {
    String inputData = Serial.readStringUntil('\n');
    
    // Parse the comma-separated string back into distinct data pieces
    int firstComma = inputData.indexOf(',');
    int secondComma = inputData.indexOf(',', firstComma + 1);
    int thirdComma = inputData.indexOf(',', secondComma + 1);
    
    if (firstComma != -1 && secondComma != -1 && thirdComma != -1) {
      String cpuStr = inputData.substring(0, firstComma);
      String gpuStr = inputData.substring(firstComma + 1, secondComma);
      String batStr = inputData.substring(secondComma + 1, thirdComma);
      String chargeStr = inputData.substring(thirdComma + 1);
      
      // Update screen layout
      display.clearDisplay();
      
      // Header
      display.setCursor(20, 0);
      display.println("SYSTEM MONITOR");
      display.println("---------------------");
      
      // CPU Metrics
      display.setCursor(0, 20);
      display.print("CPU Usage:  "); display.print(cpuStr); display.println("%");
      
      // GPU Metrics
      display.setCursor(0, 34);
      display.print("GPU Usage:  "); display.print(gpuStr); display.println("%");
      
      // Laptop Battery Metrics
      display.setCursor(0, 48);
      display.print("Battery:    "); display.print(batStr); display.print("%");
      if(chargeStr.toInt() == 1) {
        display.print(" [ + ]"); // Show charging icon indicator
      }
      
      display.display();
    }
  }
}

No comments:

Post a Comment

The Language-Creation Confusion: How Humans Mistake Naming for Reality

 J. Rogers, SE Ohio Abstract Humans systematically confuse language with creation—both in ancient creation myths where naming brings things ...