Skip to main content
Building HayBox from source allows you to customize pin mappings, button functions, SOCD behavior, and create custom input modes for your specific controller setup.

Choose Your Build Method

HayBox supports three main build methods:

Local Build

Full control, fastest iteration. Requires installing dependencies.

GitHub Codespaces

Cloud-based VS Code. No local setup needed. Free tier available.

GitHub Actions

Automated builds on push. No local setup, but slower iteration.

Building Locally

Recommended for active development and customization.

Prerequisites

1

Install dependencies

Required:Optional (for cloning repository):
  • Git - Recommended for Pico builds and easier updates
2

Get the source code

git clone https://github.com/JonnyHaystack/HayBox.git
cd HayBox
Cloning with Git makes it easy to pull updates later with git pull.

Build Process

1

Open project in VS Code

  1. Launch Visual Studio Code
  2. Click File → Open Folder
  3. Select the HayBox folder (containing platformio.ini)
Make sure to open the folder containing platformio.ini, not the parent folder.
2

Configure long paths (Windows only - first time)

On Windows, if this is your first time building HayBox, run this command in any terminal:
git config --global core.longpaths true
This prevents path length issues when building for Pico/RP2040.
3

Select build environment

Click the environment selector button in the bottom left of VS Code:Environment SelectorChoose your controller’s environment:RP2040/Pico Environments:
  • pico - Generic Raspberry Pi Pico
  • b0xx_r4 - B0XX R4
  • c53 - Crane’s Model C≤53
  • schism - Schism controller
Arduino/AVR Environments:
  • b0xx_r1 / b0xx_r2 - B0XX R1/R2
  • arduino_leonardo - Arduino Leonardo
  • arduino_micro - Arduino Micro
  • arduino_nano - Arduino Nano
  • gccpcb1 / gccpcb2 - GCCPCB
  • lbx - LBX
Environment List
4

Customize configuration (optional)

Edit button mappings and pin assignments in your config file:
config/<environment>/config.cpp
GpioButtonMapping button_mappings[] = {
    { BTN_LF1, 2  },  // Map Left-1 to GPIO 2
    { BTN_LF2, 3  },  // Map Left-2 to GPIO 3
    { BTN_LF3, 4  },  // Map Left-3 to GPIO 4
    { BTN_LF4, 5  },  // Map Left-4 to GPIO 5
    // Add more mappings as needed
    // Delete mappings for buttons your controller doesn't have
};
Delete button mappings for buttons your controller doesn’t have - they’re optional.
5

Wait for indexing to complete

Watch the bottom status bar for:
  • “Rebuilding IntelliSense Index”
  • “Loading Project Tasks”
First build for Pico: May take 5-10 minutes as PlatformIO downloads 2-3GB of dependencies. Subsequent builds are much faster.
6

Build the firmware

Click the Build button (checkmark icon) in the bottom left.The compiled firmware will be located at:
HayBox/.pio/build/<environment>/firmware.uf2  (Pico)
HayBox/.pio/build/<environment>/firmware.hex  (Arduino)
# Build specific environment
pio run -e pico

# Build all environments
pio run

# Clean build
pio run -t clean
pio run -e pico
7

Upload firmware

For Pico/RP2040:
  1. Hold BOOTSEL button while plugging in (or RT2/C-Down if HayBox already installed)
  2. Drag firmware.uf2 onto the RPI-RP2 drive that appears
For Arduino:
  1. Plug in controller via USB
  2. Click Upload button (right arrow icon) in bottom left
Or use the installation guide for alternative flashing methods.

Building with GitHub Codespaces

Build and edit HayBox in the cloud without installing anything locally.
1

Create a fork

  1. Log in to GitHub
  2. Navigate to HayBox repository
  3. Click Fork in the top right
  4. Create the fork under your account
2

Create a Codespace

  1. In your fork, click the green Code button
  2. Select the Codespaces tab
  3. Click Create codespace on master
VS Code will open in your browser with all dependencies pre-installed.
GitHub’s free tier includes 60 core-hours per month. Make sure to stop your Codespace when not using it.
3

Build firmware

Follow the same steps as building locally:
  1. Select your environment
  2. Edit config files if needed
  3. Click Build
The build runs on GitHub’s servers, not your local machine.
4

Download compiled firmware

Since you can’t use the Upload button in Codespaces:
  1. Open the Explorer sidebar (folder icon)
  2. Navigate to .pio/build/<environment>/
  3. Right-click firmware.uf2 (or firmware.hex)
  4. Select Download
  5. Flash manually using the installation guide
5

Stop Codespace when done

To preserve your free tier quota:
  1. Click your profile icon in bottom left
  2. Select Stop Current Codespace
Or manage all Codespaces at github.com/codespaces.
Always stop your Codespace when finished to avoid consuming your monthly quota.

Building with GitHub Actions

Automatically build firmware whenever you push changes.
1

Fork and enable Actions

  1. Fork the HayBox repository
  2. In your fork, go to Settings → Actions → General
  3. Select Allow all actions and reusable workflows
  4. Click Save
2

Edit code in browser

Use GitHub’s built-in editor:
  1. Navigate to your fork
  2. Press . (period key) to open github.dev editor
  3. Edit your config files in config/<environment>/config.cpp
// In config/pico/config.cpp
GpioButtonMapping button_mappings[] = {
    { BTN_LF1, 2  },  // Changed from GPIO 3
    { BTN_LF2, 4  },  // Changed from GPIO 5
    // ... modify as needed
};
3

Commit and push changes

  1. Click Source Control icon in left sidebar
  2. Stage your changes (click + next to files)
  3. Write a commit message
  4. Click Commit & Push
This triggers a GitHub Actions build automatically.
4

Monitor build progress

  1. Go to the Actions tab in your repository
  2. Click on your latest workflow run
  3. Watch the build progress for each environment
Builds typically take 5-10 minutes depending on environments.
5

Download artifacts

Once the build completes:
  1. Scroll to the bottom of the workflow run page
  2. Download the artifact for your environment (e.g., HayBox-<commit>-pico.uf2)
  3. Flash the firmware using the installation guide

Customize Build Matrix

Optimize build times by only building environments you need:
jobs:
  build:
    strategy:
      matrix:
        include:
          # Keep only the environments you need
          - env: pico
            bin_ext: uf2
          - env: b0xx_r4
            bin_ext: uf2
          # Remove environments you don't use to speed up builds
          # - env: arduino_leonardo
          #   bin_ext: hex
Remove unused environments from the matrix to reduce build time and resource usage.

PlatformIO Configuration

Environment Structure

Each build environment is configured in platformio.ini and its corresponding config/<env>/env.ini:
[platformio]
name = HayBox
default_envs = pico
extra_configs = config/*/env.ini  # Loads all env.ini files

[env]
build_type = release
custom_firmware_version = 3.1.0
build_flags =
    -D 'DEVICE_NAME="${PIOENV}"'
    -D 'FIRMWARE_VERSION="${this.custom_firmware_version}"'

Available Base Environments

Base EnvironmentPlatformUse Case
arduino_pico_baseRP2040Raspberry Pi Pico and RP2040 boards
avr_usbAVRArduino with native USB (Leonardo, Micro)
avr_nousbAVRArduino without USB (Uno, Nano, Mega)

Customizing Button Mappings

Edit Config File

Each environment has its own config in config/<environment>/config.cpp:
#include "comms/backend_init.hpp"
#include "config_defaults.hpp"
#include "input/DebouncedGpioButtonInput.hpp"
// ... other includes

// Define your button-to-GPIO mappings
GpioButtonMapping button_mappings[] = {
    // Left side buttons
    { BTN_LF1, 2  },  // Left face 1 -> GPIO 2
    { BTN_LF2, 3  },  // Left face 2 -> GPIO 3  
    { BTN_LF3, 4  },  // Left face 3 -> GPIO 4
    { BTN_LF4, 5  },  // Left face 4 -> GPIO 5
    { BTN_LF5, 1  },  // Left face 5 -> GPIO 1
    
    // Modifier buttons
    { BTN_LT1, 6  },  // Mod X -> GPIO 6
    { BTN_LT2, 7  },  // Mod Y -> GPIO 7
    
    // Center buttons
    { BTN_MB1, 0  },  // Start -> GPIO 0
    { BTN_MB2, 10 },  // Select -> GPIO 10
    { BTN_MB3, 11 },  // Home -> GPIO 11
    
    // Right triggers
    { BTN_RT1, 14 },  // A -> GPIO 14
    { BTN_RT2, 15 },  // C-Down -> GPIO 15
    { BTN_RT3, 13 },  // C-Left -> GPIO 13
    { BTN_RT4, 12 },  // C-Up -> GPIO 12
    { BTN_RT5, 16 },  // C-Right -> GPIO 16
    
    // Right face buttons
    { BTN_RF1, 26 },  // B -> GPIO 26
    { BTN_RF2, 21 },  // X -> GPIO 21
    { BTN_RF3, 19 },  // Z -> GPIO 19
    { BTN_RF4, 17 },  // R -> GPIO 17
    
    // Lightshield buttons (optional)
    { BTN_RF5, 27 },
    { BTN_RF6, 22 },
    { BTN_RF7, 20 },
    { BTN_RF8, 18 },
};

Button Name Reference

Button CodeTypical FunctionMelee Mapping
BTN_LF1 - BTN_LF5Left face buttonsUp, Down, Left, Right, L
BTN_LT1, BTN_LT2Left triggersMod X, Mod Y
BTN_MB1 - BTN_MB3Middle buttonsStart, Select, Home
BTN_RT1 - BTN_RT5Right triggersA, C-Down, C-Left, C-Up, C-Right
BTN_RF1 - BTN_RF4Right face buttonsB, X, Z, R
BTN_RF5 - BTN_RF8Extra buttonsLightshield buttons
Button codes are logical identifiers. The actual function depends on the game mode you select.

Advanced Customization

Creating Custom Input Modes

Create custom modes for different games:
#ifndef _MODES_CUSTOMMODE_HPP
#define _MODES_CUSTOMMODE_HPP

#include "core/ControllerMode.hpp"
#include "core/socd.hpp"
#include "core/state.hpp"

class CustomMode : public ControllerMode {
  public:
    CustomMode(socd::SocdType socd_type);

  private:
    void UpdateDigitalOutputs(const InputState &inputs, OutputState &outputs);
    void UpdateAnalogOutputs(const InputState &inputs, OutputState &outputs);
};

#endif

Configuring SOCD Cleaning

Set simultaneous opposite cardinal direction (SOCD) behavior:
_socd_pair_count = 4;
_socd_pairs = new socd::SocdPair[_socd_pair_count]{
    socd::SocdPair{&InputState::left,    &InputState::right,   socd::SOCD_2IP_NO_REAC},
    socd::SocdPair{&InputState::down,    &InputState::up,      socd::SOCD_2IP_NO_REAC},
    socd::SocdPair{&InputState::c_left,  &InputState::c_right, socd::SOCD_NEUTRAL},
    socd::SocdPair{&InputState::c_down,  &InputState::c_up,    socd::SOCD_NEUTRAL},
};
Available SOCD Types:
SOCD TypeBehavior
SOCD_NEUTRALLeft + Right = Neutral (default)
SOCD_2IPSecond Input Priority with reactivation
SOCD_2IP_NO_REACSecond Input Priority without reactivation
SOCD_DIR1_PRIORITYFirst direction always wins
SOCD_DIR2_PRIORITYSecond direction always wins
SOCD_NONENo SOCD resolution (game decides)

Troubleshooting Builds

Common Build Errors

Run this command and rebuild:
git config --global core.longpaths true
PlatformIO should auto-install dependencies. If it fails:
pio pkg install
pio lib install
For Pico: Make sure it’s in bootsel mode (RPI-RP2 drive visible)For Arduino: Check that the correct COM port is selected in Device Manager
Wait for “Rebuilding IntelliSense Index” to complete. If persists:
# Reload VS Code
Ctrl+Shift+P -> "Reload Window"

First Build Taking Forever?

Pico builds: First build downloads 2-3GB of dependencies (SDK, toolchains, libraries). This is normal and only happens once. Subsequent builds are much faster (typically under 30 seconds).

Next Steps

Installation Guide

Learn how to flash your compiled firmware

Customization

Deep dive into SOCD modes, modifiers, and advanced features

Mode Selection

Configure button combinations for switching game modes

Input Sources

Add Nunchuk, GameCube controller, or switch matrix inputs

Build docs developers (and LLMs) love