Andrew Kesterson

"Love God. Live Righteously. Die Well."
Source Code || LinkedIn
38 Posts 9 Categories 0 Tags
Categories
Books 14
Philosophy 9
Faith 7
Technology 17
Outdoors 1
Current-Events 7
Leadership 8
History 1
Liberal-Education 1
hot tags

# Devlog Entry - 30 May 2026

2026-05-30 Technology 1.7k words

## libakstdlib

Several improvements were driven into libakstdlib, my libc layer that wraps my platform’s native libc in libakerror.

- Added `aksl_atol`
- Added `aksl_atoll`
- Added `aksl_atoi`
- Fixed a bug in `aksl_sprintf`, it wasn’t using va_args properly, leading to segfault
- Added `aksl_atof`
- Added `aksl_realpath`, which takes a string that contains a filesystem path which may or may not be relative, and populates a destination string with the absolute path to that file reference

    akerr_ErrorContext AKERR_NOIGNORE *aksl_atoi(const char *nptr, int *dest);
    akerr_ErrorContext AKERR_NOIGNORE *aksl_atol(const char *nptr, long *dest);
    akerr_ErrorContext AKERR_NOIGNORE *aksl_atoll(const char *nptr, long long *dest);
    akerr_ErrorContext AKERR_NOIGNORE *aksl_atof(const char *nptr, double *dest);

    akerr_ErrorContext AKERR_NOIGNORE *aksl_realpath(const char *restrict path, char *restrict resolved_path);

## libakgl

I made several improvements to my C/SDL3 game library.

### Locking game state for thread safety

The SDL3 implementation is, by default, implemented with threaded events. For the akgl-test demonstration app I’m writing alongside `libakgl`, this eventually reared its ugly head as some nasty race conditions on the various `AKGL_HEAP` objects that are statically allocated and centrally managed. Consider this:

- Input processing happens during `SDL_AppEvent`
- Physics and rendering happens during `SDL_AppIterate`
- The player presses `ESC` to go back to the main menu to select a different demonstration
- Part of that process is that the `AKGL_HEAP_ACTORS` is reset
- While that is happening, the `SDL_AppIterate` fires from another thread. It goes to render actors that are in the process of being reset.
- KABOOM

To resolve this quickly, I added a `SDL_Mutex` object to the global `akgl_Game` object. Anything that modifies the core game state (like the `AKGL_HEAP` objects) calls `akgl_game_state_lock()` to lock the state, and `akgl_game_state_unlock()` when they are done. This resolved the race condition.

Right now this is being applied on some really large operations. It can probably be applied in a far more granular fashion. But for right now it works.

### Controllers

Added a method (`akgl_controller_open_gamepads()`) that automatically opens all connected gamepads at the time of `akgl_game_init()`.

- The user doesn’t have to call this, it’s done automatically
- You have to open a gamepad before you can receive events from it. So asking the player “Press any button on the controller” to figure out which controller they’re using doesn’t work until those gamepads are open. There may be consequences to opening gamepads we don’t need - I don’t know yet. If that comes up, it’s easy enough to close the ones we aren’t using.

### Pluggable rendering backends

At some point I am going to want to use the SDL3 GPU renderer as the primary renderer for my engine. Right now I’m using the Rendering API because it’s familiar, comfortable, and very simple for the 2D stuff I’m doing right now. But I need to reduce the coupling between the rendering API and the rest of the game logic. So I abstracted away the existing SDL_Renderer rendering backend into a new `akgl_RenderBackend` structure, and refactored everything to call the methods on that structure instead of touching the renderer directly.

    typedef struct akgl_RenderBackend {
        SDL_Renderer *sdl_renderer;
        akerr_ErrorContext AKERR_NOIGNORE *(*shutdown)(struct akgl_RenderBackend *self);
        akerr_ErrorContext AKERR_NOIGNORE *(*frame_start)(struct akgl_RenderBackend *self);
        akerr_ErrorContext AKERR_NOIGNORE *(*frame_end)(struct akgl_RenderBackend *self);
        akerr_ErrorContext AKERR_NOIGNORE *(*draw_texture)(struct akgl_RenderBackend *self, SDL_Texture *texture, SDL_FRect *src, SDL_FRect *dest, double angle, SDL_FPoint *center, SDL_FlipMode flip);
        akerr_ErrorContext AKERR_NOIGNORE *(*draw_mesh)(struct akgl_RenderBackend *self);
        akerr_ErrorContext AKERR_NOIGNORE *(*draw_world)(struct akgl_RenderBackend *self, akgl_Iterator *opflags);
    } akgl_RenderBackend;

The usage of this new abstraction is pretty simple:

    PASS(e, akgl_game_init());
    PASS(e, akgl_game_state_lock());
    PASS(e, akgl_registry_load_properties("assets/properties.json"));
    PASS(e, akgl_render_init2d(&renderer));
    PASS(e, akgl_game_state_unlock());

The `akgl_render_init2d` function sets up the renderer with a bunch of backend functions that know how to render the various on-screen parts of libakgl (like `akgl_Actor`, `akgl_Sprite`, `akgl_Tilemap`, etc) through the SDL Rendering API. All of this detail is transparent to theuser, and to the library author as well, because they just do things `renderer->draw_texture(&renderer)` and `renderer->draw_world(&renderer)`. The real detail of how that all gets done is tucked away in those functions.

Now `SDL_AppIterate` can look like this to draw the world and all the actors in it:

    PASS(e, renderer.frame_start(&renderer));
    PASS(e, akgl_game_update(NULL));
    PASS(e, renderer.frame_end(&renderer));

… because `akgl_game_update` calls `renderer->draw_world()` for you.

Now when I want to implement a GPU renderer (or any other kind of renderer, really - ASCII ncurses anyone?), all I have to do is write some new pluggable methods, write an initializer function like `akgl_render_init2d()` that populates a renderer structure, and we’re off to the races.

At least that’s the idea. We’ll see what happens when I implement SDL3 GPU at some point in the future.

## Pluggable physics systems

Following on the pluggable rendering system, I am at the point where I need to put in some amount of physics simulation before I can start making games with this thing. So I followed the same basic pattern I did with the rendering backend, and made a structure that holds a bunch of function pointers and other information

    typedef struct akgl_PhysicsBackend {
        akerr_ErrorContext AKERR_NOIGNORE *(*simulate)(struct akgl_PhysicsBackend *self, akgl_Iterator *opflags);
        akerr_ErrorContext AKERR_NOIGNORE *(*gravity)(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);
        akerr_ErrorContext AKERR_NOIGNORE *(*collide)(struct akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2);
        akerr_ErrorContext AKERR_NOIGNORE *(*move)(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt);

        double drag_x;
        double drag_y;
        double drag_z;
        double gravity_x;
        double gravity_y;
        double gravity_z;
        SDL_Time gravity_time;
        SDL_Time timer_gravity;

    } akgl_PhysicsBackend;

… and made it easy to initialize with the backend of my choice:

    PASS(e, akgl_game_init());
    PASS(e, akgl_game_state_lock());
    PASS(e, akgl_registry_load_properties("assets/properties.json"));
    PASS(e, akgl_physics_init_sidescroller(&physics));
    PASS(e, akgl_game_state_unlock());

… and now simulating physics in the world full of actors is as simple as calling one method inside of `SDL_AppIterate`:

    PASS(e, renderer.frame_start(&renderer));
    PASS(e, akgl_game_update(NULL));
    PASS(e, renderer.frame_end(&renderer));

… because `akgl_game_update()` calls the physics simulator for you:

    PASS(e, physics.simulate(&physics, NULL));

… and the physics backend takes care of calculating acceleration, velocity, movement, and (soon) collisions.

Collision detection isn’t there yet. It’s coming soon, with both other actors and world geometry.

### Tests and Documentation

This thing is basically completely undocumented. That’s okay, because I’m only really writing it for myself, but as the complexit grows, I really should write some docs. I’ll put something together.

I really need to write some new tests. I was very disciplined about that in the early days of this library. But then again, back then, I didn’t have demo games to work with. So the testing harness was the only metric for success I had. But I’ve gotten away from writing or even checking the test harness, and that’s bad. Future updates will see me do something about this.

## ESP32 projects

I’ve started studying embedded development. I’ve got a long road ahead of me, but it all starts with an ESP32-S3 Arduino kit. Even with the experience I have with technology, I figured it was best to start somewhere simple before diving in to RTOS and FPGA on more complex platforms. I completed 3 projects on my Freenove ESP32-S3 WROOM devkit this week.

### Flowing LED lights with a button

> The journey of a thousand miles begins with an esp32, some blinking leds and a button pic.twitter.com/i9rOphuY60

> — Andrew Kesterson (@AKLabsDotNet) May 24, 2026

This program sends a cascading light effect through the LED bar that fades on either side of a strong central point. It normally flows in one direction, but if you press the button, it changes direction.

I learned several things from this project:

- How to use the Arduino IDE to deploy code to the ESP32-S3 WROOM
- How to use the Arduino IDE debugger to step through code running on the device
- How to configure GPIO pins on the ESP32-S3 as input or output
- How to read and write data to and from those GPIO pins
- Using a physical button to control program flow in the microcontroller
- Debouncing mechanical buttons
- What is pulse width modulation and how do we control it on the ESP32-S3
- Using pulse width modulation with an LED to create a smooth visual effect simulating an analog curve

There is a more detailed writeup about this project in my source repository

### Controlling buzzers with transistors

> Love it when the manufacturers tutorials have bugs in them. Still better than diagnosing istio problems in kubernetes. One project down, thousands more to go. pic.twitter.com/53WQetCTmQ

> — Andrew Kesterson (@AKLabsDotNet) May 27, 2026

These two programs allow you to control a buzzer with a button to make a noise on command. They both use the exact same breadboard, the only difference is that one uses a passive buzzer, while the other uses an active buzzer (pn TMB12A05).

I learned several things from these projects:

- The differences in active vs passive buzzers
- How transistors work, and how you can use them to control the flow of power in a circuit
- Why you might place a transistor upstream or downstream of the component whose power you are controlling
- How to turn a transistor on or off using the GPIO pin on an ESP32
- How to use my oscilloscope to diagnose PWM output issues

There is a more detailed writeup about this project in my source repository

### Communicating over Serial UART

> A good digital logic analyzer is worth its weight in gold
> Thankfully they tend to be lightweight devices pic.twitter.com/FlSPL95DyW

> — Andrew Kesterson (@AKLabsDotNet) May 29, 2026

This project shows using the ESP32-S3 serial port for communication via UART.

Lessons Learned:

- The Arduino HAL is surprisingly thick
- You can’t use the ESP32-S3 UART for communication via USB when you’re debugging
- libc is libc anywhere you go
- How to use my digital logic analyzer to snoop on, record and inspect UART protocol traffic
- How to use my oscilloscope to look at UART traffic over the wire, and how that compares to using the logic analyzer

There is a more detailed writeup about this project in my source repository

Recent Posts
2026-07-10
News-2026-Week-2
2026-07-09
News - 2026 - Week - 28
2026-06-30
Just Be Yourself ... Whoever that is
2026-06-30
You Cant Go Home Again