Primal Layers — Building a Digital Brainstem
The inspiration for this article is Mike the Headless Chicken. The story of Mike is one of the more bizarre episodes in history, an…
Primal Layers — Building a Digital Brainstem
The inspiration for this article is Mike the Headless Chicken. The story of Mike is one of the more bizarre episodes in history, an accident that became a rural American legend. In 1945, a farmer in Fruita, Colorado, attempted to butcher a chicken for dinner but missed the jugular vein and left part of the brain stem intact. Amazingly, the bird survived and continued to walk, perch, and even attempt to peck for over 18 months.

Image created with Midjourney. All images are by the author unless noted otherwise.
In chickens, essential life-sustaining functions are managed by the brainstem, so even without the higher brain centers, these reflexive processes can continue. This phenomenon demonstrates the role of autonomic regulation and central pattern generators which are neural circuits capable of producing rhythmic motor outputs (like breathing or walking) without input from the brain’s higher regions.
Many thanks to Sui Huang, MD, PhD (https://medium.com/@cancerwarrior)
for alerting us to Mike's story.
Adding to this, birds possess a specialized balance system beyond what we see in mammals. Located in the pelvis, the lumbosacral organ helps control walking and posture independently of the inner ear structures typically used for balance during flight. This unique adaptation may explain how Mike was able to walk and perch, even after losing much of the brain capacity that normally handles balance.
Primal Layers
Our hypothesis is that current AI language models are built on shaky ground. Of course anyone can make up a theory, and the onus is on us to show that we have a better way. In short, we feel that modelling AI on the neocortex alone is a problem and a problem that evolution solved millions of years ago. Current language models lack the primal grounding that gives biological systems their resilience, autonomy, and survival instincts. In a previous article we introduced the Primal Layers intelligence framework, which suggests an alternate approach to AI design.
![Figure 1. AI Primal Layers framework — Foundation details [drawn using Napkin.ai & Photoshop].](https://miro.medium.com/v2/resize:fit:1400/1*ljOUP8U7XDbGS3VRFZXzTQ.png)
Figure 1. AI Primal Layers framework — Foundation details [drawn using Napkin.ai & Photoshop].
The Primal Layer model (Figure 1) offers a biologically inspired framework for building intelligent systems from the ground up, mirroring the layered evolution of the brain. At its core is the Foundational Layer, responsible for maintaining basic operational stability, akin to the brainstem’s role in regulating heartbeat, respiration, and homeostasis. Above this sits the Motivational Layer, drawing parallels to the limbic system, which introduces drives, emotions, and prioritization of behaviors. The Control Layer governs motor planning and coordination, much like the cerebellum and spinal circuits that manage complex yet unconscious movement patterns. Finally, the Cognitive Layer reflects neocortical functions, enabling abstract reasoning, symbolic processing, and conscious decision-making. By organizing intelligence in this layered manner, the Primal Layer model hard wires in robustness, adaptability, and safety.
PrimalBot
When we began developing this framework, it quickly became clear that it made little sense without a body. Whether this is a profound insight about consciousness or merely a reminder that nature never produced a species of disembodied brains, we can’t say for certain. Either way, our intelligence model needs a physical form - a robot we’re calling PrimalBot.
To test our hypothesis that an intelligence built on primal layers outperforms one based solely on neural networks, we’ll build and evaluate it using PrimalBot as our proving ground. The PrimalBot intelligence package reflects the four primal layers:
- The Foundational Layer replicates reflexes, basic movement, and homeostatic controls (like obstacle avoidance and energy management).
- The Motivational Layer introduces states and goal priorities, such as “seek charging” or “avoid noise.”
- The Control Layer governs procedural behaviors using central pattern generators and basic routines for navigation or expression.
- The Cognitive Layer, performs higher reasoning and planning, but only when needed.

Figure 2. The PrimalBot concept design.
The goal of this layered architecture is to allow PrimalBot to respond instinctively to stimuli (like a biological organism), operate safely with minimal supervision, and adapt its behavior without requiring exhaustive retraining. We will compare its performance to a purely neural network-controlled equivalent, especially in situations with sparse data, novel inputs, or time-critical reflexes.
![Figure 3. The AceBott ESP32-Max-V1.0 Controller Board [Image Credit: AceBott]](https://miro.medium.com/v2/resize:fit:1400/1*aM9gTyC6k75Zd-WsHk5n4A.png)
Figure 3. The AceBott ESP32-Max-V1.0 Controller Board [Image Credit: AceBott]
Our layered architecture will combine microprocessors with discrete electronic components. The cognitive layer will interface with a cloud-based large language model via an API, while the digital brainstem will run on an ESP32 microcontroller (Figure 3), supported by a custom shield designed for reflex processing and sensor input monitoring. To save time we are using the AceBott Smart Car kit as the hardware platform, but it wouldn’t be hard to replicate this if you wanted a custom build.
The Foundational Layer
As Mike demonstrated, you can survive with just a brainstem and nervous system. Many invertebrates thrive with no brain or use a decentralized nervous system. Flatworms have nerve cords and simple ganglia that coordinate movement and responses without a true brain. Jellyfish and sea anemones use a nerve net, allowing reflexive action without a centralized brain. Starfish and sea urchins operate purely on a nerve ring and radial nerves, coordinating movement and reaction without any brain structure.
A ganglion (plural: ganglia) is a cluster of neuronal cell bodies. Ganglia
serve as relay points and processing hubs for nerve signals. In
invertebrates (like insects or worms), ganglia often act as mini-brains,
capable of handling local tasks like movement or reflexes without input
from a central brain.
This foundational layer will be the focus of our initial design. In the digital brainstem (Figure 4) we will simulate internal stability mechanisms like battery voltage monitoring, thermal protection, and stress indicators (e.g., current overload or motor strain). These will trigger compensatory behaviors such as low-power mode, cooling fan activation, or rest state.
![Figure 4. Foundational Layer A — The Brainstem [drawn using Napkin.ai].](https://miro.medium.com/v2/resize:fit:1400/1*4FVuEU05RHO-ztkKgOZ8Og.png)
Figure 4. Foundational Layer A — The Brainstem [drawn using Napkin.ai].
We will also implement hardwired reflexes like obstacle avoidance, edge detection, and rapid withdrawal from harmful stimuli (like sudden heat or noise). These will be fast, non-blocking, and bypass the higher reasoning layers, mimicking the way that brainstem reflex arcs work.
Finally, the digital brainstem will use a global arousal signal based on sensor activity (e.g., noise level, light changes, motion) to modulate how reactive or passive the bot is. This mimics the reticular activating system and can influence the motivational or cognitive layers’ activation thresholds.
The second part of the Foundational Layer is the digital spinal column and nervous system (Figure 5). We will explain the code for the spinal column/nervous system functionality in the next article.
![Figure 5. Foundational Layer B — The Nervous System [drawn using Napkin.ai].](https://miro.medium.com/v2/resize:fit:1400/1*O00r9gWTBCr85wAHAI4iQQ.png)
Figure 5. Foundational Layer B — The Nervous System [drawn using Napkin.ai].
The digital spinal column/nervous system will include Central Pattern Generators (CPGs) which use low-level rhythmic control systems for activities like moving and turning. These patterns can be modulated by higher level layers but don’t need conscious direction.
Sensors for position, acceleration, and orientation will be implemented to facilitate closed-loop control of PrimalBot’s attitude. This is important for adaptive stability.
Hierarchical Sensory Routing will direct sensor data directly to motor responses (reflex paths) while also sending copies to higher primal layers for interpretation.
Software Architecture
That’s enough theory, let’s start building our Primal Layers Arduino library. To start, this will be C++ scaffolding for an Arduino library that models the Primal Layers architecture, beginning with the Foundational Layer (i.e., the Brainstem and NervousSystem). The PrimalLayers code is open source, distributed under the MIT licence, and is available from the Reefwing Software GitHub repository.

Figure 6. The PrimalLayers Class UML diagram.
The PrimalLayers class (Figure 6) initializes and wires together the core low-level control architecture. It serves as the entry point and runtime coordinator.
// PrimalLayers.h
#pragma once
#include "Brainstem.h"
#include "NervousSystem.h"
#include "AutonomicController.h"
#include "ReflexController.h"
#include "AlertnessController.h"
class PrimalLayers {
public:
void begin();
void update();
private:
Brainstem brainstem;
NervousSystem nervousSystem;
ReflexController reflexController;
AutonomicController autonomicController;
AlertnessController alertnessController;
};
The PrimalLayers::begin() function initializes the components of our PrimalLayers framework in a specific sequence to ensure correct interdependencies are established between the subsystems of the artificial brain model.
// PrimalLayers.cpp
#include "PrimalLayers.h"
void PrimalLayers::begin() {
nervousSystem.begin();
autonomicController.begin();
alertnessController.begin();
reflexController.begin(&alertnessController);
brainstem.begin(&nervousSystem, &reflexController, &autonomicController);
}
void PrimalLayers::update() {
brainstem.update();
}
The three controllers in our PrimalLayers framework (AutonomicController, AlertnessController, and ReflexController) are reflections of the distinct functional subsystems found in the biological brainstem (Figure 4). The ReflexController needs access to the AlertnessController to suppress or modulate reflex responses based on alertness. It will also allow us to dynamically adapt reflex thresholds depending on arousal or fatigue.
Passing in class instances via begin() enables runtime wiring of dependencies rather than hardcoding them into constructors. This is used in embedded or real-time systems to simplify initialization order and avoid circular dependencies.
The Brainstem is a central coordinating component for the three controllers (Figure 7). It reads Signals from the NervousSystem, and passes these to the ReflexController if they require immediate response, to the AutonomicController for background regulation, and to the AlertnessController for state modulation.

Figure 7. The Brainstem Class UML diagram.
// Brainstem.h
#pragma once
#include "NervousSystem.h"
#include "ReflexController.h"
#include "AutonomicController.h"
class Brainstem {
public:
void begin(NervousSystem* nervousSystem,
ReflexController* reflexController,
AutonomicController* autonomicController);
void update();
void monitorTemperature(float tempC);
void monitorBattery(float volts);
void monitorTilt(float degrees);
private:
NervousSystem* ns;
ReflexController* reflex;
AutonomicController* autonomic;
void processCortexLevel(const Signal& signal);
};
Sensors report internal or external conditions to the Brainstem via monitor*() methods. The Brainstem converts these into Signal objects and sends them through the NervousSystem queue.
// Brainstem.cpp
#include "Brainstem.h"
void Brainstem::begin(NervousSystem* nervousSystem,
ReflexController* reflexController,
AutonomicController* autonomicController) {
ns = nervousSystem;
reflex = reflexController;
autonomic = autonomicController;
}
void Brainstem::update() {
while (ns->hasPendingSignal()) {
Signal s = ns->getNextSignal();
if (reflex->isReflex(s)) {
reflex->execute(s);
} else if (autonomic->isAutonomic(s)) {
autonomic->regulate(s);
} else {
processCortexLevel(s);
}
}
}
void Brainstem::monitorTemperature(float tempC) {
if (tempC > 45.0) {
ns->sendSignal({ TEMP_HIGH, tempC });
}
}
void Brainstem::monitorBattery(float volts) {
if (volts < 3.3) {
ns->sendSignal({ BATTERY_LOW, volts });
}
}
void Brainstem::monitorTilt(float degrees) {
if (abs(degrees) > 20.0) {
ns->sendSignal({ TILT_EXCEEDED, degrees });
}
}
... // Other Brainstem::monitor* here
void Brainstem::processCortexLevel(const Signal& s) {
// hook for higher-level processing
}
Nervous System
The NervousSystem class acts as a circular message queue for carrying Signal messages between components like sensors, the brainstem, or effectors in the PrimalLayers architecture. The additional nervous system modules shown in Figure 5 will be built out in the next article.
// NervousSystem.h
#pragma once
#include "Messages.h"
class NervousSystem {
public:
void begin();
void sendSignal(const Signal& signal);
bool hasPendingSignal();
Signal getNextSignal();
private:
static const int MAX_QUEUE = 10;
Signal queue[MAX_QUEUE];
int head = 0;
int tail = 0;
};
This class provides an efficient, low-memory way to simulate neural-like signal transmission between robot subsystems. It maintains the order of signal processing (FIFO), and is non-blocking. The queue is circular, meaning once it reaches the end of the array, it wraps around to the beginning using modulo (% MAX_QUEUE) arithmetic. The method sendSignal() places a new signal at the tail position and moves the tail forward. getNextSignal() returns the signal at the head position and advances the head.
// NervousSystem.cpp
#include "NervousSystem.h"
void NervousSystem::begin() {
head = 0;
tail = 0;
}
void NervousSystem::sendSignal(const Signal& signal) {
int next = (tail + 1) % MAX_QUEUE;
if (next != head) { // queue not full
queue[tail] = signal;
tail = next;
}
}
bool NervousSystem::hasPendingSignal() {
return head != tail;
}
Signal NervousSystem::getNextSignal() {
Signal signal = queue[head];
head = (head + 1) % MAX_QUEUE;
return signal;
}
We thought about using interrupts in lieu of a polled queue, but decided against them. Neurons don’t use interrupts instead, signals are continuously sampled, integrated, and processed which is more like polling. This makes implicit timing and latency part of the model, which is often desirable when modeling primitive or subcognitive systems. It also makes it easier to layer on plasticity, habituation, and priority queues later.
Some biological reactions (like reflex arcs) feel “interrupt-like,” but they’re actually fast-path polled loops or dedicated circuits. We will use discrete electronics to emulate this functionality for critical events.
The update() method is periodically called in the main control loop. It polls the NervousSystem for pending signals and processes them according to type. Each Signal message contains a type and value. Based on the type, appropriate low-level actions are triggered, like slowing motors, initiating a cooling mechanism, or correcting tilt.
// Messages.h
#pragma once
enum SignalType {
TEMP_HIGH,
BATTERY_LOW,
OBSTACLE_DETECTED,
EDGE_DETECTED,
NOISE_HIGH,
LIGHT_HIGH,
TILT_EXCEEDED,
HEARTBEAT,
STATE
};
struct Signal {
SignalType type;
float value; // optional payload
};
Autonomic Regulation
The AutonomicController manages continuous regulation, adaptive set-points, and background loops. This sub-system handles tasks that are ongoing and regulated over time. They are for things that we want to be largely subconscious and involuntary (e.g., temperature regulation, battery voltage regulation or load shedding, CPU clock speed, sensor self-calibration, and watchdog timer).
// AutonomicController.h
#pragma once
#include "Messages.h"
class AutonomicController {
public:
void begin(); // optional setup if needed
bool isAutonomic(const Signal& s) const;
void regulate(const Signal& s);
private:
void regulateTemperature(float value);
void regulateBattery(float value);
// Add more homeostatic systems as needed
};
The monitoring functions (e.g., monitorTemperature()) checks a specific input value and sends a signal if the value crosses a critical threshold. These mimic biological sensory neurons triggering autonomic responses.
// AutonomicController.cpp
#include "AutonomicController.h"
void AutonomicController::begin() {
// Initialize setpoints, modes, etc. if needed
}
bool AutonomicController::isAutonomic(const Signal& s) const {
switch (s.type) {
case TEMP_HIGH:
case BATTERY_LOW:
return true;
default:
return false;
}
}
void AutonomicController::regulate(const Signal& s) {
switch (s.type) {
case TEMP_HIGH:
regulateTemperature(s.value);
break;
case BATTERY_LOW:
regulateBattery(s.value);
break;
default:
// Unknown autonomic signal
break;
}
}
void AutonomicController::regulateTemperature(float value) {
// Example: scale fan speed or throttle system slowly
// e.g., fanController.setPWM(value > 50 ? 255 : 128);
}
void AutonomicController::regulateBattery(float value) {
// Example: reduce non-essential activity or dim LEDs
// e.g., powerManager.setPowerMode(LOW_POWER);
}
A simple example of an Arduino sketch using this scaffold to perform autonomic monitoring is shown below.
// simpleExample.ino
#include "PrimalLayers.h"
// Instantiate the top-level Primal Layer controller
PrimalLayers primal;
void setup() {
// Initialize the nervous system and brainstem
primal.begin();
}
void loop() {
// Simulated sensor readings
float temp = readTemp();
float battery = readBattery();
float tilt = readIMU();
// Provide these values to the brainstem for monitoring
primal.brainstem.monitorTemperature(temp);
primal.brainstem.monitorBattery(battery);
primal.brainstem.monitorTilt(tilt);
// Process any pending nervous system signals (i.e., autonomic actions)
primal.update();
}
We will flesh out the PrimalLayers framework as we add additional foundation layer capability.
Reflexive Action Loops
We have an engineering decision to make here, hardware or software. In biology, reflexes are hardwired, fast, and local. They bypass conscious processing, and route through the spinal cord. For survival-critical actions they need to operate with minimal latency. The downside of doing these in hardware, is integration with higher-layer systems (e.g., cognitive overrides or stateful context) becomes complex. In a human, reflexes are 3–10 times faster than conscious cortical responses.
Based on this we have opted for a hybrid approach. We will implement a few critical reflexes in hardware, then signal that event into PrimalLayers afterward for awareness. Something like:
void onHardwareReflexTriggered() {
Signal s = Signal::fromHardwareEvent("overload");
nervousSystem.sendSignal(s);
}
For now, a PrimalLayers reflex path will be added. This allows reflexes to be integrated into the evolving nervous system software.
// ReflexController.h
#pragma once
#include "Messages.h"
#include "AlertnessController.h"
class ReflexController {
public:
void begin(AlertnessController* alertController);
bool isReflex(const Signal& s) const;
void execute(const Signal& s) const;
private:
AlertnessController* alertness = nullptr;
void handleTiltExceeded(float value) const;
void handleObstacleDetected(float distance) const;
void handleEdgeDetected(float position) const;
void handleLoudNoise(float decibels) const;
void handleBrightLight(float lux) const;
};
Unlike the autonomic regulation of internal states, reflexes are discrete responses to external triggers. You can use Table 1 to help decide whether a signal should be handled by the AutonomicController or the ReflexController.

Table 1. Autonomic versus Reflex Actions.
// ReflexController.cpp
#include "ReflexController.h"
void ReflexController::begin(AlertnessController* alertController) {
alertness = alertController;
}
bool ReflexController::isReflex(const Signal& s) const {
switch (s.type) {
case TILT_EXCEEDED:
case OBSTACLE_DETECTED:
case EDGE_DETECTED:
case NOISE_HIGH:
case LIGHT_HIGH:
return true;
default:
return false;
}
}
void ReflexController::execute(const Signal& s) const {
switch (s.type) {
case TILT_EXCEEDED:
handleTiltExceeded(s.value);
break;
case OBSTACLE_DETECTED:
handleObstacleDetected(s.value);
break;
case EDGE_DETECTED:
handleEdgeDetected(s.value);
break;
case NOISE_HIGH:
handleLoudNoise(s.value);
break;
case LIGHT_HIGH:
handleBrightLight(s.value);
break;
default:
break;
}
}
void ReflexController::handleTiltExceeded(float value) const {
if (alertness && alertness->getAlertLevel() != ALERT_LOW) {
// Example: immediately stop motors to prevent falling
// motorController.stop();
}
}
void ReflexController::handleObstacleDetected(float distance) const {
if (alertness && alertness->getAlertLevel() != ALERT_LOW) {
// Example: back up or stop immediately
// motorController.reverse();
}
}
void ReflexController::handleEdgeDetected(float position) const {
if (alertness && alertness->getAlertLevel() != ALERT_LOW) {
// Example: prevent fall by halting forward motion
// motorController.halt();
}
}
void ReflexController::handleLoudNoise(float decibels) const {
if (alertness && alertness->getAlertLevel() == ALERT_HIGH) {
// Example: enter alert posture or look toward sound
// postureController.raiseHead();
}
}
void ReflexController::handleBrightLight(float lux) const {
if (alertness && alertness->getAlertLevel() != ALERT_LOW) {
// Example: close protective shutters or shield sensors
// visionController.dimExposure();
}
}
Alertness Control
In biological terms, alertness is not a reflex or a homeostatic process. It’s a modulatory state that governs how sensitive, responsive, or selective the nervous system is overall. It’s a core function of the reticular activating system (RAS) in the brainstem and integrates with the limbic system and cortex to regulate consciousness, arousal, and attention.
// AlertnessController.h
#pragma once
#include "Messages.h"
enum AlertLevel {
ALERT_LOW,
ALERT_NORMAL,
ALERT_HIGH
};
class AlertnessController {
public:
void begin();
void update(); // Call periodically (e.g. from loop)
void adjustBasedOn(const Signal& s);
AlertLevel getAlertLevel() const;
private:
AlertLevel currentLevel = ALERT_NORMAL;
unsigned long lastStimulusTime = 0;
int alertnessScore = 0; // crude internal state
void evaluateState();
};
We will use the AlertnessController to manage system-wide arousal levels (e.g., LOW, NORMAL, HIGH alert). It will modulate the processes shown in Figure 8, based on the arousal level. This includes, whether reflexes are suppressed or amplified, whether certain signals are ignored or prioritized, how frequently sensors are polled, and whether cortex-level processing is engaged.
![Figure 8. The AlertnessController modulation effects [drawn using Napkin.ai].](https://miro.medium.com/v2/resize:fit:1400/1*ENMpEIilbHZ4DTu1Go77gQ.png)
Figure 8. The AlertnessController modulation effects [drawn using Napkin.ai].
The AlertnessController responds to a variety of sensory and internal cues, such as sudden loud noises, shifts in ambient light, unexpected motion, or signs of internal fatigue like sustained high temperatures. Based on these inputs, the controller adjusts the system’s global alert state, influencing how reactive or conservative the robot becomes. For example, it can trigger transitions into high-alert or low-power modes, selectively override inhibition to allow suppressed reflexes to fire, adjust sensor polling rates to conserve energy, or initiate sleep and wake cycles. This modulation allows the system to remain efficient yet responsive, scaling its attention and reactivity based on context and internal condition.
// AlertnessController.cpp
#include "AlertnessController.h"
#include <Arduino.h> // for millis()
void AlertnessController::begin() {
currentLevel = ALERT_NORMAL;
alertnessScore = 0;
lastStimulusTime = millis();
}
void AlertnessController::update() {
unsigned long now = millis();
unsigned long timeSinceLast = now - lastStimulusTime;
// Decay alertness over time
if (timeSinceLast > 5000) {
alertnessScore -= 1;
evaluateState();
lastStimulusTime = now; // decay every 5 sec
}
}
void AlertnessController::adjustBasedOn(const Signal& s) {
lastStimulusTime = millis();
switch (s.type) {
case TILT_EXCEEDED:
case TEMP_HIGH:
alertnessScore += 2;
break;
case BATTERY_LOW:
alertnessScore += 1;
break;
default:
alertnessScore += 0;
break;
}
evaluateState();
}
void AlertnessController::evaluateState() {
if (alertnessScore >= 5) {
currentLevel = ALERT_HIGH;
} else if (alertnessScore <= -2) {
currentLevel = ALERT_LOW;
} else {
currentLevel = ALERT_NORMAL;
}
// Clamp the score to avoid runaway values
if (alertnessScore > 10) alertnessScore = 10;
if (alertnessScore < -5) alertnessScore = -5;
}
AlertLevel AlertnessController::getAlertLevel() const {
return currentLevel;
}
Next Steps
In this article we have sketched out the bones of our PrimalLayers framework. The simple.ino Arduino sketch compiles but it doesn’t really do anything yet. Note that the code shown here is descriptive and incomplete, the latest version of the library should be download from the PrimalLayers GitHub repository. When it gets to a useful state we will add it to the Arduino IDE Library Manager to make installation simpler. To finish off the Foundation layer we need to add Central Pattern Generators, orientation detection, and sensor routing. We will attend to this next and start running tests on the PrimalBot hardware platform.
If you enjoyed this article and would like to help support my writing, then please show your appreciation by following me, or subscribe to get an email whenever I publish a new article.
메타데이터
- post_id
- 9f8f3a55b91d
- slug
- primal-layers-building-a-digital-brainstem-9f8f3a55b91d
- url
- https://ai.gopubby.com/primal-layers-building-a-digital-brainstem-9f8f3a55b91d
- canonical_url
- https://ai.gopubby.com/primal-layers-building-a-digital-brainstem-9f8f3a55b91d
- author_url
- https://medium.com/@reefwing
- status
- ok
- fetched_at
- 2026-06-09 15:37:30