KeyNoType
Less Strokes, More Words.
Less Strokes, More Words.
Overview:
This project uses and ESP32 and 16 buttons to create a tiny stenographic keyboard that outputs words efficiently based on most used English words stored in an external flash.
Description:
We store hundreds of common english words in the I2C EEPROM, and program ESP32 to handle switch debouncing.
Features:
18 switches, 3 LED indicators, and common switch layout
ESP32S2 for good interrupt support and large GPIO pin count
Extreme Responsiveness / Soft Realtime through FreeRTOS
Arduino Core for Compatibility
Supports over 400 possible words!
Featured Code Snippets:
void ARDUINO_ISR_ATTR ButtonsBase::buttonInterrupt(void *pinMetadata) {
auto &pinMetadataRef = *static_cast<PinMetadata *>(pinMetadata);
detachInterrupt(pinMetadataRef.pin);
auto &self = *pinMetadataRef.buttons;
if (pinMetadataRef.falling) {
self.pressedPins->set(pinMetadataRef.pos);
} else {
self.toDelete->set(pinMetadataRef.pos);
if (*self.pressedPins == *self.toDelete) {
timerStop(self.timer);
self.alarmed = false;
self.sendBitset(&self);
self.pressedPins->reset();
self.toDelete->reset();
} else {
timerRestart(self.timer);
if (!self.alarmed) {
self.alarmed = true;
timerStart(self.timer);
}
}
}
pinMetadataRef.falling = !pinMetadataRef.falling;
esp_timer_start_once(pinMetadataRef.timer, BOUNCE_DELAY);
}
template <size_t size>
void ARDUINO_ISR_ATTR Bitset<size>::set_impl(volatile void *num, std::size_t pos, bool value) {
auto &numRef = *static_cast<volatile num_t *>(num);
using oneType = std::remove_extent_t<num_t>;
auto const state = taskENTER_CRITICAL_FROM_ISR();
if constexpr (words == 0) {
if (value) {
numRef |= oneType{1U} << pos;
} else {
numRef &= ~(oneType{1U} << pos);
}
} else {
auto const arrPos = pos / wordSize;
auto const bitPos = pos % wordSize;
auto &elemRef = numRef[arrPos];
if (value) {
elemRef |= oneType{1U} << bitPos;
} else {
elemRef &= ~(oneType{1U} << bitPos);
}
}
taskEXIT_CRITICAL_FROM_ISR(state);
}
template <std::ranges::sized_range Range, std::size_t size>
requires PinDataType<size>
void ARDUINO_ISR_ATTR Buttons<Range, size>::sendBitset(void *selfPtr) {
auto &self = *static_cast<Buttons<Range, size>*>(selfPtr);
auto xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(self.xButtonsQueue, self.pressedPins, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
template <std::ranges::sized_range Range, std::size_t size>
requires PinDataType<size>
void Keyboard<Range, size>::vTaskConsumer(void *pvParameters) {
auto &self = *static_cast<Keyboard<Range, size>*>(pvParameters);
auto recievedBitset = Bitset<size>{};
for (;;) {
if (xQueueReceive(self.xBitsetQueue, &recievedBitset, portMAX_DELAY) == pdPASS) {
auto const retrievedString = self.getStringFromBitset(recievedBitset.getSTDBitset());
if (retrievedString) {
self.keyboard.print(retrievedString);
}
}
}
}
Completed Schematic with USB-C, 18 Switches, 3 LEDs, 3.3V LDO, EEPROM, and ESP32
Completed Layout (Front)
PCB without Components (Front)
PCB without Components (Back)
PCB with Components (Front)
PCB with Components (Back)
Bill of Materials (BOM)
Since we cannot program on the board, we can't check if the code works!
Issues with the board revolve around the USB-C receptacle not being soldered on properly.
The problem could have been avoided if this component was tested prior to soldering the rest of the components
We found out it was not soldered properly after all the soldering was complete.
The board showed proper continuity on all other components
Ordering a stencil would have saved lots of time!
If no stencil: Soldering USB-C -> smallest passive components -> LDO -> EEPROM -> ESP32
Use plenty of flux, have a soldering wick and tweezers at hand.