Evan McCord Morse

projects

The WHALE platform

Three and a half years of NSF EPSCoR funded research (the "Wisdom" team) into open technology for remote environmental sensing and infrastructure monitoring. The result is a complete open firmware platform for the RP2040: solar-powered sensor nodes on sodium-ion cells, RFM69 packet radio to a cellular gateway, MQTT out the far end. Deployed networks have 100% data retention, including through a gateway failure.

Everything is open source and open hardware (CERN-OHL-W), designed so other researchers can reproduce it from commonly available parts. There is no vendor SDK anywhere in the stack. Startup code, linker scripts, register definitions, build system, HAL, drivers, and transport are all first-party Zig, grown bottom-up as independent libraries. Each library has its own tests, its own documentation, and its own reason to exist.

munch codeberg.org/e-mo/munch
A lexer and parser toolkit for Zig. Grammars are plain Zig types and comptime declarations; parse results are ordinary structs, unions, and slices. 172 tests.
fig codeberg.org/e-mo/fig-zig
A small configuration format. Define the schema as a Zig type and fig parses a .fig file into that type, with typed errors and rendered file:line:col diagnostics. 305 tests.
jig codeberg.org/e-mo/jig
Generates thin Zig register-access modules from fig hardware manifests, wired directly into the build graph. 28 manifests describe the full RP2040 peripheral set. Hardware as data, not hand-maintained headers.
rp2040-build codeberg.org/e-mo/rp2040-build
Bare-metal build support for the RP2040: startup code, boot stage 2 CRC, vector tables, UF2 conversion, and firmware build APIs for the Zig build system.
rp2040-drivers codeberg.org/e-mo/rp2040-drivers
The HAL: 13 MCU peripheral modules and 8 device drivers (RFM69 radio, SIM7080G cellular modem, ultrasonic and temperature sensors, accelerometer, EEPROM, RTC, battery charger), with explicit ownership contracts. 324 tests.
loom codeberg.org/e-mo/loom
Reusable protocol frameworks. Currently a USB device stack with CDC ACM, split so loom owns protocol state and the HAL owns registers. The framework fell out of a driver the moment the seam became real.
rp2040-rfm69-ota codeberg.org/e-mo/rp2040-rfm69-ota
Over-the-air firmware updates across the RFM69 radio link: receiver boot firmware, transmitter relay, and a host REPL. 101 tests.
whale-build codeberg.org/WISDOM-Research/whale-build
The board-level platform for the Whale v7, including RUDP, a reliable transport for lossy low-power radio links: selective-repeat fragmentation, duplicate suppression across reboots, EEPROM-durable acknowledgments, and over-the-air wake of a dormant node in a measured 19 ms. Verified byte-for-byte at 40% injected bidirectional loss. Every number in its documentation names the bench firmware that produced it. 244 tests.
whale-examples codeberg.org/WISDOM-Research/whale-examples
26 standalone, documented example firmwares. Reference material that doubles as a standing audit of the platform's API ergonomics.
wisdom_node_hardware codeberg.org/WISDOM-Research/wisdom_node_hardware
Five revisions of custom sensor-node PCBs in KiCad, plus a 915 MHz Yagi antenna. The beautiful, hand-routed work of the Wisdom team's hardware engineer, designed in weekly collaboration. CERN-OHL-W.

This work won the Outstanding Student Presentation Award at the AGU Fall Meeting 2025.


Wine

gitlab.winehq.org/wine/wine

Contributor to Wine, the compatibility layer that runs Windows software on Linux. Thirty years of accumulated small problems, each one some specific program depending on some specific behavior, fixed one conformance test at a time. It is the largest grown system I have worked in and my favorite kind of codebase.

!11763 ntdll: Return the object manager status from NtQueryFullAttributesFile merged
NtQueryFullAttributesFile returned an internal status, STATUS_BAD_DEVICE_TYPE, for names that are not filesystem paths, where Windows returns STATUS_OBJECT_NAME_INVALID. The fix asks the server to open the object and reports its status, the same lookup NtCreateFile already performs for those names.
!11788 server: Rewind the directory stream when checking if it is empty merged
The directory emptiness check read through a dup(), which shares the file offset, so POSIX leaves it unspecified whether recent entries appear. A non-empty directory could be reported as empty. Filesystem dependent, so CI never saw it: on btrfs the ntdll:file suite went from 54 failures and a crash on the second run against the same prefix, to 1 failure and stable. Reproducible outside Wine in about thirty lines. The fix is one call, in the one ordering the standard actually guarantees.
!11826 ntdll: Require SYNCHRONIZE access for the synchronous I/O options open
A todo_wine carried since 2010. Windows rejects FILE_SYNCHRONOUS_IO_NONALERT without SYNCHRONIZE in the access mask; Wine returned a path error instead and validated the combination nowhere. The marker survived sixteen years partly because the original test's subject line blamed the filename, which is not what causes the status. I measured the real behavior on TestBot (the access mask outranks the path, generic rights do not satisfy the requirement, neither does MAXIMUM_ALLOWED), which decided where the check belongs. Three commits across seven files: the fix cannot land alone, so an audit of all 252 call sites found ten passing a synchronous option without SYNCHRONIZE, fixed first, and the check added after with zero breakage.

limpet

codeberg.org/e-mo/limpet

An IO monitor that holds on. A tmux-style client/server tool in Go that watches many serial (USB CDC) and MQTT streams at once, survives disconnects and reconnects, names every stream uniformly (type:watch@unit), and formats output through pluggable formatters. Built as daily-driver tooling for sensor-network development. About 12k lines, with tests.


Fortran vs. C: an ECS performance study

The core of an entity component system, implemented twice: once in C, once in modern Fortran, a language I learned for the project. The goal was to test Fortran's claims of superior compiler auto-vectorization. Across hundreds of thousands of transformations of large data vectors, repeated thousands of times with randomized data, the Fortran implementation was consistently 10-15% faster than the C.