• Power to the people.

    shane.snipe03/29/2025 at 13:20 0 comments

    I noticed an annoying characteristic of the device.  The device will not run on battery unless the battery has been charged a little bit. Even then, it will shut off after about 30 seconds due to the low power inactivity shut off function in the IP-5306.  This is why power banks shut off on makers trying to run microcontrollers from them.

    In my conversations with Grok, it suggested two ways to deal with this. One was to run a 1K resistor from Pin 5 (Key) of the IP-5306 to a GPIO pin and pull the pin down every 20 seconds or so to simulate a button press.  

    Bodge resistor #1

    Well I ran a 1.2K resistor to GPIO3 resistor and with the following code, my board will start working after plugging in the Supermini board and then unplugging it. It pulled battery power and ran. It also did not shut off.  Ran it for about an hour and saw a 0.2V drop in the battery. 

    from machine import Pin
    import time
    
    # GPIO pin to control the KEY pin of the IP5306 (GPIO 3)
    key_pin = Pin(3, Pin.OUT)
    
    # Function to simulate a short press on the KEY pin (50ms to 2s)
    def simulate_short_press():
        print("Simulating short press on KEY pin to enable/keep boost output on...")
        key_pin.value(0)  # Pull KEY pin low
        time.sleep(0.1)   # Hold for 100ms
        key_pin.value(1)  # Release the KEY pin
        print("Short press completed.")
    
    # Initialize the KEY pin to high (inactive)
    key_pin.value(1)
    
    # Wait a moment to ensure the ESP32C6 is fully powered
    time.sleep(1)
    
    # Simulate an initial short press to enable the boost output
    simulate_short_press()
    
    # Main loop to periodically simulate short presses and keep the system running
    while True:
        # Simulate a short press every 20 seconds to ensure the boost output stays on
        simulate_short_press()
        print("System is running...")
        time.sleep(20)  # Repeat every 20 seconds

    I also tried the I2C pins input that should have worked. However, the only I2C device that came up when I did a sweep was the accelerometer. I tried swapping the SCL and SDA pins to see if I got the order reversed by to no avail. 

    Well I got some learnings!  Chat GPT suggested that the I2C part might have to be turned on. It wrote a little skit to simulated the button press on the Key pin and then scanned for I2C addresses and all of a sudden we got an 0x75 address.  Here was the code. 

    import machine, time
    
    # Simulate a button press on the KEY pin via GPIO3.
    key_pin = machine.Pin(3, machine.Pin.OUT)
    # Ensure KEY is high by default (if not, adjust logic accordingly)
    key_pin.value(1)
    time.sleep_ms(10)  # a brief pause
    
    # Pull KEY low to simulate a short press (active low assumed)
    key_pin.value(0)
    time.sleep_ms(2000)  # hold low for 50ms (adjust as needed)
    key_pin.value(1)
    time.sleep_ms(50)
    
    # Optionally, set the pin back to input mode
    key_pin.init(machine.Pin.IN)
    
    i2c = machine.I2C(0, scl=machine.Pin(19), sda=machine.Pin(20), freq=400000)
    devices = i2c.scan()
    print("I2C freaky devices found:", devices)

    So the next job was to manipulate some registers to limit the current to 200 mA for charging because I am using a 200 mAh battery and to remove the low current shut off "feature". This is called Boost always on.  I needed to modify the time to pull the Key low from 50ms to 500ms to make it work.  

    import machine
    import time
    
    # --- 1. Simulate KEY press on GPIO3 to enable I2C mode on the IP5306 ---
    key_pin = machine.Pin(3, machine.Pin.OUT)
    key_pin.value(1)             # Ensure KEY is high by default.
    time.sleep_ms(10)            # Brief pause.
    key_pin.value(0)             # Pull KEY low to simulate a press.
    time.sleep_ms(500)            # Hold low for 50ms (adjust if needed).
    key_pin.value(1)             # Release KEY.
    time.sleep_ms(50)
    key_pin.init(machine.Pin.IN) # Return KEY pin to input.
    
    # --- 2. Initialize I2C on GPIO19 (SCL) and GPIO20 (SDA) at 400KHz ---
    i2c = machine.I2C(0, scl=machine.Pin(19), sda=machine.Pin(20), freq=400000)
    print("I2C devices found:", i2c.scan())  # Expect: [14, 117]
    
    # --- 3. Define the IP5306 class with...
    Read more »

  • Using a KXTJ3-1057 2x2mm accelerometer using Grok 3 in Micropython

    shane.snipe03/08/2025 at 16:24 0 comments

    I created a board that had 3 functions:

    1) Charge a lipo battery and boost the output to 5V with a IP5306 chip.

    2) Provide an Audio amp to allow the ESP32-C6 to place files through I2S and out 3V of sound on a single channel.

    3) Receive the accelerometer input to moderate the sound being played.  The accelerometer is the KXTJ3-1057 3 axis accelerometer.

    Here is a 3D rendering of the board.

    And the backside with the ESP32-C6 Super Mini.

    This was the first time I used the castellated connections to surface mount the board by hand. I am finding the soldering is much quicker than the 40 solder connections needed if I used headers and the package is much sleeker. It also makes the rounding of the connections on the top easier. Lastly, as you can see this is version 5.  I finally decided to be more disciplined about my through holes and I made this version breadboard compatible. It is a little wide so it barely fits, but proving out the board is easier than spaghetti wiring.

    So I got the board and within an hour I had the LED's going and I also got the accelerometer responding.  On my last version I had used attached a ESP32-C3 Supermini and the SDA and SCL pins were overlapping with the internal SPI pins.  The accelerometer function was very inconsistent. This time is was smooth as butter. I fired up Grok 3 and got the following code that worked on the first time. I just put a screen shot of the schematic in and this is the code that was output. 

    import machine
    import time
    import struct
    
    # Define I2C pins for the KXTJ3-1057
    sda_pin = machine.Pin(20)  # SDA on GPIO20
    scl_pin = machine.Pin(19)  # SCL on GPIO19
    i2c = machine.I2C(0, scl=scl_pin, sda=sda_pin, freq=400000)  # Use I2C0, 400kHz frequency
    
    # Define the KXTJ3-1057 I2C address (default is 0x0F, but verify with datasheet or scan)
    KXTJ3_ADDR = 0x0E
    
    # Register addresses (approximate, adjust based on KXTJ3-1057 datasheet)
    # Common registers for accelerometers like KXTJ3-1057:
    XOUT_L = 0x06  # Low byte of X-axis data
    XOUT_H = 0x07  # High byte of X-axis data
    YOUT_L = 0x08  # Low byte of Y-axis data
    YOUT_H = 0x09  # High byte of Y-axis data
    ZOUT_L = 0x0A  # Low byte of Z-axis data
    ZOUT_H = 0x0B  # High byte of Z-axis data
    CTRL_REG1 = 0x1B  # Control register 1 (to enable measurement mode)
    
    # Function to initialize the KXTJ3-1057
    def init_kxtj3():
        try:
            # Scan I2C bus to verify the device is present
            devices = i2c.scan()
            if len(devices) == 0:
                print("No I2C devices found!")
                return False
            print("I2C devices found at addresses:", [hex(addr) for addr in devices])
    
            # Check if KXTJ3-1057 is at the expected address
            if KXTJ3_ADDR not in devices:
                print(f"KXTJ3-1057 not found at address 0x{KXTJ3_ADDR:02X}")
                return False
    
            # Configure the KXTJ3-1057 (enable measurement mode, set range, etc.)
            # Example: Enable 2g range and 50Hz output data rate (adjust based on datasheet)
            i2c.writeto_mem(KXTJ3_ADDR, CTRL_REG1, bytes([0xC0]))  # Example value, adjust as needed
            print("KXTJ3-1057 initialized successfully")
            return True
    
        except Exception as e:
            print("Error initializing KXTJ3-1057:", e)
            return False
    
    # Function to read acceleration data (X, Y, Z axes)
    def read_acceleration():
        try:
            # Read 6 bytes (2 bytes each for X, Y, Z)
            data = i2c.readfrom_mem(KXTJ3_ADDR, XOUT_L, 6)
            
            # Unpack 16-bit values (little-endian, signed)
            x = struct.unpack('<h', data[0:2])[0]  # X-axis
            y = struct.unpack('<h', data[2:4])[0]  # Y-axis
            z = struct.unpack('<h', data[4:6])[0]  # Z-axis
            
            return x, y, z
    
        except Exception as e:
            print("Error reading acceleration data:", e)
            return None, None, None
    
    # Main loop to read and print accelerometer data
    if init_kxtj3():
        while True:
            x, y, z = read_acceleration()
            if x is not None and y is not None and z is not None:
                print(f"Acceleration - X: {x}, Y: {y}, Z: {z}")
            else:
                print("Failed to read acceleration data")
            time.sleep(0.1)  # Read every 100ms (adjust as needed)
    else:
        print("Initialization failed. Check connections...
    Read more »

  • Littlefs saving files with Thonny MicroPython and ESP32-C6

    shane.snipe03/02/2025 at 16:50 0 comments

    Most Esp32 modules have SPIFFS. This is is usually achieved with and EEPROM that is external to the ESP32 chip. However, some of the smaller packages like the 32 pin C3 or C6 have the flash built into the chip. That is why when you look at the chip there is no shield over it, or at least that is my understanding of it.

    The problem with the ESP32-C3 supermini was that the SPI memory had dedicated GPIO for it that were actually broken out on the board. The ESP32-C6 has more pins, so even though the SuperMini has 20 pins broken out to the headers the internal SPI pins are not exposed and overlapping.


    I tried to go straight for the Micropython sound creation using I2C. I was able to do this with the ESP32-C3 and a previous version of this board. In fact, I was able to get ChatGPT to write code to play Happy Birthday. However, both ChatGPT and Grok failed me on this one. Looks like the ESP32-C6 driver for Micropython is not updated to run I2S.  Grok offer to take me through recompiling the driver to include it but I will try some other options first.

    Lets first confirm that the LittleFS (Little file system) is working. The ESP32-C6 uses the ESP32-C6H4 chip. It is the 32 pin version that has the memory on board, instead of in a separate flash chip. I asked Grok to write some code to provide that it is working and this is what I go. 

    import machine
    import os
    import time
    import uos
    
    # Define pins for the onboard switch and red LED
    button_pin = machine.Pin(8, machine.Pin.IN, machine.Pin.PULL_UP)  # Onboard switch on GPIO8, with pull-up
    led_pin = machine.Pin(7, machine.Pin.OUT)  # Red LED on GPIO7
    
    # Function to initialize and mount LittleFS
    def init_littlefs():
        try:
            # Check if LittleFS is already formatted
            vfs = uos.VfsLfs2
            block_size = 4096  # Typical block size for ESP32-C6 flash
            block_count = 32   # Number of blocks (adjust based on flash size, e.g., 128KB)
    
            # Format and mount LittleFS
            print("Formatting LittleFS...")
            uos.VfsLfs2.mkfs(block_size, block_count)
            uos.mount(uos.VfsLfs2(block_size, block_count), "/")
            print("LittleFS mounted successfully")
        except Exception as e:
            print("Error initializing LittleFS:", e)
            try:
                # If already formatted, just mount
                uos.mount(uos.VfsLfs2(block_size, block_count), "/")
                print("LittleFS mounted successfully (already formatted)")
            except Exception as e:
                print("Failed to mount LittleFS:", e)
                return False
        return True
    
    # Function to write data to a file
    def write_file(filename, data):
        try:
            with open(filename, "w") as f:
                f.write(data)
            print(f"Wrote data to {filename}")
        except Exception as e:
            print(f"Error writing to {filename}:", e)
    
    # Function to read data from a file
    def read_file(filename):
        try:
            with open(filename, "r") as f:
                data = f.read()
            print(f"Read from {filename}: {data}")
            return data
        except Exception as e:
            print(f"Error reading from {filename}:", e)
            return None
    
    # Main demo function
    def littlefs_demo():
        # Initialize LittleFS
        if not init_littlefs():
            print("Cannot proceed with demo due to LittleFS failure.")
            return
    
        # Sample data to write
        sample_data = "Hello, ESP32-C6 LittleFS Demo!\n"
        filename = "/demo.txt"
    
        # Write to file
        write_file(filename, sample_data)
    
        # Read from file
        read_file(filename)
    
        # List directory contents
        print("Directory contents:", os.listdir("/"))
    
    # Main loop to trigger the demo using the onboard switch
    print("Press the onboard switch (GPIO8) to run the LittleFS demo...")
    while True:
        if button_pin.value() == 0:  # Button pressed (low when using pull-up)
            print("Button pressed - Running LittleFS demo")
            led_pin.value(1)  # Turn red LED ON (HIGH)
            littlefs_demo()
            led_pin.value(0)  # Turn red LED OFF (LOW)
            time.sleep(1)  # Debounce delay to avoid multiple triggers
        time.sleep(0.01)  # Small delay to prevent excessive CPU usage

     We got an error this time.

    MPY: soft reboot
    Press the onboard switch (GPIO8) to run the LittleFS demo...
    Button pressed - Running LittleFS demo
    Formatting LittleFS...
    Error initializing...

    Read more »

  • Getting to Blinky with Thonny MicroPython and the ESP32-C6

    shane.snipe03/02/2025 at 16:19 0 comments

    I have made the choice to start with Micropython because the burn and learn cycle is so much shorter than ESP-IDF or Arduino. I was concerned that the ESP32-C6 support would not be there yet in Micropython but it is looking good so far.

    I started with wanting to blink the LED. The latest ChatGPT and GROK models allow you to attach a picture and they are pretty good an pulling information from it.  I have been attaching schematics and with good success and I save on the prompting I have to do. I wanted to try it with this round and attached the Pinout and asked for a blinking LED Micropython script.

    I have been using Thonny for my Micropython. I connected the ESP32-C6 and under Tools->Options it recognized the USB port.. 


    Clicking on the install link, I had the ESP32-C6 option. I picked the Espressif version and it installed.

    I find Thonny a little cludgy to program. You may have to do some combination of pressing the stop program and the reset on the board. If a program is running it will not allow you to overwrite the main.py script. 

    ChatGPT provided working code but did not recognize the right pin for the LED. It  suggested the builtin LED was on GPIO2 but it was on 15. 

     So I copied the LED code from ChaptGPT as follows:

    from machine import Pin
    import time
    
    # Create a Pin object for the onboard LED (usually GPIO 2 for ESP32 boards)
    led = Pin(15, Pin.OUT)
    
    while True:
        led.value(1)  # Turn the LED on
        time.sleep(1)  # Wait for 1 second
        led.value(0)  # Turn the LED off
        time.sleep(1)  # Wait for 1 second

    Saved it to MicroPython Device as main.py and pressed the play button. Blue light is blinking in less then 5 minutes.

    I noticed there was another LED on the module with 4 pads and a 8 designator.

    The pinout shows this LED to be a WS2812 (Neopixel) and I asked GROK to write me the code to run it. 

    import machine
    import neopixel
    import time
    
    # Define the pin for the WS2812 LED (GPIO8)
    led_pin = machine.Pin(8)
    # Define the number of NeoPixels (in this case, 1 for the onboard LED)
    num_pixels = 1
    
    # Create a NeoPixel object
    np = neopixel.NeoPixel(led_pin, num_pixels)
    
    # Function to set the LED to a specific color (RGB values, 0-255 each)
    def set_color(red, green, blue):
        np[0] = (red, green, blue)  # Set the color for the first (and only) pixel
        np.write()  # Update the LED
    
    # Function to cycle through colors (example animation)
    def cycle_colors():
        while True:
            # Red
            set_color(255, 0, 0)
            print("Red")
            time.sleep(1)
            
            # Green
            set_color(0, 255, 0)
            print("Green")
            time.sleep(1)
            
            # Blue
            set_color(0, 0, 255)
            print("Blue")
            time.sleep(1)
            
            # White
            set_color(255, 255, 255)
            print("White")
            time.sleep(1)
            
            # Off (black)
            set_color(0, 0, 0)
            print("Off")
            time.sleep(1)
    
    # Run the color cycle
    cycle_colors()
    
    

    30 seconds later I have updated the code in the main.py file on the ESP32-C6 and my board lights up like a Christmas tree. 

    So, if the LLM knows what pin and what type of LED, this is all super smooth.

    I unplugged in and connected it to a Voltaic power bank and it blinks away happily.

    Note some power banks will shut down because the MCU does not drawing enough power.

    The Voltaic will continue to work.