Can I make the button more pressable?

This article is part of a series.

So this project needs it’s button situation improved. The little boot button that I’m pressing with a bamboo skewer isn’t going to cut it.

Also I want to make the HTTP call “onPress”, not a repeat timer nor “while pressed”. I need to add that feature to MomentaryInput.

For state of the code at the end of these notes see:

New Parts

So I bought some parts from Adafruit. I generally buy 2 of each thing because accidents happen.

Desired Button:

Fallback Button (only 1x because its a fall back anyway):

If decide to take on battery power:

Short USB C cable:

3 sets of headers went in to assembling the prototype, each a different type.

These all need to be cut down to a length of 7, so if bulk sourcing getting them precut is easier.

Not gonna lie, my husband todbot zipped these on for me since he’s in the middle of a soldering palooza. (Adafruit tutorial on soldering headers and my old play list on soldering in general) I traded firmware-loading duties later in the week. Hero shot for my hero!

A small tower of three stories. The bottom level being the Xiao, which one can tell from the USB-C port, the middle one the battery BFF, and the top of the spire is a white key cap.

To test the new hardware, move the button pin:

//new button on pin 2 instead of on the boot pin (9)
  guard var button = MomentaryInput(2) else {
    fatalError("Difficulty setting up button.")
  }

return it to a simple switch

if button.isActive {
  led.turnOn()
} else {
  led.turnOff()
}
delay(50);

Why the delay? because without it the esp-if monitor gets a little overwhelmed.

Please enable CONFIG_ESP_SYSTEM_USE_FRAME_POINTER option to have a full backtrace.
E (24238) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (24238) task_wdt:  - IDLE (CPU 0)
E (24238) task_wdt: Tasks currently running:
E (24238) task_wdt: CPU 0: main
E (24238) task_wdt: Print CPU 0 (current core) registers
Core  0 register dump:
MEPC    : 0x4200b03a  RA      : 0x4200b03a  SP      : 0x40811000  GP      : 0x4080bfa4  
--- 0x4200b03a: $e10__idf_main0B0yyF at ??:?
--- 0x4200b03a: $e10__idf_main0B0yyF at ??:?
TP      : 0x40811060  T0      : 0x4200b088  T1      : 0x20000000  T2      : 0xffffffff  
--- 0x4200b088: $es5print_10terminatorys12StaticStringV_ADtF at ??:?
S0/FP   : 0x0000000f  S1      : 0x00000001  A0      : 0x00000000  A1      : 0x00000001  
A2      : 0x00000000  A3      : 0x00000004  A4      : 0x00008000  A5      : 0x60091000  
A6      : 0x00000001  A7      : 0x0000000a  S2      : 0x42020550  S3      : 0x00000000  
S4      : 0x00000000  S5      : 0x00000000  S6      : 0x00000000  S7      : 0x00000000  
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000  
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x00000000  T6      : 0x00000000  
MSTATUS : 0x00001889  MTVEC   : 0x40800001  MCAUSE  : 0xdeadc0de  MTVAL   : 0xdeadc0de  
--- 0x40800001: _vector_table at /Users/$USER/esp/esp-idf/components/riscv/vectors_intc.S:54
MHARTID : 0x00000000  

The New Code

Because modern buttons are so great, and because I’ve picked one with a real CLICK to it I am not going to worry about a software debounce yet. On this chip I could activate the Espressif provided glitch filter.

Added Inverse to GPIOLevel

extension GPIOLevel {
    var inverse:GPIOLevel {
        (self == .high) ? .high : .low
    }
}

Updates to MomentaryInput

Added a variable to the struct to store the last state, which will need to be initialized using that inverse value just created.

extension MomentaryInput {
    init?(_ pinNum: UInt32, activeLow:Bool = true, useInternalHardware:Bool = true) {
        self.pin = InputPin(pinNumber: pinNum, activeLow:activeLow, useInternalHardware: useInternalHardware)
        self.activeLevel = GPIOLevel(!activeLow)
        self.lastState = self.activeLevel.inverse
    }
}
    var lastState:GPIOLevel

    typealias OnActiveBehavior = () -> Void

    mutating func onActivate(do behavior:OnActiveBehavior) {
        let currentLevel = pin.readLevel()
        if currentLevel != lastState && currentLevel == activeLevel {
            behavior()
        }
        lastState = currentLevel
    }

Since my code will just be using the button for this one behavior it’s fine to do the pin read in the call. If I had a lot of behaviors on different button states I’d have to be careful not to do a new read for every method, but only once per while loop. Future versions of the code will have to decide if the setup will register listeners, if listeners get called explicitly in the loop, if the button itself will register behaviors to do after the poll… TBD.

On a chip like the ESP23C6, it would be even better to hook into their existing button interrupt types.

All of that is research for another day. Today is for the easy win.

back on Main.swift

  while true {
    button.onActivate { print("hello") }
    delay(50); 
  }

TADA! Thats it.

screen shot of a terminal window with the word hello printed repeatedly below green setup text from the esp-idf

Add the HTTP call back in…

Where I’ve left the code in the branch is with the Wifi and HTTP stuff all un commented again and the new while loop looks like:

  while true {

    button.onActivate { 
      let _ = exampleClient.fetch("/")
    }
    delay(50); //to help out the monitor
  }

Works a treat.

Summary

This was a fairly easy task to knock off the list. Especially with a soldering assist.

More things to do would be to bring the neo-pixel that came on the key cap BFF into the mix to signal different states of the send.

Theres a bit more server work to do, too.

And then both the button and the server have to decide what the secret handshake is going the be for this demo.

I guess this mean what the next post will be is a bit of a surprise… even to me!

This article is part of a series.