Debugging An Issue Only On Power Timer Hat

Hiya,

I’ve run into an annoying bug which I can only see when I run my project off battery power (and through a Power Timer Hat)

Essentially, all I need to do, is for my RPi Pico W to read the ADC, and send the reading over MQTT to my server.

My Current setup work when it is powered from

  • My server’s USB
  • Another Computers USB
  • A USB power supply plugged into the wall

However, when I power it up using batteries plugged into the timer hat, my server does not get anny messages. I can see the LED on both the HAT and the RPi toggling on and off with the timmer, so I can see that it is powering up, and receiving a shut down signal, but I cannot see where it is going wrong.

Any ideas how I might approach isolating the issue?

Thanks!

Picture for reference:

import machine
import time
from machine import Pin, ADC
import network

# Import MQTT with error handling
try:
    from umqtt.simple import MQTTClient
except ImportError:
    import mip
    mip.install("umqtt.simple")
    from umqtt.simple import MQTTClient

from config import *

# Initialize hardware
led = Pin('LED', Pin.OUT)
csm_sensor = ADC(CSM_PIN)
timer = machine.Timer()

def blink(timer):
    """Toggle LED state periodically"""
    led.toggle()

def read_CSM():
    """Read and return the CSM sensor value"""
    try:
        return csm_sensor.read_u16()
    except Exception as e:
        print(f"Sensor read error: {e}")
        return None

class MQTTHandler:
    def __init__(self, broker, port, client_id):
        self.broker = broker
        self.port = port
        self.client_id = client_id
        self.client = None
    
    def connect(self):
        """Connect to MQTT broker with retry logic"""
        try:
            self.client = MQTTClient(self.client_id, self.broker, port=self.port)
            self.client.connect()
            return True
        except Exception as e:
            print(f"MQTT connection failed: {e}")
            return False
    
    def send_message(self, topic, message):
        """Send message to MQTT topic with reconnection logic"""
        try:
            if not self.client:
                if not self.connect():
                    return False
            self.client.publish(topic, str(message).encode())
            return True
        except Exception as e:
            print(f"Failed to send MQTT message: {e}")
            self.client = None  # Force reconnection on next attempt
            return False

class WiFiManager:
    def __init__(self, ssid, password):
        self.ssid = ssid
        self.password = password
        self.wlan = network.WLAN(network.STA_IF)
    
    def connect(self, max_wait=10):
        """Connect to WiFi with timeout"""
        print("Connecting to WiFi...")
        self.wlan.active(True)
        self.wlan.connect(self.ssid, self.password)
        
        while not self.wlan.isconnected() and max_wait > 0:
            time.sleep(1)
            max_wait -= 1
            print("Waiting for connection...")
        
        if not self.wlan.isconnected():
            raise RuntimeError("WiFi connection failed")
        
        print("WiFi connected:", self.wlan.ifconfig())
    
    def ensure_connected(self):
        """Check and restore WiFi connection if needed"""
        if not self.wlan.isconnected():
            try:
                self.connect()
                return True
            except Exception as e:
                print(f"WiFi reconnection failed: {e}")
                return False
        return True

def BOOTUP():
    """Shutdown the device"""
    # print("Shutting down...")
    # Initialize shutdown pin
    shutdown_pin = Pin(SHUTDOWN_PIN, Pin.OUT)
    shutdown_pin.value(0)  # Set pin low
    

    

def main():
    led.on() # Turn off LED
    DONE = Pin(22, Pin.OUT) # setting this pin High will remove power, and wait for the next interval

    # Initialize components
    wifi = WiFiManager(SSID, PASSWORD)
    mqtt = MQTTHandler(MQTT_BROKER, MQTT_PORT, CLIENT_ID)
    
    # Initial connections
    try:
        wifi.connect()
        mqtt.connect()
    except Exception as e:
        print(f"Startup error: {e}")
        machine.reset()  # Reset device on startup failure
    
    print("Entering main loop...")
    try:
        # Read sensor and send data
        reading = read_CSM()
        if reading is not None:
            mqtt.send_message(TOPIC, reading)
        
    except Exception as e:
        print(f"Loop error: {e}")

    # Shutdown
    print("Shutting down...")
    # Initialize shutdown pin
    led.off()
    DONE.on()  # Set pin high to trigger shutdown

if __name__ == "__main__":
    main()

NOTE: Config.py is a seperate file with the Wifi details and pin locations etc, omitted for privacy.

1 Like

Hi Cameron,

When working on battery powered projects I like to use an OLED or blinking the LED x amount of times to indicate when something is happening,

Removing the machine.reset() might also help figure out where the problem is.

Good idea!

I don’t want to mess around with adding an extra OLED if I can avoid it, but I’ll start beeping my LEDs out for progress

2 Likes

Hi @Cameron165290

Another thing that you could do is to run the example code from the Makerverse Nano Power Timer HAT for Raspberry Pi Pico page, if you get that working with no issues you can be safe to assume that the timer hat is fine, I would then start introducing other aspects of your project until you get to a point where it is no longer functioning as anticipated to narrow down where the issue is.

1 Like

Satisfying update:

I implemented the advice from both of you. Plugged in a SSD1306 and rebuilt the project from the demo script, and then the problem never returned.

I think the problem was that for reasons unknown, the Pico can connect to the wifi faster when it is on USB power than on battery power. So my 6 second power cycling was not enough time for it to connect before the timer reset… I only spotted this thanks to printing out to the OLED when it connected, so thanks for the advice @Liam120347

in case any future readers find it convenient, here is the new code

from machine import Pin, ADC
import time
import network

from config import *

# Import MQTT with error handling
try:
    from umqtt.simple import MQTTClient
except ImportError:
    import mip
    mip.install("umqtt.simple")
    from umqtt.simple import MQTTClient


DEBUG = True
if DEBUG:
    from PiicoDev_SSD1306 import *
    display = create_PiicoDev_SSD1306()

        
class debugOLED():
    def __init__(self):
        self.display = create_PiicoDev_SSD1306()
    def printADC(self, adc):
        self.display.fill(0)
        self.display.text('ADC Reading:',0,0,1)
        self.display.text(str(adc),0,10,1)
        self.display.show()
    def text(self, text):
        self.display.fill(0)
        self.display.text(text, 0, 0, 1)
        self.display.show()
    def showWifi(self, ip):
        self.display.fill(0)
        self.display.text("WiFi connected:", 0, 0, 1)
        self.display.text(ip[0], 0, 10, 1)
        self.display.text(ip[1], 0, 20, 1)
        self.display.text(ip[2], 0, 30, 1)
        self.display.text(ip[3], 0, 40, 1)
        self.display.show()


class WiFiManager:
    def __init__(self, ssid, password, debugTerminal = None):
        self.ssid = ssid
        self.password = password
        self.wlan = network.WLAN(network.STA_IF)
        if debugTerminal == None:
            self.debug = False
            self.debugTerminal = None
        else:
            self.debug = True
            self.debugTerminal = debugTerminal
    
    def connect(self, max_wait=10):
        """Connect to WiFi with timeout"""
        print("Connecting to WiFi...")
        self.wlan.active(True)
        self.wlan.connect(self.ssid, self.password)
        
        while not self.wlan.isconnected() and max_wait > 0:
            time.sleep(1)
            max_wait -= 1
            if self.debug:
                self.debugTerminal.text("Waiting for connection...")
        
        if not self.wlan.isconnected():
            if self.debug:
                self.debugTerminal.text("WiFi connection failed")
            raise RuntimeError("WiFi connection failed")
        
        if self.debug:
            self.debugTerminal.showWifi(self.wlan.ifconfig())
        print("WiFi connected:", self.wlan.ifconfig())
        
        # print("WiFi connected:", self.wlan.ifconfig())
    
    def ensure_connected(self):
        """Check and restore WiFi connection if needed"""
        if not self.wlan.isconnected():
            try:
                self.connect()
                return True
            except Exception as e:
                if self.debug:
                    self.debugTerminal.text(f"WiFi reconnection failed: {e}")
                print(f"WiFi reconnection failed: {e}")
                return False
        return True
        
class MQTTHandler:
    def __init__(self, broker, port, client_id, debugTerminal = None):
        self.broker = broker
        self.port = port
        self.client_id = client_id
        self.client = None
        if debugTerminal == None:
            self.debug = False
            self.debugTerminal = None
        else:
            self.debug = True
            self.debugTerminal = debugTerminal
    
    def connect(self):
        """Connect to MQTT broker with retry logic"""
        try:
            self.client = MQTTClient(self.client_id, self.broker, port=self.port)
            self.client.connect()
            return True
        except Exception as e:
            print(f"MQTT connection failed: {e}")
            if self.debug:
                self.debugTerminal.text(f"MQTT connection failed: {e}")
            return False
    
    def send_message(self, topic, message):
        """Send message to MQTT topic with reconnection logic"""
        try:
            if not self.client:
                if not self.connect():
                    return False
            self.client.publish(topic, str(message).encode())
            return True
        except Exception as e:
            print(f"Failed to send MQTT message: {e}")
            if self.debug:
                self.debugTerminal.text(f"Failed to send MQTT message: {e}")
            self.client = None  # Force reconnection on next attempt
            return False

class LED_Manager:
    def __init__(self, pin):
        self.pin = Pin(pin, Pin.OUT)
        self.pin.on()
    def on(self):
        self.pin.on()
    def off(self):
        self.pin.off()
    def toggle(self):
        self.pin.toggle()
    

def main():
    LED = LED_Manager('LED') # Pin 25
    DONE = Pin(22, Pin.OUT) # setting this pin High will remove power, and wait for the next interval

    debugScrn = debugOLED()
    debugScrn.text("Starting...")
    time.sleep(0.1)
    wifi = WiFiManager(SSID, PASSWORD, debugScrn)
    mqtt = MQTTHandler(MQTT_BROKER, MQTT_PORT, CLIENT_ID)
    try:
        wifi.connect()
        mqtt.connect()
    except Exception as e:
        if DEBUG:
            debugScrn.text(f"Error: {e}")

    time.sleep(0.5)
    adc = ADC(0) # Pin 26

    x = adc.read_u16()
    debugScrn.printADC(x)


    # Opening with "a" for appending
    with open("log.txt", "a") as logFile:
        logFile.write(str(x))
        logFile.write('\n') # New line
        logFile.flush() # Ensure data is written
        logFile.close() # Really ensure data is written
        
    mqtt.send_message(TOPIC, x)
    print("Message sent")
    if DEBUG:
        debugScrn.text("Message sent")
    
    
    #sleep for half a second so we can actually see the LED
    time.sleep(0.5)
    if DEBUG:
        debugScrn.text("Shutting down...")
        
    LED.off() # Turn off LED
    DONE.on() # Assert DONE signal; powers down Pico
    
main()
1 Like

Hey @Cameron165290

Great to hear that you got it sorted and thankyou for posting an update on your code it should give someone else in the future an idea as to how they can get around a similar issue in the future!