Mastodon Politics, Power, and Science: Automated, self-contained smartphone charging cutoff device using an ESP32, an electronic switch, and an INA219 current sensor.

Friday, June 12, 2026

Automated, self-contained smartphone charging cutoff device using an ESP32, an electronic switch, and an INA219 current sensor.

 Project Overview

The device monitors the real-time electrical current (amperage) flowing to your phone. When you first plug the phone in, it draws high current (1.5A to 2.0A). When the phone hits its saturation point (around 75–80% battery) and throttles its power draw down below 0.5A, the ESP32 opens the electronic switch, cutting the power completely to preserve battery lifespan.

Phase 1: Required Components
  • Microcontroller: ESP32 Development Board (e.g., DevKit V1, NodeMCU-32S).
  • Current Sensor: INA219 I2C Current Sensor Module (reads voltage and current up to 3.2A).
  • Electronic Switch: Your low-power DC switch (MOSFET module or solid-state relay) rated for at least 5V/2A.
  • USB Cable: A standard USB-A or USB-C cable to sacrifice/modify.
  • Power Source: A standard 5V USB wall adapter to power the phone and the ESP32 (via its Vin/5V pin).

Phase 2: Hardware Wiring & Circuit Layout
Because this is a low-voltage DC project (5V), it is completely safe to build on a breadboard or solder onto a protoboard.
1. Modifying the USB Cable
  1. Strip away 2 inches of the outer insulation of your USB cable.
  2. Locate the four internal wires: Red (5V Power), Black (Ground), White (Data-), and Green (Data+).
  3. Cut only the Red wire. Leave the Black, White, and Green wires perfectly intact.
2. Wiring Schematic
Connect the components to your ESP32 exactly as follows:
From ComponentPin NameTo ESP32 PinNotes
INA219 SensorVCC3.3VPowers the sensor logic
INA219 SensorGNDGNDCommon ground
INA219 SensorSCLGPIO 22I2C Clock line
INA219 SensorSDAGPIO 21I2C Data line
Electronic SwitchSignal/InputGPIO 23Controls the switch state
Electronic SwitchGNDGNDCommon ground
3. Splicing the Power Line (The Red USB Wire)
Route the cut Red wire through the sensor and the switch sequentially:
  1. Connect the Wall Plug side of the cut Red wire to Vin+ on the INA219.
  2. Connect Vin- on the INA219 to the Input/Positive power terminal of your electronic switch.
  3. Connect the Phone side of the cut Red wire to the Output/Negative power terminal of your electronic switch.

Phase 3: Software Implementation
1. Library Installation
Open the Arduino IDE and go to Sketch > Include Library > Manage Libraries. Search for and install "Adafruit INA219".
2. The Microcontroller Code
Upload this code to your ESP32. It uses a smart "State Machine" logic. It assumes a phone is plugged in if it sees a current draw above 100mA, and it cuts power the exact moment that current drops below 500mA.
cpp
#include <Wire.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 shortcutSensor;

// Pin Definitions
const int switchPin = 23; 

// Charging Thresholds (in Milliamps)
const float PLUGGED_IN_THRESHOLD = 100.0; // Current indicating a phone is attached
const float THROTTLE_THRESHOLD = 500.0;    // Current indicating phone has capped its charge

// Device States
enum ChargeState { WAITING_FOR_PHONE, CHARGING, POWER_CUT };
ChargeState currentState = WAITING_FOR_PHONE;

void setup() {
  Serial.begin(115200);
  while (!Serial) { delay(10); }

  pinMode(switchPin, OUTPUT);
  digitalWrite(switchPin, HIGH); // Default the switch to ON so it can detect a phone

  // Initialize the current sensor
  if (!shortcutSensor.begin()) {
    Serial.println("Error: Could not find INA219 current sensor!");
    while (1) { delay(10); }
  }
  
  Serial.println("Smart Charger Initialized. Ready and waiting for phone...");
}

void loop() {
  // Read current in milliamps (mA)
  float current_mA = shortcutSensor.getCurrent_mA();
  
  Serial.print("State: ");
  Serial.print(currentState);
  Serial.print(" | Current Draw: ");
  Serial.print(current_mA);
  Serial.println(" mA");

  switch (currentState) {
    
    case WAITING_FOR_PHONE:
      digitalWrite(switchPin, HIGH); // Keep power enabled to detect connection
      if (current_mA > PLUGGED_IN_THRESHOLD) {
        Serial.println("Phone detected! Transitioning to CHARGING state.");
        currentState = CHARGING;
      }
      break;

    case CHARGING:
      // Phone is actively drawing bulk power. Watch for the throttle drop.
      if (current_mA < THROTTLE_THRESHOLD && current_mA > 10.0) {
        Serial.println("Phone charging has throttled down. Activating cutoff!");
        digitalWrite(switchPin, LOW); // KILL THE POWER
        currentState = POWER_CUT;
      }
      break;

    case POWER_CUT:
      digitalWrite(switchPin, LOW); // Explicitly keep power disconnected
      
      // AUTO-RESET LOGIC: If you physically unplug the phone from the cable, 
      // the residual current drops completely to 0 mA.
      if (current_mA <= 5.0) { 
        Serial.println("Phone unplugged. Resetting charger for next use.");
        delay(2000); // Debounce delay
        currentState = WAITING_FOR_PHONE;
      }
      break;
  }

  delay(1000); // Sample the power line once per second
}
Phase 4: Testing & Calibration
  1. Initial Power-On: Plug the device into the wall adapter without a phone attached. Open your Arduino Serial Monitor (115200 baud). It should print State: 0 | Current Draw: ~0 mA.
  2. Bulk Charge Phase: Plug your Moto G Play in at 30% battery. The state should jump to 1 (CHARGING) and show a high mA reading (e.g., 1200 mA to 1500 mA).
  3. The Cutoff: Leave it plugged in. Watch the percentage on your phone screen. Note down the exact current reading when your phone hits your target percentage. If it hits 80% and the current drops to 350 mA, your THROTTLE_THRESHOLD of 500.0 in the code will have triggered perfectly, changing the state to 2 (POWER_CUT) and turning the phone screen off.
If you find that your Moto phone throttles down earlier or later than expected, you can easily adjust the THROTTLE_THRESHOLD value up or down in the code variables to pin down your exact target battery percentage.

Display upgrade

Adding a small display is a great upgrade. It lets you see exactly what the charger is doing without needing to plug the ESP32 into a computer to read the Serial Monitor.
1. Choosing the Display: The SSD1306 OLED
The absolute easiest and most compact screen for an ESP32 project is a 0.96-inch SSD1306 OLED display (128x64 resolution). They cost just a few dollars, take up almost no space in an enclosure, and use the exact same I2C communication pins as your INA219 current sensor.
Because they both use I2C, you do not need to waste any extra pins on your ESP32. You simply chain the wires together.
2. Updated Wiring Layout (With Screen)
You will connect both the INA219 and the OLED display to the same two I2C pins (GPIO 21 and GPIO 22).
From ComponentPin NameTo ESP32 PinNotes
OLED DisplayVCC3.3VPowers the display
OLED DisplayGNDGNDCommon ground
OLED DisplaySCLGPIO 22Shared I2C Clock
OLED DisplaySDAGPIO 21Shared I2C Data
  • Wiring Tip: You can just solder or twist the two VCC wires together, the two GND wires together, the two SCL wires together, and the two SDA wires together, then run those four combined lines directly to the ESP32 pins.
3. The Updated ESP32 Code (With Display Support)
First, install the "Adafruit SSD1306" and "Adafruit GFX" libraries using the Arduino IDE Library Manager. Then use this updated code to drive both the switch and the mini display:
cpp
#include <Wire.h>
#include <Adafruit_INA219.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);
Adafruit_INA219 shortcutSensor;

const int switchPin = 23; 
const float PLUGGED_IN_THRESHOLD = 100.0; 
const float THROTTLE_THRESHOLD = 500.0;    

enum ChargeState { WAITING_FOR_PHONE, CHARGING, POWER_CUT };
ChargeState currentState = WAITING_FOR_PHONE;

// Helper function to handle screen updates cleanly
void updateDisplay(String stateText, float current_mA) {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  
  // Draw Header
  display.setCursor(0, 0);
  display.println("--- SMART CHARGER ---");
  
  // Draw Current State
  display.setCursor(0, 20);
  display.print("STATUS: ");
  display.println(stateText);
  
  // Draw Real-time Current
  display.setCursor(0, 40);
  display.print("DRAW:   ");
  if (current_mA < 0) current_mA = 0; // Clean up tiny negative readings
  display.print(current_mA, 0); 
  display.println(" mA");
  
  display.display();
}

void setup() {
  Serial.begin(115200);
  pinMode(switchPin, OUTPUT);
  digitalWrite(switchPin, HIGH); 

  // Initialize Screen
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    Serial.println("SSD1306 allocation failed");
    for(;;);
  }
  
  // Initialize Current Sensor
  if (!shortcutSensor.begin()) {
    Serial.println("Could not find INA219 sensor!");
    while (1) { delay(10); }
  }
  
  display.clearDisplay();
  display.display();
}

void loop() {
  float current_mA = shortcutSensor.getCurrent_mA();
  
  switch (currentState) {
    
    case WAITING_FOR_PHONE:
      digitalWrite(switchPin, HIGH);
      updateDisplay("READY / IDLE", current_mA);
      
      if (current_mA > PLUGGED_IN_THRESHOLD) {
        currentState = CHARGING;
      }
      break;

    case CHARGING:
      updateDisplay("CHARGING...", current_mA);
      
      if (current_mA < THROTTLE_THRESHOLD && current_mA > 10.0) {
        digitalWrite(switchPin, LOW); 
        currentState = POWER_CUT;
      }
      break;

    case POWER_CUT:
      digitalWrite(switchPin, LOW);
      updateDisplay("CHARGED (CUT)", 0); // Force 0mA display when cut
      
      if (current_mA <= 5.0) { 
        delay(2000); 
        currentState = WAITING_FOR_PHONE;
      }
      break;
  }

  delay(1000); 
}
Enclosure Planning
When building your project box, the 0.96-inch OLED screens are incredibly easy to mount. You can just cut a small rectangular window into the face of your box and hot-glue or screw the screen flush against the inside opening.

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 ...