Full Code of FeralAI/GP2040 for AI

main 14b01e8ee2b5 cached
170 files
5.0 MB
1.3M tokens
386 symbols
1 requests
Download .txt
Showing preview only (5,227K chars total). Download the full file or copy to clipboard to get everything.
Repository: FeralAI/GP2040
Branch: main
Commit: 14b01e8ee2b5
Files: 170
Total size: 5.0 MB

Directory structure:
gitextract_notmhc6x/

├── .editorconfig
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── main.yml
│       └── release.yml
├── .gitignore
├── .gitmodules
├── .vscode/
│   └── extensions.json
├── LICENSE
├── README.md
├── build-web.py
├── configs/
│   ├── CrushCounter/
│   │   ├── BoardConfig.h
│   │   └── env.ini
│   ├── DURAL/
│   │   ├── BoardConfig.h
│   │   ├── README.md
│   │   └── env.ini
│   ├── DebugBoard/
│   │   ├── BoardConfig.h
│   │   ├── README.md
│   │   ├── assets/
│   │   │   └── picoprobe_adafruit_qtpy_rp2040.uf2
│   │   └── env.ini
│   ├── Fightboard/
│   │   ├── BoardConfig.h
│   │   ├── README.md
│   │   ├── assets/
│   │   │   └── PinMapping.xcf
│   │   └── env.ini
│   ├── FlatboxRev4/
│   │   ├── BoardConfig.h
│   │   ├── README.md
│   │   └── env.ini
│   ├── GeeekPiStick/
│   │   ├── BoardConfig.h
│   │   ├── README.md
│   │   ├── assets/
│   │   │   └── PinMapping.xcf
│   │   └── env.ini
│   ├── Hydra/
│   │   ├── BoardConfig.h
│   │   └── env.ini
│   ├── OSFRD/
│   │   ├── BoardConfig.h
│   │   └── env.ini
│   └── Pico/
│       ├── BoardConfig.h
│       ├── README.md
│       ├── assets/
│       │   └── PinMapping.xcf
│       └── env.ini
├── docs/
│   ├── .nojekyll
│   ├── CNAME
│   ├── _navbar.md
│   ├── assets/
│   │   └── images/
│   │       └── led-themes/
│   │           └── _base-template.xcf
│   ├── development.md
│   ├── downloads/
│   │   └── flash_nuke.uf2
│   ├── faq.md
│   ├── index.html
│   ├── usage.md
│   └── web-configurator.md
├── include/
│   ├── display.h
│   ├── enums.h
│   ├── gamepad.h
│   ├── gp2040.h
│   ├── leds.h
│   ├── pico/
│   │   └── config_autogen.h
│   ├── pleds.h
│   ├── storage.h
│   ├── themes.h
│   └── tusb_config.h
├── lib/
│   ├── AnimationStation/
│   │   ├── .editorconfig
│   │   ├── library.json
│   │   └── src/
│   │       ├── Animation.cpp
│   │       ├── Animation.hpp
│   │       ├── AnimationStation.cpp
│   │       ├── AnimationStation.hpp
│   │       ├── AnimationStorage.hpp
│   │       ├── Effects/
│   │       │   ├── Chase.cpp
│   │       │   ├── Chase.hpp
│   │       │   ├── Rainbow.cpp
│   │       │   ├── Rainbow.hpp
│   │       │   ├── StaticColor.cpp
│   │       │   ├── StaticColor.hpp
│   │       │   ├── StaticTheme.cpp
│   │       │   └── StaticTheme.hpp
│   │       └── Pixel.hpp
│   ├── BitBang_I2C/
│   │   ├── BitBang_I2C.c
│   │   ├── BitBang_I2C.h
│   │   └── README.md
│   ├── CRC32/
│   │   └── src/
│   │       ├── CRC32.cpp
│   │       └── CRC32.h
│   ├── FlashPROM/
│   │   ├── include/
│   │   │   └── FlashPROM.h
│   │   ├── library.json
│   │   └── src/
│   │       └── FlashPROM.cpp
│   ├── NeoPico/
│   │   ├── .editorconfig
│   │   ├── library.json
│   │   └── src/
│   │       ├── NeoPico.cpp
│   │       ├── NeoPico.hpp
│   │       ├── ws2812.pio
│   │       └── ws2812.pio.h
│   ├── OneBitDisplay/
│   │   ├── OneBitDisplay.cpp
│   │   ├── OneBitDisplay.h
│   │   ├── README.md
│   │   ├── fonts/
│   │   │   └── FreeSerif12pt7b.h
│   │   └── obd.inl
│   ├── PlayerLEDs/
│   │   ├── include/
│   │   │   └── PlayerLEDs.h
│   │   └── src/
│   │       └── PlayerLEDs.cpp
│   ├── TinyUSB_Gamepad/
│   │   ├── include/
│   │   │   ├── hid_driver.h
│   │   │   ├── net_driver.h
│   │   │   ├── usb_driver.h
│   │   │   ├── webserver_descriptors.h
│   │   │   └── xinput_driver.h
│   │   ├── library.json
│   │   └── src/
│   │       ├── hid_driver.cpp
│   │       ├── net_driver.cpp
│   │       ├── tusb_driver.cpp
│   │       ├── usb_descriptors.cpp
│   │       └── xinput_driver.cpp
│   ├── httpd/
│   │   ├── fs.c
│   │   ├── fs.h
│   │   ├── fscustom.h
│   │   ├── fsdata.c
│   │   ├── fsdata.h
│   │   ├── httpd.c
│   │   ├── httpd.h
│   │   └── httpd_structs.h
│   ├── lwip-port/
│   │   ├── arch/
│   │   │   └── cc.h
│   │   ├── lwipopts.h
│   │   └── server/
│   │       ├── dhserver.c
│   │       ├── dhserver.h
│   │       ├── dnserver.c
│   │       └── dnserver.h
│   └── rndis/
│       ├── ndis.h
│       ├── rndis.c
│       ├── rndis.h
│       ├── rndis_protocol.h
│       └── rndis_reports.c
├── platformio.ini
├── src/
│   ├── display.cpp
│   ├── gamepad.cpp
│   ├── leds.cpp
│   ├── main.cpp
│   ├── pleds.cpp
│   ├── storage.cpp
│   └── webserver.cpp
├── tools/
│   └── makefsdata
└── www/
    ├── .gitignore
    ├── README.md
    ├── package.json
    ├── public/
    │   ├── index.html
    │   └── manifest.json
    ├── server/
    │   ├── app.js
    │   └── docs/
    │       ├── GP2040 (Dev).postman_environment.json
    │       └── GP2040.postman_collection.json
    └── src/
        ├── App.js
        ├── App.scss
        ├── Components/
        │   ├── DangerSection.js
        │   ├── DraggableListGroup.js
        │   ├── DraggableListGroup.scss
        │   ├── FormCheck.js
        │   ├── FormCheck.scss
        │   ├── FormControl.js
        │   ├── FormSelect.js
        │   ├── Navigation.js
        │   ├── Navigation.scss
        │   ├── Section.js
        │   └── Section.scss
        ├── Contexts/
        │   └── AppContext.js
        ├── Data/
        │   ├── Boards.json
        │   ├── Buttons.json
        │   └── Controllers.json
        ├── Pages/
        │   ├── DisplayConfig.js
        │   ├── HomePage.js
        │   ├── LEDConfigPage.js
        │   ├── PinMapping.js
        │   ├── PinMappings.scss
        │   ├── ResetSettingsPage.js
        │   └── SettingsPage.js
        ├── Services/
        │   ├── Storage.js
        │   └── WebApi.js
        ├── index.js
        └── index.scss

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = tab
indent_size = 2
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md, *.yml]
indent_style = space
indent_size = 2
trim_trailing_whitespace = false

[*.scss]
end_of_line = lf


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: [FeralAI]


================================================
FILE: .github/workflows/main.yml
================================================
name: PlatformIO CI

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
      with:
        submodules: recursive
    - name: Cache pip
      uses: actions/cache@v2
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
        restore-keys: |
          ${{ runner.os }}-pip-
    - name: Cache PlatformIO
      uses: actions/cache@v2
      with:
        path: ~/.platformio
        key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
    - name: Set up Python
      uses: actions/setup-python@v2
    - name: Install PlatformIO
      run: |
        python -m pip install --upgrade pip
        pip install --upgrade platformio
        pio platform install https://github.com/Wiz-IO/wizio-pico
    - name: Run PlatformIO
      run: |
        pio run -e osfrd
        mv .pio/build/osfrd/APPLICATION.uf2 .pio/build/osfrd/GP2040-OSFRD.uf2
        pio run -e crush-counter
        mv .pio/build/crush-counter/APPLICATION.uf2 .pio/build/crush-counter/GP2040-CrushCounter.uf2
        pio run -e raspberry-pi-pico
        mv .pio/build/raspberry-pi-pico/APPLICATION.uf2 .pio/build/raspberry-pi-pico/GP2040-RaspberryPiPico.uf2
        pio run -e pico-fighting-board
        mv .pio/build/pico-fighting-board/APPLICATION.uf2 .pio/build/pico-fighting-board/GP2040-PicoFightingBoard.uf2
        pio run -e dural
        mv .pio/build/dural/APPLICATION.uf2 .pio/build/dural/GP2040-DURAL.uf2
        pio run -e flatbox-rev-4
        mv .pio/build/flatbox-rev-4/APPLICATION.uf2 .pio/build/flatbox-rev-4/GP2040-FlatboxRev4.uf2


================================================
FILE: .github/workflows/release.yml
================================================
name: PlatformIO Release

on:
  release:
    types: [created]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: olegtarasov/get-tag@v2.1
      id: tagName
    - uses: actions/checkout@v2
      with:
        submodules: recursive
    - name: Cache pip
      uses: actions/cache@v2
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
        restore-keys: |
          ${{ runner.os }}-pip-
    - name: Cache PlatformIO
      uses: actions/cache@v2
      with:
        path: ~/.platformio
        key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
    - name: Set up Python
      uses: actions/setup-python@v2
    - name: Install PlatformIO
      run: |
        python -m pip install --upgrade pip
        pip install --upgrade platformio
        pio platform install https://github.com/Wiz-IO/wizio-pico
    - name: Run PlatformIO
      run: |
        pio run -e osfrd
        mv .pio/build/osfrd/APPLICATION.uf2 .pio/build/osfrd/GP2040-OSFRD_${{ steps.tagName.outputs.tag }}.uf2
        pio run -e crush-counter
        mv .pio/build/crush-counter/APPLICATION.uf2 .pio/build/crush-counter/GP2040-CrushCounter_${{ steps.tagName.outputs.tag }}.uf2
        pio run -e raspberry-pi-pico
        mv .pio/build/raspberry-pi-pico/APPLICATION.uf2 .pio/build/raspberry-pi-pico/GP2040-RaspberryPiPico_${{ steps.tagName.outputs.tag }}.uf2
        pio run -e pico-fighting-board
        mv .pio/build/pico-fighting-board/APPLICATION.uf2 .pio/build/pico-fighting-board/GP2040-PicoFightingBoard_${{ steps.tagName.outputs.tag }}.uf2
        pio run -e dural
        mv .pio/build/dural/APPLICATION.uf2 .pio/build/dural/GP2040-DURAL_${{ steps.tagName.outputs.tag }}.uf2
        pio run -e flatbox-rev-4
        mv .pio/build/flatbox-rev-4/APPLICATION.uf2 .pio/build/flatbox-rev-4/GP2040-FlatboxRev4_${{ steps.tagName.outputs.tag }}.uf2
    - name: Release
      uses: softprops/action-gh-release@v1
      if: startsWith(github.ref, 'refs/tags/')
      with:
        files: |
          .pio/build/osfrd/GP2040-OSFRD_${{ steps.tagName.outputs.tag }}.uf2
          .pio/build/crush-counter/GP2040-CrushCounter_${{ steps.tagName.outputs.tag }}.uf2
          .pio/build/raspberry-pi-pico/GP2040-RaspberryPiPico_${{ steps.tagName.outputs.tag }}.uf2
          .pio/build/pico-fighting-board/GP2040-PicoFightingBoard_${{ steps.tagName.outputs.tag }}.uf2
          .pio/build/dural/GP2040-DURAL_${{ steps.tagName.outputs.tag }}.uf2
          .pio/build/flatbox-rev-4/GP2040-FlatboxRev4_${{ steps.tagName.outputs.tag }}.uf2


================================================
FILE: .gitignore
================================================
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.vscode/settings.json
node_modules/
configs/MyBoard


================================================
FILE: .gitmodules
================================================
[submodule "configs/PicoFightingBoard"]
	path = configs/PicoFightingBoard
	url = https://github.com/FeralAI/GP2040-Config-PicoFightingBoard.git


================================================
FILE: .vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "platformio.platformio-ide"
    ]
}


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Jason Skuby (mytechtoybox.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
> GP2040 is no longer being maintained. Please check out the more fully-featured community fork called [GP2040-CE](https://github.com/OpenStickCommunity/GP2040-CE). Documentation for GP2040-CE can be found at <https://gp2040-ce.info>. Thank you to the [Open Stick Community](https://github.com/OpenStickCommunity) members for picking up the slack and advancing this project in my absence.

# GP2040 Firmware

GP2040 is a gamepad firmware for the Raspberry Pi Pico and other boards based on the RP2040 microcontroller, and provides high performance with a rich feature set across multiple platforms. GP2040 is compatible with PC, MiSTer, Android, Raspberry Pi, Nintendo Switch, PS3 and PS4 (legacy controller support).

Full documentation can be found at <https://gp2040.info>.

## Features

* Selectable input modes (XInput, DirectInput and Nintendo Switch)
* Overclocked polling rate to 1000 Hz (1 ms) in all modes, with less than 1 ms of input latency
* Multiple SOCD cleaning modes - Neutral, Up Priority (a.k.a. Hitbox), Second Input Priority
* Left and Right stick emulation via D-pad inputs
* Per-button RGB LED support
* PWM and RGB player indicator LED support (XInput only)
* Saves options to internal memory
* Support for 128x64 monochrome I2C displays using SSD1306, SH1106 or SH1107 display drivers.
* [Built-in configuration app](https://gp2040.info/#/web-configurator) hosted via embedded webserver...no downloading a separate app!

Take a look at the [GP2040 Usage](https://gp2040.info/#/usage) page for more details.

## Performance

Input latency is tested using the methodology outlined at [WydD's inputlag.science website](https://inputlag.science/controller/methodology), using the default 1000 Hz (1 ms) polling rate in the firmware.

| Version | Mode | Poll Rate | Min | Max | Avg | Stdev | % on time | %1f skip | %2f skip |
| - | - | - | - | - | - | - | - | - | - |
| v0.3.1 | All | 1 ms | 0.56 ms | 1.32 ms | 0.85 ms | 0.24 ms | 95.95% | 4.05% | 0% |

Full results can be found in the [GP2040 Firmware Latency Test Results](https://docs.google.com/spreadsheets/d/1eeX0SCOYnUDZMYzt_69wDpjnB_XUtvsfvHJYxxgTj28/edit#gid=1559471406) Google Sheet.

## Installation

Prebuilt `uf2` files are available in the [Releases](https://github.com/FeralAI/GP2040/releases) section for the following boards and controllers:

* [Raspberry Pi Pico](https://github.com/FeralAI/GP2040/tree/main/configs/Pico) and other pin-compatible boards such as the Pimoroni Pico Lipo ([wiring diagram](https://raw.githubusercontent.com/FeralAI/GP2040/main/configs/Pico/assets/PinMapping.png))
* [Pico Fighting Board](https://github.com/FeralAI/GP2040-Config-PicoFightingBoard/)
* [Crush Counter](https://github.com/FeralAI/GP2040/tree/main/configs/CrushCounter) (formerly the [OSFRD](https://github.com/FeralAI/GP2040/tree/main/configs/OSFRD))
* [DURAL](https://github.com/FeralAI/GP2040/tree/main/configs/DURAL)
* [Flatbox Rev 4](https://github.com/jfedor2/flatbox/tree/master/hardware-rev4)

Several other working example configurations are located in the [configs](https://github.com/FeralAI/GP2040/tree/main/configs) folder.

The instructions will slightly vary based on your device. These instructions are for a Raspberry Pi Pico.

> If the device has been previously used for something other than GP2040, please flash this file first to clear the on-board storage: [flash_nuke.uf2](docs/downloads/flash_nuke.uf2). After flashing the nuke file, wait a minute for the clear program to run and the RPI-RP2 drive to reappear.

1. Download the latest `GP2040.uf2` file from the [Releases](https://github.com/FeralAI/GP2040/releases) section for your board (e.g. `GP2040-PiPico.uf2` for the Raspberry Pi Pico).
1. Unplug your Pico.[label](https://www.reddit.com/r/fightsticks/comments/zt628p/gp2040ce_open_stick_community_introduction_and/)
1. Hold the BOOTSEL button on the Pico and plug into your computer. A new removable drive named `RPI-RP2` should appear in your file explorer.
1. Drag and drop the `GP2040.uf2` file into the removable drive. This will flash the board.
1. The board is now running the GP2040 firmware and will appear as a controller on your computer.

## Support

If you would like to discuss features, issues or anything else related to GP2040 please [create an issue](https://github.com/FeralAI/GP2040/issues/new) or join the [OpenStick GP2040 Discord channel](https://discord.gg/KyQCHcjwJ2).

### Frequently Asked Questions

#### Which input mode should I use?

Generally speaking, XInput will be the mode of choice for everything except Nintendo Switch and PlayStation 3. XInput mode is the most fully-featured, has the best compatibility with PC games and is compatible with console adapters like the Brook Wingman product line. All things being equal, performance is the same in all modes.

#### What is the extent of PS4 support in GP2040?

GP2040 will work on PS4 games that implement support for legacy PS3 controllers. Many of the popular PS4 fighting games have this support.

#### Does/can/will GP2040 natively support the PS4, PS5, Xbox One or Xbox Series consoles?

These consoles implement security to prevent unauthorized accessories from being used. The process of cracking or bypassing that security may not be legal everywhere. These consoles could be supported in the future if a user-friendly and completely legal implementation method is found.

#### Can I use multiple controllers with GP2040 on the same system?

Yes! Each board with GP2040 is treated as a separate controller. The one thing to keep in mind would be to only run the web configurator for one controller at a time.

#### Does GP2040 really have less than 1 ms of latency?

Yes...if your platform supports 1000 Hz USB polling. GP2040 is configured for 1000 Hz / 1 ms polling by default in all modes, however some systems override or ignore the polling rate the controller requests. PC and MiSTer are confirmed to work with 1000 Hz polling. Even if your system doesn't support a USB polling rate that high, you can feel comfortable knowing GP2040 is still reading and processing your inputs as fast as the target system will allow.

#### Do the additional features like RGB LEDs, Player LEDs and OLED displays affect performance?

No! The RP2040 chip contains two processing cores. GP2040 dedicates one core to reading inputs and sending them via USB, while the second core is used to handle any auxiliary modules like LEDs and display support. No matter how crazy the feature set of GP2040 becomes, it's unlikely your controller's input latency will be affected.

#### Why do the buttons have weird labels like B3, A1, S2, etc.?

GP2040 uses a generic system for handling button inputs that most closely maps to a traditional PlayStation controller layout with a few extra buttons. This means 4 face buttons (B1-B4), 4 shoulder buttons (L1, L2, R1, R2), Select and Start (S1, S2), 2 stick buttons (L3, R3) and 2 auxiliary buttons for things like Home and Capture (A1, A2) on the Switch. The GP2040 documentation and web configurator have a dropdown to change the labels to more familiar controller layouts. You can also refer to the button mapping table on the [GP2040 Usage](https://gp2040.info/#/usage?id=buttons) page.

#### Why use PlatformIO instead of \<insert favorite project setup\>?

Setting up a development environment to build Pico SDK projects is a manual process which requires several components to be installed and configured. Using PlatformIO allows easy installation and updating of build and project dependencies, and makes for a less confusing experience for new developers and people that just want to make a few tweaks for a custom build.

#### What kind of voodoo is that built-in web configurator?

There's no magic here, just some useful libraries working together:

* Single page application using React and Bootstrap is embedded in the GP2040 firmware
* TinyUSB library provides virtual network connection over USB via RNDIS
* lwIP library provides an HTTP server for the embedded React app and the web configuration API
* ArduinoJson library is used for serialization and deserialization of web API requests

## Contributions

Want to help improve GP2040? There are a bunch of ways to contribute!

### Pull Requests

Pull requests are welcome and encouraged for enhancements, bug fixes and documentation updates.

Please respect the coding style of the file(s) you are working in, and enforce the use of the `.editorconfig` file when present.

### Donations

If you'd like to make a donation to my open source work, you can

<a href="https://github.com/sponsors/FeralAI"><img src="https://github.com/FeralAI/GP2040/raw/main/.github/assets/github-sponsor-dimmed.png" alt="Sponsor Feral AI on Github" style="height: 40px !important;" ></a>

or

<a href="https://www.buymeacoffee.com/feralai" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 40px !important;" ></a>

## Acknowledgements

* Ha Thach's excellent [TinyUSB library](https://github.com/hathach/tinyusb) examples
* fluffymadness's [tinyusb-xinput](https://github.com/fluffymadness/tinyusb-xinput) sample
* Kevin Boone's [blog post on using RP2040 flash memory as emulated EEPROM](https://kevinboone.me/picoflash.html)
* [bitbank2](https://github.com/bitbank2) for the [OneBitDisplay](https://github.com/bitbank2/OneBitDisplay) and [BitBang_I2C](https://github.com/bitbank2/BitBang_I2C) libraries, which were ported for use with the Pico SDK


================================================
FILE: build-web.py
================================================
import os
import os.path

website_dir = "www/build/"
fsdata_filename = "lib/httpd/fsdata.c"

print("Building React app")
os.chdir("www")
if not os.path.isdir("node_modules"):
  print("Running npm install")
  os.system("npm i")
print("Running npm run build")
os.system("npm run build")
os.chdir("..")
print("Done")

print("Regenerating " + fsdata_filename)
dirname = os.path.dirname(__file__)
makefsdata = os.path.join(dirname, 'tools/makefsdata')
os.system(makefsdata + " " + website_dir + " -f:" + fsdata_filename)
print("Done")

print("Replace includes")
os.system('perl -i -p0e "s/#include.*?"lwip\/def.h"/#include ""fsdata.h"/s" ' + fsdata_filename)
print("Done")


================================================
FILE: configs/CrushCounter/BoardConfig.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef CRUSH_COUNTER_BOARD_CONFIG_H_
#define CRUSH_COUNTER_BOARD_CONFIG_H_

#include <GamepadEnums.h>
#include "enums.h"
#include "NeoPico.hpp"
#include "PlayerLEDs.h"


#define PIN_DPAD_UP     20
#define PIN_DPAD_DOWN   8
#define PIN_DPAD_LEFT   1
#define PIN_DPAD_RIGHT  14
#define PIN_BUTTON_B1   18
#define PIN_BUTTON_B2   17
#define PIN_BUTTON_B3   13
#define PIN_BUTTON_B4   9
#define PIN_BUTTON_L1   12
#define PIN_BUTTON_R1   10
#define PIN_BUTTON_L2   19
#define PIN_BUTTON_R2   16
#define PIN_BUTTON_S1   3
#define PIN_BUTTON_S2   0
#define PIN_BUTTON_L3   6
#define PIN_BUTTON_R3   7
#define PIN_BUTTON_A1   4
#define PIN_BUTTON_A2   5
#define PIN_SETTINGS    11

#define DEFAULT_SOCD_MODE SOCD_MODE_NEUTRAL
#define BUTTON_LAYOUT BUTTON_LAYOUT_HITBOX

#define BOARD_LEDS_PIN 2

#define LED_BRIGHTNESS_MAXIMUM 100
#define LED_BRIGHTNESS_STEPS 5
#define LED_FORMAT LED_FORMAT_GRB
#define LEDS_PER_PIXEL 1

#define LEDS_DPAD_LEFT   0
#define LEDS_DPAD_DOWN   1
#define LEDS_DPAD_RIGHT  2
#define LEDS_DPAD_UP     3
#define LEDS_BUTTON_B3   4
#define LEDS_BUTTON_B4   5
#define LEDS_BUTTON_R1   6
#define LEDS_BUTTON_L1   7
#define LEDS_BUTTON_B1   8
#define LEDS_BUTTON_B2   9
#define LEDS_BUTTON_R2   10
#define LEDS_BUTTON_L2   11

#define PLED_TYPE PLED_TYPE_RGB
#define PLED1_PIN 12
#define PLED2_PIN 13
#define PLED3_PIN 14
#define PLED4_PIN 15

#endif


================================================
FILE: configs/CrushCounter/env.ini
================================================
[env:crush-counter]
build_flags =
	${env.build_flags}
	-I configs/CrushCounter/
upload_port = .pio/build/crush-counter/


================================================
FILE: configs/DURAL/BoardConfig.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef DURAL_CONFIG_H_
#define DURAL_CONFIG_H_

#include <GamepadEnums.h>

#define PIN_DPAD_UP     9
#define PIN_DPAD_DOWN   7
#define PIN_DPAD_LEFT   6
#define PIN_DPAD_RIGHT  8
#define PIN_BUTTON_B1   21
#define PIN_BUTTON_B2   20
#define PIN_BUTTON_B3   23
#define PIN_BUTTON_B4   22
#define PIN_BUTTON_L1   27
#define PIN_BUTTON_R1   29
#define PIN_BUTTON_L2   26
#define PIN_BUTTON_R2   28
#define PIN_BUTTON_S1   5
#define PIN_BUTTON_S2   4
#define PIN_BUTTON_L3   1
#define PIN_BUTTON_R3   0
#define PIN_BUTTON_A1   3
#define PIN_BUTTON_A2   2

#define DEFAULT_SOCD_MODE SOCD_MODE_UP_PRIORITY
#define BUTTON_LAYOUT BUTTON_LAYOUT_HITBOX

#endif


================================================
FILE: configs/DURAL/README.md
================================================
# GP2040 Configuration for DURAL

![DURAL](assets/DURAL.jpg)

<https://dural.gg/>


================================================
FILE: configs/DURAL/env.ini
================================================
[env:dural]
build_flags =
	${env.build_flags}
	-D BOARD_SPARKFUN_MICRO_RP2040
	-I configs/DURAL/
upload_port = .pio/build/dural/


================================================
FILE: configs/DebugBoard/BoardConfig.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 *
 * Debug setup
 * ------------------------------------------------------------------------------------
 * 2x Raspberry Pi Pico (2nd is for picoprobe debugging)
 * 1x Waveshare 1.3inch LCD Display Module - https://www.waveshare.com/pico-lcd-1.3.htm
 * 1x Waveshare RGB 16x10 LED Matrix - https://www.waveshare.com/pico-rgb-led.htm
 * 1x Waveshare Quad GPIO Expander - https://www.waveshare.com/pico-quad-expander.htm
 *
 */

#ifndef DEBUG_BOARD_CONFIG_H_
#define DEBUG_BOARD_CONFIG_H_

#include <GamepadEnums.h>
#include "hardware/i2c.h"
#include "OneBitDisplay/OneBitDisplay.h"
#include "enums.h"
#include "NeoPico.hpp"
#include "PlayerLEDs.h"

// Remaining pins (0, 1 reserved for I2C): 16

#define PIN_DPAD_UP    4
#define PIN_DPAD_DOWN  5
#define PIN_DPAD_LEFT  2
#define PIN_DPAD_RIGHT 3
#define PIN_BUTTON_B3  10
#define PIN_BUTTON_B4  12
#define PIN_BUTTON_R1  14
#define PIN_BUTTON_L1  15
#define PIN_BUTTON_B1  22
#define PIN_BUTTON_B2  21
#define PIN_BUTTON_R2  19
#define PIN_BUTTON_L2  17
#define PIN_BUTTON_S1  28
#define PIN_BUTTON_S2  27
#define PIN_BUTTON_L3  6
#define PIN_BUTTON_R3  8
#define PIN_BUTTON_A1  26
#define PIN_BUTTON_A2  9 // Not connected

#define DEFAULT_SOCD_MODE SOCD_MODE_NEUTRAL
#define BUTTON_LAYOUT BUTTON_LAYOUT_WASD

#define BOARD_LEDS_PIN 7

#define LED_BRIGHTNESS_MAXIMUM 50
#define LED_BRIGHTNESS_STEPS 5
#define LED_FORMAT LED_FORMAT_GRB
#define LEDS_PER_PIXEL 4

#define LEDS_DPAD_LEFT   0
#define LEDS_DPAD_DOWN   1
#define LEDS_DPAD_RIGHT  2
#define LEDS_DPAD_UP     3
#define LEDS_BUTTON_B3   4
#define LEDS_BUTTON_B4   5
#define LEDS_BUTTON_R1   6
#define LEDS_BUTTON_L1   7
#define LEDS_BUTTON_B1   8
#define LEDS_BUTTON_B2   9
#define LEDS_BUTTON_R2   10
#define LEDS_BUTTON_L2   11

#define PLED_TYPE PLED_TYPE_PWM
#define PLED1_PIN 20
#define PLED2_PIN 11
#define PLED3_PIN 18
#define PLED4_PIN 13

#define HAS_I2C_DISPLAY 1
#define I2C_SDA_PIN 0
#define I2C_SCL_PIN 1
#define I2C_BLOCK i2c0
#define I2C_SPEED 800000

#endif


================================================
FILE: configs/DebugBoard/README.md
================================================
# GP2040 Configuration for Debugging

![Debug setup](assets/DebugBoard.jpg)

Feral's debugging setup. This is what peak performance looks like.

But seriously, it contains (from left to right):

- Custom Hat #1: "Fightstick" with 17 total inputs
  - 1x [10x10x9mm 5 Way Tact Switch](https://www.amazon.com/Bestol-Direction-Switch-10109mm-Navigation/dp/B07F71N26Z/)
  - 12x [3x6x4.3mm Tact Switch](https://www.amazon.com/gp/product/B008DS188Y/)
- Custom Hat #2: Aux function module
  - 0.96" 128x32 SSD1306 I2C OLED
  - 4x Player LEDs
  - 1x DIP switch
- [Pimoroni Pico LiPo](https://shop.pimoroni.com/products/pimoroni-pico-lipo) (USB-C FTW!)
- [Waveshare RGB 16x10 LED Matrix](https://www.waveshare.com/pico-rgb-led.htm)
- Custom Hat #3: I2C expansion board
  - 256k EEPROM module
  - 2x JST-XH 4-pin connectors

All connected to a [Waveshare Quad GPIO Expander](https://www.waveshare.com/pico-quad-expander.htm). Custom hats are built on [Pimoroni Pico Proto boards](https://shop.pimoroni.com/products/pico-proto).

An [Adafruit QT Py RP2040](https://www.adafruit.com/product/4900) is flashed with a [custom Picoprobe build](assets/picoprobe_adafruit_qtpy_rp2040.uf2) and attached to the Pico LiPo's JST-SH connectors for debugging.


================================================
FILE: configs/DebugBoard/env.ini
================================================
[env:debug]
debug_tool = picoprobe
upload_protocol = picoprobe
build_type = debug
build_flags =
	${env.build_flags}
	-I configs/DebugBoard/
upload_port = .pio/build/debug/


================================================
FILE: configs/Fightboard/BoardConfig.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 *
 * Custom config for a modded Fightboard / Fightboard MX using an ItsyBitsy RP2040.
 *
 */

#ifndef FIGHTBOARD_CONFIG_H_
#define FIGHTBOARD_CONFIG_H_

#include <vector>
#include <GamepadEnums.h>
#include <GamepadState.h>
#include "Pixel.hpp"
#include "enums.h"

#define PIN_DPAD_UP     10
#define PIN_DPAD_DOWN   8
#define PIN_DPAD_LEFT   9
#define PIN_DPAD_RIGHT  7
#define PIN_BUTTON_B1   20
#define PIN_BUTTON_B2   19
#define PIN_BUTTON_B3   24
#define PIN_BUTTON_B4   29
#define PIN_BUTTON_L1   27
#define PIN_BUTTON_R1   28
#define PIN_BUTTON_L2   25
#define PIN_BUTTON_R2   18
#define PIN_BUTTON_S1   2
#define PIN_BUTTON_S2   1
#define PIN_BUTTON_L3   3
#define PIN_BUTTON_R3   4
#define PIN_BUTTON_A1   0
#define PIN_BUTTON_A2   5

#define DEFAULT_SOCD_MODE SOCD_MODE_NEUTRAL
#define BUTTON_LAYOUT BUTTON_LAYOUT_WASD

#define BOARD_LEDS_PIN 14

#define LED_BRIGHTNESS_MAXIMUM 255
#define LED_BRIGHTNESS_STEPS 5
#define LED_FORMAT LED_FORMAT_GRBW
#define LEDS_PER_PIXEL 1

#define LEDS_DPAD_LEFT  10
#define LEDS_DPAD_DOWN   9
#define LEDS_DPAD_RIGHT  8
#define LEDS_DPAD_UP    11
#define LEDS_BUTTON_B3   0
#define LEDS_BUTTON_B4   1
#define LEDS_BUTTON_R1   2
#define LEDS_BUTTON_L1   3
#define LEDS_BUTTON_B1   7
#define LEDS_BUTTON_B2   6
#define LEDS_BUTTON_R2   5
#define LEDS_BUTTON_L2   4

#endif


================================================
FILE: configs/Fightboard/README.md
================================================
# GP2040 Configuration for Fightboard and Fightboard MX

The Fightboard and Fightboard MX come with the ItsyBitsy 32u4. If you're brave/skilled enough, you can *upgrade* your Fightboard with an ItsyBitsy RP2040 running GP2040 firmware. This is an example pin mapping for a possible DIY swap.

![Pin Mapping](assets/PinMapping.png)


================================================
FILE: configs/Fightboard/env.ini
================================================
[env:fightboard]
build_flags =
	${env.build_flags}
	-I configs/Fightboard/
upload_port = .pio/build/fightboard/


================================================
FILE: configs/FlatboxRev4/BoardConfig.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef FLATBOX_REV4_CONFIG_H_
#define FLATBOX_REV4_CONFIG_H_

#include <GamepadEnums.h>

#define PIN_DPAD_UP     16
#define PIN_DPAD_DOWN   10
#define PIN_DPAD_LEFT   9
#define PIN_DPAD_RIGHT  11
#define PIN_BUTTON_B1   19
#define PIN_BUTTON_B2   24
#define PIN_BUTTON_B3   18
#define PIN_BUTTON_B4   25
#define PIN_BUTTON_L1   29
#define PIN_BUTTON_R1   27
#define PIN_BUTTON_L2   28
#define PIN_BUTTON_R2   26
#define PIN_BUTTON_S1   3
#define PIN_BUTTON_S2   1
#define PIN_BUTTON_L3   6
#define PIN_BUTTON_R3   4
#define PIN_BUTTON_A1   2
#define PIN_BUTTON_A2   5

#define DEFAULT_SOCD_MODE SOCD_MODE_UP_PRIORITY
#define BUTTON_LAYOUT BUTTON_LAYOUT_HITBOX

#endif


================================================
FILE: configs/FlatboxRev4/README.md
================================================
# GP2040 Configuration for Flatbox Rev 4

![Flatbox](assets/Flatbox-rev2b-finished-product.jpg)

Configuration for the [Flatbox Rev 4](https://github.com/jfedor2/flatbox/tree/master/hardware-rev4), one of the excellent revisions of the [Flatbox](https://github.com/jfedor2/flatbox) design by [jfedor2](https://github.com/jfedor2) powered by an RP2040 MCU.


================================================
FILE: configs/FlatboxRev4/env.ini
================================================
[env:flatbox-rev-4]
upload_port = .pio/build/flatbox-rev-4/
build_flags =
	${env.build_flags}
	-I configs/FlatboxRev4/


================================================
FILE: configs/GeeekPiStick/BoardConfig.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 *
 * Preset config for Pico with a GeeekPi Pico Screw Terminal Expansion Board:
 * https://www.amazon.com/gp/product/B0998594PL/
 *
 */

#ifndef GEEEK_PI_STICK_CONFIG_H_
#define GEEEK_PI_STICK_CONFIG_H_

#include <GamepadEnums.h>

#define PIN_DPAD_DOWN    4
#define PIN_DPAD_UP      5
#define PIN_DPAD_LEFT    6
#define PIN_DPAD_RIGHT   7

#define PIN_BUTTON_B1    8
#define PIN_BUTTON_B2    9
#define PIN_BUTTON_R2   10
#define PIN_BUTTON_L2   11
#define PIN_BUTTON_B3   12
#define PIN_BUTTON_B4   13
#define PIN_BUTTON_R1   14
#define PIN_BUTTON_L1   15

#define PIN_BUTTON_S1   17
#define PIN_BUTTON_S2   18
#define PIN_BUTTON_L3   19
#define PIN_BUTTON_R3   20
#define PIN_BUTTON_A1   21
#define PIN_BUTTON_A2   22

#define DEFAULT_SOCD_MODE SOCD_MODE_NEUTRAL
#define BUTTON_LAYOUT BUTTON_LAYOUT_ARCADE

#endif


================================================
FILE: configs/GeeekPiStick/README.md
================================================
# GP2040 Configuration for GeeekPi GPIO Breakout

Preset config for Pico with a GeeekPi Pico Screw Terminal Expansion Board: <https://www.amazon.com/gp/product/B0998594PL/>

![Pin Mapping](assets/PinMapping.png)

Notes:

* GP2040 button labels are blue on white background, no slant.
* Each screw terminal row has a ground (`GND`) connection available.
* If hooking up RGB LEDs, use the `VBUS` terminal on the bottom row for power.


================================================
FILE: configs/GeeekPiStick/env.ini
================================================
[env:geeek-pi-stick]
build_flags =
	${env.build_flags}
	-I configs/GeeekPiStick/
upload_port = .pio/build/geeek-pi-stick/


================================================
FILE: configs/Hydra/BoardConfig.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef HYDRA_CONFIG_H_
#define HYDRA_CONFIG_H_

#include <GamepadEnums.h>
#include "enums.h"
#include "NeoPico.hpp"
#include "PlayerLEDs.h"

#define PIN_DPAD_DOWN   0
#define PIN_DPAD_UP     1
#define PIN_DPAD_LEFT   2
#define PIN_DPAD_RIGHT  3
#define PIN_BUTTON_B3   4
#define PIN_BUTTON_B4   5
#define PIN_BUTTON_R1   6
#define PIN_BUTTON_L1   7
#define PIN_BUTTON_B1   8
#define PIN_BUTTON_B2   9
#define PIN_BUTTON_R2   10
#define PIN_BUTTON_L2   11
#define PIN_BUTTON_S2   12
#define PIN_BUTTON_S1   13
#define PIN_BUTTON_R3   14
#define PIN_BUTTON_A1   15
#define PIN_BUTTON_A2   18
#define PIN_BUTTON_L3   19


#define DEFAULT_SOCD_MODE SOCD_MODE_UP_PRIORITY
#define BUTTON_LAYOUT BUTTON_LAYOUT_HITBOX

#define BOARD_LEDS_PIN 22

#define LED_BRIGHTNESS_MAXIMUM 200
#define LED_BRIGHTNESS_STEPS 5
#define LED_FORMAT LED_FORMAT_GRB
#define LEDS_PER_PIXEL 2

#define LEDS_DPAD_LEFT   11
#define LEDS_DPAD_DOWN   10
#define LEDS_DPAD_RIGHT  9
#define LEDS_DPAD_UP     0
#define LEDS_BUTTON_B3   8
#define LEDS_BUTTON_B4   7
#define LEDS_BUTTON_R1   6
#define LEDS_BUTTON_L1   5
#define LEDS_BUTTON_B1   1
#define LEDS_BUTTON_B2   2
#define LEDS_BUTTON_R2   3
#define LEDS_BUTTON_L2   4

// #define PLED_TYPE PLED_TYPE_PWM
// #define PLED1_PIN 16
// #define PLED2_PIN 17
// #define PLED3_PIN 18
// #define PLED4_PIN 19

#define HAS_I2C_DISPLAY 1
#define I2C_SDA_PIN 20
#define I2C_SCL_PIN 21
#define I2C_BLOCK i2c0
#define I2C_SPEED 800000

#endif


================================================
FILE: configs/Hydra/env.ini
================================================
[env:hydra]
build_flags =
	${env.build_flags}
	-I configs/Hydra/
upload_port = .pio/build/hydra/

[env:hydra-debug]
debug_tool = picoprobe
upload_protocol = picoprobe
build_type = debug
build_flags =
	${env.build_flags}
	-I configs/Hydra/
upload_port = .pio/build/hydra-debug/


================================================
FILE: configs/OSFRD/BoardConfig.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef OSFRD_BOARD_CONFIG_H_
#define OSFRD_BOARD_CONFIG_H_

#include <GamepadEnums.h>
#include "enums.h"
#include "NeoPico.hpp"
#include "PlayerLEDs.h"

#define PIN_DPAD_UP     13
#define PIN_DPAD_DOWN   11
#define PIN_DPAD_LEFT   10
#define PIN_DPAD_RIGHT  12
#define PIN_BUTTON_B1   4
#define PIN_BUTTON_B2   5
#define PIN_BUTTON_B3   0
#define PIN_BUTTON_B4   1
#define PIN_BUTTON_L1   3
#define PIN_BUTTON_R1   2
#define PIN_BUTTON_L2   7
#define PIN_BUTTON_R2   6
#define PIN_BUTTON_S1   8
#define PIN_BUTTON_S2   9
#define PIN_BUTTON_L3   17
#define PIN_BUTTON_R3   16
#define PIN_BUTTON_A1   28
#define PIN_BUTTON_A2   18

#define DEFAULT_SOCD_MODE SOCD_MODE_NEUTRAL
#define BUTTON_LAYOUT BUTTON_LAYOUT_HITBOX

#define BOARD_LEDS_PIN 14

#define LED_BRIGHTNESS_MAXIMUM 100
#define LED_BRIGHTNESS_STEPS 5
#define LED_FORMAT LED_FORMAT_GRB
#define LEDS_PER_PIXEL 1

#define LEDS_DPAD_LEFT   0
#define LEDS_DPAD_DOWN   1
#define LEDS_DPAD_RIGHT  2
#define LEDS_DPAD_UP     3
#define LEDS_BUTTON_B3   4
#define LEDS_BUTTON_B4   5
#define LEDS_BUTTON_R1   6
#define LEDS_BUTTON_L1   7
#define LEDS_BUTTON_B1   8
#define LEDS_BUTTON_B2   9
#define LEDS_BUTTON_R2   10
#define LEDS_BUTTON_L2   11

#define PLED_TYPE PLED_TYPE_RGB
#define PLED1_PIN 12
#define PLED2_PIN 13
#define PLED3_PIN 14
#define PLED4_PIN 15

#endif


================================================
FILE: configs/OSFRD/env.ini
================================================
[env:osfrd]
build_flags =
	${env.build_flags}
	-I configs/OSFRD/
upload_port = .pio/build/osfrd/

[env:osfrd-pirate]
build_flags =
	${env.build_flags}
	-D BOARD_PIMORONI_PICO_LIPO_16MB
	-I configs/OSFRD/
upload_port = .pio/build/osfrd-pirate/


================================================
FILE: configs/Pico/BoardConfig.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef PICO_BOARD_CONFIG_H_
#define PICO_BOARD_CONFIG_H_

#include <GamepadEnums.h>

#define PIN_DPAD_UP     2
#define PIN_DPAD_DOWN   3
#define PIN_DPAD_RIGHT  4
#define PIN_DPAD_LEFT   5
#define PIN_BUTTON_B1   6
#define PIN_BUTTON_B2   7
#define PIN_BUTTON_R2   8
#define PIN_BUTTON_L2   9
#define PIN_BUTTON_B3   10
#define PIN_BUTTON_B4   11
#define PIN_BUTTON_R1   12
#define PIN_BUTTON_L1   13
#define PIN_BUTTON_S1   16
#define PIN_BUTTON_S2   17
#define PIN_BUTTON_L3   18
#define PIN_BUTTON_R3   19
#define PIN_BUTTON_A1   20
#define PIN_BUTTON_A2   21

#define DEFAULT_SOCD_MODE SOCD_MODE_NEUTRAL
#define BUTTON_LAYOUT BUTTON_LAYOUT_ARCADE

#endif


================================================
FILE: configs/Pico/README.md
================================================
# GP2040 Configuration for Raspberry Pi Pico

![Pin Mapping](assets/PinMapping.png)

Basic pin setup for a stock Raspberry Pi Pico. Combine with a simple GPIO breakout/screw terminal board for an easy DIY arcade stick.


================================================
FILE: configs/Pico/env.ini
================================================
[env:raspberry-pi-pico]
upload_port = .pio/build/raspberry-pi-pico/
build_flags =
	${env.build_flags}
	-I configs/Pico/

[env:pimoroni-pico-lipo]
build_flags =
	${env.build_flags}
	-D BOARD_PIMORONI_PICO_LIPO
	-I configs/Pico/
upload_port = .pio/build/pimoroni-pico-lipo/


================================================
FILE: docs/.nojekyll
================================================


================================================
FILE: docs/CNAME
================================================
gp2040.info

================================================
FILE: docs/_navbar.md
================================================
* [Home](/)
* [Usage](usage)
* [Configuration](web-configurator)
* [Development](development)


================================================
FILE: docs/development.md
================================================
# GP2040 Development

GP2040 is written in C++ and set up as a [PlatformIO](https://platformio.org/) project, using the [Wiz-IO Raspberry Pi Pico](https://github.com/Wiz-IO/wizio-pico) platform package in the `baremetal` (Pico SDK) configuration.

## Environment Setup

The recommended setup is to develop using the [PlatformIO IDE](https://platformio.org/platformio-ide), which is an extension to the excellent [Visual Studio Code (VS Code)](https://code.visualstudio.com/) editor. If a dedicated IDE for embedded development isn't your thing, you can easily build the project using the [PlatformIO CLI](https://platformio.org/install/cli) instead. This section will cover using the PlatformIO IDE.

1. Use Git to clone the [GP2040 repository](https://github.com/FeralAI/GP2040.git), or [download the latest version](https://github.com/FeralAI/GP2040/archive/refs/heads/main.zip) and extract it.
1. Follow the [installation instructions for the PlatformIO IDE](https://platformio.org/install/ide?install=vscode).
1. Open VS Code and you should be greeted with the PlatformIO Home screen.
1. Select the PlatformIO tab in the activity bar (bug icon), then go to `PIO Home > Platforms`.
1. On the Platforms tab click the `Advanced Installation` button, then type `https://github.com/Wiz-IO/wizio-pico` and click `Install`.
1. Open the `GP2040` (`GP2040-main` if from zip) folder in VS Code and it should automatically get picked up as a Platform IO project.
1. Click on the VS Code Explorer tab (or Ctrl+Shift+E) and expand the folders and files in your project.

PlatformIO will download any dependencies not already included with the project.

## Configuration

There are two simple options for building GP2040 for your board. You can either edit an existing board definition, or create your own and configure PlatformIO to build it. Several example configurations are located in the repository **[configs](https://github.com/FeralAI/GP2040/tree/main/configs)** folder. This document will outline setting up a new build configuration.

### Board Configuration Folder

Each subfolder in [`configs`](https://github.com/FeralAI/GP2040/tree/main/configs) contains a separate PlatformIO build configuration, which consists of the following:

| Name | Required? | Description |
| ----------- | --------- | ----------- |
| `BoardConfig.h` | Yes | The configuration file used when building GP2040 for a specific controller/board. Contains initial pin mappings, LED configuration, etc. |
| `env.ini` | Yes | A partial PlatformIO project configuration file which defines the build parameters for this board. All `env.ini` files in subfolders of `configs` will be parsed and selectable when loading the project in the PlatformIO IDE (may require a restart to pick up the new build config).
| `README.md` | No | Provides information related to this board configuration. Not required for the build process, but suggested for pull requests of new board configurations. |
| `assets/` | No | Folder for containing assets included in the `README.md`. Not required for the build process.

### Build Configuration (`env.ini`)

1. Create a new folder in `configs` for your board, e.g. `configs/NewBoard`.
1. Create `configs/NewBoard/env.ini` using the following template:

    ```ini
    [env:new-board]
    upload_port = .pio/build/new-board/
    build_flags =
        ${env.build_flags}
        -I configs/NewBoard/
    ```

    a. If you're not using a Pico or bare RP2040, check the `include/pico/config_autogen.h` file to see if there is a define for your board. If so, add or update the `-D BOARD_...` option in `build_flags`, for example if using the SparkFun Pro Micro RP2040:

    ```ini
    [env:sparkfun-pro-micro]
    upload_port = .pio/build/sparkfun-pro-micro/
    build_flags =
        ${env.build_flags}
        -D BOARD_SPARKFUN_MICRO_RP2040
        -I configs/SparkFunProMicro/
    ```

This will create a new PlatformIO build environment named `new-board`. Select the new environment from the VS Code status bar menu. You may need to restart VS Code in order for PlatformIO to pick up on the `env.ini` changes.

### Board Configuration (`BoardConfig.h`)

The following board options are available in the `BoardConfig.h` file:

| Name             | Description                  | Required? |
| ---------------- | ---------------------------- | --------- |
| **PIN_DPAD_*X***<br>**PIN_BUTTON_*X*** | The GPIO pin for the button. Replace the *`X`* with GP2040 button or D-pad direction. | Yes |
| **DEFAULT_SOCD_MODE** | The default SOCD mode to use, defaults to `SOCD_MODE_NEUTRAL`.<br>Available options are:<br>`SOCD_MODE_NEUTRAL`<br>`SOCD_MODE_UP_PRIORITY`<br>`SOCD_MODE_SECOND_INPUT_PRIORITY` | No |
| **BUTTON_LAYOUT** | The layout of controls/buttons for use with per-button LEDs and external displays.<br>Available options are:<br>`BUTTON_LAYOUT_HITBOX`<br>`BUTTON_LAYOUT_HITBOX`<br>`BUTTON_LAYOUT_WASD` | Yes |

Create `configs/NewBoard/BoardConfig.h` and add your pin configuration and options. An example `BoardConfig.h` file:

```cpp
// BoardConfig.h

#include <GamepadEnums.h>

#define PIN_DPAD_UP     2
#define PIN_DPAD_DOWN   3
#define PIN_DPAD_LEFT   4
#define PIN_DPAD_RIGHT  5
#define PIN_BUTTON_B1   6
#define PIN_BUTTON_B2   7
#define PIN_BUTTON_B3   8
#define PIN_BUTTON_B4   9
#define PIN_BUTTON_L1   10
#define PIN_BUTTON_R1   11
#define PIN_BUTTON_L2   26
#define PIN_BUTTON_R2   27
#define PIN_BUTTON_S1   16
#define PIN_BUTTON_S2   17
#define PIN_BUTTON_L3   18
#define PIN_BUTTON_R3   19
#define PIN_BUTTON_A1   20
#define PIN_BUTTON_A2   21

#define DEFAULT_SOCD_MODE SOCD_MODE_NEUTRAL
#define BUTTON_LAYOUT BUTTON_LAYOUT_ARCADE
```

#### RGB LEDs

GP2040 supports per-button WS2812 and similar RGB LEDs.

The following RGB LED options are available in the `BoardConfig.h` file:

| Name             | Description                  | Required? |
| ---------------- | ---------------------------- | --------- |
| **BUTTON_LAYOUT** | The layout of controls/buttons for use with per-button LEDs and external displays.<br>Available options are:<br>`BUTTON_LAYOUT_HITBOX`<br>`BUTTON_LAYOUT_HITBOX`<br>`BUTTON_LAYOUT_WASD` | Yes |
| **BOARD_LEDS_PIN** | Data PIN for your LED strand | Yes       |
| **LED_FORMAT** | The color data format for the LED chain.<br>Available options are:<br>`LED_FORMAT_GRB`<br>`LED_FORMAT_RGB`<br>`LED_FORMAT_GRBW`<br>`LED_FORMAT_RGBW` | No, default value `LED_FORMAT_GRB` |
| **LEDS_PER_PIXEL** | The number of LEDs per button. | Yes |
| **LED_BRIGHTNESS_MAXIMUM** | Max brightness value, `uint8_t` 0-255. | Yes |
| **LED_BRIGHTNESS_STEPS** | The number of brightness steps when using the up/down hotkey. | Yes |
| **LEDS_DPAD_*X***<br>**LEDS_BUTTON_*X*** | The index of the button on the LED chain. Replace the *`X`* with GP2040 button or D-pad direction. | Yes |
| **LEDS_BASE_ANIMATION_INDEX** | The default LED animation index. | No, defaults to `1` |
| **LEDS_STATIC_COLOR_INDEX** | The default color index for the static color theme  | No, defaults to `2` |
| **LEDS_BUTTON_COLOR_INDEX** | The default color index for the pressed button color | No, defaults to `1` |
| **LEDS_THEME_INDEX** | The default theme index for static themes | No, defaults to `0` |
| **LEDS_RAINBOW_CYCLE_TIME** | The color cycle time for rainbow cycle theme | No, defaults to `40` |
| **LEDS_CHASE_CYCLE_TIME** | The animation speed for the rainbow chase theme | No, defaults to `85` |

An example RGB LED setup in the `BoardConfig.h` file:

```cpp
// BoardConfig.h

#include "gp2040.h"
#include "NeoPico.hpp"

#define BUTTON_LAYOUT BUTTON_LAYOUT_HITBOX

#define BOARD_LEDS_PIN 22

#define LED_BRIGHTNESS_MAXIMUM 100
#define LED_BRIGHTNESS_STEPS 5
#define LED_FORMAT LED_FORMAT_GRB
#define LEDS_PER_PIXEL 2

#define LEDS_DPAD_LEFT   0
#define LEDS_DPAD_DOWN   1
#define LEDS_DPAD_RIGHT  2
#define LEDS_DPAD_UP     3
#define LEDS_BUTTON_B3   4
#define LEDS_BUTTON_B4   5
#define LEDS_BUTTON_R1   6
#define LEDS_BUTTON_L1   7
#define LEDS_BUTTON_B1   8
#define LEDS_BUTTON_B2   9
#define LEDS_BUTTON_R2   10
#define LEDS_BUTTON_L2   11
```

#### Player LEDs

GP2040 supports PWM and RGB player LEDs (PLEDs) and can be configured in the `BoardConfig.h` file.

> NOTE: RGB PLEDs require [RGB LEDs](#rgb-leds) to be configured.

The following PLED options are available in the `BoardConfig.h` file:

| Name             | Description                  | Required? |
| ---------------- | ---------------------------- | --------- |
| **PLED_TYPE** | Configures the type of PLEDs.<br>Available options are: `PLED_TYPE_PWM`, `PLED_TYPE_RGB` | Yes |
| **PLED1_PIN** | (PWM) The GPIO pin for PLED #1.<br>(RGB) The index of PLED #1 on the LED chain. | Yes |
| **PLED2_PIN** | (PWM) The GPIO pin for PLED #2.<br>(RGB) The index of PLED #2 on the LED chain. | Yes |
| **PLED3_PIN** | (PWM) The GPIO pin for PLED #3.<br>(RGB) The index of PLED #3 on the LED chain. | Yes |
| **PLED4_PIN** | (PWM) The GPIO pin for PLED #4.<br>(RGB) The index of PLED #4 on the LED chain. | Yes |

An example PLED setup in the `BoardConfig.h` file:

```cpp
// BoardConfig.h

#include "PlayerLEDs.h"

#define PLED_TYPE PLED_TYPE_RGB
#define PLED1_PIN 12
#define PLED2_PIN 13
#define PLED3_PIN 14
#define PLED4_PIN 15
```

#### I2C Displays

GP2040 supports 128x64 monochrome displays that run on the SSD1306, SH1106 or SH1107 drivers. The following options are available for displays:

| Name | Description | Required? |
| - | - | - |
| **BUTTON_LAYOUT** | The layout of controls/buttons for use with per-button LEDs and external displays.<br>Available options are:<br>`BUTTON_LAYOUT_HITBOX`<br>`BUTTON_LAYOUT_HITBOX`<br>`BUTTON_LAYOUT_WASD` | Yes |
| **HAS_I2C_DISPLAY** | Flag to indicate the controller contains an I2C display module. | No |
| **DISPLAY_I2C_ADDR** | The I2C address of the display. | No, defaults to `0x3C` |
| **I2C_SDA_PIN** | The GPIO pin for the I2C SDA line. | If `HAS_I2C_DISPLAY` is enabled |
| **I2C_SCL_PIN** | The GPIO pin for the I2C SCL line. | If `HAS_I2C_DISPLAY` is enabled |
| **I2C_BLOCK** | The I2C block on the Pico. Refer to the [Pico Pinout Diagram](https://datasheets.raspberrypi.com/pico/Pico-R3-A4-Pinout.pdf) to identify which block is in use based on the SDA and SCL pins being used.<br>Available options are:<br>`i2c0`<br>`i2c1` | No, defaults to `i2c0` |
| **I2C_SPEED** | The speed of the I2C bus. `100000` is standard mode, while `400000` is used for fast mode communication. Higher values may be used but will require testing the device for support. | No, defaults to `400000` |
| **DISPLAY_FLIP** | Flag to flip the rendered display output. Set to `1` to enable. | No, defaults to `0` |
| **DISPLAY_INVERT** | Flag to invert the rendered display output. Set to `1` to enable. | No, defaults to `0` |

An example I2C display setup in the `BoardConfig.h` file:

```cpp
#define BUTTON_LAYOUT BUTTON_LAYOUT_WASD
#define HAS_I2C_DISPLAY 1
#define I2C_SDA_PIN 0
#define I2C_SCL_PIN 1
#define I2C_BLOCK i2c0
#define I2C_SPEED 800000
```

## Building

You should now be able to build or upload the project to your RP2040 board from the Build and Upload status bar icons. You can also open the PlatformIO tab and select the actions to execute for a particular environment. Output folders are defined in the `platformio.ini` file and should default to a path under `.pio/build/${env:NAME}`.


================================================
FILE: docs/faq.md
================================================
# GP2040 FAQ

## General Questions

### Which input mode should I use?

Generally speaking, XInput will be the mode of choice for everything except Nintendo Switch and PlayStation 3. XInput mode is the most fully-featured, has the best compatibility with PC games and is compatible with console adapters like the Brook Wingman product line.

### What is the extent of PS4 support in GP2040?

GP2040 will work on PS4 games that implement support for legacy PS3 controllers. Many of the popular PS4 fighting games have this support.

### Does/can/will GP2040 natively support the PS4, PS5, Xbox One or Xbox Series consoles?

These consoles implement security to prevent unauthorized accessories from being used. The process of cracking or bypassing that security may not be legal everywhere. These consoles could be supported in the future if a user-friendly and completely legal implementation method is found.

### Can I use multiple controllers on the same system?

Yes! Each board with GP2040 is treated as a separate controller. The one thing to keep in mind would be to only run the web configurator for one controller at a time.

### Does GP2040 really have less than 1 ms of latency?

Yes...if you're platform supports 1000 Hz USB polling. GP2040 is configured for 1000 Hz / 1 ms polling, however some systems override or ignore the polling rate the controller requests. PC and MiSTer are confirmed to work with 1000 Hz polling. Even if your system doesn't support a USB polling rate that high, you can feel comfortable knowing GP2040 is still reading and processing your inputs as fast as the target system will allow.

### Do the additional features like RGB LEDs, Player LEDs and OLED displays affect performance?

No! The RP2040 chip contains two processing cores. GP2040 dedicates one core to reading inputs and sending them via USB, while the second core is used to handle any auxiliary modules like LEDs and display support. No matter how crazy the feature set of GP2040 becomes, it's unlikely your controller's input latency will be affected.

### Why do the buttons have weird labels like B3, A1, S2, etc.?

GP2040 uses a generic system for handling button inputs that most closely maps to a traditional PlayStation controller layout with a few extra buttons. This means 4 face buttons (B1-B4), 4 shoulder buttons (L1, L2, R1, R2), Select and Start (S1, S2), 2 stick buttons (L3, R3) and 2 auxiliary buttons for things like Home and Capture (A1, A2) on the Switch. The GP2040 documentation and web configurator have a dropdown to change the labels to more familiar controller layouts. You can also refer to the button mapping table on the [GP2040 Usage](https://gp2040.info/#/usage?id=buttons) page.

## Technical Questions

### Why use PlatformIO instead of \<insert favorite project setup\>?

Setting up a development environment to build Pico SDK projects is a manual process which requires several components to be installed and configured. Using PlatformIO allows easy installation and updating of build and project dependencies, and makes for a less confusing experience for new developers and people that just want to make a few tweaks for a custom build.

### What kind of voodoo is that built-in web configurator?

There's no magic here, just a few cool libraries working together:

* Single page application using React and Bootstrap is embedded in the GP2040 firmware
* TinyUSB library provides virtual network connection via RNDIS
* lwIP library provides an HTTP server which serves up the embedded React app and the web configuration API
* ArduinoJson library is used for serialization and deserialization of web API requests


================================================
FILE: docs/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
	<meta name="description" content="Description">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
	<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple-dark.css">
	<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/prism-themes@1.9.0/themes/prism-coldark-dark.min.css">
	<style>
		.form-dropdown label {
			display: none;
			margin-right: 4px;
		}

		.hotkey {
			text-transform: uppercase;
		}

		.label-select {
			display: inline-block;
			margin-left: 4px;
		}

		.sticky-label-select {
			/* width: 100%; */
			position: fixed!important;
			top: var(--sidebar-toggle-offset-top);
			height: var(--sidebar-toggle-height);
			right: 0px;
			background: var(--sidebar-toggle-background, transparent);
			padding: 4px;
			border-radius: 4px;
			transform: translateY(-100%);
			opacity: 0.8;
			z-index: 10;
		}

		.sticky-label-select label {
			display: block;
			text-align: center;
			margin-top: -4px;
			margin-bottom: 4px;
		}
	</style>
</head>
<body>
	<div id="app"></div>

	<!-- Vue 2.x -->
	<script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.min.js"></script>
	<!-- <script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> -->
	<script src="//cdn.jsdelivr.net/npm/v-scroll-threshold@2.0.0/dist/v-scroll-threshold.min.js"></script>
	<script>var thresholdPlugin = VScrollThreshold;</script>
	<script src="//cdn.jsdelivr.net/npm/vue-sticky-element@1.1.2/dist/vue-sticky-element.min.js"></script>
	<!-- Docsify v4 -->
	<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
	<!-- docsify-themeable (latest v0.x.x) -->
	<script src="//cdn.jsdelivr.net/npm/docsify-themeable@0/dist/js/docsify-themeable.min.js"></script>
	<script src="//cdn.jsdelivr.net/npm/prismjs@1.x/components/prism-c.min.js"></script>
	<script src="//cdn.jsdelivr.net/npm/prismjs@1.x/components/prism-cpp.min.js"></script>
	<script src="//cdn.jsdelivr.net/npm/prismjs@1.x/components/prism-ini.min.js"></script>

	<script>
		Vue.use(VScrollThreshold);

		window.$docsify = {
			name: 'GP2040',
			repo: 'https://github.com/FeralAI/GP2040/',
			homepage: 'https://raw.githubusercontent.com/FeralAI/GP2040/main/README.md',
			loadNavbar: '_navbar.md',
			themeable: {
					readyTransition : true, // default
					responsiveTables: true  // default
			},
			vueGlobalOptions: {
				directives: {
					VScrollThreshold,
					VueStickyElement,
				},
				data() {
					return {
						labelData: {
							"GP2040": { "name": "GP2040", "Up": "Up", "Down": "Down", "Left": "Left", "Right": "Right", "B1": "B1", "B2": "B2", "B3": "B3", "B4": "B4", "L1": "L1", "R1": "R1", "L2": "L2", "R2": "R2", "S1": "S1", "S2": "S2", "L3": "L3", "R3": "R3", "A1": "A1", "A2": "A2" },
							"XInput": { "name": "XInput", "Up": "Up", "Down": "Down", "Left": "Left", "Right": "Right", "B1": "A", "B2": "B", "B3": "X", "B4": "Y", "L1": "LB", "R1": "RB", "L2": "LT", "R2": "RT", "S1": "Back", "S2": "Start", "L3": "LS", "R3": "RS", "A1": "Guide", "A2": "(A2)" },
							"DirectInput": { "name": "DirectInput", "Up": "Up", "Down": "Down", "Left": "Left", "Right": "Right", "B1": "2", "B2": "3", "B3": "1", "B4": "4", "L1": "5", "R1": "6", "L2": "7", "R2": "8", "S1": "9", "S2": "10", "L3": "11", "R3": "12", "A1": "13", "A2": "14" },
							"Nintendo Switch": { "name": "Nintendo Switch", "Up": "Up", "Down": "Down", "Left": "Left", "Right": "Right", "B1": "B", "B2": "A", "B3": "Y", "B4": "X", "L1": "L", "R1": "R", "L2": "ZL", "R2": "ZR", "S1": "Minus", "S2": "Plus", "L3": "LS", "R3": "RS", "A1": "Home", "A2": "Capture" },
							"PS3":    { "name": "PS3", "Up": "Up", "Down": "Down", "Left": "Left", "Right": "Right", "B1": "Cross", "B2": "Circle", "B3": "Square", "B4": "Triangle", "L1": "L1", "R1": "R1", "L2": "L2", "R2": "R2", "S1": "Select", "S2": "Start", "L3": "L3", "R3": "R3", "A1": "(A1)", "A2": "(A2)" },
							"Arcade": { "name": "Arcade", "Up": "Up", "Down": "Down", "Left": "Left", "Right": "Right", "B1": "K1", "B2": "K2", "B3": "P1", "B4": "P2", "L1": "P4", "R1": "P3", "L2": "K4", "R2": "K3", "S1": "Select", "S2": "Start", "L3": "LS", "R3": "RS", "A1": "Home", "A2": "(A2)" },
						},
						selectedLabels: localStorage.getItem('selectedLabels') || 'GP2040',
					}
				},
			},
			vueComponents: {
				'label-selector': {
					template: `
						<VueStickyElement visibleOnDirection="disabled" class="label-select" stuckClass="sticky-label-select">
							<div class="form-dropdown">
								<select name="labelSelect" v-model="state.selectedLabels" v-on:change="setSelectedLabels">
									<option v-for="item in labelData" :key="item.name" v-bind:value="item.name">{{ item.name }}</option>
								</select>
							</div>
						</VueStickyElement>
					`,
					data() {
						return {
							state: this.$root.$data,
							labelData: this.$root.labelData,
						};
					},
					methods: {
						setSelectedLabels($event) {
							localStorage.setItem('selectedLabels', $event.target.value);
							this.$root.$data.selectedLabels = $event.target.value;
						},
					},
				},

				'hotkey': {
					props: ['buttons'],
					template: `
						<strong>
							<code class="hotkey">{{ buttons.reduce((prev, next) => prev ? prev + " + " + labelData[state.selectedLabels][next] : labelData[state.selectedLabels][next] , "") }}</code>
						</strong>
					`,
					data() {
						return {
							state: this.$root.$data,
							labelData: this.$root.labelData,
						};
					},
				},
			}
		};
	</script>
</body>
</html>


================================================
FILE: docs/usage.md
================================================
# GP2040 Usage

Select the button labels to be displayed in the usage guide: <label-selector></label-selector>

## Buttons

GP2040 uses a generic button labeling for gamepad state, which is then converted to the appropriate input type before sending. This table provides a map of GP2040 buttons to the supported input types and layouts:

| GP2040  | XInput | Switch  | PS3          | DirectInput  | Arcade |
| ------- | ------ | ------- | ------------ | ------------ | ------ |
| B1      | A      | B       | Cross        | 2            | K1     |
| B2      | B      | A       | Circle       | 3            | K2     |
| B3      | X      | Y       | Square       | 1            | P1     |
| B4      | Y      | X       | Triangle     | 4            | P2     |
| L1      | LB     | L       | L1           | 5            | P4     |
| R1      | RB     | R       | R1           | 6            | P3     |
| L2      | LT     | ZL      | L2           | 7            | K4     |
| R2      | RT     | ZR      | R2           | 8            | K3     |
| S1      | Back   | Minus   | Select       | 9            | Coin   |
| S2      | Start  | Plus    | Start        | 10           | Start  |
| L3      | LS     | LS      | L3           | 11           | LS     |
| R3      | RS     | RS      | R3           | 12           | RS     |
| A1      | Guide  | Home    | -            | 13           | -      |
| A2      | -      | Capture | -            | 14           | -      |

If you do not have a dedicated Home button, you can activate it via the <hotkey v-bind:buttons='["S1", "S2", "Up"]'></hotkey> button combination.

## Input Modes

To change the input mode, **hold one of the following buttons as the controller is plugged in:**

* <hotkey v-bind:buttons='["B1"]'></hotkey> for Nintendo Switch
* <hotkey v-bind:buttons='["B2"]'></hotkey> for XInput
* <hotkey v-bind:buttons='["B3"]'></hotkey> for DirectInput/PS3

Input mode is saved across power cycles.

## D-Pad Modes

You can switch between the 3 modes for the D-Pad **while the controller is in use by pressing one of the following combinations:**

* <hotkey v-bind:buttons='["S1", "S2", "Down"]'></hotkey> - D-Pad
* <hotkey v-bind:buttons='["S1", "S2", "Left"]'></hotkey> - Emulate Left Analog stick
* <hotkey v-bind:buttons='["S1", "S2", "Right"]'></hotkey> - Emulate Right Analog stick

D-Pad mode is saved across power cycles.

## SOCD Modes

Simultaneous Opposite Cardinal Direction (SOCD) cleaning will ensure the controller doesn't send invalid directional inputs to the computer/console, like Left + Right at the same time. There are 3 modes to choose from **while the controller is in use by pressing one of the following combinations:**

* <hotkey v-bind:buttons='["S2", "A1", "Up"]'></hotkey> - **Up Priority mode**: Up + Down = Up, Left + Right = Neutral (Hitbox behavior)
* <hotkey v-bind:buttons='["S2", "A1", "Down"]'></hotkey> - **Neutral mode**: Up + Down = Neutral, Left + Right = Neutral
* <hotkey v-bind:buttons='["S2", "A1", "Left"]'></hotkey> - **Last Input Priority (Last Win)**: Hold Up then hold Down = Down, then release and re-press Up = Up. Applies to both axes.

SOCD mode is saved across power cycles.

## Invert D-Pad Y-axis

A toggle is available to invert the Y-axis input of the D-pad, allowing some additional input flexibility. To toggle, press <hotkey v-bind:buttons='["S2", "A1", "Right"]'></hotkey>. This is a temporary hotkey mapping for this feature, so keep an eye on updated releases for this to change.

## RGB LEDs

> LED modes are available on the Pico Fighting Board, Crush Counter/OSFRD and custom builds only.

### RGB LED Animations

The following animations are available:

| Name | Description | LED Parameter |
| - | - | - |
| Off | Turn off per-button RGB LEDs | - |
| Static Color | Sets all LEDs to the same color | Cycle through colors: *Red*, *Orange*, *Yellow*, *Lime Green*, *Green*, *Seafoam*, *Aqua*, *Sky Blue*, *Blue*, *Purple*, *Pink*, *Magenta* |
| Rainbow Cycle | All LEDs cycle through the color wheel displaying the same color | Adjust animation speed |
| Rainbow Chase | A fading, rainbow cycling lines travels across the LED chain | Adjust animation speed |
| Static Theme | Set the LEDs to a pre-defined static theme | Cycle through themes, see [RGB LED Static Themes](#rgb-led-static-themes) for details. |

### RGB LED Hotkeys

| Hotkey | Description |
| - | - |
| <hotkey v-bind:buttons='["S1", "S2", "B3"]'></hotkey> | Next Animation |
| <hotkey v-bind:buttons='["S1", "S2", "B1"]'></hotkey> | Previous Animation |
| <hotkey v-bind:buttons='["S1", "S2", "B4"]'></hotkey> | Brightness Up |
| <hotkey v-bind:buttons='["S1", "S2", "B2"]'></hotkey> | Brightness Down |
| <hotkey v-bind:buttons='["S1", "S2", "R1"]'></hotkey> | LED Parameter Up |
| <hotkey v-bind:buttons='["S1", "S2", "R2"]'></hotkey> | LED Parameter Down |
| <hotkey v-bind:buttons='["S1", "S2", "L1"]'></hotkey> | Pressed Parameter Up |
| <hotkey v-bind:buttons='["S1", "S2", "L2"]'></hotkey> | Pressed Parameter Down |

The `LED Parameter` hotkeys may affect color, speed or theme depending on the current RGB LED animation. The `Pressed Parameter` options will change the colors/effects for the on-press animations.

### RGB LED Static Themes

| Name | Preview |
| - | - |
| **Static Rainbow** | ![Static Rainbow](./assets/images/led-themes/static-rainbow.png) |
| **Xbox** | ![Xbox](./assets/images/led-themes/xbox.png) |
| **Xbox (All)** | ![Xbox (All)](./assets/images/led-themes/xbox-all.png) |
| **Super Famicom** | ![Super Famicom](./assets/images/led-themes/super-famicom.png) |
| **Super Famicom (All)** | ![Super Famicom (All)](./assets/images/led-themes/super-famicom-all.png) |
| **PlayStation** | ![Xbox](./assets/images/led-themes/playstation.png) |
| **PlayStation (All)** | ![Xbox (All)](./assets/images/led-themes/playstation-all.png) |
| **Neo Geo Straight** | ![Neo Geo Classic](./assets/images/led-themes/neogeo-straight.png) |
| **Neo Geo Curved** | ![Neo Geo Curved](./assets/images/led-themes/neogeo-curved.png) |
| **Neo Geo Modern** | ![Neo Geo Modern](./assets/images/led-themes/neogeo-modern.png) |
| **Six Button Fighter** | ![Six Button Fighter](./assets/images/led-themes/six-button-fighter.png) |
| **Six Button Fighter +** | ![Six Button Fighter +](./assets/images/led-themes/six-button-fighter-plus.png) |
| **Street Fighter 2** | ![Street Fighter 2](./assets/images/led-themes/street-fighter-2.png) |
| **Guilty Gear Type-A** | ![Guilty Gear Type-A](./assets/images/led-themes/guilty-gear-type-a.png) |
| **Guilty Gear Type-B** | ![Guilty Gear Type-B](./assets/images/led-themes/guilty-gear-type-b.png) |
| **Guilty Gear Type-C** | ![Guilty Gear Type-C](./assets/images/led-themes/guilty-gear-type-c.png) |
| **Guilty Gear Type-D** | ![Guilty Gear Type-D](./assets/images/led-themes/guilty-gear-type-d.png) |
| **Guilty Gear Type-E** | ![Guilty Gear Type-E](./assets/images/led-themes/guilty-gear-type-e.png) |


================================================
FILE: docs/web-configurator.md
================================================
# GP2040 Web Configurator

Select the button labels to be displayed in the web configurator guide: <label-selector></label-selector>

GP2040 contains a built-in web-based configuration application which can be started up by holding <hotkey v-bind:buttons='["S2"]'></hotkey> when plugging your controller into a PC. Then access <http://192.168.7.1> to begin configuration.

## Home

![GP2040 Configurator - Home](assets/images/gpc-home.png)

Here you can see the current version of your firmware and the latest version available on Github in the releases section. If a firmware update is available, a link to that release will appear.

The options in the main menu are:

* [Home](#home) - The start page
* [Settings](#settings) - Adjust settings like input mode, d-pad mode, etc.
* [Configuration > Pin Mapping](#pin-mapping) - Allows for remapping of GPIO pins to different buttons.
* [Configuration > LED Configuration](#led-configuration) - Enable and configure RGB LEDs here.
* Links - Useful links to the project and documentation
* [DANGER ZONE](#danger-zone) - Don't be afraid of the big red button. If something becomes misconfigured, you can reset your settings here.

## Settings

![GP2040 Configurator - Settings](assets/images/gpc-settings.png)

Here you can select the basic settings which are normally available via hotkeys.

## Pin Mapping

![GP2040 Configurator - Pin Mapping](assets/images/gpc-pin-mapping.png)

Here you can remap the GP2040 buttons to different GPIO pins on the RP2040 chip. This can be used to simply remap buttons, or bypass a GPIO pin that may have issues on your device.

## LED Configuration

If you have a setup with per-button RGB LEDs, they can be configured here.

![GP2040 Configurator - LED Configuration](assets/images/gpc-rgb-led-config.png)

* `Data Pin` - The GPIO pin that will drive the data line for your RGB LED chain. Set to `-1` to disable RGB LEDs.
* `LED Format` - The data format used to communicate with your RGB LEDs. If unsure the default `GRB` value is usually safe.
* `LED Layout` - Select the layout for your controls/buttons. This is used for static themes and some per-button animations.
* `LEDs Per Button` - Set the number of LEDs in each button on your chain.
* `Max Brightness` - Set the maximum brightness for the LEDs. Ranges from 0-255.
* `Brightness Steps` - The number of levels of brightness to cycle through when turning brightness up and down.
* `LED Button Order` - Configure which buttons and what order they reside on the LED chain.

## Display Configuration

![GP2040 Configurator - Display Configuration](assets/images/gpc-display-config.png)

* `Use Display` - Turns on/off the display module.
* `I2C Block` - The Pico I2C block that will be used. Set based on pins, refer to table on page.
* `SDA Pin` - The GPIO pin used for the I2C SDA channel.
* `SCL Pin` - The GPIO pin used for the I2C SCL channel.
* `I2C Address` - The I2C address of your device, defaults to the very commonly used `0x3C`
* `I2C Speed` - Sets the speed of I2C communication. Common values are `100000` for standard, `400000` for fast and `800000` ludicrous speed.
* `Flip Display` - Rotates the display 180°.
* `Invert Display` - Inverts the pixel colors, effectively giving you a negative image when enabled.

## DANGER ZONE

![GP2040 Configurator - Reset Settings](assets/images/gpc-reset-settings.png)


================================================
FILE: include/display.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef DISPLAY_H_
#define DISPLAY_H_

#include <hardware/i2c.h>
#include "OneBitDisplay.h"
#include "BoardConfig.h"
#include "gp2040.h"
#include "gamepad.h"

#ifndef BUTTON_LAYOUT
#define BUTTON_LAYOUT BUTTON_LAYOUT_ARCADE
#endif

#ifndef HAS_I2C_DISPLAY
#define HAS_I2C_DISPLAY 0
#endif

#ifndef DISPLAY_I2C_ADDR
#define DISPLAY_I2C_ADDR 0x3C
#endif

#ifndef DISPLAY_SIZE
#define DISPLAY_SIZE OLED_128x64
#endif

#ifndef DISPLAY_FLIP
#define DISPLAY_FLIP 0
#endif

#ifndef DISPLAY_INVERT
#define DISPLAY_INVERT 0
#endif

#ifndef DISPLAY_USEWIRE
#define DISPLAY_USEWIRE 1
#endif

#ifndef I2C_SDA_PIN
#define I2C_SDA_PIN -1
#endif

#ifndef I2C_SCL_PIN
#define I2C_SCL_PIN -1
#endif

#ifndef I2C_BLOCK
#define I2C_BLOCK i2c0
#endif

#ifndef I2C_SPEED
#define I2C_SPEED 400000
#endif

class DisplayModule : public GPModule
{
public:
	void setup();
	void loop();
	void process(Gamepad *gamepad);
};

#endif


================================================
FILE: include/enums.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef ENUMS_H_
#define ENUMS_H_

typedef enum
{
	BUTTON_LAYOUT_ARCADE,
	BUTTON_LAYOUT_HITBOX,
	BUTTON_LAYOUT_WASD,
} ButtonLayout;

#endif


================================================
FILE: include/gamepad.h
================================================
#ifndef _GAMEPAD_H_
#define _GAMEPAD_H_

#include "BoardConfig.h"
#include <string.h>
#include <MPGS.h>
#include "pico/stdlib.h"
#include "storage.h"

#define GAMEPAD_FEATURE_REPORT_SIZE 32

struct GamepadButtonMapping
{
	GamepadButtonMapping(uint8_t p, uint16_t bm) : pin(p), pinMask((1 << p)), buttonMask(bm) {}

	uint8_t pin;
	uint32_t pinMask;
	const uint16_t buttonMask;

	inline void setPin(uint8_t p)
	{
		pin = p;
		pinMask = 1 << p;
	}
};

class Gamepad : public MPGS
{
public:
	Gamepad(int debounceMS = 5, GamepadStorage *storage = &GamepadStore)
			: MPGS(debounceMS, storage) {}

	void setup();
	void read();

	void process()
	{
		memcpy(&rawState, &state, sizeof(GamepadState));
		MPGS::process();
	}

	inline bool __attribute__((always_inline)) pressedF1()
	{
#ifdef PIN_SETTINGS
		return state.aux & (1 << 0);
#else
		return MPGS::pressedF1();
#endif
	}

	GamepadState rawState;

	GamepadButtonMapping *mapDpadUp;
	GamepadButtonMapping *mapDpadDown;
	GamepadButtonMapping *mapDpadLeft;
	GamepadButtonMapping *mapDpadRight;
	GamepadButtonMapping *mapButtonB1;
	GamepadButtonMapping *mapButtonB2;
	GamepadButtonMapping *mapButtonB3;
	GamepadButtonMapping *mapButtonB4;
	GamepadButtonMapping *mapButtonL1;
	GamepadButtonMapping *mapButtonR1;
	GamepadButtonMapping *mapButtonL2;
	GamepadButtonMapping *mapButtonR2;
	GamepadButtonMapping *mapButtonS1;
	GamepadButtonMapping *mapButtonS2;
	GamepadButtonMapping *mapButtonL3;
	GamepadButtonMapping *mapButtonR3;
	GamepadButtonMapping *mapButtonA1;
	GamepadButtonMapping *mapButtonA2;

	GamepadButtonMapping **gamepadMappings;
};

#endif


================================================
FILE: include/gp2040.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef GP2040_H_
#define GP2040_H_

#include <stdint.h>
#include <string>
#include "pico/stdlib.h"
#include "gamepad.h"

using namespace std;

const string BUTTON_LABEL_UP = "Up";
const string BUTTON_LABEL_DOWN = "Down";
const string BUTTON_LABEL_LEFT = "Left";
const string BUTTON_LABEL_RIGHT = "Right";
const string BUTTON_LABEL_B1 = "B1";
const string BUTTON_LABEL_B2 = "B2";
const string BUTTON_LABEL_B3 = "B3";
const string BUTTON_LABEL_B4 = "B4";
const string BUTTON_LABEL_L1 = "L1";
const string BUTTON_LABEL_R1 = "R1";
const string BUTTON_LABEL_L2 = "L2";
const string BUTTON_LABEL_R2 = "R2";
const string BUTTON_LABEL_S1 = "S1";
const string BUTTON_LABEL_S2 = "S2";
const string BUTTON_LABEL_L3 = "L3";
const string BUTTON_LABEL_R3 = "R3";
const string BUTTON_LABEL_A1 = "A1";
const string BUTTON_LABEL_A2 = "A2";

class GPModule
{
public:
	virtual void setup() = 0;
	virtual void loop() = 0;
	virtual void process(Gamepad *gamepad) = 0;
	absolute_time_t nextRunTime;
	const uint32_t intervalMS = 10;
	inline bool isEnabled() { return enabled; }
protected:
	bool enabled = true;
};

#endif


================================================
FILE: include/leds.h
================================================
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
*/

#ifndef LEDS_H_
#define LEDS_H_

#include "BoardConfig.h"
#include <vector>
#include "AnimationStation.hpp"
#include "NeoPico.hpp"
#include "gamepad.h"
#include "enums.h"
#include "gp2040.h"

#ifndef BOARD_LEDS_PIN
#define BOARD_LEDS_PIN -1
#endif

#ifndef BUTTON_LAYOUT
#define BUTTON_LAYOUT BUTTON_LAYOUT_ARCADE
#endif

#ifndef LED_FORMAT
#define LED_FORMAT LED_FORMAT_GRB
#endif

#ifndef LEDS_PER_PIXEL
#define LEDS_PER_PIXEL 1
#endif

#ifndef LEDS_BRIGHTNESS
#define LEDS_BRIGHTNESS 75
#endif

#ifndef LEDS_BASE_ANIMATION_INDEX
#define LEDS_BASE_ANIMATION_INDEX 1
#endif

#ifndef LEDS_STATIC_COLOR_INDEX
#define LEDS_STATIC_COLOR_INDEX 2
#endif

#ifndef LEDS_BUTTON_COLOR_INDEX
#define LEDS_BUTTON_COLOR_INDEX 1
#endif

#ifndef LEDS_THEME_INDEX
#define LEDS_THEME_INDEX 0
#endif

#ifndef LEDS_RAINBOW_CYCLE_TIME
#define LEDS_RAINBOW_CYCLE_TIME 40
#endif

#ifndef LEDS_CHASE_CYCLE_TIME
#define LEDS_CHASE_CYCLE_TIME 85
#endif

#ifndef LED_BRIGHTNESS_MAXIMUM
#define LED_BRIGHTNESS_MAXIMUM 128
#endif

#ifndef LED_BRIGHTNESS_STEPS
#define LED_BRIGHTNESS_STEPS 5
#endif

#ifndef LEDS_DPAD_LEFT
#define LEDS_DPAD_LEFT  -1
#endif

#ifndef LEDS_DPAD_DOWN
#define LEDS_DPAD_DOWN  -1
#endif

#ifndef LEDS_DPAD_RIGHT
#define LEDS_DPAD_RIGHT -1
#endif

#ifndef LEDS_DPAD_UP
#define LEDS_DPAD_UP    -1
#endif

#ifndef LEDS_BUTTON_B1
#define LEDS_BUTTON_B1  -1
#endif

#ifndef LEDS_BUTTON_B2
#define LEDS_BUTTON_B2  -1
#endif

#ifndef LEDS_BUTTON_B3
#define LEDS_BUTTON_B3  -1
#endif

#ifndef LEDS_BUTTON_B4
#define LEDS_BUTTON_B4  -1
#endif

#ifndef LEDS_BUTTON_R1
#define LEDS_BUTTON_R1  -1
#endif

#ifndef LEDS_BUTTON_L1
#define LEDS_BUTTON_L1  -1
#endif

#ifndef LEDS_BUTTON_L2
#define LEDS_BUTTON_L2  -1
#endif

#ifndef LEDS_BUTTON_R2
#define LEDS_BUTTON_R2  -1
#endif

#ifndef LEDS_BUTTON_S1
#define LEDS_BUTTON_S1  -1
#endif

#ifndef LEDS_BUTTON_S2
#define LEDS_BUTTON_S2  -1
#endif

#ifndef LEDS_BUTTON_L3
#define LEDS_BUTTON_L3  -1
#endif

#ifndef LEDS_BUTTON_R3
#define LEDS_BUTTON_R3  -1
#endif

#ifndef LEDS_BUTTON_A1
#define LEDS_BUTTON_A1  -1
#endif

#ifndef LEDS_BUTTON_A2
#define LEDS_BUTTON_A2  -1
#endif

void configureAnimations(AnimationStation *as);
AnimationHotkey animationHotkeys(Gamepad *gamepad);
void configureLEDs(LEDOptions ledOptions);
PixelMatrix createLedButtonLayout(ButtonLayout layout, int ledsPerPixel);
PixelMatrix createLedButtonLayout(ButtonLayout layout, std::vector<uint8_t> *positions);

class LEDModule : public GPModule {
public:
	void setup();
	void loop();
	void process(Gamepad *gamepad);
	void trySave();
	void configureLEDs();
	uint32_t frame[100];
	LEDOptions ledOptions;
};

extern LEDModule ledModule;

#endif


================================================
FILE: include/pico/config_autogen.h
================================================
#ifndef CONFIG_AUTOGEN_H_
#define CONFIG_AUTOGEN_H_

// Boards supported in PlatformIO build configuration

#ifdef BOARD_ADAFRUIT_FEATHER_RP2040
#include "boards/adafruit_feather_rp2040.h"
#elif BOARD_ADAFRUIT_ITSYBITSY_RP2040
#include "boards/adafruit_itsybitsy_rp2040.h"
#elif BOARD_SPARKFUN_MICRO_RP2040
#include "boards/sparkfun_promicro.h"
#elif BOARD_SPARKFUN_THING_PLUS
#include "boards/sparkfun_thingplus.h"
#elif BOARD_ARDUINO_NANO_CONNECT
#include "boards/arduino_nano_rp2040_connect.h"
#elif BOARD_PIMORONI_PICO_LIPO
#include "boards/pimoroni_picolipo_4mb.h"
#else
#include "boards/pico.h"
#endif

#endif


================================================
FILE: include/pleds.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef PLEDS_H_
#define PLEDS_H_

#include "BoardConfig.h"
#include <stdint.h>
#include "AnimationStation.hpp"
#include "PlayerLEDs.h"
#include "gp2040.h"

#define PLED_REPORT_SIZE 32

#ifndef PLED1_PIN
#define PLED1_PIN -1
#endif
#ifndef PLED2_PIN
#define PLED2_PIN -1
#endif
#ifndef PLED3_PIN
#define PLED3_PIN -1
#endif
#ifndef PLED4_PIN
#define PLED4_PIN -1
#endif
#ifndef PLED_TYPE
#define PLED_TYPE PLED_TYPE_NONE
#endif

#define PLED_MASK_ALL ((1U << PLED1_PIN) | (1U << PLED2_PIN) | (1U << PLED3_PIN) | (1U << PLED4_PIN))

extern NeoPico *neopico;
extern AnimationStation as;

class PWMPlayerLEDs : public PlayerLEDs
{
public:
	void setup();
	void display();
};

class RGBPlayerLEDs : public PlayerLEDs
{
public:
	void setup();
	void display();
};

class PLEDModule : public GPModule
{
public:
	PLEDModule(PLEDType type) : type(type) { }

	void setup();
	void loop();
	void process(Gamepad *gamepad);
	queue_t featureQueue;
protected:
	PLEDType type;
	PlayerLEDs *pleds = nullptr;
	PLEDAnimationState animationState;
};

extern PLEDModule pledModule;

#endif


================================================
FILE: include/storage.h
================================================
 /*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef STORAGE_H_
#define STORAGE_H_

#include <stdint.h>
#include "NeoPico.hpp"
#include "enums.h"

#define GAMEPAD_STORAGE_INDEX      0 // 1024 bytes for gamepad options
#define BOARD_STORAGE_INDEX     1024 //  512 bytes for hardware options
#define LED_STORAGE_INDEX       1536 //  512 bytes for LED configuration
#define ANIMATION_STORAGE_INDEX 2048 // ???? bytes for LED animations

struct BoardOptions
{
	bool hasBoardOptions;
	uint8_t pinDpadUp;
	uint8_t pinDpadDown;
	uint8_t pinDpadLeft;
	uint8_t pinDpadRight;
	uint8_t pinButtonB1;
	uint8_t pinButtonB2;
	uint8_t pinButtonB3;
	uint8_t pinButtonB4;
	uint8_t pinButtonL1;
	uint8_t pinButtonR1;
	uint8_t pinButtonL2;
	uint8_t pinButtonR2;
	uint8_t pinButtonS1;
	uint8_t pinButtonS2;
	uint8_t pinButtonL3;
	uint8_t pinButtonR3;
	uint8_t pinButtonA1;
	uint8_t pinButtonA2;
	ButtonLayout buttonLayout;

	int i2cSDAPin;
	int i2cSCLPin;
	int i2cBlock;
	uint32_t i2cSpeed;

	bool hasI2CDisplay;
	int displayI2CAddress;
	uint8_t displaySize;
	bool displayFlip;
	bool displayInvert;
	uint32_t checksum;
};

struct LEDOptions
{
	bool useUserDefinedLEDs;
	int dataPin;
	LEDFormat ledFormat;
	ButtonLayout ledLayout;
	uint8_t ledsPerButton;
	uint8_t brightnessMaximum;
	uint8_t brightnessSteps;
	int indexUp;
	int indexDown;
	int indexLeft;
	int indexRight;
	int indexB1;
	int indexB2;
	int indexB3;
	int indexB4;
	int indexL1;
	int indexR1;
	int indexL2;
	int indexR2;
	int indexS1;
	int indexS2;
	int indexL3;
	int indexR3;
	int indexA1;
	int indexA2;
};

BoardOptions getBoardOptions();
void setBoardOptions(BoardOptions options);

LEDOptions getLEDOptions();
void setLEDOptions(LEDOptions options);

#endif


================================================
FILE: include/themes.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef LED_THEMES_H_
#define LED_THEMES_H_

#include "BoardConfig.h"
#include <vector>
#include <MPG.h>
#include "AnimationStation.hpp"
#include "enums.h"

using namespace std;

static map<uint32_t, RGB> themeStaticRainbow({
	{ GAMEPAD_MASK_DL, ColorRed },
	{ GAMEPAD_MASK_DD, ColorOrange },
	{ GAMEPAD_MASK_DR, ColorYellow },
	{ GAMEPAD_MASK_DU, ColorOrange },
	{ GAMEPAD_MASK_B3, ColorGreen },
	{ GAMEPAD_MASK_B1, ColorGreen },
	{ GAMEPAD_MASK_B4, ColorAqua },
	{ GAMEPAD_MASK_B2, ColorAqua },
	{ GAMEPAD_MASK_R1, ColorBlue },
	{ GAMEPAD_MASK_R2, ColorBlue },
	{ GAMEPAD_MASK_L1, ColorMagenta },
	{ GAMEPAD_MASK_L2, ColorMagenta },
});

static map<uint32_t, RGB> themeGuiltyGearTypeA({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B1, ColorPink },
	{ GAMEPAD_MASK_B3, ColorBlue },
	{ GAMEPAD_MASK_B4, ColorGreen },
	{ GAMEPAD_MASK_R1, ColorRed },
	{ GAMEPAD_MASK_R2, ColorOrange },
});

static map<uint32_t, RGB> themeGuiltyGearTypeB({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B1, ColorRed },
	{ GAMEPAD_MASK_B3, ColorPink },
	{ GAMEPAD_MASK_B4, ColorBlue },
	{ GAMEPAD_MASK_R1, ColorGreen },
	{ GAMEPAD_MASK_R2, ColorOrange },
});

static map<uint32_t, RGB> themeGuiltyGearTypeC({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B1, ColorOrange },
	{ GAMEPAD_MASK_B3, ColorPink },
	{ GAMEPAD_MASK_B4, ColorBlue },
	{ GAMEPAD_MASK_R1, ColorGreen },
	{ GAMEPAD_MASK_R2, ColorRed },
});

static map<uint32_t, RGB> themeGuiltyGearTypeD({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B3, ColorPink },
	{ GAMEPAD_MASK_B1, ColorBlue },
	{ GAMEPAD_MASK_B4, ColorGreen },
	{ GAMEPAD_MASK_B2, ColorRed },
	{ GAMEPAD_MASK_R1, ColorOrange },
});

static map<uint32_t, RGB> themeGuiltyGearTypeE({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B3, ColorPink },
	{ GAMEPAD_MASK_B1, ColorGreen },
	{ GAMEPAD_MASK_B4, ColorBlue },
	{ GAMEPAD_MASK_B2, ColorRed },
	{ GAMEPAD_MASK_R1, ColorOrange },
});

static map<uint32_t, RGB> themeNeoGeo({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B3, ColorRed },
	{ GAMEPAD_MASK_B4, ColorYellow },
	{ GAMEPAD_MASK_R1, ColorGreen },
	{ GAMEPAD_MASK_L1, ColorBlue },
});

static map<uint32_t, RGB> themeNeoGeoCurved({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B1, ColorRed },
	{ GAMEPAD_MASK_B3, ColorYellow },
	{ GAMEPAD_MASK_B4, ColorGreen },
	{ GAMEPAD_MASK_R1, ColorBlue },
});

static map<uint32_t, RGB> themeNeoGeoModern({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B3, ColorRed },
	{ GAMEPAD_MASK_B1, ColorYellow },
	{ GAMEPAD_MASK_B4, ColorGreen },
	{ GAMEPAD_MASK_B2, ColorBlue },
});

static map<uint32_t, RGB> themeSixButtonFighter({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B3, ColorBlue },
	{ GAMEPAD_MASK_B1, ColorBlue },
	{ GAMEPAD_MASK_B4, ColorYellow },
	{ GAMEPAD_MASK_B2, ColorYellow },
	{ GAMEPAD_MASK_R1, ColorRed },
	{ GAMEPAD_MASK_R2, ColorRed },
});

static map<uint32_t, RGB> themeSixButtonFighterPlus({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B3, ColorBlue },
	{ GAMEPAD_MASK_B1, ColorBlue },
	{ GAMEPAD_MASK_B4, ColorYellow },
	{ GAMEPAD_MASK_B2, ColorYellow },
	{ GAMEPAD_MASK_R1, ColorRed },
	{ GAMEPAD_MASK_R2, ColorRed },
	{ GAMEPAD_MASK_L1, ColorGreen },
	{ GAMEPAD_MASK_L2, ColorGreen },
});

static map<uint32_t, RGB> themeStreetFighter2({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B3, ColorRed },
	{ GAMEPAD_MASK_B1, ColorRed },
	{ GAMEPAD_MASK_B4, ColorWhite },
	{ GAMEPAD_MASK_B2, ColorWhite },
	{ GAMEPAD_MASK_R1, ColorBlue },
	{ GAMEPAD_MASK_R2, ColorBlue },
	{ GAMEPAD_MASK_L1, ColorBlack },
	{ GAMEPAD_MASK_L2, ColorBlack },
});

static map<uint32_t, RGB> themeTekken({
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_B3, ColorYellow },
	{ GAMEPAD_MASK_B1, ColorAqua },
	{ GAMEPAD_MASK_B4, ColorGreen },
	{ GAMEPAD_MASK_B2, ColorPink },
	{ GAMEPAD_MASK_R1, ColorRed },
});

static map<uint32_t, RGB> themePlayStation({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B1, ColorBlue },
	{ GAMEPAD_MASK_B2, ColorRed },
	{ GAMEPAD_MASK_B3, ColorMagenta },
	{ GAMEPAD_MASK_B4, ColorGreen },
	{ GAMEPAD_MASK_R1, ColorBlack },
	{ GAMEPAD_MASK_R2, ColorBlack },
	{ GAMEPAD_MASK_L1, ColorBlack },
	{ GAMEPAD_MASK_L2, ColorBlack },
});

static map<uint32_t, RGB> themePlayStationAll({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B1, ColorBlue },
	{ GAMEPAD_MASK_B2, ColorRed },
	{ GAMEPAD_MASK_B3, ColorMagenta },
	{ GAMEPAD_MASK_B4, ColorGreen },
	{ GAMEPAD_MASK_R1, ColorWhite },
	{ GAMEPAD_MASK_R2, ColorWhite },
	{ GAMEPAD_MASK_L1, ColorWhite },
	{ GAMEPAD_MASK_L2, ColorWhite },
});

static map<uint32_t, RGB> themeSuperFamicom({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B1, ColorYellow },
	{ GAMEPAD_MASK_B2, ColorRed },
	{ GAMEPAD_MASK_B3, ColorGreen },
	{ GAMEPAD_MASK_B4, ColorBlue },
	{ GAMEPAD_MASK_R1, ColorBlack },
	{ GAMEPAD_MASK_R2, ColorBlack },
	{ GAMEPAD_MASK_L1, ColorBlack },
	{ GAMEPAD_MASK_L2, ColorBlack },
});

static map<uint32_t, RGB> themeSuperFamicomAll({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B1, ColorYellow },
	{ GAMEPAD_MASK_B2, ColorRed },
	{ GAMEPAD_MASK_B3, ColorGreen },
	{ GAMEPAD_MASK_B4, ColorBlue },
	{ GAMEPAD_MASK_R1, ColorWhite },
	{ GAMEPAD_MASK_R2, ColorWhite },
	{ GAMEPAD_MASK_L1, ColorWhite },
	{ GAMEPAD_MASK_L2, ColorWhite },
});

static map<uint32_t, RGB> themeXbox({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B1, ColorGreen },
	{ GAMEPAD_MASK_B2, ColorRed },
	{ GAMEPAD_MASK_B3, ColorBlue },
	{ GAMEPAD_MASK_B4, ColorYellow },
	{ GAMEPAD_MASK_R1, ColorBlack },
	{ GAMEPAD_MASK_R2, ColorBlack },
	{ GAMEPAD_MASK_L1, ColorBlack },
	{ GAMEPAD_MASK_L2, ColorBlack },
});

static map<uint32_t, RGB> themeXboxAll({
	{ GAMEPAD_MASK_DU, ColorWhite },
	{ GAMEPAD_MASK_DD, ColorWhite },
	{ GAMEPAD_MASK_DL, ColorWhite },
	{ GAMEPAD_MASK_DR, ColorWhite },
	{ GAMEPAD_MASK_B1, ColorGreen },
	{ GAMEPAD_MASK_B2, ColorRed },
	{ GAMEPAD_MASK_B3, ColorBlue },
	{ GAMEPAD_MASK_B4, ColorYellow },
	{ GAMEPAD_MASK_R1, ColorWhite },
	{ GAMEPAD_MASK_R2, ColorWhite },
	{ GAMEPAD_MASK_L1, ColorWhite },
	{ GAMEPAD_MASK_L2, ColorWhite },
});

void addStaticThemes(LEDOptions options)
{
	// Rainbow theme on a Hitbox layout should use green for up button
	themeStaticRainbow[GAMEPAD_MASK_DU] = (options.ledLayout == BUTTON_LAYOUT_HITBOX) ? ColorGreen : ColorOrange;

	StaticTheme::ClearThemes();

	StaticTheme::AddTheme(themeStaticRainbow);

	StaticTheme::AddTheme(themeXbox);
	StaticTheme::AddTheme(themeXboxAll);
	StaticTheme::AddTheme(themeSuperFamicom);
	StaticTheme::AddTheme(themeSuperFamicomAll);
	StaticTheme::AddTheme(themePlayStation);
	StaticTheme::AddTheme(themePlayStationAll);

	StaticTheme::AddTheme(themeNeoGeo);
	StaticTheme::AddTheme(themeNeoGeoCurved);
	StaticTheme::AddTheme(themeNeoGeoModern);
	StaticTheme::AddTheme(themeSixButtonFighter);
	StaticTheme::AddTheme(themeSixButtonFighterPlus);

	StaticTheme::AddTheme(themeStreetFighter2);
	StaticTheme::AddTheme(themeTekken);
	StaticTheme::AddTheme(themeGuiltyGearTypeA);
	StaticTheme::AddTheme(themeGuiltyGearTypeB);
	StaticTheme::AddTheme(themeGuiltyGearTypeC);
	StaticTheme::AddTheme(themeGuiltyGearTypeD);
	StaticTheme::AddTheme(themeGuiltyGearTypeE);
}

#endif


================================================
FILE: include/tusb_config.h
================================================
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Ha Thach (tinyusb.org)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_

#ifdef __cplusplus
 extern "C" {
#endif

//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------

// defined by board.mk
#ifndef CFG_TUSB_MCU
  #error CFG_TUSB_MCU must be defined
#endif

// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_DEVICE_RHPORT_NUM
  #define BOARD_DEVICE_RHPORT_NUM     0
#endif

// RHPort max operational speed can defined by board.mk
// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
#ifndef BOARD_DEVICE_RHPORT_SPEED
  #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \
       CFG_TUSB_MCU == OPT_MCU_NUC505  || CFG_TUSB_MCU == OPT_MCU_CXD56)
    #define BOARD_DEVICE_RHPORT_SPEED   OPT_MODE_HIGH_SPEED
  #else
    #define BOARD_DEVICE_RHPORT_SPEED   OPT_MODE_FULL_SPEED
  #endif
#endif

// Device mode with rhport and speed defined by board.mk
#if   BOARD_DEVICE_RHPORT_NUM == 0
  #define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
#elif BOARD_DEVICE_RHPORT_NUM == 1
  #define CFG_TUSB_RHPORT1_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
#else
  #error "Incorrect RHPort configuration"
#endif

#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS               OPT_OS_PICO
#endif

// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
// #define CFG_TUSB_DEBUG           0

/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
 * Tinyusb use follows macros to declare transferring memory so that they can be put
 * into those specific section.
 * e.g
 * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
 * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
 */
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif

#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN          __attribute__ ((aligned(4)))
#endif

//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------

#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE    64
#endif

//------------- CLASS -------------//
#define CFG_TUD_CDC               0
#define CFG_TUD_MSC               0
#define CFG_TUD_HID               2
#define CFG_TUD_MIDI              0
#define CFG_TUD_VENDOR            0
#define CFG_TUD_NET               1

// HID buffer size Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_EP_BUFSIZE    64

#ifdef __cplusplus
 }
#endif

#endif /* _TUSB_CONFIG_H_ */


================================================
FILE: lib/AnimationStation/.editorconfig
================================================
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
indent_style = space
indent_size = 2
trim_trailing_whitespace = false


================================================
FILE: lib/AnimationStation/library.json
================================================
{
	"name": "AnimationStation",
	"version": "0.0.1",
	"description": "LED Handling for GP2040",
	"keywords": "c c++ baremetal neopixel led",
	"authors": [
		{
			"name": "Jonathan Barket",
			"url": "https://jonathanbarket.com"
		}
	],
	"license": "MIT"
}


================================================
FILE: lib/AnimationStation/src/Animation.cpp
================================================
#include "Animation.hpp"

LEDFormat Animation::format;

Animation::Animation(PixelMatrix &matrix) : matrix(&matrix) {
}

void Animation::UpdatePixels(std::vector<Pixel> pixels) {
  this->pixels = pixels;
}

void Animation::ClearPixels() {
  this->pixels.clear();
}

/* Some of these animations are filtered to specific pixels, such as button press animations.
This somewhat backwards named method determines if a specific pixel is _not_ included in the filter */
bool Animation::notInFilter(Pixel pixel) {
  if (!this->filtered) {
    return false;
  }

  for (size_t i = 0; i < this->pixels.size(); i++) {
    if (pixel == this->pixels.at(i)) {
      return false;
    }
  }

  return true;
}


================================================
FILE: lib/AnimationStation/src/Animation.hpp
================================================
#ifndef _ANIMATION_H_
#define _ANIMATION_H_

#include "Pixel.hpp"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include "NeoPico.hpp"

struct RGB {
  RGB() : r(0), g(0), b(0) {}

  RGB(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b), w(0) {}

  RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w)
    : r(r), g(g), b(b), w(w) { }

  uint8_t r;
  uint8_t g;
  uint8_t b;
  uint8_t w;

  inline static RGB wheel(uint8_t pos) {
    pos = 255 - pos;
    if (pos < 85) {
      return RGB(255 - pos * 3, 0, pos * 3);
    } else if (pos < 170) {
      pos -= 85;
      return RGB(0, pos * 3, 255 - pos * 3);
    } else {
      pos -= 170;
      return RGB(pos * 3, 255 - pos * 3, 0);
    }
  }

  inline uint32_t value(LEDFormat format, float brightnessX = 1.0F) {
    switch (format) {
      case LED_FORMAT_GRB:
        return ((uint32_t)(g * brightnessX) << 16)
            | ((uint32_t)(r * brightnessX) << 8)
            | (uint32_t)(b * brightnessX);

      case LED_FORMAT_RGB:
        return ((uint32_t)(r * brightnessX) << 16)
            | ((uint32_t)(g * brightnessX) << 8)
            | (uint32_t)(b * brightnessX);

      case LED_FORMAT_GRBW:
      {
        if ((r == g) && (r == b))
          return (uint32_t)(r * brightnessX);

        return ((uint32_t)(g * brightnessX) << 24)
            | ((uint32_t)(r * brightnessX) << 16)
            | ((uint32_t)(b * brightnessX) << 8)
            | (uint32_t)(w * brightnessX);
      }

      case LED_FORMAT_RGBW:
      {
        if ((r == g) && (r == b))
          return (uint32_t)(r * brightnessX);

        return ((uint32_t)(r * brightnessX) << 24)
            | ((uint32_t)(g * brightnessX) << 16)
            | ((uint32_t)(b * brightnessX) << 8)
            | (uint32_t)(w * brightnessX);
      }
    }
  }
};

static const RGB ColorBlack(0, 0, 0);
static const RGB ColorWhite(255, 255, 255);
static const RGB ColorRed(255, 0, 0);
static const RGB ColorOrange(255, 128, 0);
static const RGB ColorYellow(255, 255, 0);
static const RGB ColorLimeGreen(128, 255, 0);
static const RGB ColorGreen(0, 255, 0);
static const RGB ColorSeafoam(0, 255, 128);
static const RGB ColorAqua(0, 255, 255);
static const RGB ColorSkyBlue(0, 128, 255);
static const RGB ColorBlue(0, 0, 255);
static const RGB ColorPurple(128, 0, 255);
static const RGB ColorPink(255, 0, 255);
static const RGB ColorMagenta(255, 0, 128);

static const std::vector<RGB> colors = {
    ColorBlack,     ColorWhite,  ColorRed,     ColorOrange, ColorYellow,
    ColorLimeGreen, ColorGreen,  ColorSeafoam, ColorAqua,   ColorSkyBlue,
    ColorBlue,      ColorPurple, ColorPink,    ColorMagenta};

class Animation {
public:
  Animation(PixelMatrix &matrix);
  void UpdatePixels(std::vector<Pixel> pixels);
  void ClearPixels();
  virtual ~Animation(){};

  static LEDFormat format;

  bool notInFilter(Pixel pixel);
  virtual void Animate(RGB (&frame)[100]) = 0;
  virtual void ParameterUp() = 0;
  virtual void ParameterDown() = 0;

protected:
/* We track both the full matrix as well as individual pixels here to support
button press changes. Rather than adjusting the matrix to represent a subset of pixels,
we provide a subset of pixels to use as a filter. */
  PixelMatrix *matrix;
  std::vector<Pixel> pixels;
  bool filtered = false;
};

#endif


================================================
FILE: lib/AnimationStation/src/AnimationStation.cpp
================================================
/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * Modified by Jonathan Barket - 2021
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "AnimationStation.hpp"

uint8_t AnimationStation::brightnessMax = 100;
uint8_t AnimationStation::brightnessSteps = 5;
float AnimationStation::brightnessX = 0;
absolute_time_t AnimationStation::nextChange = 0;
AnimationOptions AnimationStation::options = {};


AnimationStation::AnimationStation() {
  AnimationStation::SetBrightness(1);
}

void AnimationStation::ConfigureBrightness(uint8_t max, uint8_t steps) {
  brightnessMax = max;
  brightnessSteps = steps;
}

void AnimationStation::HandleEvent(AnimationHotkey action) {
  if (action == HOTKEY_LEDS_NONE || !time_reached(AnimationStation::nextChange)) {
    return;
  }

  if (action == HOTKEY_LEDS_BRIGHTNESS_UP) {
    AnimationStation::IncreaseBrightness();
  }

  if (action == HOTKEY_LEDS_BRIGHTNESS_DOWN) {
    AnimationStation::DecreaseBrightness();
  }

  if (action == HOTKEY_LEDS_ANIMATION_UP) {
    ChangeAnimation(1);
  }

  if (action == HOTKEY_LEDS_ANIMATION_DOWN) {
    ChangeAnimation(-1);
  }

  if (action == HOTKEY_LEDS_PARAMETER_UP) {
    this->baseAnimation->ParameterUp();
  }

  if (action == HOTKEY_LEDS_PARAMETER_DOWN) {
    this->baseAnimation->ParameterDown();
  }

  if (action == HOTKEY_LEDS_PRESS_PARAMETER_UP) {
    this->buttonAnimation->ParameterUp();
  }

  if (action == HOTKEY_LEDS_PRESS_PARAMETER_DOWN) {
    this->buttonAnimation->ParameterDown();
  }

  AnimationStation::nextChange = make_timeout_time_ms(250);
}

void AnimationStation::ChangeAnimation(int changeSize) {
  this->SetMode(this->AdjustIndex(changeSize));
}

uint16_t AnimationStation::AdjustIndex(int changeSize) {
  uint16_t newIndex = this->options.baseAnimationIndex + changeSize;

  if (newIndex >= TOTAL_EFFECTS) {
    return 0;
  }

  if (newIndex < 0) {
    return (TOTAL_EFFECTS - 1);
  }

  return newIndex;
}

void AnimationStation::HandlePressed(std::vector<Pixel> pressed) {
  if (pressed != this->lastPressed) {
    this->lastPressed = pressed;
    if (this->buttonAnimation == nullptr)
      this->buttonAnimation = new StaticColor(matrix, pressed);

    this->buttonAnimation->UpdatePixels(pressed);
  }
}

void AnimationStation::ClearPressed() {
  if (this->buttonAnimation != nullptr) {
    this->buttonAnimation->ClearPixels();
  }
  this->lastPressed.clear();
}

void AnimationStation::Animate() {
  if (baseAnimation == nullptr) {
    this->Clear();
    return;
  }

  baseAnimation->Animate(this->frame);

  if (buttonAnimation != nullptr) {
    buttonAnimation->Animate(this->frame);
  }
}

void AnimationStation::Clear() { memset(frame, 0, sizeof(frame)); }

float AnimationStation::GetBrightnessX() {
  return AnimationStation::brightnessX;
}

uint8_t AnimationStation::GetBrightness() {
  return AnimationStation::options.brightness;
}

uint8_t AnimationStation::GetMode() { return this->options.baseAnimationIndex; }

void AnimationStation::SetMode(uint8_t mode) {
  this->options.baseAnimationIndex = mode;
  AnimationEffects newEffect =
      static_cast<AnimationEffects>(this->options.baseAnimationIndex);

  if (this->baseAnimation != nullptr) {
    delete this->baseAnimation;
  }

  switch (newEffect) {
  case AnimationEffects::EFFECT_RAINBOW:
    this->baseAnimation = new Rainbow(matrix);
    break;
  case AnimationEffects::EFFECT_CHASE:
    this->baseAnimation = new Chase(matrix);
    break;
  case AnimationEffects::EFFECT_STATIC_THEME:
    this->baseAnimation = new StaticTheme(matrix);
    break;
  default:
    this->baseAnimation = new StaticColor(matrix);
    break;
  }
}

void AnimationStation::SetMatrix(PixelMatrix matrix) {
  this->matrix = matrix;
}

void AnimationStation::SetOptions(AnimationOptions options) {
  AnimationStation::options = options;
  AnimationStation::SetBrightness(options.brightness);
}

void AnimationStation::ApplyBrightness(uint32_t *frameValue) {
  for (int i = 0; i < 100; i++)
    frameValue[i] = this->frame[i].value(Animation::format, brightnessX);
}

void AnimationStation::SetBrightness(uint8_t brightness) {
  AnimationStation::options.brightness =
      (brightness > brightnessSteps) ? brightnessSteps : options.brightness;
  AnimationStation::brightnessX =
      (AnimationStation::options.brightness * getBrightnessStepSize()) / 255.0F;

  if (AnimationStation::brightnessX > 1)
    AnimationStation::brightnessX = 1;
  else if (AnimationStation::brightnessX < 0)
    AnimationStation::brightnessX = 0;
}

void AnimationStation::DecreaseBrightness() {
  if (AnimationStation::options.brightness > 0)
    AnimationStation::SetBrightness(--AnimationStation::options.brightness);
}

void AnimationStation::IncreaseBrightness() {
  if (AnimationStation::options.brightness < getBrightnessStepSize())
    AnimationStation::SetBrightness(++AnimationStation::options.brightness);
  else if (AnimationStation::options.brightness > getBrightnessStepSize())
    AnimationStation::SetBrightness(brightnessSteps);
}


================================================
FILE: lib/AnimationStation/src/AnimationStation.hpp
================================================
#ifndef _ANIMATION_STATION_H_
#define _ANIMATION_STATION_H_

#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include "hardware/clocks.h"

#include "NeoPico.hpp"
#include "Animation.hpp"
#include "Effects/Chase.hpp"
#include "Effects/Rainbow.hpp"
#include "Effects/StaticColor.hpp"
#include "Effects/StaticTheme.hpp"

typedef enum
{
  EFFECT_STATIC_COLOR,
  EFFECT_RAINBOW,
  EFFECT_CHASE,
  EFFECT_STATIC_THEME
} AnimationEffects;

// We can't programmatically determine how many elements are in an enum. Yes, that's dumb.
const int TOTAL_EFFECTS = 4;

typedef enum
{
  HOTKEY_LEDS_NONE,
	HOTKEY_LEDS_ANIMATION_UP,
	HOTKEY_LEDS_ANIMATION_DOWN,
	HOTKEY_LEDS_PARAMETER_UP,
  HOTKEY_LEDS_PRESS_PARAMETER_UP,
  HOTKEY_LEDS_PRESS_PARAMETER_DOWN,
	HOTKEY_LEDS_PARAMETER_DOWN,
	HOTKEY_LEDS_BRIGHTNESS_UP,
	HOTKEY_LEDS_BRIGHTNESS_DOWN
} AnimationHotkey;

struct __attribute__ ((__packed__)) AnimationOptions
{
  uint32_t checksum;
	uint8_t baseAnimationIndex;
  uint8_t brightness;
  uint8_t staticColorIndex;
  uint8_t buttonColorIndex;
  int16_t chaseCycleTime;
  int16_t rainbowCycleTime;
  uint8_t themeIndex;
};

class AnimationStation
{
public:
  AnimationStation();

  void Animate();
  void HandleEvent(AnimationHotkey action);
  void Clear();
  void ChangeAnimation(int changeSize);
  void ApplyBrightness(uint32_t *frameValue);
  uint16_t AdjustIndex(int changeSize);
  void HandlePressed(std::vector<Pixel> pressed);
  void ClearPressed();

  uint8_t GetMode();
  void SetMode(uint8_t mode);
  void SetMatrix(PixelMatrix matrix);
  static void ConfigureBrightness(uint8_t max, uint8_t steps);
  static float GetBrightnessX();
  static uint8_t GetBrightness();
  static void SetBrightness(uint8_t brightness);
  static void DecreaseBrightness();
  static void IncreaseBrightness();
  static void SetOptions(AnimationOptions options);

  Animation* baseAnimation;
  Animation* buttonAnimation;
  std::vector<Pixel> lastPressed;
  static AnimationOptions options;
  static absolute_time_t nextChange;
  RGB frame[100];

protected:
  inline static uint8_t getBrightnessStepSize() { return (brightnessMax / brightnessSteps); }
  static uint8_t brightnessMax;
  static uint8_t brightnessSteps;
  static float brightnessX;
  PixelMatrix matrix;
};

#endif


================================================
FILE: lib/AnimationStation/src/AnimationStorage.hpp
================================================
#ifndef _CONFIG_STORAGE_H_
#define _CONFIG_STORAGE_H_

#include "AnimationStation.hpp"

class AnimationStorage
{
  public:
    void save();

    AnimationOptions getAnimationOptions();
    void setAnimationOptions(AnimationOptions options);
};

static AnimationStorage AnimationStore;

#endif


================================================
FILE: lib/AnimationStation/src/Effects/Chase.cpp
================================================
#include "Chase.hpp"

Chase::Chase(PixelMatrix &matrix) : Animation(matrix) {
}

void Chase::Animate(RGB (&frame)[100]) {
  if (!time_reached(this->nextRunTime)) {
    return;
  }

  for (auto &col : matrix->pixels) {
    for (auto &pixel : col) {
      if (pixel.index == NO_PIXEL.index)
        continue;

      if (this->IsChasePixel(pixel.index)) {
        RGB color = RGB::wheel(this->WheelFrame(pixel.index));
        for (auto &pos : pixel.positions)
          frame[pos] = color;
      }
      else {
        for (auto &pos : pixel.positions)
          frame[pos] = ColorBlack;
      }
    }
  }

  currentPixel++;

  if (currentPixel > matrix->getPixelCount() - 1) {
    currentPixel = 0;
  }

  if (reverse) {
    currentFrame--;

    if (currentFrame < 0) {
      currentFrame = 1;
      reverse = false;
    }
  } else {
    currentFrame++;

    if (currentFrame > 255) {
      currentFrame = 254;
      reverse = true;
    }
  }

  this->nextRunTime = make_timeout_time_ms(AnimationStation::options.chaseCycleTime);
}

bool Chase::IsChasePixel(int i) {
  if (i == this->currentPixel || i == (this->currentPixel - 1) ||
      i == (this->currentPixel - 2)) {
    return true;
  }

  return false;
}

int Chase::WheelFrame(int i) {
  int frame = this->currentFrame;
  int pixelCount = matrix->getPixelCount();
  if (i == (this->currentPixel - 1) % pixelCount) {
    if (this->reverse) {
      frame = frame + 16;
    } else {
      frame = frame - 16;
    }
  }

  if (i == (this->currentPixel - 2) % pixelCount) {
    if (this->reverse) {
      frame = frame + 32;
    } else {
      frame = frame - 32;
    }
  }

  if (frame < 0) {
    return 0;
  }

  return frame;
}

void Chase::ParameterUp() {
  AnimationStation::options.chaseCycleTime = AnimationStation::options.chaseCycleTime + 10;
}

void Chase::ParameterDown() {
  if (AnimationStation::options.chaseCycleTime > 0) {
    AnimationStation::options.chaseCycleTime = AnimationStation::options.chaseCycleTime - 10;
  }
}



================================================
FILE: lib/AnimationStation/src/Effects/Chase.hpp
================================================
#ifndef _CHASE_H_
#define _CHASE_H_

#include "../Animation.hpp"
#include "hardware/clocks.h"
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include "../AnimationStation.hpp"

class Chase : public Animation {
public:
  Chase(PixelMatrix &matrix);
  ~Chase() {};

  void Animate(RGB (&frame)[100]);
  void ParameterUp();
  void ParameterDown();

protected:
  bool IsChasePixel(int i);
  int WheelFrame(int i);
  int currentFrame = 0;
  int currentPixel = 0;
  bool reverse = false;
  absolute_time_t nextRunTime = 0;
};

#endif


================================================
FILE: lib/AnimationStation/src/Effects/Rainbow.cpp
================================================
#include "Rainbow.hpp"

Rainbow::Rainbow(PixelMatrix &matrix) : Animation(matrix) {
}

void Rainbow::Animate(RGB (&frame)[100]) {
  if (!time_reached(this->nextRunTime)) {
    return;
  }

  for (auto &col : matrix->pixels) {
    for (auto &pixel : col) {
      if (pixel.index == NO_PIXEL.index)
        continue;

      RGB color = RGB::wheel(this->currentFrame);
      for (auto &pos : pixel.positions)
        frame[pos] = color;
    }
  }

  if (reverse) {
    currentFrame--;

    if (currentFrame < 0) {
      currentFrame = 1;
      reverse = false;
    }
  } else {
    currentFrame++;

    if (currentFrame > 255) {
      currentFrame = 254;
      reverse = true;
    }
  }

  this->nextRunTime = make_timeout_time_ms(AnimationStation::options.rainbowCycleTime);
}

void Rainbow::ParameterUp() {
  AnimationStation::options.rainbowCycleTime =AnimationStation::options.rainbowCycleTime + 10;
}

void Rainbow::ParameterDown() {
  if (AnimationStation::options.rainbowCycleTime > 0) {
    AnimationStation::options.rainbowCycleTime = AnimationStation::options.rainbowCycleTime - 10;
  }
}


================================================
FILE: lib/AnimationStation/src/Effects/Rainbow.hpp
================================================
#ifndef _RAINBOW_H_
#define _RAINBOW_H_

#include "../Animation.hpp"
#include "hardware/clocks.h"
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include "../AnimationStation.hpp"

class Rainbow : public Animation {
public:
  Rainbow(PixelMatrix &matrix);
  ~Rainbow() {};

  void Animate(RGB (&frame)[100]);
  void ParameterUp();
  void ParameterDown();

protected:
  int currentFrame = 0;
  bool reverse = false;
  absolute_time_t nextRunTime = 0;
};

#endif


================================================
FILE: lib/AnimationStation/src/Effects/StaticColor.cpp
================================================
 #include "StaticColor.hpp"

StaticColor::StaticColor(PixelMatrix &matrix) : Animation(matrix) {
}

StaticColor::StaticColor(PixelMatrix &matrix, std::vector<Pixel> &pixels) : Animation(matrix), pixels(&pixels) {
  this->filtered = true;
}

void StaticColor::Animate(RGB (&frame)[100]) {
  for (size_t r = 0; r != matrix->pixels.size(); r++) {
    for (size_t c = 0; c != matrix->pixels[r].size(); c++) {
      if (matrix->pixels[r][c].index == NO_PIXEL.index || this->notInFilter(matrix->pixels[r][c]))
        continue;

      for (size_t p = 0; p != matrix->pixels[r][c].positions.size(); p++) {
        frame[matrix->pixels[r][c].positions[p]] = colors[this->GetColor()];
      }
    }
  }
}

uint8_t StaticColor::GetColor() {
  if (this->filtered) {
    return AnimationStation::options.buttonColorIndex;
  }
  else {
    return AnimationStation::options.staticColorIndex;
  }
}

void StaticColor::ParameterUp() {
  uint8_t colorIndex;
  if (this->filtered) {
    colorIndex = AnimationStation::options.buttonColorIndex;
  }
  else {
    colorIndex = AnimationStation::options.staticColorIndex;
  }

  if (colorIndex < colors.size() - 1)
  {
    colorIndex++;
  }
  else {
    colorIndex = 0;
  }

  this->SaveIndexOptions(colorIndex);
}

void StaticColor::SaveIndexOptions(uint8_t colorIndex) {
  if (this->filtered) {
    AnimationStation::options.buttonColorIndex = colorIndex;
  }
  else {
    AnimationStation::options.staticColorIndex = colorIndex;
  }
}

void StaticColor::ParameterDown() {
  uint8_t colorIndex;
  if (this->filtered) {
    colorIndex = AnimationStation::options.buttonColorIndex;
  }
  else {
    colorIndex = AnimationStation::options.staticColorIndex;
  }

  if (colorIndex > 0) {
    colorIndex--;
  }
  else {
    colorIndex = colors.size() - 1;
  }
  this->SaveIndexOptions(colorIndex);
}


================================================
FILE: lib/AnimationStation/src/Effects/StaticColor.hpp
================================================
#ifndef _STATIC_COLOR_H_
#define _STATIC_COLOR_H_

#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include "../Animation.hpp"
#include "../AnimationStation.hpp"

class StaticColor : public Animation {
public:
  StaticColor(PixelMatrix &matrix);
  StaticColor(PixelMatrix &matrix, std::vector<Pixel> &pixels);
  ~StaticColor() {};

  void Animate(RGB (&frame)[100]);
  void SaveIndexOptions(uint8_t colorIndex);
  uint8_t GetColor();
  void ParameterUp();
  void ParameterDown();
protected:
  std::vector<Pixel> *pixels;
};

#endif


================================================
FILE: lib/AnimationStation/src/Effects/StaticTheme.cpp
================================================
#include "StaticTheme.hpp"

std::vector<std::map<uint32_t, RGB>> StaticTheme::themes = {};

StaticTheme::StaticTheme(PixelMatrix &matrix) : Animation(matrix) {
  if (AnimationStation::options.themeIndex >= StaticTheme::themes.size()) {
    AnimationStation::options.themeIndex = 0;
  }
}

void StaticTheme::Animate(RGB (&frame)[100]) {
  if (StaticTheme::themes.size() > 0) {
    for (size_t r = 0; r != matrix->pixels.size(); r++) {
      for (size_t c = 0; c != matrix->pixels[r].size(); c++) {
        if (matrix->pixels[r][c].index == NO_PIXEL.index)
          continue;

        std::map<uint32_t, RGB> theme =
            StaticTheme::themes.at(AnimationStation::options.themeIndex);
        auto itr = theme.find(matrix->pixels[r][c].mask);
        if (itr != theme.end()) {
          for (size_t p = 0; p != matrix->pixels[r][c].positions.size(); p++) {
            frame[matrix->pixels[r][c].positions[p]] = itr->second;
          }
        } else {
          for (size_t p = 0; p != matrix->pixels[r][c].positions.size(); p++) {
            frame[matrix->pixels[r][c].positions[p]] = defaultColor;
          }
        }
      }
    }
  }
}

void StaticTheme::AddTheme(std::map<uint32_t, RGB> theme) {
  themes.push_back(theme);
}

void StaticTheme::ClearThemes() {
  themes.clear();
}

void StaticTheme::ParameterUp() {
  if (AnimationStation::options.themeIndex < StaticTheme::themes.size() - 1) {
    AnimationStation::options.themeIndex++;
  } else {
    AnimationStation::options.themeIndex = 0;
  }
}

void StaticTheme::ParameterDown() {

  if (AnimationStation::options.themeIndex > 0) {
    AnimationStation::options.themeIndex--;
  } else {
    AnimationStation::options.themeIndex = StaticTheme::themes.size() - 1;
  }
}


================================================
FILE: lib/AnimationStation/src/Effects/StaticTheme.hpp
================================================
#ifndef STATIC_THEME_H_
#define STATIC_THEME_H_

#include <iterator>
#include <map>
#include <vector>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "../Animation.hpp"
#include "../AnimationStation.hpp"

class StaticTheme : public Animation {
public:
  StaticTheme(PixelMatrix &matrix);
  ~StaticTheme() {};

  static void AddTheme(std::map<uint32_t, RGB> theme);
  static void ClearThemes();
  void Animate(RGB (&frame)[100]);
  void ParameterUp();
  void ParameterDown();
protected:
  RGB defaultColor = ColorBlack;
  static std::vector<std::map<uint32_t, RGB>> themes;
};

#endif


================================================
FILE: lib/AnimationStation/src/Pixel.hpp
================================================
#ifndef PIXEL_HPP_
#define PIXEL_HPP_

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>

struct Pixel {
  Pixel(int index, uint32_t mask = 0) : index(index), mask(mask) { }
  Pixel(int index, std::vector<uint8_t> positions) : index(index), positions(positions) { }
  Pixel(int index, uint32_t mask, std::vector<uint8_t> positions) : index(index), mask(mask), positions(positions) { }

  int index;                      // The pixel index
  uint32_t mask;                  // Used to detect per-pixel lighting
  std::vector<uint8_t> positions; // The actual LED indexes on the chain
};

const Pixel NO_PIXEL(-1);

struct PixelMatrix {
  PixelMatrix() { }

  std::vector<std::vector<Pixel>> pixels;
  uint8_t ledsPerPixel;
  void setup(std::vector<std::vector<Pixel>> pixels, int ledsPerPixel = -1) {
    this->pixels = pixels;
    this->ledsPerPixel = ledsPerPixel;
  }

  inline int getLedCount() {
    int count = 0;
    for (auto &col : pixels)
      for (auto &pixel : col)
        if (pixel.index == NO_PIXEL.index)
          continue;
        else
          count += pixel.positions.size();

    return count;
  }

  inline uint16_t getPixelCount() const {
    uint16_t count = 0;
    for (auto &col : pixels)
      count += col.size();

    return count;
  }

};

inline bool operator==(const Pixel &lhs, const Pixel &rhs) {
  return lhs.index == rhs.index;
}

#endif


================================================
FILE: lib/BitBang_I2C/BitBang_I2C.c
================================================
//
// Bit Bang I2C library
// Copyright (c) 2018 BitBank Software, Inc.
// Written by Larry Bank (bitbank@pobox.com)
// Project started 10/12/2018
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "pico/binary_info.h"
#include "hardware/i2c.h"

#include "BitBang_I2C.h"


//
// Transmit a byte and read the ack bit
// if we get a NACK (negative acknowledge) return 0
// otherwise return 1 for success
//
//
// Initialize the I2C BitBang library
// Pass the pin numbers used for SDA and SCL
// as well as the clock rate in Hz
//
void I2CInit(BBI2C *pI2C, uint32_t iClock)
{
	if (pI2C == NULL) return;
	if ((pI2C->iSDA + 2 * i2c_hw_index(pI2C->picoI2C))%4 != 0) return ;
	if ((pI2C->iSCL + 3 + 2 * i2c_hw_index(pI2C->picoI2C))%4 != 0) return ;
      i2c_init(pI2C->picoI2C, iClock);
      gpio_set_function(pI2C->iSDA, GPIO_FUNC_I2C);
      gpio_set_function(pI2C->iSCL, GPIO_FUNC_I2C);
      gpio_pull_up(pI2C->iSDA);
      gpio_pull_up(pI2C->iSCL);
      return;
}
//
// Test a specific I2C address to see if a device responds
// returns 0 for no response, 1 for a response
//
uint8_t I2CTest(BBI2C *pI2C, uint8_t addr)
{
	int ret;
    uint8_t rxdata;
    ret = i2c_read_blocking(pI2C->picoI2C, addr, &rxdata, 1, false);
    return (ret >= 0);
} /* I2CTest() */

//
// Scans for I2C devices on the bus
// returns a bitmap of devices which are present (128 bits = 16 bytes, LSB first)
// A set bit indicates that a device responded at that address
//
void I2CScan(BBI2C *pI2C, uint8_t *pMap)
{
  int i;
  for (i=0; i<16; i++) // clear the bitmap
    pMap[i] = 0;
  for (i=1; i<128; i++) // try every address
  {
    if (I2CTest(pI2C, i))
    {
      pMap[i >> 3] |= (1 << (i & 7));
    }
  }
} /* I2CScan() */


//
// Write I2C data
// quits if a NACK is received and returns 0
// otherwise returns the number of bytes written
//
int I2CWrite(BBI2C *pI2C, uint8_t iAddr, uint8_t *pData, int iLen)
{
	int rc = 0;

    rc = i2c_write_blocking(pI2C->picoI2C, iAddr, pData, iLen, true); // true to keep master control of bus
    return rc >= 0 ? iLen : 0;


} /* I2CWrite() */

//
// Read N bytes starting at a specific I2C internal register
//
int I2CReadRegister(BBI2C *pI2C, uint8_t iAddr, uint8_t u8Register, uint8_t *pData, int iLen)
{
	int rc;
  
    rc = i2c_write_blocking(pI2C->picoI2C, iAddr, &u8Register, 1, true); // true to keep master control of bus 
    if (rc >= 0) {
        rc = i2c_read_blocking(pI2C->picoI2C, iAddr, pData, iLen, false);
    }
    return (rc >= 0);
} /* I2CReadRegister() */

//
// Read N bytes
//
int I2CRead(BBI2C *pI2C, uint8_t iAddr, uint8_t *pData, int iLen)
{
	int rc;
    rc = i2c_read_blocking(pI2C->picoI2C, iAddr, pData, iLen, false);
    return (rc >= 0);
	
} /* I2CRead() */

//
// Figure out what device is at that address
// returns the enumerated value
//
int I2CDiscoverDevice(BBI2C *pI2C, uint8_t i)
{
uint8_t j, cTemp[8];
int iDevice = DEVICE_UNKNOWN;

  if (i == 0x3c || i == 0x3d) // Probably an OLED display
  {
    I2CReadRegister(pI2C, i, 0x00, cTemp, 1);
    cTemp[0] &= 0xbf; // mask off power on/off bit
    if (cTemp[0] == 0x8) // SH1106
       iDevice = DEVICE_SH1106;
    else if (cTemp[0] == 3 || cTemp[0] == 6)
       iDevice = DEVICE_SSD1306;
    return iDevice;
  }
  
  if (i == 0x34 || i == 0x35) // Probably an AXP202/AXP192 PMU chip
  {
    I2CReadRegister(pI2C, i, 0x03, cTemp, 1); // chip ID
    if (cTemp[0] == 0x41)
       return DEVICE_AXP202;
    else if (cTemp[0] == 0x03)
       return DEVICE_AXP192;
  }
  
  if (i >= 0x40 && i <= 0x4f) // check for TI INA219 power measurement sensor
  {
    I2CReadRegister(pI2C, i, 0x00, cTemp, 2);
    if (cTemp[0] == 0x39 && cTemp[1] == 0x9f)
       return DEVICE_INA219;
  }
  
  // Check for Microchip 24AAXXXE64 family serial 2 Kbit EEPROM
  if (i >= 0x50 && i <= 0x57) {
    uint32_t u32Temp = 0;
    I2CReadRegister(pI2C, i, 0xf8, (uint8_t *)&u32Temp,
                    3); // check for Microchip's OUI
    if (u32Temp == 0x000004a3 || u32Temp == 0x00001ec0 ||
        u32Temp == 0x00d88039 || u32Temp == 0x005410ec)
      return DEVICE_24AAXXXE64;
  }
  
//  else if (i == 0x5b) // MLX90615?
//  {
//    I2CReadRegister(pI2C, i, 0x10, cTemp, 3);
//    for (j=0; j<3; j++) Serial.println(cTemp[j], HEX);
//  }
  // try to identify it from the known devices using register contents
  {    
    // Check for TI HDC1080
    I2CReadRegister(pI2C, i, 0xff, cTemp, 2);
    if (cTemp[0] == 0x10 && cTemp[1] == 0x50)
       return DEVICE_HDC1080;

    // Check for BME680
    if (i == 0x76 || i == 0x77)
    {
       I2CReadRegister(pI2C, i, 0xd0, cTemp, 1); // chip ID
       if (cTemp[0] == 0x61) // BME680
          return DEVICE_BME680;
    }
    // Check for VL53L0X
    I2CReadRegister(pI2C, i, 0xc0, cTemp, 3);
    if (cTemp[0] == 0xee && cTemp[1] == 0xaa && cTemp[2] == 0x10)
       return DEVICE_VL53L0X;

    // Check for CCS811
    I2CReadRegister(pI2C, i, 0x20, cTemp, 1);
    if (cTemp[0] == 0x81) // Device ID
       return DEVICE_CCS811;

    // Check for LIS3DSH accelerometer from STMicro
    I2CReadRegister(pI2C, i, 0x0f, cTemp, 1);
    if (cTemp[0] == 0x3f) // WHO_AM_I
       return DEVICE_LIS3DSH;

    // Check for LIS3DH accelerometer from STMicro
    I2CReadRegister(pI2C, i, 0x0f, cTemp, 1);
    if (cTemp[0] == 0x33) // WHO_AM_I
       return DEVICE_LIS3DH;

    // Check for LSM9DS1 magnetometer/gyro/accel sensor from STMicro
    I2CReadRegister(pI2C, i, 0x0f, cTemp, 1);
    if (cTemp[0] == 0x68) // WHO_AM_I
       return DEVICE_LSM9DS1;

    // Check for LPS25H pressure sensor from STMicro
    I2CReadRegister(pI2C, i, 0x0f, cTemp, 1);
    if (cTemp[0] == 0xbd) // WHO_AM_I
       return DEVICE_LPS25H;
    
    // Check for HTS221 temp/humidity sensor from STMicro
    I2CReadRegister(pI2C, i, 0x0f, cTemp, 1);
    if (cTemp[0] == 0xbc) // WHO_AM_I
       return DEVICE_HTS221;
    
    // Check for MAG3110
    I2CReadRegister(pI2C, i, 0x07, cTemp, 1);
    if (cTemp[0] == 0xc4) // WHO_AM_I
       return DEVICE_MAG3110;

    // Check for LM8330 keyboard controller
    I2CReadRegister(pI2C, i, 0x80, cTemp, 2);
    if (cTemp[0] == 0x0 && cTemp[1] == 0x84) // manufacturer code + software revision
       return DEVICE_LM8330;

    // Check for MAX44009
    if (i == 0x4a || i == 0x4b)
    {
      for (j=0; j<8; j++)
        I2CReadRegister(pI2C, i, j, &cTemp[j], 1); // check for power-up reset state of registers
      if ((cTemp[2] == 3 || cTemp[2] == 2) && cTemp[6] == 0 && cTemp[7] == 0xff)
         return DEVICE_MAX44009;
    }
       
    // Check for ADS1115
    I2CReadRegister(pI2C, i, 0x02, cTemp, 2); // Lo_thresh defaults to 0x8000
    I2CReadRegister(pI2C, i, 0x03, &cTemp[2], 2); // Hi_thresh defaults to 0x7fff
    if (cTemp[0] == 0x80 && cTemp[1] == 0x00 && cTemp[2] == 0x7f && cTemp[3] == 0xff)
       return DEVICE_ADS1115;

    // Check for MCP9808
    I2CReadRegister(pI2C, i, 0x06, cTemp, 2); // manufacturer ID && get device ID/revision
    I2CReadRegister(pI2C, i, 0x07, &cTemp[2], 2); // need to read them individually
    if (cTemp[0] == 0 && cTemp[1] == 0x54 && cTemp[2] == 0x04 && cTemp[3] == 0x00)
       return DEVICE_MCP9808;
       
    // Check for BMP280/BME280
    I2CReadRegister(pI2C, i, 0xd0, cTemp, 1);
    if (cTemp[0] == 0x55) // BMP180
       return DEVICE_BMP180;
    else if (cTemp[0] == 0x58)
       return DEVICE_BMP280;
    else if (cTemp[0] == 0x60) // BME280
       return DEVICE_BME280;

    // Check for LSM6DS3
    I2CReadRegister(pI2C, i, 0x0f, cTemp, 1); // WHO_AM_I
    if (cTemp[0] == 0x69)
       return DEVICE_LSM6DS3;
       
    // Check for ADXL345
    I2CReadRegister(pI2C, i, 0x00, cTemp, 1); // DEVID
    if (cTemp[0] == 0xe5)
       return DEVICE_ADXL345;
       
    // Check for MPU-60x0i, MPU-688x, and MPU-9250
    I2CReadRegister(pI2C, i, 0x75, cTemp, 1);
    if (cTemp[0] == (i & 0xfe)) // Current I2C address (low bit set to 0)
       return DEVICE_MPU6000;
    else if (cTemp[0] == 0x71)
       return DEVICE_MPU9250;
    else if (cTemp[0] == 0x19)
       return DEVICE_MPU6886;

    // Check for DS3231 RTC
    I2CReadRegister(pI2C, i, 0x0e, cTemp, 1); // read the control register
    if (i == 0x68 &&
        cTemp[0] == 0x1c) // fixed I2C address and power on reset value
      return DEVICE_DS3231;

    // Check for DS1307 RTC
    I2CReadRegister(pI2C, i, 0x07, cTemp, 1); // read the control register
    if (i == 0x68 &&
        cTemp[0] == 0x03) // fixed I2C address and power on reset value
      return DEVICE_DS1307;
        
  }
  return iDevice;
} /* I2CDiscoverDevice() */


================================================
FILE: lib/BitBang_I2C/BitBang_I2C.h
================================================
//
// Bit Bang I2C library
// Copyright (c) 2018 BitBank Software, Inc.
// Written by Larry Bank (bitbank@pobox.com)
// Project started 10/12/2018
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
#ifndef __BITBANG_I2C__
#define __BITBANG_I2C__

#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "hardware/spi.h"

// supported devices
enum {
  DEVICE_UNKNOWN = 0,
  DEVICE_SSD1306,
  DEVICE_SH1106,
  DEVICE_VL53L0X,
  DEVICE_BMP180,
  DEVICE_BMP280,
  DEVICE_BME280,
  DEVICE_MPU6000,
  DEVICE_MPU9250,
  DEVICE_MCP9808,
  DEVICE_LSM6DS3,
  DEVICE_ADXL345,
  DEVICE_ADS1115,
  DEVICE_MAX44009,
  DEVICE_MAG3110,
  DEVICE_CCS811,
  DEVICE_HTS221,
  DEVICE_LPS25H,
  DEVICE_LSM9DS1,
  DEVICE_LM8330,
  DEVICE_DS3231,
  DEVICE_LIS3DH,
  DEVICE_LIS3DSH,
  DEVICE_INA219,
  DEVICE_SHT3X,
  DEVICE_HDC1080,
  DEVICE_MPU6886,
  DEVICE_BME680,
  DEVICE_AXP202,
  DEVICE_AXP192,
  DEVICE_24AAXXXE64,
  DEVICE_DS1307
};

#ifndef LOW
#define LOW 0
#define HIGH 1
#endif

typedef struct mybbi2c
{
uint8_t iSDA, iSCL;   // pin numbers (0xff = disabled)
uint8_t bWire;
i2c_inst_t * picoI2C; // used pico I2C
spi_inst_t * picoSPI; // used pico SPI
} BBI2C;

#ifdef __cplusplus
extern "C" {
#endif

//
// Read N bytes
//
int I2CRead(BBI2C *pI2C, uint8_t iAddr, uint8_t *pData, int iLen);
//
// Read N bytes starting at a specific I2C internal register
//
int I2CReadRegister(BBI2C *pI2C, uint8_t iAddr, uint8_t u8Register, uint8_t *pData, int iLen);
//
// Write I2C data
// quits if a NACK is received and returns 0
// otherwise returns the number of bytes written
//
int I2CWrite(BBI2C *pI2C, uint8_t iAddr, uint8_t *pData, int iLen);
//
// Scans for I2C devices on the bus
// returns a bitmap of devices which are present (128 bits = 16 bytes, LSB first)
//
// Test if an address responds
// returns 0 if no response, 1 if it responds
//
uint8_t I2CTest(BBI2C *pI2C, uint8_t addr);

// A set bit indicates that a device responded at that address
//
void I2CScan(BBI2C *pI2C, uint8_t *pMap);
//
// Initialize the I2C BitBang library
// Pass the pin numbers used for SDA and SCL
// as well as the clock rate in Hz
//
void I2CInit(BBI2C *pI2C, uint32_t iClock);
//
// Figure out what device is at that address
// returns the enumerated value
//
int I2CDiscoverDevice(BBI2C *pI2C, uint8_t i);

#ifdef __cplusplus
}
#endif

#endif //__BITBANG_I2C__



================================================
FILE: lib/BitBang_I2C/README.md
================================================
Bit Bang I2C library
--------------------
Copyright (c) 2018 BitBank Software, Inc.
Written by Larry Bank (bitbank@pobox.com)
Project started 10/12/2018

The purpose of this code is to provide a simple C library which can bit-bang
the I2C protocol on any 2 GPIO pins on any system. The I2C protocol doesn't
require any special functionality of the pins beyond standard GPIO features.
The reason I wrote it was for getting easy access to I2C devices on
various microcontrollers that don't necessarily have exposed I2C interfaces.
This has come in handy on a variety of projects including AVR, ESP32, and nRF5
micrcontrollers.

The pin access functions can be wrapper functions for the native versions (e.g. on the nRF5 SDK)
On AVR micros, the digitalWrite/digitalRead/pinMode functions are somewhat
slow because they check the pin numbers against tables and do other tasks.
This library includes logic to speed that up. By specifying pin numbers as the
port name + bit, the library will run considerably faster on AVR
microcontrollers. For example, On the Arduino Uno (ATmega328P), I/O pin 9 is
actually I/O Port B, bit 1. To use the direct pin method, you would specify
the pin number as `0xB1`. On the ATtiny85, this is the only pin numbering
supported so that the Wire library doesn't get linked in (to save FLASH space). 

This latest version allows you to use this library for both bit-bang I2C or
make use of the Wire library indirectly. Since each BBI2C object is independent,
you can have as many buses as you like operating on any combination of
bit-bang and hardware I2C.
 
Usage:
-----
Start by initializing a BBI2C structure with the desired pin numbers for SDA/SCL
along with the desired clock frequency. The bWire flag tells the library to use
hardware I2C when set to true. If using the hardware I2C (Wire library), the
pin numbers can be set to `0xff` to use the default I2C pins or to specific pins
on systems which support multiple I2C buses. Frequencies above 400Khz are 
possible, but not necessarily accurate. Luckily I2C devices don't really
care about the exact clock frequency, only that the signals are stable
within the given periods.

For Example:
```C++
BBI2C bbi2c;
bbi2c.bWire = 0; // use bit banging
bbi2c.iSDA = 10; // SDA on GPIO pin 10
bbi2c.iSCL = 11; // SCL on GPIO pin 11
I2CInit(&bbi2c, 100000); // SDA=pin 10, SCL=pin 11, 100K clock
```

Instead of exposing functions to start and stop I2C transactions, I decided
to make it simpler by providing composite functions that hide the details of
I2C protocol. For scanning the I2C bus for devices, I provide the I2CScan()
function which returns a bitmap (16 bytes x 8 bits) with a bit set for every
device it finds. Call it like this:

```C++
unsigned char ucMap[16];
I2CScan(&bbi2c, ucMap);
```

To detect if a single address is active, use `I2CTest(addr)`.

To identify the device, use `I2CDiscoverDevice(uint8_t iAddress)`.

For reading and writing data to the I2C device, use the following functions:

```C++
I2CRead(&bbi2c, uint8_t u8Address, uint8_t *pu8Data, int iLength);
I2CReadRegister(&bbi2c, uint8_t iAddr, uint8_t u8Register, uint8_t *pData, int iLen);
I2CWrite(&bbi2c, uint8_t iAddr, uint8_t *pData, int iLen);
```

There are currently 29 devices recognized by the discover function:
- SSD1306
- SH1106
- VL53L0X
- BMP180
- BMP280
- BME280
- BME680
- MPU6000
- MPU9250
- MCP9808
- LSM6DS3
- ADXL345
- ADS1115
- MAX44009
- MAG3110
- CCS811
- HTS221
- LPS25H
- LSM9DS1
- LM8330
- DS3231
- DS1307
- LIS3DH
- LIS3DSH
- INA219
- SHT3X
- HDC1080
- AXP192
- AXP202
- 24AAXXXE64

If you find this code useful, please consider sending a donation.

[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SR4F44J2UR8S4)




================================================
FILE: lib/CRC32/src/CRC32.cpp
================================================
//
// Copyright (c) 2013 Christopher Baker <https://christopherbaker.net>
//
// SPDX-License-Identifier:	MIT
//

#include "CRC32.h"

static const uint32_t crc32_table[] = {
	0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
	0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
	0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
	0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};

CRC32::CRC32() {
	reset();
}

void CRC32::reset() {
	_state = ~0L;
}

void CRC32::update(const uint8_t &data) {
	// via http://forum.arduino.cc/index.php?topic=91179.0
	uint8_t tbl_idx = 0;

	tbl_idx = _state ^ (data >> (0 * 4));
	_state = *(uint32_t *)(crc32_table + (tbl_idx & 0x0f)) ^ (_state >> 4);
	tbl_idx = _state ^ (data >> (1 * 4));
	_state = *(uint32_t *)(crc32_table + (tbl_idx & 0x0f)) ^ (_state >> 4);
}

uint32_t CRC32::finalize() const
{
	return ~_state;
}


================================================
FILE: lib/CRC32/src/CRC32.h
================================================
//
// Copyright (c) 2013 Christopher Baker <https://christopherbaker.net>
//
// SPDX-License-Identifier:	MIT
//

#pragma once

#include <stdint.h>

/// \brief A class for calculating the CRC32 checksum from arbitrary data.
/// \sa http://forum.arduino.cc/index.php?topic=91179.0
class CRC32 {
public:
	/// \brief Initialize an empty CRC32 checksum.
	CRC32();

	/// \brief Reset the checksum claculation.
	void reset();

	/// \brief Update the current checksum caclulation with the given data.
	/// \param data The data to add to the checksum.
	void update(const uint8_t &data);

	/// \brief Update the current checksum caclulation with the given data.
	/// \tparam Type The data type to read.
	/// \param data The data to add to the checksum.
	template <typename Type>
	void update(const Type &data) {
		update(&data, 1);
	}

	/// \brief Update the current checksum caclulation with the given data.
	/// \tparam Type The data type to read.
	/// \param data The array to add to the checksum.
	/// \param size Size of the array to add.
	template <typename Type>
	void update(const Type *data, uint16_t size) {
		uint16_t nBytes = size * sizeof(Type);
		const uint8_t *pData = (const uint8_t *)data;

		for (uint16_t i = 0; i < nBytes; i++)
		{
			update(pData[i]);
		}
	}

	/// \returns the caclulated checksum.
	uint32_t finalize() const;

	/// \brief Calculate the checksum of an arbitrary data array.
	/// \tparam Type The data type to read.
	/// \param data A pointer to the data to add to the checksum.
	/// \param size The size of the data to add to the checksum.
	/// \returns the calculated checksum.
	template <typename Type>
	static uint32_t calculate(const Type *data, uint16_t size = 1) {
		CRC32 crc;
		crc.update(data, size);
		return crc.finalize();
	}

private:
	/// \brief The internal checksum state.
	uint32_t _state = ~0L;
};


================================================
FILE: lib/FlashPROM/include/FlashPROM.h
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#ifndef FLASHPROM_H_
#define FLASHPROM_H_

#include <stdint.h>
#include <string.h>
#include <pico/lock_core.h>
#include <pico/multicore.h>
#include <hardware/flash.h>
#include <hardware/timer.h>

#define EEPROM_SIZE_BYTES    4096           // Reserve 4k of flash memory (ensure this value is divisible by 256)
#define EEPROM_ADDRESS_START _u(0x101FF000) // The arduino-pico EEPROM lib starts here, so we'll do the same
// Warning: If the write wait is too long it can stall other processes
#define EEPROM_WRITE_WAIT    50             // Amount of time in ms to wait before blocking core1 and committing to flash

class FlashPROM
{
	public:
		void start();
		void commit();
		void reset();

		template<typename T>
		T &get(uint16_t const index, T &value)
		{
			if (index < EEPROM_SIZE_BYTES)
				memcpy(&value, &cache[index], sizeof(T));

			return value;
		}

		template<typename T>
		void set(uint16_t const index, const T &value)
		{
			uint16_t size = sizeof(T);

			if ((index + size) <= EEPROM_SIZE_BYTES)
				memcpy(&cache[index], &value, sizeof(T));
		}

	private:
		static uint8_t cache[EEPROM_SIZE_BYTES];
};

static FlashPROM EEPROM;

#endif


================================================
FILE: lib/FlashPROM/library.json
================================================
{
	"name": "FlashPROM",
	"version": "0.0.1",
	"description": "Basic EEPROM-like interface for saving to flash memory on the RP2040.",
	"keywords": "pico rp2040 eeprom flash",
	"authors": [
		{
			"name": "Jason Skuby",
			"url": "https://mytechtoybox.com"
		}
	],
	"license": "MIT"
}


================================================
FILE: lib/FlashPROM/src/FlashPROM.cpp
================================================
/*
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)
 */

#include "FlashPROM.h"

uint8_t FlashPROM::cache[EEPROM_SIZE_BYTES] = { };
volatile static alarm_id_t flashWriteAlarm = 0;
volatile static spin_lock_t *flashLock = nullptr;

int64_t writeToFlash(alarm_id_t id, void *flashCache)
{
	while (is_spin_locked(flashLock));

	multicore_lockout_start_blocking();
	uint32_t interrupts = spin_lock_blocking(flashLock);

	flash_range_erase((intptr_t)EEPROM_ADDRESS_START - (intptr_t)XIP_BASE, EEPROM_SIZE_BYTES);
	flash_range_program((intptr_t)EEPROM_ADDRESS_START - (intptr_t)XIP_BASE, reinterpret_cast<uint8_t *>(flashCache), EEPROM_SIZE_BYTES);

	flashWriteAlarm = 0;
	multicore_lockout_end_blocking();
	spin_unlock(flashLock, interrupts);

	return 0;
}

void FlashPROM::start()
{
	if (flashLock == nullptr)
		flashLock = spin_lock_instance(spin_lock_claim_unused(true));

	memcpy(cache, reinterpret_cast<uint8_t *>(EEPROM_ADDRESS_START), EEPROM_SIZE_BYTES);

	// When flash is new/reset, all bits are set to 1.
	// If all bits from the FlashPROM section are 1's then set to 0's.
	bool reset = true;
	for (int i = 0; i < EEPROM_SIZE_BYTES; i++)
	{
		if (cache[i] != 0xFF)
		{
			reset = false;
			break;
		}
	}

	if (reset)
		this->reset();
}

/* We don't have an actual EEPROM, so we need to be extra careful about minimizing writes. Instead
	of writing when a commit is requested, we update a time to actually commit. That way, if we receive multiple requests
	to commit in that timeframe, we'll hold off until the user is done sending changes. */
void FlashPROM::commit()
{
	while (is_spin_locked(flashLock));
	cancel_alarm(flashWriteAlarm);
	flashWriteAlarm = add_alarm_in_ms(EEPROM_WRITE_WAIT, writeToFlash, cache, true);
}

void FlashPROM::reset()
{
	memset(cache, 0, EEPROM_SIZE_BYTES);
	commit();
}


================================================
FILE: lib/NeoPico/.editorconfig
================================================
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
indent_style = space
indent_size = 2
trim_trailing_whitespace = false


================================================
FILE: lib/NeoPico/library.json
================================================
{
	"name": "NeoPico",
	"version": "0.0.1",
	"description": "WS2812 PIO handling based on official Pico examples",
	"keywords": "c c++ baremetal neopixel led",
	"authors": [
		{
			"name": "Jonathan Barket",
			"url": "https://jonathanbarket.com"
		}
	],
	"license": "MIT"
}


================================================
FILE: lib/NeoPico/src/NeoPico.cpp
================================================
/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * Modified by Jonathan Barket - 2021
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "NeoPico.hpp"

LEDFormat NeoPico::GetFormat() {
  return format;
}

void NeoPico::PutPixel(uint32_t pixelData) {
  switch (format) {
    case LED_FORMAT_GRB:
    case LED_FORMAT_RGB:
      pio_sm_put_blocking(pio0, 0, pixelData << 8u);
      break;
    case LED_FORMAT_GRBW:
    case LED_FORMAT_RGBW:
      pio_sm_put_blocking(pio0, 0, pixelData);
      break;
  }
}

NeoPico::NeoPico(int ledPin, int numPixels, LEDFormat format) : format(format), numPixels(numPixels) {
  PIO pio = pio0;
  int sm = 0;
  uint offset = pio_add_program(pio, &ws2812_program);
  bool rgbw = (format == LED_FORMAT_GRBW) || (format == LED_FORMAT_RGBW);
  ws2812_program_init(pio, sm, offset, ledPin, 800000, rgbw);
  this->Clear();
  sleep_ms(10);
}

void NeoPico::Clear() {
  memset(frame, 0, sizeof(frame));
}

void NeoPico::SetFrame(uint32_t newFrame[100]) {
  memcpy(frame, newFrame, sizeof(frame));
}

void NeoPico::Show() {
  for (int i = 0; i < this->numPixels; ++i) {
     this->PutPixel(this->frame[i]);
  }
  sleep_ms(10);
}

void NeoPico::Off() {
  Clear();
  for (int i = 0; i < this->numPixels; ++i) {
     this->PutPixel(this->frame[i]);
  }
  sleep_ms(10);
}


================================================
FILE: lib/NeoPico/src/NeoPico.hpp
================================================
#ifndef _NEO_PICO_H_
#define _NEO_PICO_H_

#include "ws2812.pio.h"
#include <vector>

typedef enum
{
  LED_FORMAT_GRB = 0,
  LED_FORMAT_RGB = 1,
  LED_FORMAT_GRBW = 2,
  LED_FORMAT_RGBW = 3,
} LEDFormat;

class NeoPico
{
public:
  NeoPico(int ledPin, int numPixels, LEDFormat format = LED_FORMAT_GRB);
  void Show();
  void Clear();
  void Off();
  LEDFormat GetFormat();
  // void SetPixel(int pixel, uint32_t color);
  void SetFrame(uint32_t newFrame[100]);
private:
  void PutPixel(uint32_t pixel_grb);
  LEDFormat format;
  PIO pio = pio0;
  int numPixels = 0;
  uint32_t frame[100];
};

#endif


================================================
FILE: lib/NeoPico/src/ws2812.pio
================================================
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;

.program ws2812
.side_set 1

.define public T1 2
.define public T2 5
.define public T3 3

.lang_opt python sideset_init = pico.PIO.OUT_HIGH
.lang_opt python out_init     = pico.PIO.OUT_HIGH
.lang_opt python out_shiftdir = 1

.wrap_target
bitloop:
    out x, 1       side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
    jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
do_one:
    jmp  bitloop   side 1 [T2 - 1] ; Continue driving high, for a long pulse
do_zero:
    nop            side 0 [T2 - 1] ; Or drive low, for a short pulse
.wrap

% c-sdk {
#include "hardware/clocks.h"

static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {

    pio_gpio_init(pio, pin);
    pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);

    pio_sm_config c = ws2812_program_get_default_config(offset);
    sm_config_set_sideset_pins(&c, pin);
    sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);

    int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
    float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
    sm_config_set_clkdiv(&c, div);

    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}
%}

.program ws2812_parallel

.define public T1 2
.define public T2 5
.define public T3 3

.wrap_target
    out x, 32
    mov pins, !null [T1-1]
    mov pins, x     [T2-1]
    mov pins, null  [T3-2]
.wrap

% c-sdk {
#include "hardware/clocks.h"

static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) {
    for(uint i=pin_base; i<pin_base+pin_count; i++) {
        pio_gpio_init(pio, i);
    }
    pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true);

    pio_sm_config c = ws2812_parallel_program_get_default_config(offset);
    sm_config_set_out_shift(&c, true, true, 32);
    sm_config_set_out_pins(&c, pin_base, pin_count);
    sm_config_set_set_pins(&c, pin_base, pin_count);
    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);

    int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_T3;
    float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
    sm_config_set_clkdiv(&c, div);

    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}
%}

================================================
FILE: lib/NeoPico/src/ws2812.pio.h
================================================
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //

#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif

// ------ //
// ws2812 //
// ------ //

#define ws2812_wrap_target 0
#define ws2812_wrap 3

#define ws2812_T1 2
#define ws2812_T2 5
#define ws2812_T3 3

static const uint16_t ws2812_program_instructions[] = {
            //     .wrap_target
    0x6221, //  0: out    x, 1            side 0 [2] 
    0x1123, //  1: jmp    !x, 3           side 1 [1] 
    0x1400, //  2: jmp    0               side 1 [4] 
    0xa442, //  3: nop                    side 0 [4] 
            //     .wrap
};

#if !PICO_NO_HARDWARE
static const struct pio_program ws2812_program = {
    .instructions = ws2812_program_instructions,
    .length = 4,
    .origin = -1,
};

static inline pio_sm_config ws2812_program_get_default_config(uint offset) {
    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_wrap(&c, offset + ws2812_wrap_target, offset + ws2812_wrap);
    sm_config_set_sideset(&c, 1, false, false);
    return c;
}

#include "hardware/clocks.h"
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {
    pio_gpio_init(pio, pin);
    pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
    pio_sm_config c = ws2812_program_get_default_config(offset);
    sm_config_set_sideset_pins(&c, pin);
    sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
    int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
    float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
    sm_config_set_clkdiv(&c, div);
    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}

#endif

// --------------- //
// ws2812_parallel //
// --------------- //

#define ws2812_parallel_wrap_target 0
#define ws2812_parallel_wrap 3

#define ws2812_parallel_T1 2
#define ws2812_parallel_T2 5
#define ws2812_parallel_T3 3

static const uint16_t ws2812_parallel_program_instructions[] = {
            //     .wrap_target
    0x6020, //  0: out    x, 32                      
    0xa10b, //  1: mov    pins, !null            [1] 
    0xa401, //  2: mov    pins, x                [4] 
    0xa103, //  3: mov    pins, null             [1] 
            //     .wrap
};

#if !PICO_NO_HARDWARE
static const struct pio_program ws2812_parallel_program = {
    .instructions = ws2812_parallel_program_instructions,
    .length = 4,
    .origin = -1,
};

static inline pio_sm_config ws2812_parallel_program_get_default_config(uint offset) {
    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_wrap(&c, offset + ws2812_parallel_wrap_target, offset + ws2812_parallel_wrap);
    return c;
}

#include "hardware/clocks.h"
static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) {
    for(uint i=pin_base; i<pin_base+pin_count; i++) {
        pio_gpio_init(pio, i);
    }
    pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true);
    pio_sm_config c = ws2812_parallel_program_get_default_config(offset);
    sm_config_set_out_shift(&c, true, true, 32);
    sm_config_set_out_pins(&c, pin_base, pin_count);
    sm_config_set_set_pins(&c, pin_base, pin_count);
    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
    int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_T3;
    float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
    sm_config_set_clkdiv(&c, div);
    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}

#endif



================================================
FILE: lib/OneBitDisplay/OneBitDisplay.cpp
================================================
//
// OneBitDisplay (OLED+LCD library)
// Copyright (c) 2020 BitBank Software, Inc.
// Written by Larry Bank (bitbank@pobox.com)
// Project started 3/23/2020
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"

#include "BitBang_I2C.h"
#include "OneBitDisplay.h"
// All of the drawing code is in here
#include "obd.inl"

// Initialization sequences
const unsigned char oled128_initbuf[] = {0x00, 0xae, 0xdc, 0x00, 0x81, 0x40,
                                         0xa1, 0xc8, 0xa8, 0x7f, 0xd5, 0x50, 0xd9, 0x22, 0xdb, 0x35, 0xb0, 0xda, 0x12,
                                         0xa4, 0xa6, 0xaf};

const unsigned char oled64x128_initbuf[] = {
		0x00, 0xae, 0xd5, 0x51, 0x20, 0xa8, 0x3f, 0xdc, 0x00, 0xd3, 0x60, 0xad, 0x80, 0xa6, 0xa4, 0xa0, 0xc0, 0x81, 0x40, 0xd9, 0x22, 0xdb, 0x35, 0xaf};

const unsigned char oled132_initbuf[] = {0x00, 0xae, 0x02, 0x10, 0x40, 0x81, 0xa0, 0xc0, 0xa6, 0xa8, 0x3f, 0xd3, 0x00, 0xd5, 0x80, 0xd9, 0xf1, 0xda, 0x12, 0xdb, 0x40, 0x20, 0x02, 0xa4, 0xa6};

const unsigned char oled64_initbuf[] = {0x00, 0xae, 0xa8, 0x3f, 0xd3, 0x00, 0x40, 0xa1, 0xc8,
                                        0xda, 0x12, 0x81, 0xff, 0xa4, 0xa6, 0xd5, 0x80, 0x8d, 0x14,
                                        0xaf, 0x20, 0x02};

const unsigned char oled32_initbuf[] = {
		0x00, 0xae, 0xd5, 0x80, 0xa8, 0x1f, 0xd3, 0x00, 0x40, 0x8d, 0x14, 0xa1, 0xc8, 0xda, 0x02,
		0x81, 0x7f, 0xd9, 0xf1, 0xdb, 0x40, 0xa4, 0xa6, 0xaf};

const unsigned char oled72_initbuf[] = {0x00, 0xae, 0xa8, 0x3f, 0xd3, 0x00, 0x40, 0xa1, 0xc8,
                                        0xda, 0x12, 0x81, 0xff, 0xad, 0x30, 0xd9, 0xf1, 0xa4, 0xa6, 0xd5, 0x80, 0x8d, 0x14,
                                        0xaf, 0x20, 0x02};

const unsigned char uc1701_initbuf[] = {0xe2, 0x40, 0xa0, 0xc8, 0xa2, 0x2c, 0x2e, 0x2f, 0xf8, 0x00, 0x23, 0x81, 0x28, 0xac, 0x00, 0xa6};

const unsigned char hx1230_initbuf[] = {0x2f, 0x90, 0xa6, 0xa4, 0xaf, 0x40, 0xb0, 0x10, 0x00};
const unsigned char nokia5110_initbuf[] = {0x21, 0xa4, 0xb1, 0x04, 0x14, 0x20, 0x0c};

static void obdCachedFlush(OBDISP *pOBD, int bRender);
static void obdCachedWrite(OBDISP *pOBD, uint8_t *pData, uint8_t u8Len, int bRender);
void obdSetPosition(OBDISP *pOBD, int x, int y, int bRender);
void obdWriteCommand(OBDISP *pOBD, unsigned char c);
void obdWriteDataBlock(OBDISP *pOBD, unsigned char *ucBuf, int iLen, int bRender);

//
// Draw the contents of a memory buffer onto a display
// The sub-window will be clipped if it specifies too large an area
// for the destination display. The source OBDISP structure must have
// a valid back buffer defined
// The top and bottom destination edges will be drawn on byte boundaries (8 rows)
// The source top/bot edges can be on pixel boundaries
// This can be used for partial screen updates
//
void obdDumpWindow(OBDISP *pOBDSrc, OBDISP *pOBDDest, int srcx, int srcy, int destx, int desty, int width, int height)
{
	uint8_t *s, ucTemp[32]; // temp buffer to gather source pixels
	int x, y, tx, i;
	int iPitch;

	if (pOBDSrc == NULL || pOBDDest == NULL || pOBDSrc->ucScreen == NULL)
		return; // invalid pointers
	if (width > pOBDDest->width)
		width = pOBDDest->width;
	if (height > pOBDDest->height)
		height = pOBDDest->height;
	iPitch = pOBDSrc->width;
	for (y = 0; y < height; y += 8)
	{
		obdSetPosition(pOBDDest, destx, (desty + y) / 8, 1);
		for (x = 0; x < width; x += 32)
		{
			tx = 32;
			if (width - x < 32)
				tx = width - x;
			s = &pOBDSrc->ucScreen[((srcy + y) / 8) * iPitch + srcx + x];
			if (srcy & 7) // need to shift the bits to get 8 rows of src data
			{
				uint8_t uc, ucShift = srcy & 7;
				for (i = 0; i < tx; i++)
				{ // combine current and next line to capture 8 pixels
					uc = s[0] >> ucShift;
					uc |= s[iPitch] << (7 - ucShift);
					ucTemp[i] = uc;
				}
				obdCachedWrite(pOBDDest, ucTemp, tx, 1);
			}
			else
			{                                     // simpler case
				obdCachedWrite(pOBDDest, s, tx, 1); // just copy it
			}
		} // for x
	}   // for y
	obdCachedFlush(pOBDDest, 1);
} /* obdDumpWindow() */

//
// Write a single line to a Sharp memory LCD
// You must provide the exact number of bytes needed for a complete line
// e.g. for the 144x168 display, pSrc must provide 144 pixels (18 bytes)
//
void obdWriteLCDLine(OBDISP *pOBD, uint8_t *pSrc, int iLine)
{
	int x;
	uint8_t c, ucInvert, *d, ucStart;
	uint8_t ucLineBuf[54]; // 400 pixels is max supported width = 50 bytes + 4
	int iPitch = pOBD->width / 8;
	static int iVCOM = 0;

	//    if (pOBD == NULL || pSrc == NULL || pOBD->type < SHARP_144x168)
	//        return; // invalid request
	if (iLine < 0 || iLine >= pOBD->height)
		return;

	ucInvert = (pOBD->invert) ? 0x00 : 0xff;
	gpio_put(pOBD->iCSPin, HIGH); // active high

	ucStart = 0x80; // write command
	iVCOM++;
	if (iVCOM & 0x100) // flip it every 256 lines
		ucStart |= 0x40; // VCOM bit
	ucLineBuf[1] = ucStart;
	// this code assumes I2C, so the first byte is ignored
	_I2CWrite(pOBD, ucLineBuf, 2); // write command(01) + vcom(02)

	d = &ucLineBuf[2];
	ucLineBuf[1] = ucMirror[iLine + 1]; // current line number
	for (x = 0; x < iPitch; x++)
	{
		c = pSrc[0] ^ ucInvert; // we need to brute-force invert it
		*d++ = ucMirror[c];
		pSrc++;
	} // for x
	// write this line to the display
	ucLineBuf[iPitch + 2] = 0; // end of line
	_I2CWrite(pOBD, ucLineBuf, iPitch + 3);
	ucLineBuf[1] = 0;
	_I2CWrite(pOBD, ucLineBuf, 2); // final transfer
	gpio_put(pOBD->iCSPin, LOW);   // de-activate
} /* obdWriteLCDLine() */

//
// Turn the display on or off
//
void obdPower(OBDISP *pOBD, int bOn)
{
	uint8_t ucCMD;

	if (pOBD->type == LCD_NOKIA5110)
		ucCMD = (bOn) ? 0x20 : 0x24;
	else // all other supported displays
		ucCMD = (bOn) ? 0xaf : 0xae;
	obdWriteCommand(pOBD, ucCMD);
} /* obdPower() */

// Controls the LED backlight
void obdBacklight(OBDISP *pOBD, int bOn)
{
	if (pOBD->iLEDPin != 0xff)
	{
		gpio_put(pOBD->iLEDPin, (bOn) ? HIGH : LOW);
	}
} /* obdBacklight() */

//
// Send the command sequence to power up the LCDs
static void LCDPowerUp(OBDISP *pOBD)
{
	int iLen;
	uint8_t *s, uc[32];
	obdSetDCMode(pOBD, MODE_COMMAND);
	gpio_put(pOBD->iCSPin, LOW);
	if (pOBD->type == LCD_UC1701 || pOBD->type == LCD_UC1609)
	{
		s = (uint8_t *)uc1701_initbuf;
		iLen = sizeof(uc1701_initbuf);
	}
	else if (pOBD->type == LCD_HX1230)
	{
		s = (uint8_t *)hx1230_initbuf;
		iLen = sizeof(hx1230_initbuf);
	}
	else // Nokia 5110
	{
		s = (uint8_t *)nokia5110_initbuf;
		iLen = sizeof(nokia5110_initbuf);
	}
	memcpy(uc, s, iLen);

	if (pOBD->iMOSIPin == 0xff)
		spi_write_blocking(pOBD->bbi2c.picoSPI, s, iLen);
	else
		SPI_BitBang(pOBD, s, iLen, pOBD->iMOSIPin, pOBD->iCLKPin);

	sleep_ms(100);
	obdWriteCommand(pOBD, 0xa5);
	sleep_ms(100);
	obdWriteCommand(pOBD, 0xa4);
	obdWriteCommand(pOBD, 0xaf);
	gpio_put(pOBD->iCSPin, HIGH);
	obdSetDCMode(pOBD, MODE_DATA);
} /* LCDPowerUp() */

//
// Initialize the display controller on an SPI bus
//
void obdSPIInit(OBDISP *pOBD, int iType, int iDC, int iCS, int iReset, int iMOSI, int iCLK, int iLED, int bFlip, int bInvert, int bBitBang, int32_t iSpeed)
{
	uint8_t uc[32], *s;
	int iLen;

	pOBD->ucScreen = NULL; // start with no backbuffer; user must provide one later
	pOBD->iDCPin = iDC;
	pOBD->iCSPin = iCS;
	pOBD->iMOSIPin = iMOSI;
	pOBD->iCLKPin = iCLK;
	pOBD->iLEDPin = iLED;
	pOBD->type = iType;
	pOBD->flip = bFlip;
	pOBD->invert = bInvert;
	pOBD->wrap = 0;           // default - disable text wrap
	pOBD->com_mode = COM_SPI; // communication mode

	gpio_set_dir(pOBD->iCSPin, true);
	gpio_put(pOBD->iCSPin, 0); //(pOBD->type < SHARP_144x168)); // set to not-active

	if (pOBD->iDCPin != 0xff) // Note - not needed on Sharp Memory LCDs
	{
		gpio_set_dir(pOBD->iDCPin, true);
		gpio_put(pOBD->iDCPin, 0); // for some reason, command mode must be set or some OLEDs/LCDs won't initialize correctly even if set later
	}

	if (bBitBang)
	{
		gpio_set_dir(iMOSI, true);
		gpio_set_dir(iCLK, true);
	}

	// Reset it
	if (iReset != -1)
	{
		gpio_set_dir(iReset, true);
		gpio_put(iReset, LOW);
		sleep_ms(100);
		gpio_put(iReset, HIGH);
		sleep_ms(100);
	}

	if (iLED != -1)
	{
		gpio_set_dir(iLED, true);
	}

	// Initialize SPI
	if (!bBitBang)
	{
		pOBD->iMOSIPin = 0xff; // mark it as hardware SPI
		gpio_set_function(pOBD->iCLKPin, GPIO_FUNC_SPI);
		gpio_set_function(pOBD->iMOSIPin, GPIO_FUNC_SPI);

		spi_init(pOBD->bbi2c.picoSPI, 1000 * 1000);
		spi_set_format(pOBD->bbi2c.picoSPI, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
	}

	pOBD->width = 128; // assume 128x64
	pOBD->height = 64;
	if (iType == SHARP_144x168)
	{
		pOBD->width = 144;
		pOBD->height = 168;
		pOBD->iDCPin = 0xff; // no D/C wire on this display
	}
	else if (iType == SHARP_400x240)
	{
		pOBD->width = 400;
		pOBD->height = 240;
		pOBD->iDCPin = 0xff; // no D/C wire on this display
	}
	else if (iType == LCD_UC1609)
	{
		pOBD->width = 192;
		pOBD->height = 64;
	}
	else if (iType == LCD_HX1230)
	{
		pOBD->width = 96;
		pOBD->height = 68;
		pOBD->iDCPin = 0xff; // flag this as being 3-wire SPI
	}
	else if (iType == LCD_NOKIA5110)
	{
		pOBD->width = 84;
		pOBD->height = 48;
	}
	else if (iType == OLED_96x16)
	{
		pOBD->width = 96;
		pOBD->height = 16;
	}
	else if (iType == OLED_64x128)
	{
		pOBD->width = 64;
		pOBD->height = 128;
	}
	else if (iType == OLED_128x32)
		pOBD->height = 32;
	else if (iType == OLED_128x128)
		pOBD->height = 128;
	else if (iType == OLED_64x32)
	{
		pOBD->width = 64;
		pOBD->height = 32;
	}
	else if (iType == OLED_72x40)
	{
		pOBD->width = 72;
		pOBD->height = 40;
	}
	if (iType == OLED_128x32 || iType == OLED_96x16)
	{
		s = (uint8_t *)oled32_initbuf;
		iLen = sizeof(oled32_initbuf);
	}
	else if (iType == OLED_64x128)
	{
		s = (uint8_t *)oled64x128_initbuf;
		iLen = sizeof(oled64x128_initbuf);
	}
	else if (iType == OLED_128x128)
	{
		s = (uint8_t *)oled128_initbuf;
		iLen = sizeof(oled128_initbuf);
	}
	//  else if (iType == OLED_132x64) { // SH1106
	//     s = (uint8_t *)oled132_initbuf;
	//     iLen = sizeof(oled132_initbuf);
	//  }
	else if (iType < LCD_UC1701) // 128x64 and 64x32
	{
		s = (uint8_t *)oled64_initbuf;
		iLen = sizeof(oled64_initbuf);
	}
	// OLED
	if (iType < LCD_UC1701)
	{
		memcpy(uc, s, iLen); // do it from RAM
		_I2CWrite(pOBD, uc, iLen);
		sleep_ms(100); // on SPI display this delay is needed or the display
		// never sees the "display on" command at the end of the sequence
		obdWriteCommand(pOBD, 0xaf); // turn on display
		if (bInvert)
		{
			uc[0] = 0;    // command
			uc[1] = 0xa7; // invert command
			_I2CWrite(pOBD, uc, 2);
		}
		if (bFlip) // rotate display 180
		{
			uc[0] = 0; // command
			uc[1] = 0xa0;
			_I2CWrite(pOBD, uc, 2);
			uc[0] = 0;
			uc[1] = 0xc0;
			_I2CWrite(pOBD, uc, 2);
		}
	} // OLED
	if (iType == LCD_UC1701 || iType == LCD_HX1230)
	{
		uint8_t cCOM = 0xc0;

		LCDPowerUp(pOBD);
		if (iType == LCD_HX1230)
		{
			obdSetContrast(pOBD, 0); // contrast of 0 looks good
			cCOM = 0xc8;
		}
		if (bFlip) // flip horizontal + vertical
		{
			obdWriteCommand(pOBD, 0xa1); // set SEG direction (A1 to flip horizontal)
			obdWriteCommand(pOBD, cCOM); // set COM direction (C0 to flip vert)
		}
		if (bInvert)
		{
			obdWriteCommand(pOBD, 0xa7); // set inverted pixel mode
		}
	}
	if (iType == LCD_UC1609)
	{
		obdWriteCommand(pOBD, 0xe2); // system reset
		obdWriteCommand(pOBD, 0xa0); // set frame rate to 76fps
		obdWriteCommand(pOBD, 0xeb); // set BR
		obdWriteCommand(pOBD, 0x2f); // set Power Control
		obdWriteCommand(pOBD, 0xc4); // set LCD mapping control
		obdWriteCommand(pOBD, 0x81); // set PM
		obdWriteCommand(pOBD, 0x90); // set contrast to 144
		obdWriteCommand(pOBD, 0xaf); // display enable
		if (bFlip)                   // flip horizontal + vertical
		{
			obdWriteCommand(pOBD, 0xa1); // set SEG direction (A1 to flip horizontal)
			obdWriteCommand(pOBD, 0xc2); // set COM direction (C0 to flip vert)
		}
		if (bInvert)
		{
			obdWriteCommand(pOBD, 0xa7); // set inverted pixel mode
		}
	} // UC1609
} /* obdSPIInit() */

//
// Initializes the OLED controller into "page mode"
//
int obdI2CInit(OBDISP *pOBD, int iType, int iAddr, int bFlip, int bInvert, int bWire, int sda, int scl, i2c_inst_t *picoI2C, int reset, int32_t iSpeed)
{
	unsigned char uc[32];
	uint8_t u8Len, *s;
	int rc = OLED_NOT_FOUND;

	pOBD->ucScreen = NULL; // reset backbuffer; user must provide one later
	pOBD->type = iType;
	pOBD->flip = bFlip;
	pOBD->invert = bInvert;
	pOBD->wrap = 0; // default - disable text wrap
	pOBD->bbi2c.iSDA = sda;
	pOBD->bbi2c.iSCL = scl;
	pOBD->bbi2c.picoI2C = picoI2C;
	pOBD->bbi2c.bWire = bWire;
	pOBD->com_mode = COM_I2C; // communication mode

	I2CInit(&pOBD->bbi2c, iSpeed); // on Linux, SDA = bus number, SCL = device address

	// Reset it
	if (reset != -1)
	{
		gpio_set_dir(reset, true);
		gpio_put(reset, HIGH);
		sleep_ms(50);
		gpio_put(reset, LOW);
		sleep_ms(50);
		gpio_put(reset, HIGH);
		sleep_ms(10);
	}
	// find the device address if requested
	if (iAddr == -1 || iAddr == 0 || iAddr == 0xff) // find it
	{
		I2CTest(&pOBD->bbi2c, 0x3c);
		if (I2CTest(&pOBD->bbi2c, 0x3c))
			pOBD->oled_addr = 0x3c;
		else if (I2CTest(&pOBD->bbi2c, 0x3d))
			pOBD->oled_addr = 0x3d;
		else
			return rc; // no display found!
	}
	else
	{
		pOBD->oled_addr = iAddr;
		I2CTest(&pOBD->bbi2c, iAddr);
		if (!I2CTest(&pOBD->bbi2c, iAddr))
			return rc; // no display found
	}

	// Detect the display controller (SSD1306, SH1107 or SH1106)
	uint8_t u = 0;
	I2CReadRegister(&pOBD->bbi2c, pOBD->oled_addr, 0x00, &u, 1); // read the status register
	u &= 0x0f;                                                   // mask off power on/off bit
	if ((u == 0x7 || u == 0xf) && pOBD->type == OLED_128x128)    // SH1107
	{                                                            // A single SSD1306 display returned 7, so only act on it if the
		// user specified that they're working with a 128x128 display
		rc = OLED_SH1107_3C;
		bFlip = !bFlip; // SH1107 seems to have this reversed from the usual direction
	}
	else if (u == 0x8) // SH1106
	{
		rc = OLED_SH1106_3C;
		pOBD->type = OLED_132x64; // needs to be treated a little differently
	}
	else if (u == 3 || u == 6 || u == 7) // 7=128x64(rare),6=128x64 display, 3=smaller
	{
		rc = OLED_SSD1306_3C;
	}
	if (pOBD->oled_addr == 0x3d)
		rc++; // return the '3D' version of the type

	if (iType == OLED_128x32 || iType == OLED_96x16)
	{
		s = (uint8_t *)oled32_initbuf;
		u8Len = sizeof(oled32_initbuf);
	}
	else if (iType == OLED_128x128)
	{
		s = (uint8_t *)oled128_initbuf;
		u8Len = sizeof(oled128_initbuf);
	}
	else if (iType == OLED_72x40)
	{
		s = (uint8_t *)oled72_initbuf;
		u8Len = sizeof(oled72_initbuf);
	}
	else if (iType == OLED_64x128)
	{
		s = (uint8_t *)oled64x128_initbuf;
		u8Len = sizeof(oled64x128_initbuf);
	}
	else // 132x64, 128x64 and 64x32
	{
		s = (uint8_t *)oled64_initbuf;
		u8Len = sizeof(oled64_initbuf);
	}

	memcpy(uc, s, u8Len);
	_I2CWrite(pOBD, uc, u8Len);
	if (bInvert)
	{
		uc[0] = 0;    // command
		uc[1] = 0xa7; // invert command
		_I2CWrite(pOBD, uc, 2);
	}
	if (bFlip) // rotate display 180
	{
		uc[0] = 0; // command
		uc[1] = 0xa0;
		_I2CWrite(pOBD, uc, 2);
		uc[1] = 0xc0;
		_I2CWrite(pOBD, uc, 2);
	}
	pOBD->width = 128; // assume 128x64
	pOBD->height = 64;
	if (iType == OLED_96x16)
	{
		pOBD->width = 96;
		pOBD->height = 16;
	}
	else if (iType == OLED_64x128)
	{
		pOBD->width = 64;
		pOBD->height = 128;
	}
	else if (iType == OLED_128x32)
		pOBD->height = 32;
	else if (iType == OLED_128x128)
		pOBD->height = 128;
	else if (iType == OLED_64x32)
	{
		pOBD->width = 64;
		pOBD->height = 32;
	}
	else if (iType == OLED_72x40)
	{
		pOBD->width = 72;
		pOBD->height = 40;
	}
	return rc;
} /* obdInit() */
//
// Sends a command to turn on or off the OLED display
//
void oledPower(OBDISP *pOBD, uint8_t bOn)
{
	if (bOn)
		obdWriteCommand(pOBD, 0xaf); // turn on OLED
	else
		obdWriteCommand(pOBD, 0xae); // turn off OLED
} /* oledPower() */

//
// Bit Bang the data on GPIO pins
//
void SPI_BitBang(OBDISP *pOBD, uint8_t *pData, int iLen, uint8_t iMOSIPin, uint8_t iSCKPin)
{
	int i;
	uint8_t c;

	while (iLen)
	{
		c = *pData++;

		if (pOBD->iDCPin == 0xff) // 3-wire SPI, write D/C bit first
		{
			gpio_put(iMOSIPin, (pOBD->mode == MODE_DATA));
			gpio_put(iSCKPin, HIGH);
			sleep_ms(0);
			gpio_put(iSCKPin, LOW);
		}

		if (c == 0 || c == 0xff) // quicker for all bits equal
		{
			gpio_put(iMOSIPin, (c & 1));

			for (i = 0; i < 8; i++)
			{
				gpio_put(iSCKPin, HIGH);
				sleep_ms(0);
				gpio_put(iSCKPin, LOW);
			}
		}
		else
		{
			for (i = 0; i < 8; i++)
			{
				gpio_put(iMOSIPin, (c & 0x80) != 0); // MSB first
				gpio_put(iSCKPin, HIGH);
				c <<= 1;
				sleep_ms(0);
				gpio_put(iSCKPin, LOW);
			}
		}
		iLen--;
	}
} /* SPI_BitBang() */

// Sets the D/C pin to data or command mode
void obdSetDCMode(OBDISP *pOBD, int iMode)
{
	if (pOBD->iDCPin == 0xff) // 9-bit SPI
		pOBD->mode = (uint8_t)iMode;
	else // set the GPIO line
		gpio_put(pOBD->iDCPin, (iMode == MODE_DATA));
} /* obdSetDCMode() */

static void obdWriteCommand2(OBDISP *pOBD, unsigned char c, unsigned char d)
{
	unsigned char buf[4];

	if (pOBD->com_mode == COM_I2C)
	{ // I2C device
		buf[0] = 0x00;
		buf[1] = c;
		buf[2] = d;
		_I2CWrite(pOBD, buf, 3);
	}
	else
	{ // must be SPI
		obdWriteCommand(pOBD, c);
		obdWriteCommand(pOBD, d);
	}
} /* obdWriteCommand2() */

//
// Sets the brightness (0=off, 255=brightest)
//
void obdSetContrast(OBDISP *pOBD, unsigned char ucContrast)
{
	if (pOBD->type == LCD_HX1230)
	{ // valid values are 0-31, so scale it
		ucContrast >>= 3;
		obdWriteCommand(pOBD, 0x80 + ucContrast);
	}
	else if (pOBD->type == LCD_NOKIA5110)
	{
		// we allow values of 0xb0-0xbf, so shrink the range
		ucContrast >>= 4;
		obdWriteCommand(pOBD, 0x21); // set advanced command mode
		obdWriteCommand(pOBD, 0xb0 | ucContrast);
		obdWriteCommand(pOBD, 0x20); // set simple command mode
	}
	else // OLEDs + UC1701
		obdWriteCommand2(pOBD, 0x81, ucContrast);
} /* obdSetContrast() */

//
// Special case for Sharp Memory LCD
//
static void SharpDumpBuffer(OBDISP *pOBD, uint8_t *pBuffer)
{
	int x, y;
	uint8_t c, ucInvert, *s, *d, ucStart;
	uint8_t ucLineBuf[56];
	int iPitch = pOBD->width / 8;
	static uint8_t ucVCOM = 0;
	int iBit;
	uint8_t ucMask;

	ucInvert = (pOBD->invert) ? 0x00 : 0xff;
	gpio_put(pOBD->iCSPin, HIGH); // active high

	ucLineBuf[0] = 0;
	ucStart = 0x80; // write command
	if (ucVCOM)
		ucStart |= 0x40; // VCOM bit
	ucLineBuf[1] = ucStart;
	// this code assumes I2C, so the first byte is ignored
	_I2CWrite(pOBD, ucLineBuf, 2); // write command(01) + vcom(02)
	ucVCOM = !ucVCOM;              // need to toggle this each transaction

	// We need to flip and invert the image in code because the Sharp memory LCD
	// controller only has the simplest of commands for data writing
	if (pOBD->flip)
	{
		for (y = 0; y < pOBD->height; y++) // we have to write the memory in the wrong direction
		{
			ucMask = 0x80 >> (y & 7);
			s = &pBuffer[pOBD->width - 1 + (pOBD->width * ((pOBD->height - 1 - y) >> 3))]; // point to last line first
			d = &ucLineBuf[2];
			ucLineBuf[1] = ucMirror[y + 1]; // current line number
			for (x = 0; x < pOBD->width / 8; x++)
			{
				c = ucInvert; // we need to brute-force invert it
				for (iBit = 7; iBit >= 0; iBit--)
				{
					if (s[0] & ucMask)
						c ^= (1 << iBit);
					s--;
				}
				*d++ = c;
			} // for y
			// write this line to the display
			ucLineBuf[iPitch + 2] = 0; // end of line
			_I2CWrite(pOBD, ucLineBuf, iPitch + 3);
		} // for x
	}
	else // normal orientation
	{
		for (y = 0; y < pOBD->height; y++) // we have to write the memory in the wrong direction
		{
			ucMask = 1 << (y & 7);
			s = &pBuffer[pOBD->width * (y >> 3)]; // point to last line first
			d = &ucLineBuf[2];

			ucLineBuf[1] = ucMirror[y + 1]; // current line number
			for (x = 0; x < pOBD->width / 8; x++)
			{
				c = ucInvert;
				for (iBit = 7; iBit >= 0; iBit--)
				{
					if (s[0] & ucMask)
						c ^= (1 << iBit);
					s++;
				}
				*d++ = c;
			} // for y
			// write this line to the display
			ucLineBuf[iPitch + 2] = 0; // end of line
			_I2CWrite(pOBD, ucLineBuf, iPitch + 3);
		} // for x
	}
	ucLineBuf[1] = 0;
	_I2CWrite(pOBD, ucLineBuf, 2); // final transfer
	gpio_put(pOBD->iCSPin, LOW);   // de-activate
} /* SharpDumpBuffer() */
//
// Dump a screen's worth of data directly to the display
// Try to speed it up by comparing the new bytes with the existing buffer
//
void obdDumpBuffer(OBDISP *pOBD, uint8_t *pBuffer)
{
	int x, y, iPitch;
	int iLines, iCols;
	uint8_t bNeedPos;
	uint8_t *pSrc = pOBD->ucScreen;

	iPitch = pOBD->width;
	if (pOBD->type == LCD_VIRTUAL) // wrong function for this type of display
		return;
	if (pBuffer == NULL) // dump the internal buffer if none is given
		pBuffer = pOBD->ucScreen;
	if (pBuffer == NULL)
		return; // no backbuffer and no provided buffer

	if (pOBD->type >= SHARP_144x168) // special case for Sharp Memory LCD
	{
		SharpDumpBuffer(pOBD, pBuffer);
		return;
	}
	iLines = pOBD->height >> 3;
	iCols = pOBD->width >> 4;
	for (y = 0; y < iLines; y++)
	{
		bNeedPos = 1;               // start of a new line means we need to set the position too
		for (x = 0; x < iCols; x++) // wiring library has a 32-byte buffer, so send 16 bytes so that the data prefix (0x40) can fit
		{
			if (pOBD->ucScreen == NULL || pBuffer == pSrc || memcmp(pSrc, pBuffer, 16) != 0) // doesn't match, need to send it
			{
				if (bNeedPos) // need to reposition output cursor?
				{
					bNeedPos = 0;
					obdCachedFlush(pOBD, 1);
					obdSetPosition(pOBD, x * 16, y, 1);
				}
				obdCachedWrite(pOBD, pBuffer, 16, 1);
			}
			else
			{
				bNeedPos = 1; // we're skipping a block, so next time will need to set the new position
			}
			pSrc += 16;
			pBuffer += 16;
		}                               // for x
		pSrc += (iPitch - pOBD->width); // for narrow displays, skip to the next line
		pBuffer += (iPitch - pOBD->width);
	} // for y
	obdCachedFlush(pOBD, 1);
} /* obdDumpBuffer() */

// A valid CW or CCW move returns 1 or -1, invalid returns 0.
static int obdMenuReadRotary(SIMPLEMENU *sm)
{
	static int8_t rot_enc_table[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0};
	uint8_t c;
	int rc = 0;

	sm->prevNextCode <<= 2;
	if (gpio_get(sm->u8Dn) == sm->iPressed)
		sm->prevNextCode |= 0x02;
	if (gpio_get(sm->u8Up) == sm->iPressed)
		sm->prevNextCode |= 0x01;
	sm->prevNextCode &= 0x0f;

	// If valid then store as 16 bit data.
	if (rot_enc_table[sm->prevNextCode])
	{
		sm->store <<= 4;
		sm->store |= sm->prevNextCode;
		c = sm->store & 0xff;
		//if (store==0xd42b) return 1;
		//if (store==0xe817) return -1;
		if ((c & 0xf) == 2)
			rc = -1;
		else if ((c & 0xf) == 1)
			rc = 1;
	}
	//   Serial.printf("store = 0x%04x, val = %d\n", sm->store, rc);
	return rc;
} /* obdMenuReadRotary() */

//
// Initialize the simple menu structure
//
int obdMenuInit(OBDISP *pOBD, SIMPLEMENU *sm, char **pText, int iFontSize, int bCenter, int btnUp, int btnDn, int btnEnter, int iPressedState, int bIsRotary)
{
	int iLen;
	if (sm == NULL || pText == NULL)
		return 0;
	sm->pOBD = pOBD;
	sm->u8Up = btnUp;       // pin numbers of the action buttons
	sm->u8Dn = btnDn;       // or rotary A line
	sm->u8Enter = btnEnter; // or rotary B line
	sm->bIsRotary = bIsRotary;
	sm->u8BtnState = 0;           // no active buttons to start
	sm->iPressed = iPressedState; // active state of a pressed button
	sm->bCenter = bCenter;
	sm->iFontSize = iFontSize;
	sm->pMenuText = pText;
	sm->iMenuIndex = 0;                               // start at first item
	sm->iDispX = 128;                                 // DEBUG
	sm->iDispY = 64;                                  // DEBUG
	sm->bOneButton = (btnDn == -1 && btnEnter == -1); // only 1 button defined
	sm->pfnCallback = NULL;
	sm->prevNextCode = 0;
	sm->store = 0;
	iLen = 0;
	while (pText[iLen] != NULL)
	{
		iLen++;
	}
	sm->iMenuLen = iLen - 1; // don't count the title text
	return 1;                // success
} /* obdMenuInit() */

//
// Get the text and centering position for
// a specific menu item
// returns the X position
//
static int obdMenuGetItem(SIMPLEMENU *sm, int iItem, char *szText)
{
	int x, cx, len;

	if (iItem > sm->iMenuLen)
		return -1; // invalid request

	if (sm->iFontSize == FONT_6x8)
		cx = 6;
	else if (sm->iFontSize == FONT_8x8)
		cx = 8;
	else
		cx = 16;
	strcpy(szText, sm->pMenuText[iItem]);
	if (sm->pfnCallback && iItem > 0) // don't add callback for menu title
	{
		strcat(szText, " ");
		strcat(szText, (*sm->pfnCallback)(iItem - 1));
	}
	x = 0;
	if (sm->bCenter || iItem == 0) // always center the menu title
	{
		len = strlen(szText);
		x = (sm->iDispX - (len * cx)) / 2;
	}
	return x;
} /* obdMenuGetItem() */

//
// Erase the display and show the given menu
//
void obdMenuShow(SIMPLEMENU *sm, int iItem)
{
	int i, x, iCount, iStart = 0;
	int iFirst, iLast;
	char szTemp[64];

	iCount = (sm->iDispY / 8) - 1; // DEBUG - number of visible menu lines
	iFirst = iLast = iItem;
	if (iItem == -1) // show the entire menu
	{
		obdFill(sm->pOBD, 0, 0);
		x = obdMenuGetItem(sm, 0, szTemp);                               // get the title text
		obdMenuShowItem(sm->pOBD, x, 0, szTemp, 0, 0, sm->iFontSize, 0); // show title
		iFirst = 0;
		iLast = iCount - 1;
	}
	if (sm->iMenuIndex >= iCount) // needs to scroll up
		iStart = sm->iMenuIndex - (iCount - 1);
	if (sm->iMenuIndex < 0 || sm->iMenuIndex + iCount > sm->iMenuLen)
	{ // invalid
		sm->iMenuIndex = 0;
		iStart = 0;
	}

	for (i = iFirst; i <= iLast && i + iStart < sm->iMenuLen; i++) // draw the visible menu lines
	{
		x = obdMenuGetItem(sm, i + iStart + 1, szTemp);
		if (x >= 0) // display if valid
			obdMenuShowItem(sm->pOBD, x, i + 1, szTemp, (i + iStart == sm->iMenuIndex), (iFirst == iLast), sm->iFontSize, (iFirst == iLast));
	}
	if (iItem == -1) // now the display it in one shot
		obdDumpBuffer(sm->pOBD, NULL);
} /* obdMenuShow() */

//
// Set a callback function to return custom info/status
// for each menu item
//
void obdMenuSetCallback(SIMPLEMENU *sm, SIMPLECALLBACK pfnCallBack)
{
	if (sm != NULL)
		sm->pfnCallback = pfnCallBack;
} /* obdMenuSetCallback() */
//
// Display the text of a single menu item
// optionally erases what's under it to prevent left-over text when the length changes
//
void obdMenuShowItem(OBDISP *pOBD, int x, int y, char *szText, int bInvert, int bErase, int iFontSize, int bRender)
{
	static char *szBlank = (char *)"                      ";
	if (bErase)
		obdWriteString(pOBD, 0, 0, y, szBlank, iFontSize, 0, bRender); // erase old info
	obdWriteString(pOBD, 0, x, y, szText, iFontSize, bInvert, bRender);
} /* obdMenuShowItem() */

//
// Flash a menu item when it is selected
//
static void obdMenuFlash(SIMPLEMENU *sm, int iItem)
{
	int x, y, i, iCount;
	char szTemp[64];

	iCount = (sm->iDispY / 8) - 1; // DEBUG - number of visible menu lines
	y = iItem + 1;
	if (y > iCount) // max bottom line
		y = iCount;

	x = obdMenuGetItem(sm, iItem + 1, szTemp);
	if (x < 0)
		return; // invalid request

	for (i = 0; i < 3; i++)
	{
		obdMenuShowItem(sm->pOBD, x, y, szTemp, 0, 0, sm->iFontSize, 1); // show non-inverted
		sleep_ms(200);
		obdMenuShowItem(sm->pOBD, x, y, szTemp, 1, 0, sm->iFontSize, 1); // show inverted
		sleep_ms(200);
	}
} /* obdMenuFlash() */

//
// Change the menu index incrementally
// redraws the minimum amount of screen to show the new info
// (this prevents flicker/flash and saves battery life)
// returns the new menu index
//
int obdMenuDelta(SIMPLEMENU *sm, int iDelta)
{
	int i, x, iNewIndex, iCount;
	int iStart1, iStart2;
	char szTemp[64];

	if (iDelta == 0)
		return sm->iMenuIndex; // nothing to do

	iNewIndex = sm->iMenuIndex + iDelta;
	if (!sm->bOneButton && (iNewIndex < 0 || iNewIndex >= sm->iMenuLen)) // no change possible, exit
		return sm->iMenuIndex;                                             // unchanged
	// If we are using a single button, wrap around the ends
	if (iNewIndex < 0)
		iNewIndex = (sm->iMenuLen - 1);
	else if (iNewIndex > sm->iMenuLen - 1)
		iNewIndex = 0;

	iCount = (sm->iDispY / 8) - 1; // DEBUG - number of visible menu lines
	iStart1 = iStart2 = 0;
	if (sm->iMenuIndex > iCount - 1)
		iStart1 = sm->iMenuIndex - (iCount - 1);
	if (iNewIndex > iCount - 1) // needs to scroll up
		iStart2 = iNewIndex - (iCount - 1);
	if (iStart1 != iStart2) // need to redraw all items
	{
		for (i = 0; i < iCount; i++)
		{
			x = obdMenuGetItem(sm, i + iStart2 + 1, szTemp);
			if (x >= 0)
				obdMenuShowItem(sm->pOBD, x, i + 1, szTemp, (i + iStart2 == iNewIndex), 1, sm->iFontSize, 0);
		}
		obdDumpBuffer(sm->pOBD, NULL);
	}
	else // need to redraw only the new and old items
	{
		i = sm->iMenuIndex - iStart1;
		x = obdMenuGetItem(sm, sm->iMenuIndex + 1, szTemp);
		if (x >= 0)
			obdMenuShowItem(sm->pOBD, x, i + 1, szTemp, 0, 0, sm->iFontSize, 1);
		i = iNewIndex - iStart2;
		x = obdMenuGetItem(sm, iNewIndex + 1, szTemp);
		if (x >= 0)
			obdMenuShowItem(sm->pOBD, x, i + 1, szTemp, 1, 0, sm->iFontSize, 1);
	}
	sm->iMenuIndex = iNewIndex;
	return iNewIndex;
} /* obdMenuDelta() */

//
// With the given setup, check for button presses
// and act accordingly
// returns -1 for normal interactions and the menu item index if the user presses the ENTER button
//
// time in milliseconds for a long press
#define MENU_LONG_PRESS 600
int obdMenuRun(SIMPLEMENU *sm)
{
	uint8_t buttons = 0;
	unsigned long ul;
	int iDelta, rc = -1;

	if (sm->bIsRotary)
	{ // read the rotary encoder
		if (gpio_get(sm->u8Enter) == sm->iPressed)
		{
			buttons |= 1; // pressed
			if (buttons != sm->u8BtnState)
				rc = sm->iMenuIndex; // user pressed ENTER, return current menu index
		}
		else
		{ // check for rotary encoder activity
			iDelta = obdMenuReadRotary(sm);
			obdMenuDelta(sm, iDelta);
		}
		sm->u8BtnState = buttons;
	}
	else
	{
		// check the button states
		if (gpio_get(sm->u8Up) == sm->iPressed)
			buttons |= 1;
		if (buttons != sm->u8BtnState) // something changed
		{
			if (sm->bOneButton) // different logic for a single button system
			{
				if (sm->u8BtnState == 0 && buttons == 1) // up button just pressed
				{
					sm->ulPressTime = to_ms_since_boot(get_absolute_time()); // record the press time
				}
				if (sm->u8BtnState == 1 && buttons == 0) // up button just released
				{
					ul = to_ms_since_boot(get_absolute_time()) - sm->ulPressTime;
					if (ul < MENU_LONG_PRESS) // short press = navigate menu
						obdMenuDelta(sm, 1);
					else                   // treat it like a long press
						rc = sm->iMenuIndex; // action
				}
			}
			else // 3 button setup (UP/DOWN/ENTER)
			{
				if (gpio_get(sm->u8Dn) == sm->iPressed)
					buttons |= 2;
				if (gpio_get(sm->u8Enter) == sm->iPressed)
					rc = sm->iMenuIndex;                               // user pressed ENTER, return current menu index
				if ((sm->u8BtnState & 1) == 0 && (buttons & 1) == 1) // Up button pressed
				{
					obdMenuDelta(sm, -1);
				}
				if ((sm->u8BtnState & 2) == 0 && (buttons & 2) == 2) // Down button pressed
				{
					obdMenuDelta(sm, 1);
				}
			}
			sm->u8BtnState = buttons; // save the latest state
		}
	}
	if (rc != -1) // selected
		obdMenuFlash(sm, sm->iMenuIndex);
	return rc;
} /* obdMenuRun() */


================================================
FILE: lib/OneBitDisplay/OneBitDisplay.h
================================================
#ifndef __ONEBITDISPLAY__
#define __ONEBITDISPLAY__

#include <BitBang_I2C.h>
#include "hardware/i2c.h"

// Proportional font data taken from Adafruit_GFX library
/// Font data stored PER GLYPH
typedef struct {
  uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
  uint8_t width;         ///< Bitmap dimensions in pixels
  uint8_t height;        ///< Bitmap dimensions in pixels
  uint8_t xAdvance;      ///< Distance to advance cursor (x axis)
  int8_t xOffset;        ///< X dist from cursor pos to UL corner
  int8_t yOffset;        ///< Y dist from cursor pos to UL corner
} GFXglyph;

/// Data stored for FONT AS A WHOLE
typedef struct {
  uint8_t *bitmap;  ///< Glyph bitmaps, concatenated
  GFXglyph *glyph;  ///< Glyph array
  uint8_t first;    ///< ASCII extents (first char)
  uint8_t last;     ///< ASCII extents (last char)
  uint8_t yAdvance; ///< Newline distance (y axis)
} GFXfont;

typedef struct obdstruct
{
uint8_t oled_addr; // requested address or 0xff for automatic detection
uint8_t wrap, flip, invert, type;
uint8_t *ucScreen;
int iCursorX, iCursorY;
int width, height;
int iScreenOffset;
BBI2C bbi2c;
uint8_t com_mode; // communication mode (I2C / SPI)
uint8_t mode; // data/command mode for 9-bit SPI
uint8_t iDCPin, iMOSIPin, iCLKPin, iCSPin;
uint8_t iLEDPin; // backlight
uint8_t bBitBang;
} OBDISP;

typedef char * (*SIMPLECALLBACK)(int iMenuItem);

typedef struct smenu {
  uint8_t u8Up, u8Dn, u8Enter; // button pin numbers
  uint8_t bIsRotary; // rotary encoder or up/down buttons?
  uint8_t bCenter; // center all menu text if true
  uint8_t u8BtnState; // state of all buttons
  uint8_t bOneButton; // flag indicating the menu operates from a single button
  uint8_t prevNextCode; // rotary encoder state machine
  int iMenuIndex; // current menu index
  int iMenuLen; // number of entries in the menu (calculated at startup)
  char **pMenuText; // string array with menu title and text
  int iFontSize;
  int iPressed; // polarity of button pressed state
  unsigned long ulPressTime; // time in millis when button was pressed
  int iDispX, iDispY; // display width/height in pixels
  SIMPLECALLBACK pfnCallback;
  OBDISP *pOBD; // display structureme
  uint16_t store;
} SIMPLEMENU;

// Make the Linux library interface C instead of C++
#if defined(_LINUX_) && defined(__cplusplus)
extern "C" {
#endif

#if !defined(BITBANK_LCD_MODES)
#define BITBANK_LCD_MODES
typedef enum
{
 MODE_DATA = 0,
 MODE_COMMAND
} DC_MODE;
#endif

typedef enum
{
  COM_I2C = 0,
  COM_SPI,
} COM_MODE;

typedef enum
{
  ROT_0 = 0,
  ROT_90,
  ROT_180,
  ROT_270
} FONT_ROTATION;

// These are defined the same in my SPI_LCD library
#ifndef SPI_LCD_H

// 5 possible font sizes: 8x8, 16x32, 6x8, 12x16 (stretched from 6x8 with smoothing), 16x16 (stretched from 8x8)
enum {
   FONT_6x8 = 0,
   FONT_8x8,
   FONT_12x16,
   FONT_16x16,
   FONT_16x32,
   FONT_COUNT
};
// For backwards compatibility, keep the old names valid
#define FONT_NORMAL FONT_8x8
#define FONT_SMALL FONT_6x8
#define FONT_STRETCHED FONT_16x16
#define FONT_LARGE FONT_16x32
#endif

// Display type for init function
enum {
  OLED_128x128 = 1,
  OLED_128x32,
  OLED_128x64,
  OLED_132x64,
  OLED_64x128,
  OLED_64x32,
  OLED_96x16,
  OLED_72x40,
  LCD_UC1701,
  LCD_UC1609,
  LCD_HX1230,
  LCD_NOKIA5110,
  LCD_VIRTUAL,
  SHARP_144x168,
  SHARP_400x240
};

// Rotation and flip angles to draw tiles
enum {
  ANGLE_0=0,
  ANGLE_90,
  ANGLE_180,
  ANGLE_270,
  ANGLE_FLIPX,
  ANGLE_FLIPY
};

// Return value from obd obdI2CInit()
enum {
  OLED_NOT_FOUND = -1, // no display found
  OLED_SSD1306_3C,  // SSD1306 found at 0x3C
  OLED_SSD1306_3D,  // SSD1306 found at 0x3D
  OLED_SH1106_3C,   // SH1106 found at 0x3C
  OLED_SH1106_3D,   // SH1106 found at 0x3D
  OLED_SH1107_3C,  // SH1107
  OLED_SH1107_3D,
  LCD_OK,
  LCD_ERROR
};
//
// Create a virtual display of any size
// The memory buffer must be provided at the time of creation
//
void obdCreateVirtualDisplay(OBDISP *pOBD, int width, int height, uint8_t *buffer);
// Constants for the obdCopy() function
// Output format options -
#define OBD_LSB_FIRST     0x001
#define OBD_MSB_FIRST     0x002
#define OBD_VERT_BYTES    0x004
#define OBD_HORZ_BYTES    0x008
// Orientation options -
#define OBD_ROTATE_90     0x010
#define OBD_FLIP_VERT     0x020
#define OBD_FLIP_HORZ     0x040
#define OBD_INVERT        0x080
// Copy the current bitmap buffer from its native form (LSB_FIRST, VERTICAL_BYTES) to the requested form
// A copy of the same format will just do a memcpy
int obdCopy(OBDISP *pOBD, int iFlags, uint8_t *pDestination);
//
// Draw the contents of a memory buffer onto a display
// The sub-window will be clipped if it specifies too large an area
// for the destination display. The source OBDISP structure must have
// a valid back buffer defined
// The top and bottom destination edges will be drawn on byte boundaries (8 rows)
// The source top/bot edges can be on pixel boundaries
//
void obdDumpWindow(OBDISP *pOBDSrc, OBDISP *pOBDDest, int srcx, int srcy, int destx, int desty, int width, int height);
//
// Write a single line to a Sharp memory LCD
// You must provide the exact number of bytes needed for a complete line
// e.g. for the 144x168 display, pSrc must provide 144 pixels (18 bytes)
//
void obdWriteLCDLine(OBDISP *pOBD, uint8_t *pSrc, int iLine);
//
// Initializes the display controller into "page mode" on I2C
// If SDAPin and SCLPin are not -1, then bit bang I2C on those pins
// Otherwise use the Wire library.
// If you don't need to use a separate reset pin, set it to -1
//
int obdI2CInit(OBDISP *pOBD, int iType, int iAddr, int bFlip, int bInvert, int bWire, int iSDAPin, int iSCLPin, i2c_inst_t *picoI2C, int iResetPin, int32_t iSpeed);
//
// Initialize an SPI version of the display
//
void obdSPIInit(OBDISP *pOBD, int iType, int iDC, int iCS, int iReset, int iMOSI, int iCLK, int iLED, int bFlip, int bInvert, int iBitBang, int32_t iSpeed);

//
// Provide or revoke a back buffer for your OLED graphics
// This allows you to manage the RAM used by ss_oled on tiny
// embedded platforms like the ATmega series
// Pass NULL to revoke the buffer. Make sure you provide a buffer
// large enough for your display (e.g. 128x64 needs 1K - 1024 bytes)
//
void obdSetBackBuffer(OBDISP *pOBD, uint8_t *pBuffer);
//
// Sets the brightness (0=off, 255=brightest)
//
void obdSetContrast(OBDISP *pOBD, unsigned char ucContrast);
//
// Load a 1-bpp Windows bitmap
// Pass the pointer to the beginning of the BMP file
// First pass version assumes a full screen bitmap
//
int obdLoadBMP(OBDISP *pOBD, uint8_t *pBMP, int x, int y, int bInvert);
//
// Power up/down the display
// useful for low power situations
//
void obdPower(OBDISP *pOBD, int bOn);
//
// Set the current cursor position
// The column represents the pixel column (0-127)
// The row represents the text row (0-7)
//
void obdSetCursor(OBDISP *pOBD, int x, int y);

//
// Turn text wrap on or off for the obdWriteString() function
//
void obdSetTextWrap(OBDISP *pOBD, int bWrap);
//
// Draw a string of normal (8x8), small (6x8) or large (16x32) characters
// At the given col+row with the given scroll offset. The scroll offset allows you to
// horizontally scroll text which does not fit on the width of the display. The offset
// represents the pixels to skip when drawing the text. An offset of 0 starts at the beginning
// of the text.
// The system remembers where the last text was written (the cursor position)
// To continue writing from the last position, set the x,y values to -1
// The text can optionally wrap around to the next line by calling oledSetTextWrap(true);
// otherwise text which would go off the right edge will not be drawn and the cursor will
// be left "off screen" until set to a new position explicitly
//
//  Returns 0 for success, -1 for invalid parameter
//
int obdWriteString(OBDISP *pOBD, int iScrollX, int x, int y, char *szMsg, int iSize, int bInvert, int bRender);
//
// Draw a string with a fractional scale in both dimensions
// the scale is a 16-bit integer with and 8-bit fraction and 8-bit mantissa
// To draw at 1x scale, set the scale factor to 256. To draw at 2x, use 512
// The output must be drawn into a memory buffer, not directly to the display
// The string can be drawn in one of 4 rotations (ROT_0, ROT_90, ROT_180, ROT_270)
//
int obdScaledString(OBDISP *pOBD, int x, int y, char *szMsg, int iSize, int bInvert, int iXScale, int iYScale, int iRotation);
//
// Draw a string in a proportional font you supply
// Requires a back buffer
//
int obdWriteStringCustom(OBDISP *pOBD, GFXfont *pFont, int x, int y, char *szMsg, uint8_t ucColor);
//
// Get the width of text in a custom font
//
void obdGetStringBox(GFXfont *pFont, char *szMsg, int *width, int *top, int *bottom);
//
// Fill the frame buffer with a byte pattern
// e.g. all off (0x00) or all on (0xff)
//
void obdFill(OBDISP *pOBD, unsigned char ucData, int bRender);
//
// Set (or clear) an individual pixel
// The local copy of the frame buffer is used to avoid
// reading data from the display controller
// (which isn't possible in most configurations)
// This function needs the USE_BACKBUFFER macro to be defined
// otherwise, new pixels will erase old pixels within the same byte
//
int obdSetPixel(OBDISP *pOBD, int x, int y, unsigned char ucColor, int bRender);
//
// Dump an entire custom buffer to the display
// useful for custom animation effects
//
void obdDumpBuffer(OBDISP *pOBD, uint8_t *pBuffer);
//
// Render a window of pixels from a provided buffer or the library's internal buffer
// to the display. The row values refer to byte rows, not pixel rows due to the memory
// layout of OLEDs. Pass a src pointer of NULL to use the internal backing buffer
// returns 0 for success, -1 for invalid parameter
//
int obdDrawGFX(OBDISP *pOBD, uint8_t *pSrc, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch);

//
// Draw a line between 2 points
//
void obdDrawLine(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, int bRender);
//
// Play a frame of animation data
// The animation data is assumed to be encoded for a full frame of the display
// Given the pointer to the start of the compressed data,
// it returns the pointer to the start of the next frame
// Frame rate control is up to the calling program to manage
// When it finishes the last frame, it will start again from the beginning
//
uint8_t * obdPlayAnimFrame(OBDISP *pOBD, uint8_t *pAnimation, uint8_t *pCurrent, int iLen);
void obdWriteCommand(OBDISP *pOBD, unsigned char c);
void obdSetPosition(OBDISP *pOBD, int x, int y, int bRender);
void obdWriteDataBlock(OBDISP *pOBD, unsigned char *ucBuf, int iLen, int bRender);
//
// Scroll the internal buffer by 1 scanline (up/down)
// width is in pixels, lines is group of 8 rows
// Returns 0 for success, -1 for invalid parameter
//
int obdScrollBuffer(OBDISP *pOBD, int iStartCol, int iEndCol, int iStartRow, int iEndRow, int bUp);
//
// Draw a sprite of any size in any position
// If it goes beyond the left/right or top/bottom edges
// it's trimmed to show the valid parts
// This function requires a back buffer to be defined
// The priority color (0 or 1) determines which color is painted 
// when a 1 is encountered in the source image.
// e.g. when 0, the input bitmap acts like a mask to clear
// the destination where bits are set.
//
void obdDrawSprite(OBDISP *pOBD, uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority);
//
// Draw a 16x16 tile in any of 4 rotated positions
// Assumes input image is laid out like "normal" graphics with
// the MSB on the left and 2 bytes per line
// On AVR, the source image is assumed to be in FLASH memory
// The function can draw the tile on byte boundaries, so the x value
// can be from 0 to 112 and y can be from 0 to 6
//
void obdDrawTile(OBDISP *pOBD, const uint8_t *pTile, int x, int y, int iRotation, int bInvert, int bRender);
//
// Draw an outline or filled ellipse
//
void obdEllipse(OBDISP *pOBD, int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled);
//
// Draw an outline or filled ellipse with more precision
//
void obdPreciseEllipse(OBDISP *pOBD, int x, int y, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled);
//
// Draw an outline or filled rectangle
//
void obdRectangle(OBDISP *pOBD, int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled);

//
// Turn the LCD backlight on or off
//
void obdBacklight(OBDISP *pODB, int bOn);

//
// Menu functions
//
// Initialize the simple menu structure
//
int obdMenuInit(OBDISP *pOBD, SIMPLEMENU *sm, char **pText, int iFontSize, int bCenter, int btnUp, int btnDn, int btnEnter, int iPressedState, int bIsRotary);
//
// Erase the display and show the given menu
//
void obdMenuShow(SIMPLEMENU *sm, int iItem);
//
// Set a callback function to return custom info/status
// for each menu item
//
void obdMenuSetCallback(SIMPLEMENU *sm, SIMPLECALLBACK pfnCallBack);
//
// Display the text of a single menu item
// optionally erases what's under it to prevent left-over text when the length changes
//
void obdMenuShowItem(OBDISP *pOBD, int x, int y, char *szText, int bInvert, int bErase, int iFontSize, int bRender);
//
// Change the menu index incrementally
// redraws the minimum amount of screen to show the new info
// (this prevents flicker/flash and saves battery life)
// returns the new menu index
//
int obdMenuDelta(SIMPLEMENU *sm, int iDelta);
//
// With the given setup, check for button presses
// and act accordingly
// returns -1 for normal interactions and the menu item index if the user presses the ENTER button
//
int obdMenuRun(SIMPLEMENU *sm);

#if defined(_LINUX_) && defined(__cplusplus)
}
#endif // _LINUX_

#endif // __ONEBITDISPLAY__



================================================
FILE: lib/OneBitDisplay/README.md
================================================
OneBitDisplay (1-bpp OLED/LCD library)<br>
-----------------------------------
Project started 3/23/2020<br>
Copyright (c) 2020 BitBank Software, Inc.<br>
Written by Larry Bank<br>
bitbank@pobox.com<br>
<br>
![OneBitDisplay](/demo.jpg?raw=true "OneBitDisplay")
<br>
The purpose of this code is to easily control monochrome (1-bit per pixel) OLED and LCD displays. The displays can be connected to the traditional I2C or SPI bus, or you can use GPIO pins to bit bang the signals.<br>
<br>
On AVR microcontrollers, there is an optimized option to speed up access to the GPIO pins to allow speeds which match or exceed normal I2C speeds. The pins are numbered with the Port letter as the first digit followed by the bit number. For example, To use bit 0 of Port B, you would reference pin number 0xb0.<br>
<br>
Includes the unique feature that the I2C init function can optionally detect the display address (0x3C or 0x3D) and the controller type (SSD1306, SH1106 or SH1107).<br>
<br>
I try to support as many OLEDs as I can. I was able to justify buying a bunch
of different sized SSD1306 displays because they're around $2 each. A generous patron
donated money so that I could purchase Pimoroni's 128x128 OLED and add support for it.
It uses the SH1107 controller and behaves very similarly to the SH1106.
<br>

Features:<br>
---------<br>
- Supports any number of simultaneous displays of any type (mix and match)<br>
- Optionally detect the display address and type (I2C only)<br>
- Supports 72x40, 96x16, 64x32, 128x32, 128x64, 64x128 (SH1107), 128x128 (SH1107) and 132x64 (SH1106) OLED display sizes<br>
- Supports 96x68 HX1230, 84x48 Nokia 5110 and 128x64 ST7567/UC1701 mono LCDs<br>
- Supports 144x168 and 400x240 Sharp Memory LCDs<br>
- Virtual displays of any size which can be drawn across multiple physical displays
- Flexible copy function can convert the internal pixel format to any output format and orientation
- Drive displays from I2C, SPI or any GPIO pins (virtual I2C/SPI)<br>
- Includes 5 sizes of fixed fonts (6x8, 8x8, 12x16, 16x16, 16x32)<br>
- Text drawing at any fractional scale (e.g. 1.25x), and any of 4 directions/rotations<br>
- Can use Adafruit_GFX format bitmap fonts (proportional and fixed)<br>
- Deferred rendering allows preparing a back buffer, then displaying it (usually faster)<br>
- Text scrolling features (vertical and horizontal)<br>
- Text cursor position with optional line wrap<br>
- A function to load a Windows BMP file<br>
- Pixel drawing on SH1106/7 without needing backing RAM<br>
- Optimized Bresenham line drawing<br>
- Optimized Bresenham outline and filled ellipse drawing<br>
- Optimized outline and filled rectangle drawing<br>
- Optional backing RAM (needed for some text and drawing functions)<br>
- 16x16 Tile/Sprite drawing at any angle.<br>
- Run full frame animations at high frame rates with a simple API<br>
<br>
This code depends on the BitBang_I2C library. You can download it here:<br>
https://github.com/bitbank2/BitBang_I2C
<br>
See the Wiki for help getting started<br>
https://github.com/bitbank2/OneBitDisplay/wiki <br>
<br>

![Fonts](/fonts_opt.jpg?raw=true "fonts")
A few words about fonts<br>
-----------------------<br>

The library includes 3 fixed fonts (6x8, 8x8 and 16x32). The 16x32 font is disabled when compiling for AVR targets (e.g. Arduino Uno) to save FLASH program space. The other 2 fonts offer 2x stretched versions (12x16 from 6x8 and 16x16 from 8x8). A simple smoothing algorithm is applied to the stretched 6x8 font to make it look better. In the photo above are the first 4 font sizes shown on a 128x64 yellow OLED display. Only 96 ASCII characters are defined per font to save space. To use more elaborate fonts with more extensive character support, use Adafruit_GFX format bitmap fonts with the `obdWriteStringCustom()` function.<br>

![Sharp Memory
Download .txt
gitextract_notmhc6x/

├── .editorconfig
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── main.yml
│       └── release.yml
├── .gitignore
├── .gitmodules
├── .vscode/
│   └── extensions.json
├── LICENSE
├── README.md
├── build-web.py
├── configs/
│   ├── CrushCounter/
│   │   ├── BoardConfig.h
│   │   └── env.ini
│   ├── DURAL/
│   │   ├── BoardConfig.h
│   │   ├── README.md
│   │   └── env.ini
│   ├── DebugBoard/
│   │   ├── BoardConfig.h
│   │   ├── README.md
│   │   ├── assets/
│   │   │   └── picoprobe_adafruit_qtpy_rp2040.uf2
│   │   └── env.ini
│   ├── Fightboard/
│   │   ├── BoardConfig.h
│   │   ├── README.md
│   │   ├── assets/
│   │   │   └── PinMapping.xcf
│   │   └── env.ini
│   ├── FlatboxRev4/
│   │   ├── BoardConfig.h
│   │   ├── README.md
│   │   └── env.ini
│   ├── GeeekPiStick/
│   │   ├── BoardConfig.h
│   │   ├── README.md
│   │   ├── assets/
│   │   │   └── PinMapping.xcf
│   │   └── env.ini
│   ├── Hydra/
│   │   ├── BoardConfig.h
│   │   └── env.ini
│   ├── OSFRD/
│   │   ├── BoardConfig.h
│   │   └── env.ini
│   └── Pico/
│       ├── BoardConfig.h
│       ├── README.md
│       ├── assets/
│       │   └── PinMapping.xcf
│       └── env.ini
├── docs/
│   ├── .nojekyll
│   ├── CNAME
│   ├── _navbar.md
│   ├── assets/
│   │   └── images/
│   │       └── led-themes/
│   │           └── _base-template.xcf
│   ├── development.md
│   ├── downloads/
│   │   └── flash_nuke.uf2
│   ├── faq.md
│   ├── index.html
│   ├── usage.md
│   └── web-configurator.md
├── include/
│   ├── display.h
│   ├── enums.h
│   ├── gamepad.h
│   ├── gp2040.h
│   ├── leds.h
│   ├── pico/
│   │   └── config_autogen.h
│   ├── pleds.h
│   ├── storage.h
│   ├── themes.h
│   └── tusb_config.h
├── lib/
│   ├── AnimationStation/
│   │   ├── .editorconfig
│   │   ├── library.json
│   │   └── src/
│   │       ├── Animation.cpp
│   │       ├── Animation.hpp
│   │       ├── AnimationStation.cpp
│   │       ├── AnimationStation.hpp
│   │       ├── AnimationStorage.hpp
│   │       ├── Effects/
│   │       │   ├── Chase.cpp
│   │       │   ├── Chase.hpp
│   │       │   ├── Rainbow.cpp
│   │       │   ├── Rainbow.hpp
│   │       │   ├── StaticColor.cpp
│   │       │   ├── StaticColor.hpp
│   │       │   ├── StaticTheme.cpp
│   │       │   └── StaticTheme.hpp
│   │       └── Pixel.hpp
│   ├── BitBang_I2C/
│   │   ├── BitBang_I2C.c
│   │   ├── BitBang_I2C.h
│   │   └── README.md
│   ├── CRC32/
│   │   └── src/
│   │       ├── CRC32.cpp
│   │       └── CRC32.h
│   ├── FlashPROM/
│   │   ├── include/
│   │   │   └── FlashPROM.h
│   │   ├── library.json
│   │   └── src/
│   │       └── FlashPROM.cpp
│   ├── NeoPico/
│   │   ├── .editorconfig
│   │   ├── library.json
│   │   └── src/
│   │       ├── NeoPico.cpp
│   │       ├── NeoPico.hpp
│   │       ├── ws2812.pio
│   │       └── ws2812.pio.h
│   ├── OneBitDisplay/
│   │   ├── OneBitDisplay.cpp
│   │   ├── OneBitDisplay.h
│   │   ├── README.md
│   │   ├── fonts/
│   │   │   └── FreeSerif12pt7b.h
│   │   └── obd.inl
│   ├── PlayerLEDs/
│   │   ├── include/
│   │   │   └── PlayerLEDs.h
│   │   └── src/
│   │       └── PlayerLEDs.cpp
│   ├── TinyUSB_Gamepad/
│   │   ├── include/
│   │   │   ├── hid_driver.h
│   │   │   ├── net_driver.h
│   │   │   ├── usb_driver.h
│   │   │   ├── webserver_descriptors.h
│   │   │   └── xinput_driver.h
│   │   ├── library.json
│   │   └── src/
│   │       ├── hid_driver.cpp
│   │       ├── net_driver.cpp
│   │       ├── tusb_driver.cpp
│   │       ├── usb_descriptors.cpp
│   │       └── xinput_driver.cpp
│   ├── httpd/
│   │   ├── fs.c
│   │   ├── fs.h
│   │   ├── fscustom.h
│   │   ├── fsdata.c
│   │   ├── fsdata.h
│   │   ├── httpd.c
│   │   ├── httpd.h
│   │   └── httpd_structs.h
│   ├── lwip-port/
│   │   ├── arch/
│   │   │   └── cc.h
│   │   ├── lwipopts.h
│   │   └── server/
│   │       ├── dhserver.c
│   │       ├── dhserver.h
│   │       ├── dnserver.c
│   │       └── dnserver.h
│   └── rndis/
│       ├── ndis.h
│       ├── rndis.c
│       ├── rndis.h
│       ├── rndis_protocol.h
│       └── rndis_reports.c
├── platformio.ini
├── src/
│   ├── display.cpp
│   ├── gamepad.cpp
│   ├── leds.cpp
│   ├── main.cpp
│   ├── pleds.cpp
│   ├── storage.cpp
│   └── webserver.cpp
├── tools/
│   └── makefsdata
└── www/
    ├── .gitignore
    ├── README.md
    ├── package.json
    ├── public/
    │   ├── index.html
    │   └── manifest.json
    ├── server/
    │   ├── app.js
    │   └── docs/
    │       ├── GP2040 (Dev).postman_environment.json
    │       └── GP2040.postman_collection.json
    └── src/
        ├── App.js
        ├── App.scss
        ├── Components/
        │   ├── DangerSection.js
        │   ├── DraggableListGroup.js
        │   ├── DraggableListGroup.scss
        │   ├── FormCheck.js
        │   ├── FormCheck.scss
        │   ├── FormControl.js
        │   ├── FormSelect.js
        │   ├── Navigation.js
        │   ├── Navigation.scss
        │   ├── Section.js
        │   └── Section.scss
        ├── Contexts/
        │   └── AppContext.js
        ├── Data/
        │   ├── Boards.json
        │   ├── Buttons.json
        │   └── Controllers.json
        ├── Pages/
        │   ├── DisplayConfig.js
        │   ├── HomePage.js
        │   ├── LEDConfigPage.js
        │   ├── PinMapping.js
        │   ├── PinMappings.scss
        │   ├── ResetSettingsPage.js
        │   └── SettingsPage.js
        ├── Services/
        │   ├── Storage.js
        │   └── WebApi.js
        ├── index.js
        └── index.scss
Download .txt
SYMBOL INDEX (386 symbols across 62 files)

FILE: include/display.h
  function class (line 59) | class DisplayModule : public GPModule

FILE: include/enums.h
  type ButtonLayout (line 9) | typedef enum

FILE: include/gamepad.h
  type GamepadButtonMapping (line 12) | struct GamepadButtonMapping
  function class (line 27) | class Gamepad : public MPGS

FILE: include/gp2040.h
  function class (line 35) | class GPModule

FILE: include/leds.h
  function class (line 147) | class LEDModule : public GPModule {

FILE: include/pleds.h
  function class (line 38) | class PWMPlayerLEDs : public PlayerLEDs
  function class (line 45) | class RGBPlayerLEDs : public PlayerLEDs
  function class (line 52) | class PLEDModule : public GPModule

FILE: include/storage.h
  type BoardOptions (line 18) | struct BoardOptions
  type LEDOptions (line 54) | struct LEDOptions

FILE: include/themes.h
  function addStaticThemes (line 270) | void addStaticThemes(LEDOptions options)

FILE: lib/AnimationStation/src/Animation.hpp
  type RGB (line 11) | struct RGB {
    method RGB (line 12) | RGB() : r(0), g(0), b(0) {}
    method RGB (line 14) | RGB(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b), w(0) {}
    method RGB (line 16) | RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w)
    method RGB (line 24) | inline static RGB wheel(uint8_t pos) {
    method value (line 37) | inline uint32_t value(LEDFormat format, float brightnessX = 1.0F) {
  class Animation (line 94) | class Animation {

FILE: lib/AnimationStation/src/AnimationStation.hpp
  type AnimationOptions (line 42) | struct __attribute__ ((__packed__)) AnimationOptions
  class AnimationStation (line 54) | class AnimationStation
    method getBrightnessStepSize (line 87) | inline static uint8_t getBrightnessStepSize() { return (brightnessMax ...

FILE: lib/AnimationStation/src/AnimationStorage.hpp
  class AnimationStorage (line 6) | class AnimationStorage

FILE: lib/AnimationStation/src/Effects/Chase.hpp
  class Chase (line 11) | class Chase : public Animation {

FILE: lib/AnimationStation/src/Effects/Rainbow.hpp
  class Rainbow (line 11) | class Rainbow : public Animation {

FILE: lib/AnimationStation/src/Effects/StaticColor.hpp
  class StaticColor (line 10) | class StaticColor : public Animation {

FILE: lib/AnimationStation/src/Effects/StaticTheme.hpp
  class StaticTheme (line 13) | class StaticTheme : public Animation {

FILE: lib/AnimationStation/src/Pixel.hpp
  type Pixel (line 9) | struct Pixel {
    method Pixel (line 10) | Pixel(int index, uint32_t mask = 0) : index(index), mask(mask) { }
    method Pixel (line 11) | Pixel(int index, std::vector<uint8_t> positions) : index(index), posit...
    method Pixel (line 12) | Pixel(int index, uint32_t mask, std::vector<uint8_t> positions) : inde...
  type PixelMatrix (line 21) | struct PixelMatrix {
    method PixelMatrix (line 22) | PixelMatrix() { }
    method setup (line 26) | void setup(std::vector<std::vector<Pixel>> pixels, int ledsPerPixel = ...
    method getLedCount (line 31) | inline int getLedCount() {
    method getPixelCount (line 43) | inline uint16_t getPixelCount() const {

FILE: lib/BitBang_I2C/BitBang_I2C.c
  function I2CInit (line 38) | void I2CInit(BBI2C *pI2C, uint32_t iClock)
  function I2CTest (line 54) | uint8_t I2CTest(BBI2C *pI2C, uint8_t addr)
  function I2CScan (line 67) | void I2CScan(BBI2C *pI2C, uint8_t *pMap)
  function I2CWrite (line 87) | int I2CWrite(BBI2C *pI2C, uint8_t iAddr, uint8_t *pData, int iLen)
  function I2CReadRegister (line 100) | int I2CReadRegister(BBI2C *pI2C, uint8_t iAddr, uint8_t u8Register, uint...
  function I2CRead (line 114) | int I2CRead(BBI2C *pI2C, uint8_t iAddr, uint8_t *pData, int iLen)
  function I2CDiscoverDevice (line 126) | int I2CDiscoverDevice(BBI2C *pI2C, uint8_t i)

FILE: lib/BitBang_I2C/BitBang_I2C.h
  type BBI2C (line 68) | typedef struct mybbi2c

FILE: lib/CRC32/src/CRC32.h
  function class (line 13) | class CRC32 {

FILE: lib/FlashPROM/include/FlashPROM.h
  function class (line 21) | class FlashPROM

FILE: lib/FlashPROM/src/FlashPROM.cpp
  function writeToFlash (line 12) | int64_t writeToFlash(alarm_id_t id, void *flashCache)

FILE: lib/NeoPico/src/NeoPico.cpp
  function LEDFormat (line 17) | LEDFormat NeoPico::GetFormat() {

FILE: lib/NeoPico/src/NeoPico.hpp
  class NeoPico (line 15) | class NeoPico

FILE: lib/NeoPico/src/ws2812.pio.h
  type pio_program (line 30) | struct pio_program
  function pio_sm_config (line 36) | static inline pio_sm_config ws2812_program_get_default_config(uint offse...
  function ws2812_program_init (line 44) | static inline void ws2812_program_init(PIO pio, uint sm, uint offset, ui...
  type pio_program (line 81) | struct pio_program
  function pio_sm_config (line 87) | static inline pio_sm_config ws2812_parallel_program_get_default_config(u...
  function ws2812_parallel_program_init (line 94) | static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint o...

FILE: lib/OneBitDisplay/OneBitDisplay.cpp
  function obdDumpWindow (line 72) | void obdDumpWindow(OBDISP *pOBDSrc, OBDISP *pOBDDest, int srcx, int srcy...
  function obdWriteLCDLine (line 119) | void obdWriteLCDLine(OBDISP *pOBD, uint8_t *pSrc, int iLine)
  function obdPower (line 162) | void obdPower(OBDISP *pOBD, int bOn)
  function obdBacklight (line 174) | void obdBacklight(OBDISP *pOBD, int bOn)
  function LCDPowerUp (line 184) | static void LCDPowerUp(OBDISP *pOBD)
  function obdSPIInit (line 224) | void obdSPIInit(OBDISP *pOBD, int iType, int iDC, int iCS, int iReset, i...
  function obdI2CInit (line 429) | int obdI2CInit(OBDISP *pOBD, int iType, int iAddr, int bFlip, int bInver...
  function oledPower (line 573) | void oledPower(OBDISP *pOBD, uint8_t bOn)
  function SPI_BitBang (line 584) | void SPI_BitBang(OBDISP *pOBD, uint8_t *pData, int iLen, uint8_t iMOSIPi...
  function obdSetDCMode (line 628) | void obdSetDCMode(OBDISP *pOBD, int iMode)
  function obdWriteCommand2 (line 636) | static void obdWriteCommand2(OBDISP *pOBD, unsigned char c, unsigned cha...
  function obdSetContrast (line 657) | void obdSetContrast(OBDISP *pOBD, unsigned char ucContrast)
  function SharpDumpBuffer (line 679) | static void SharpDumpBuffer(OBDISP *pOBD, uint8_t *pBuffer)
  function obdDumpBuffer (line 760) | void obdDumpBuffer(OBDISP *pOBD, uint8_t *pBuffer)
  function obdMenuReadRotary (line 811) | static int obdMenuReadRotary(SIMPLEMENU *sm)
  function obdMenuInit (line 844) | int obdMenuInit(OBDISP *pOBD, SIMPLEMENU *sm, char **pText, int iFontSiz...
  function obdMenuGetItem (line 880) | static int obdMenuGetItem(SIMPLEMENU *sm, int iItem, char *szText)
  function obdMenuShow (line 911) | void obdMenuShow(SIMPLEMENU *sm, int iItem)
  function obdMenuSetCallback (line 949) | void obdMenuSetCallback(SIMPLEMENU *sm, SIMPLECALLBACK pfnCallBack)
  function obdMenuShowItem (line 958) | void obdMenuShowItem(OBDISP *pOBD, int x, int y, char *szText, int bInve...
  function obdMenuFlash (line 969) | static void obdMenuFlash(SIMPLEMENU *sm, int iItem)
  function obdMenuDelta (line 998) | int obdMenuDelta(SIMPLEMENU *sm, int iDelta)
  function obdMenuRun (line 1054) | int obdMenuRun(SIMPLEMENU *sm)

FILE: lib/OneBitDisplay/OneBitDisplay.h
  type GFXglyph (line 9) | typedef struct {
  type GFXfont (line 19) | typedef struct {
  type OBDISP (line 27) | typedef struct obdstruct
  type SIMPLEMENU (line 45) | typedef struct smenu {
  type DC_MODE (line 71) | typedef enum
  type COM_MODE (line 78) | typedef enum
  type FONT_ROTATION (line 84) | typedef enum

FILE: lib/PlayerLEDs/include/PlayerLEDs.h
  type PLEDType (line 11) | typedef enum
  type PLEDStateMask (line 18) | typedef enum
  type PLEDAnimationType (line 26) | typedef enum
  type PLEDAnimationSpeed (line 46) | typedef enum
  type PLEDAnimationState (line 66) | struct PLEDAnimationState
  function class (line 73) | class PlayerLEDs

FILE: lib/TinyUSB_Gamepad/include/usb_driver.h
  type UsbMode (line 10) | typedef enum

FILE: lib/TinyUSB_Gamepad/include/xinput_driver.h
  type XInputPLEDPattern (line 16) | typedef enum

FILE: lib/TinyUSB_Gamepad/src/hid_driver.cpp
  function send_hid_report (line 16) | bool send_hid_report(uint8_t report_id, void *report, uint8_t report_size)
  function hid_device_control_request (line 24) | bool hid_device_control_request(uint8_t rhport, tusb_control_request_t c...

FILE: lib/TinyUSB_Gamepad/src/tusb_driver.cpp
  function InputMode (line 23) | InputMode get_input_mode(void)
  function initialize_driver (line 28) | void initialize_driver(InputMode mode)
  function receive_report (line 37) | void receive_report(uint8_t *buffer)
  function send_report (line 46) | void send_report(void *report, uint16_t report_size)
  function usbd_class_driver_t (line 74) | const usbd_class_driver_t *usbd_app_driver_get_cb(uint8_t *driver_count)
  function tud_hid_get_report_cb (line 100) | uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_repor...
  function tud_hid_set_report_cb (line 129) | void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_ty...
  function tud_mount_cb (line 141) | void tud_mount_cb(void)
  function tud_umount_cb (line 146) | void tud_umount_cb(void)
  function tud_suspend_cb (line 153) | void tud_suspend_cb(bool remote_wakeup_en)
  function tud_resume_cb (line 159) | void tud_resume_cb(void)

FILE: lib/TinyUSB_Gamepad/src/xinput_driver.cpp
  function receive_xinput_report (line 12) | void receive_xinput_report(void)
  function send_xinput_report (line 24) | bool send_xinput_report(void *report, uint8_t report_size)
  function xinput_init (line 41) | static void xinput_init(void)
  function xinput_reset (line 46) | static void xinput_reset(uint8_t rhport)
  function xinput_open (line 51) | static uint16_t xinput_open(uint8_t rhport, tusb_desc_interface_t const ...
  function xinput_device_control_request (line 79) | static bool xinput_device_control_request(uint8_t rhport, tusb_control_r...
  function xinput_control_complete (line 87) | static bool xinput_control_complete(uint8_t rhport, tusb_control_request...
  function xinput_xfer_callback (line 95) | static bool xinput_xfer_callback(uint8_t rhport, uint8_t ep_addr, xfer_r...

FILE: lib/httpd/fs.c
  type fs_file (line 53) | struct fs_file
  type fs_file (line 54) | struct fs_file
  type fs_file (line 56) | struct fs_file
  type fs_file (line 57) | struct fs_file
  function err_t (line 62) | err_t
  function fs_close (line 102) | void
  type fs_file (line 119) | struct fs_file
  type fs_file (line 122) | struct fs_file
  function fs_is_file_ready (line 156) | int
  function fs_bytes_left (line 177) | int

FILE: lib/httpd/fs.h
  type fsdata_chksum (line 82) | struct fsdata_chksum {
  type fs_file (line 95) | struct fs_file {
  type fs_file (line 117) | struct fs_file
  type fs_file (line 118) | struct fs_file
  type fs_file (line 121) | struct fs_file
  type fs_file (line 123) | struct fs_file
  type fs_file (line 127) | struct fs_file
  type fs_file (line 129) | struct fs_file
  type fs_file (line 133) | struct fs_file
  type fs_file (line 135) | struct fs_file

FILE: lib/httpd/fscustom.h
  type fs_file (line 10) | struct fs_file
  type fs_file (line 11) | struct fs_file

FILE: lib/httpd/fsdata.c
  type fsdata_file (line 56682) | struct fsdata_file
  type fsdata_file (line 56690) | struct fsdata_file
  type fsdata_file (line 56698) | struct fsdata_file
  type fsdata_file (line 56706) | struct fsdata_file
  type fsdata_file (line 56714) | struct fsdata_file
  type fsdata_file (line 56722) | struct fsdata_file
  type fsdata_file (line 56730) | struct fsdata_file
  type fsdata_file (line 56738) | struct fsdata_file
  type fsdata_file (line 56746) | struct fsdata_file
  type fsdata_file (line 56754) | struct fsdata_file

FILE: lib/httpd/fsdata.h
  type fsdata_file (line 38) | struct fsdata_file {

FILE: lib/httpd/httpd.c
  type default_filename (line 271) | typedef struct
  type tag_check_state (line 309) | enum tag_check_state {
  type http_ssi_state (line 317) | struct http_ssi_state {
  type http_state (line 336) | struct http_state {
  type tcp_pcb (line 384) | struct tcp_pcb
  type http_state (line 384) | struct http_state
  type tcp_pcb (line 385) | struct tcp_pcb
  type http_state (line 385) | struct http_state
  type http_state (line 386) | struct http_state
  type http_state (line 387) | struct http_state
  type fs_file (line 387) | struct fs_file
  type tcp_pcb (line 388) | struct tcp_pcb
  type http_state (line 415) | struct http_state
  function http_kill_oldest_connection (line 438) | static void
  type http_ssi_state (line 464) | struct http_ssi_state
  type http_ssi_state (line 467) | struct http_ssi_state
  type http_ssi_state (line 475) | struct http_ssi_state
  function http_ssi_state_free (line 481) | static void
  function http_state_init (line 496) | static void
  type http_state (line 508) | struct http_state
  type http_state (line 511) | struct http_state
  type http_state (line 525) | struct http_state
  function http_state_eof (line 538) | static void
  function http_state_free (line 568) | static void
  function err_t (line 606) | static err_t
  function err_t (line 649) | static err_t
  function err_t (line 699) | static err_t
  function http_eof (line 708) | static void
  function extract_uri_parameters (line 734) | static int
  function get_tag_insert (line 803) | static void
  function get_http_headers (line 861) | static void
  function u8_t (line 960) | static u8_t
  function u8_t (line 1023) | static u8_t
  function u8_t (line 1107) | static u8_t
  function u8_t (line 1147) | static u8_t
  function u8_t (line 1504) | static u8_t
  function err_t (line 1577) | static err_t
  type fs_file (line 1618) | struct fs_file
  type http_state (line 1619) | struct http_state
  function err_t (line 1647) | static err_t
  function err_t (line 1674) | static err_t
  function err_t (line 1713) | static err_t
  function httpd_post_data_recved (line 1805) | void httpd_post_data_recved(void *connection, u16_t recved_len)
  function http_continue (line 1839) | static void
  function err_t (line 1866) | static err_t
  function err_t (line 2041) | static err_t
  function err_t (line 2146) | static err_t
  function http_err (line 2211) | static void
  function err_t (line 2228) | static err_t
  function err_t (line 2255) | static err_t
  function err_t (line 2303) | static err_t
  function err_t (line 2391) | static err_t
  function httpd_init_addr (line 2429) | static void
  function httpd_init (line 2451) | void
  function http_set_ssi_handler (line 2473) | void
  function http_set_cgi_handlers (line 2495) | void

FILE: lib/httpd/httpd.h
  type tCGI (line 102) | typedef struct
  type u16_t (line 157) | typedef u16_t (*tSSIHandler)(int iIndex, char *pcInsert, int iInsertLen
  type pbuf (line 215) | struct pbuf

FILE: lib/httpd/httpd_structs.h
  type tHTTPHeader (line 24) | typedef struct

FILE: lib/lwip-port/arch/cc.h
  type sys_prot_t (line 37) | typedef int sys_prot_t;

FILE: lib/lwip-port/server/dhserver.c
  type DHCP_OPTIONS (line 38) | enum DHCP_OPTIONS
  type DHCP_TYPE (line 74) | typedef struct
  type udp_pcb (line 94) | struct udp_pcb
  function ip_addr_t (line 99) | static ip_addr_t get_ip(const uint8_t *pnt)
  function set_ip (line 106) | static void set_ip(uint8_t *pnt, ip_addr_t value)
  function dhcp_entry_t (line 111) | static dhcp_entry_t *entry_by_ip(ip_addr_t ip)
  function dhcp_entry_t (line 120) | static dhcp_entry_t *entry_by_mac(uint8_t *mac)
  function is_vacant (line 129) | static __inline bool is_vacant(dhcp_entry_t *entry)
  function dhcp_entry_t (line 134) | static dhcp_entry_t *vacant_address(void)
  function free_entry (line 143) | static __inline void free_entry(dhcp_entry_t *entry)
  function fill_options (line 162) | int fill_options(void *dest,
  function udp_recv_proc (line 230) | static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *...
  function err_t (line 324) | err_t dhserv_init(const dhcp_config_t *c)
  function dhserv_free (line 343) | void dhserv_free(void)

FILE: lib/lwip-port/server/dhserver.h
  type dhcp_entry_t (line 42) | typedef struct dhcp_entry
  type dhcp_config_t (line 49) | typedef struct dhcp_config

FILE: lib/lwip-port/server/dnserver.c
  type udp_pcb (line 34) | struct udp_pcb
  type dns_header_flags_t (line 38) | typedef struct
  type dns_header_t (line 61) | typedef struct
  type dns_answer_t (line 68) | typedef struct dns_answer
  type dns_query_t (line 79) | typedef struct dns_query
  function get_uint16 (line 86) | static uint16_t get_uint16(const uint8_t *pnt)
  function parse_next_query (line 93) | static int parse_next_query(void *data, int size, dns_query_t *query)
  function udp_recv_proc (line 133) | static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *...
  function err_t (line 176) | err_t dnserv_init(const ip_addr_t *bind, uint16_t port, dns_query_proc_t...
  function dnserv_free (line 195) | void dnserv_free()

FILE: lib/rndis/ndis.h
  type NDIS_DEVICE_POWER_STATE (line 55) | enum NDIS_DEVICE_POWER_STATE {
  type NDIS_PM_WAKE_UP_CAPABILITIES (line 64) | struct NDIS_PM_WAKE_UP_CAPABILITIES {

FILE: lib/rndis/rndis.c
  type netif (line 55) | struct netif
  type pbuf (line 58) | struct pbuf
  function err_t (line 89) | static err_t linkoutput_fn(struct netif *netif, struct pbuf *p)
  function err_t (line 111) | static err_t output_fn(struct netif *netif, struct pbuf *p, const ip_add...
  function err_t (line 116) | static err_t netif_init_cb(struct netif *netif)
  function init_lwip (line 129) | static void init_lwip(void)
  function dns_query_proc (line 145) | bool dns_query_proc(const char *name, ip_addr_t *addr)
  function tud_network_recv_cb (line 155) | bool tud_network_recv_cb(const uint8_t *src, uint16_t size)
  function tud_network_xmit_cb (line 179) | uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg)
  function service_traffic (line 200) | static void service_traffic(void)
  function tud_network_init_cb (line 214) | void tud_network_init_cb(void)
  function rndis_init (line 224) | int rndis_init(void)
  function rndis_task (line 242) | void rndis_task(void)
  function sys_prot_t (line 249) | sys_prot_t sys_arch_protect(void)
  function sys_arch_unprotect (line 254) | void sys_arch_unprotect(sys_prot_t pval)
  function sys_now (line 260) | uint32_t sys_now(void)

FILE: lib/rndis/rndis_protocol.h
  type rndis_MessageType_t (line 75) | typedef uint32_t rndis_MessageType_t;
  type rndis_MessageLength_t (line 76) | typedef uint32_t rndis_MessageLength_t;
  type rndis_RequestId_t (line 77) | typedef uint32_t rndis_RequestId_t;
  type rndis_MajorVersion_t (line 78) | typedef uint32_t rndis_MajorVersion_t;
  type rndis_MinorVersion_t (line 79) | typedef uint32_t rndis_MinorVersion_t;
  type rndis_MaxTransferSize_t (line 80) | typedef uint32_t rndis_MaxTransferSize_t;
  type rndis_Status_t (line 81) | typedef uint32_t rndis_Status_t;
  type rndis_DeviceFlags_t (line 87) | typedef uint32_t rndis_DeviceFlags_t;
  type rndis_Medium_t (line 91) | typedef uint32_t rndis_Medium_t;
  type rndis_MaxPacketsPerTransfer_t (line 94) | typedef uint32_t rndis_MaxPacketsPerTransfer_t;
  type rndis_PacketAlignmentFactor_t (line 95) | typedef uint32_t rndis_PacketAlignmentFactor_t;
  type rndis_AfListOffset_t (line 96) | typedef uint32_t rndis_AfListOffset_t;
  type rndis_AfListSize_t (line 97) | typedef uint32_t rndis_AfListSize_t;
  type rndis_generic_msg_t (line 100) | typedef struct{
  type rndis_initialize_msg_t (line 107) | typedef struct{
  type rndis_initialize_cmplt_t (line 117) | typedef struct{
  type rndis_halt_msg_t (line 135) | typedef struct{
  type rndis_Oid_t (line 141) | typedef uint32_t rndis_Oid_t;
  type rndis_InformationBufferLength_t (line 142) | typedef uint32_t rndis_InformationBufferLength_t;
  type rndis_InformationBufferOffset_t (line 143) | typedef uint32_t rndis_InformationBufferOffset_t;
  type rndis_DeviceVcHandle_t (line 144) | typedef uint32_t rndis_DeviceVcHandle_t;
  type rndis_query_msg_t (line 147) | typedef struct{
  type rndis_query_cmplt_t (line 159) | typedef struct{
  type rndis_set_msg_t (line 169) | typedef struct{
  type rndis_set_cmplt_t (line 180) | typedef struct{
  type rndis_ParameterNameOffset_t (line 188) | typedef uint32_t rndis_ParameterNameOffset_t;
  type rndis_ParameterNameLength_t (line 189) | typedef uint32_t rndis_ParameterNameLength_t;
  type rndis_ParameterType_t (line 190) | typedef uint32_t rndis_ParameterType_t;
  type rndis_ParameterValueOffset_t (line 191) | typedef uint32_t rndis_ParameterValueOffset_t;
  type rndis_ParameterValueLength_t (line 192) | typedef uint32_t rndis_ParameterValueLength_t;
  type rndis_config_parameter_t (line 197) | typedef struct{
  type rndis_Reserved_t (line 205) | typedef uint32_t rndis_Reserved_t;
  type rndis_reset_msg_t (line 208) | typedef struct{
  type rndis_AddressingReset_t (line 214) | typedef uint32_t rndis_AddressingReset_t;
  type rndis_reset_cmplt_t (line 217) | typedef struct{
  type rndis_indicate_status_t (line 225) | typedef struct{
  type rndis_DiagStatus_t (line 233) | typedef uint32_t rndis_DiagStatus_t;
  type rndis_ErrorOffset_t (line 234) | typedef uint32_t rndis_ErrorOffset_t;
  type rndis_diagnostic_info_t (line 236) | typedef struct {
  type rndis_keepalive_msg_t (line 242) | typedef struct{
  type rndis_keepalive_cmplt_t (line 249) | typedef struct{
  type rndis_DataOffset_t (line 258) | typedef uint32_t rndis_DataOffset_t;
  type rndis_DataLength_t (line 259) | typedef uint32_t rndis_DataLength_t;
  type rndis_OOBDataOffset_t (line 260) | typedef uint32_t rndis_OOBDataOffset_t;
  type rndis_OOBDataLength_t (line 261) | typedef uint32_t rndis_OOBDataLength_t;
  type rndis_NumOOBDataElements_t (line 262) | typedef uint32_t rndis_NumOOBDataElements_t;
  type rndis_PerPacketInfoOffset_t (line 263) | typedef uint32_t rndis_PerPacketInfoOffset_t;
  type rndis_PerPacketInfoLength_t (line 264) | typedef uint32_t rndis_PerPacketInfoLength_t;
  type rndis_data_packet_t (line 266) | typedef struct{
  type rndis_ClassInformationOffset_t (line 280) | typedef uint32_t rndis_ClassInformationOffset_t;
  type rndis_Size_t (line 281) | typedef uint32_t rndis_Size_t;
  type rndis_Type_t (line 282) | typedef uint32_t rndis_Type_t;
  type rndis_OOB_packet_t (line 284) | typedef struct{
  type rndis_state_t (line 292) | typedef enum rnids_state_e {
  type usb_eth_stat_t (line 298) | typedef struct {

FILE: lib/rndis/rndis_reports.c
  function rndis_report (line 80) | static void rndis_report(void)
  function rndis_query_cmplt32 (line 85) | static void rndis_query_cmplt32(int status, uint32_t data)
  function rndis_query_cmplt (line 98) | static void rndis_query_cmplt(int status, const void *data, int size)
  function rndis_query (line 118) | static void rndis_query(void)
  function rndis_handle_config_parm (line 158) | static void rndis_handle_config_parm(const char *data, int keyoffset, in...
  function rndis_packetFilter (line 167) | static void rndis_packetFilter(uint32_t newfilter)
  function rndis_handle_set_msg (line 172) | static void rndis_handle_set_msg(void)
  function rndis_class_set_handler (line 238) | void rndis_class_set_handler(uint8_t *data, int size)

FILE: src/display.cpp
  function clearScreen (line 16) | inline void clearScreen(int render = 0)
  function drawHitbox (line 21) | inline void drawHitbox(int startX, int startY, int buttonRadius, int but...
  function drawWasdBox (line 43) | inline void drawWasdBox(int startX, int startY, int buttonRadius, int bu...
  function drawArcadeStick (line 65) | inline void drawArcadeStick(int startX, int startY, int buttonRadius, in...
  function drawStatusBar (line 87) | inline void drawStatusBar()
  function setStatusBar (line 92) | void setStatusBar(Gamepad *gamepad)

FILE: src/leds.cpp
  function createLedLayoutArcadeButtons (line 47) | vector<vector<Pixel>> createLedLayoutArcadeButtons(vector<vector<uint8_t...
  function createLedLayoutArcadeHitbox (line 87) | vector<vector<Pixel>> createLedLayoutArcadeHitbox(vector<vector<uint8_t>...
  function createLedLayoutArcadeWasd (line 146) | vector<vector<Pixel>> createLedLayoutArcadeWasd(vector<vector<uint8_t>> ...
  function createLedButtonLayout (line 191) | vector<vector<Pixel>> createLedButtonLayout(ButtonLayout layout, vector<...
  function createLedButtonLayout (line 206) | vector<vector<Pixel>> createLedButtonLayout(ButtonLayout layout, uint8_t...
  function setupButtonPositions (line 219) | uint8_t setupButtonPositions()
  function AnimationHotkey (line 387) | AnimationHotkey animationHotkeys(Gamepad *gamepad)

FILE: src/main.cpp
  function getMillis (line 25) | uint32_t getMillis() { return to_ms_since_boot(get_absolute_time()); }
  function main (line 46) | int main()
  function setup (line 64) | void setup()
  function loop (line 107) | void loop()
  function core1 (line 144) | void core1()
  function webserver (line 163) | void webserver()

FILE: src/pleds.cpp
  function setRGBPLEDs (line 19) | void setRGBPLEDs(uint32_t *frame)
  function PLEDAnimationState (line 26) | PLEDAnimationState getXInputAnimation(uint8_t *data)

FILE: src/storage.cpp
  function BoardOptions (line 19) | BoardOptions getBoardOptions()
  function setBoardOptions (line 62) | void setBoardOptions(BoardOptions options)
  function LEDOptions (line 71) | LEDOptions getLEDOptions()
  function setLEDOptions (line 78) | void setLEDOptions(LEDOptions options)
  function GamepadOptions (line 95) | GamepadOptions GamepadStorage::getGamepadOptions()
  function AnimationOptions (line 125) | AnimationOptions AnimationStorage::getAnimationOptions()

FILE: src/webserver.cpp
  type fsdata_file (line 46) | struct fsdata_file
  function DynamicJsonDocument (line 60) | DynamicJsonDocument get_post_data()
  function string (line 71) | inline string serialize_json(DynamicJsonDocument &doc)
  function set_file_data (line 78) | int set_file_data(struct fs_file *file, string data)
  function string (line 96) | string resetSettings()
  function string (line 105) | string getDisplayOptions()
  function string (line 142) | string setDisplayOptions()
  function string (line 162) | string getGamepadOptions()
  function string (line 174) | string setGamepadOptions()
  function string (line 186) | string getLedOptions()
  function string (line 247) | string setLedOptions()
  function string (line 284) | string getPinMappings()
  function string (line 310) | string setPinMappings()
  function err_t (line 365) | err_t httpd_post_begin(void *connection, const char *uri, const char *ht...
  function err_t (line 385) | err_t httpd_post_receive_data(void *connection, struct pbuf *p)
  function httpd_post_finished (line 422) | void httpd_post_finished(void *connection, char *response_uri, uint16_t ...
  function fs_open_custom (line 431) | int fs_open_custom(struct fs_file *file, const char *name)
  function fs_close_custom (line 480) | void fs_close_custom(struct fs_file *file)

FILE: www/src/Pages/DisplayConfig.js
  constant ON_OFF_OPTIONS (line 10) | const ON_OFF_OPTIONS = [
  constant I2C_BLOCKS (line 15) | const I2C_BLOCKS = [
  function fetchData (line 50) | async function fetchData() {
  function DisplayConfigPage (line 72) | function DisplayConfigPage() {

FILE: www/src/Pages/HomePage.js
  function HomePage (line 9) | function HomePage() {

FILE: www/src/Pages/LEDConfigPage.js
  constant LED_FORMATS (line 14) | const LED_FORMATS = [
  constant BUTTON_LAYOUTS (line 21) | const BUTTON_LAYOUTS = [
  function fetchData (line 79) | async function fetchData() {
  function LEDConfigPage (line 115) | function LEDConfigPage() {

FILE: www/src/Pages/PinMapping.js
  function PinMappingPage (line 13) | function PinMappingPage() {

FILE: www/src/Pages/ResetSettingsPage.js
  function ResetSettingsPage (line 5) | function ResetSettingsPage() {

FILE: www/src/Pages/SettingsPage.js
  constant INPUT_MODES (line 8) | const INPUT_MODES = [
  constant DPAD_MODES (line 14) | const DPAD_MODES = [
  constant SOCD_MODES (line 20) | const SOCD_MODES = [
  function fetchData (line 36) | async function fetchData() {
  function SettingsPage (line 54) | function SettingsPage() {

FILE: www/src/Services/Storage.js
  constant STORAGE_BUTTON_LABELS (line 1) | const STORAGE_BUTTON_LABELS = 'buttonLabels';

FILE: www/src/Services/WebApi.js
  function resetSettings (line 26) | async function resetSettings() {
  function getDisplayOptions (line 32) | async function getDisplayOptions() {
  function setDisplayOptions (line 43) | async function setDisplayOptions(options) {
  function getGamepadOptions (line 57) | async function getGamepadOptions() {
  function setGamepadOptions (line 63) | async function setGamepadOptions(options) {
  function getLedOptions (line 75) | async function getLedOptions() {
  function setLedOptions (line 81) | async function setLedOptions(options) {
  function getPinMappings (line 93) | async function getPinMappings() {
  function setPinMappings (line 105) | async function setPinMappings(mappings) {
Condensed preview — 170 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,304K chars).
[
  {
    "path": ".editorconfig",
    "chars": 341,
    "preview": "# EditorConfig is awesome: https://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n[*]\nindent_style = tab\nin"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 65,
    "preview": "# These are supported funding model platforms\n\ngithub: [FeralAI]\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 1619,
    "preview": "name: PlatformIO CI\n\non: [push]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 2580,
    "preview": "name: PlatformIO Release\n\non:\n  release:\n    types: [created]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n  "
  },
  {
    "path": ".gitignore",
    "chars": 146,
    "preview": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n.vscode/settings.json\nnode"
  },
  {
    "path": ".gitmodules",
    "chars": 144,
    "preview": "[submodule \"configs/PicoFightingBoard\"]\n\tpath = configs/PicoFightingBoard\n\turl = https://github.com/FeralAI/GP2040-Confi"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 190,
    "preview": "{\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\n    // for the documentation about the extensions.json format"
  },
  {
    "path": "LICENSE",
    "chars": 1087,
    "preview": "MIT License\n\nCopyright (c) 2021 Jason Skuby (mytechtoybox.com)\n\nPermission is hereby granted, free of charge, to any per"
  },
  {
    "path": "README.md",
    "chars": 9515,
    "preview": "> GP2040 is no longer being maintained. Please check out the more fully-featured community fork called [GP2040-CE](https"
  },
  {
    "path": "build-web.py",
    "chars": 668,
    "preview": "import os\nimport os.path\n\nwebsite_dir = \"www/build/\"\nfsdata_filename = \"lib/httpd/fsdata.c\"\n\nprint(\"Building React app\")"
  },
  {
    "path": "configs/CrushCounter/BoardConfig.h",
    "chars": 1484,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "configs/CrushCounter/env.ini",
    "chars": 120,
    "preview": "[env:crush-counter]\nbuild_flags =\n\t${env.build_flags}\n\t-I configs/CrushCounter/\nupload_port = .pio/build/crush-counter/\n"
  },
  {
    "path": "configs/DURAL/BoardConfig.h",
    "chars": 769,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "configs/DURAL/README.md",
    "chars": 82,
    "preview": "# GP2040 Configuration for DURAL\n\n![DURAL](assets/DURAL.jpg)\n\n<https://dural.gg/>\n"
  },
  {
    "path": "configs/DURAL/env.ini",
    "chars": 129,
    "preview": "[env:dural]\nbuild_flags =\n\t${env.build_flags}\n\t-D BOARD_SPARKFUN_MICRO_RP2040\n\t-I configs/DURAL/\nupload_port = .pio/buil"
  },
  {
    "path": "configs/DebugBoard/BoardConfig.h",
    "chars": 2093,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n *\n * De"
  },
  {
    "path": "configs/DebugBoard/README.md",
    "chars": 1235,
    "preview": "# GP2040 Configuration for Debugging\n\n![Debug setup](assets/DebugBoard.jpg)\n\nFeral's debugging setup. This is what peak "
  },
  {
    "path": "configs/DebugBoard/env.ini",
    "chars": 172,
    "preview": "[env:debug]\ndebug_tool = picoprobe\nupload_protocol = picoprobe\nbuild_type = debug\nbuild_flags =\n\t${env.build_flags}\n\t-I "
  },
  {
    "path": "configs/Fightboard/BoardConfig.h",
    "chars": 1427,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n *\n * Cu"
  },
  {
    "path": "configs/Fightboard/README.md",
    "chars": 331,
    "preview": "# GP2040 Configuration for Fightboard and Fightboard MX\n\nThe Fightboard and Fightboard MX come with the ItsyBitsy 32u4. "
  },
  {
    "path": "configs/Fightboard/env.ini",
    "chars": 112,
    "preview": "[env:fightboard]\nbuild_flags =\n\t${env.build_flags}\n\t-I configs/Fightboard/\nupload_port = .pio/build/fightboard/\n"
  },
  {
    "path": "configs/FlatboxRev4/BoardConfig.h",
    "chars": 786,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "configs/FlatboxRev4/README.md",
    "chars": 356,
    "preview": "# GP2040 Configuration for Flatbox Rev 4\n\n![Flatbox](assets/Flatbox-rev2b-finished-product.jpg)\n\nConfiguration for the ["
  },
  {
    "path": "configs/FlatboxRev4/env.ini",
    "chars": 119,
    "preview": "[env:flatbox-rev-4]\nupload_port = .pio/build/flatbox-rev-4/\nbuild_flags =\n\t${env.build_flags}\n\t-I configs/FlatboxRev4/\n"
  },
  {
    "path": "configs/GeeekPiStick/BoardConfig.h",
    "chars": 928,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n *\n * Pr"
  },
  {
    "path": "configs/GeeekPiStick/README.md",
    "chars": 432,
    "preview": "# GP2040 Configuration for GeeekPi GPIO Breakout\n\nPreset config for Pico with a GeeekPi Pico Screw Terminal Expansion Bo"
  },
  {
    "path": "configs/GeeekPiStick/env.ini",
    "chars": 122,
    "preview": "[env:geeek-pi-stick]\nbuild_flags =\n\t${env.build_flags}\n\t-I configs/GeeekPiStick/\nupload_port = .pio/build/geeek-pi-stick"
  },
  {
    "path": "configs/Hydra/BoardConfig.h",
    "chars": 1569,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "configs/Hydra/env.ini",
    "chars": 277,
    "preview": "[env:hydra]\nbuild_flags =\n\t${env.build_flags}\n\t-I configs/Hydra/\nupload_port = .pio/build/hydra/\n\n[env:hydra-debug]\ndebu"
  },
  {
    "path": "configs/OSFRD/BoardConfig.h",
    "chars": 1440,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "configs/OSFRD/env.ini",
    "chars": 243,
    "preview": "[env:osfrd]\nbuild_flags =\n\t${env.build_flags}\n\t-I configs/OSFRD/\nupload_port = .pio/build/osfrd/\n\n[env:osfrd-pirate]\nbui"
  },
  {
    "path": "configs/Pico/BoardConfig.h",
    "chars": 777,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "configs/Pico/README.md",
    "chars": 219,
    "preview": "# GP2040 Configuration for Raspberry Pi Pico\n\n![Pin Mapping](assets/PinMapping.png)\n\nBasic pin setup for a stock Raspber"
  },
  {
    "path": "configs/Pico/env.ini",
    "chars": 272,
    "preview": "[env:raspberry-pi-pico]\nupload_port = .pio/build/raspberry-pi-pico/\nbuild_flags =\n\t${env.build_flags}\n\t-I configs/Pico/\n"
  },
  {
    "path": "docs/.nojekyll",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/CNAME",
    "chars": 11,
    "preview": "gp2040.info"
  },
  {
    "path": "docs/_navbar.md",
    "chars": 94,
    "preview": "* [Home](/)\n* [Usage](usage)\n* [Configuration](web-configurator)\n* [Development](development)\n"
  },
  {
    "path": "docs/development.md",
    "chars": 11325,
    "preview": "# GP2040 Development\n\nGP2040 is written in C++ and set up as a [PlatformIO](https://platformio.org/) project, using the "
  },
  {
    "path": "docs/faq.md",
    "chars": 3654,
    "preview": "# GP2040 FAQ\n\n## General Questions\n\n### Which input mode should I use?\n\nGenerally speaking, XInput will be the mode of c"
  },
  {
    "path": "docs/index.html",
    "chars": 5641,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>Document</title>\n\t<meta http-equiv=\"X-UA-Compati"
  },
  {
    "path": "docs/usage.md",
    "chars": 6932,
    "preview": "# GP2040 Usage\n\nSelect the button labels to be displayed in the usage guide: <label-selector></label-selector>\n\n## Butto"
  },
  {
    "path": "docs/web-configurator.md",
    "chars": 3364,
    "preview": "# GP2040 Web Configurator\n\nSelect the button labels to be displayed in the web configurator guide: <label-selector></lab"
  },
  {
    "path": "include/display.h",
    "chars": 1021,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "include/enums.h",
    "chars": 258,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "include/gamepad.h",
    "chars": 1595,
    "preview": "#ifndef _GAMEPAD_H_\n#define _GAMEPAD_H_\n\n#include \"BoardConfig.h\"\n#include <string.h>\n#include <MPGS.h>\n#include \"pico/s"
  },
  {
    "path": "include/gp2040.h",
    "chars": 1217,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "include/leds.h",
    "chars": 2768,
    "preview": "/*\n* SPDX-License-Identifier: MIT\n* SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n*/\n\n#ifnde"
  },
  {
    "path": "include/pico/config_autogen.h",
    "chars": 616,
    "preview": "#ifndef CONFIG_AUTOGEN_H_\n#define CONFIG_AUTOGEN_H_\n\n// Boards supported in PlatformIO build configuration\n\n#ifdef BOARD"
  },
  {
    "path": "include/pleds.h",
    "chars": 1185,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "include/storage.h",
    "chars": 1776,
    "preview": " /*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#i"
  },
  {
    "path": "include/themes.h",
    "chars": 8983,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "include/tusb_config.h",
    "chars": 3885,
    "preview": "/*\n * The MIT License (MIT)\n *\n * Copyright (c) 2019 Ha Thach (tinyusb.org)\n *\n * Permission is hereby granted, free of "
  },
  {
    "path": "lib/AnimationStation/.editorconfig",
    "chars": 309,
    "preview": "# EditorConfig is awesome: https://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n[*]\nindent_style = space\n"
  },
  {
    "path": "lib/AnimationStation/library.json",
    "chars": 255,
    "preview": "{\n\t\"name\": \"AnimationStation\",\n\t\"version\": \"0.0.1\",\n\t\"description\": \"LED Handling for GP2040\",\n\t\"keywords\": \"c c++ barem"
  },
  {
    "path": "lib/AnimationStation/src/Animation.cpp",
    "chars": 694,
    "preview": "#include \"Animation.hpp\"\n\nLEDFormat Animation::format;\n\nAnimation::Animation(PixelMatrix &matrix) : matrix(&matrix) {\n}\n"
  },
  {
    "path": "lib/AnimationStation/src/Animation.hpp",
    "chars": 3299,
    "preview": "#ifndef _ANIMATION_H_\n#define _ANIMATION_H_\n\n#include \"Pixel.hpp\"\n#include <stdint.h>\n#include <stdio.h>\n#include <stdli"
  },
  {
    "path": "lib/AnimationStation/src/AnimationStation.cpp",
    "chars": 5001,
    "preview": "/**\n * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.\n *\n * Modified by Jonathan Barket - 2021\n * SPDX-License-Identifie"
  },
  {
    "path": "lib/AnimationStation/src/AnimationStation.hpp",
    "chars": 2303,
    "preview": "#ifndef _ANIMATION_STATION_H_\n#define _ANIMATION_STATION_H_\n\n#include <algorithm>\n#include <string.h>\n#include <stdio.h>"
  },
  {
    "path": "lib/AnimationStation/src/AnimationStorage.hpp",
    "chars": 293,
    "preview": "#ifndef _CONFIG_STORAGE_H_\n#define _CONFIG_STORAGE_H_\n\n#include \"AnimationStation.hpp\"\n\nclass AnimationStorage\n{\n  publi"
  },
  {
    "path": "lib/AnimationStation/src/Effects/Chase.cpp",
    "chars": 1992,
    "preview": "#include \"Chase.hpp\"\n\nChase::Chase(PixelMatrix &matrix) : Animation(matrix) {\n}\n\nvoid Chase::Animate(RGB (&frame)[100]) "
  },
  {
    "path": "lib/AnimationStation/src/Effects/Chase.hpp",
    "chars": 538,
    "preview": "#ifndef _CHASE_H_\n#define _CHASE_H_\n\n#include \"../Animation.hpp\"\n#include \"hardware/clocks.h\"\n#include <stdio.h>\n#includ"
  },
  {
    "path": "lib/AnimationStation/src/Effects/Rainbow.cpp",
    "chars": 1096,
    "preview": "#include \"Rainbow.hpp\"\n\nRainbow::Rainbow(PixelMatrix &matrix) : Animation(matrix) {\n}\n\nvoid Rainbow::Animate(RGB (&frame"
  },
  {
    "path": "lib/AnimationStation/src/Effects/Rainbow.hpp",
    "chars": 471,
    "preview": "#ifndef _RAINBOW_H_\n#define _RAINBOW_H_\n\n#include \"../Animation.hpp\"\n#include \"hardware/clocks.h\"\n#include <stdio.h>\n#in"
  },
  {
    "path": "lib/AnimationStation/src/Effects/StaticColor.cpp",
    "chars": 1824,
    "preview": " #include \"StaticColor.hpp\"\n\nStaticColor::StaticColor(PixelMatrix &matrix) : Animation(matrix) {\n}\n\nStaticColor::StaticC"
  },
  {
    "path": "lib/AnimationStation/src/Effects/StaticColor.hpp",
    "chars": 541,
    "preview": "#ifndef _STATIC_COLOR_H_\n#define _STATIC_COLOR_H_\n\n#include <vector>\n#include <stdio.h>\n#include <stdlib.h>\n#include \".."
  },
  {
    "path": "lib/AnimationStation/src/Effects/StaticTheme.cpp",
    "chars": 1740,
    "preview": "#include \"StaticTheme.hpp\"\n\nstd::vector<std::map<uint32_t, RGB>> StaticTheme::themes = {};\n\nStaticTheme::StaticTheme(Pix"
  },
  {
    "path": "lib/AnimationStation/src/Effects/StaticTheme.hpp",
    "chars": 604,
    "preview": "#ifndef STATIC_THEME_H_\n#define STATIC_THEME_H_\n\n#include <iterator>\n#include <map>\n#include <vector>\n#include <string.h"
  },
  {
    "path": "lib/AnimationStation/src/Pixel.hpp",
    "chars": 1402,
    "preview": "#ifndef PIXEL_HPP_\n#define PIXEL_HPP_\n\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <vector>\n\nstr"
  },
  {
    "path": "lib/BitBang_I2C/BitBang_I2C.c",
    "chars": 9176,
    "preview": "//\n// Bit Bang I2C library\n// Copyright (c) 2018 BitBank Software, Inc.\n// Written by Larry Bank (bitbank@pobox.com)\n// "
  },
  {
    "path": "lib/BitBang_I2C/BitBang_I2C.h",
    "chars": 2944,
    "preview": "//\n// Bit Bang I2C library\n// Copyright (c) 2018 BitBank Software, Inc.\n// Written by Larry Bank (bitbank@pobox.com)\n// "
  },
  {
    "path": "lib/BitBang_I2C/README.md",
    "chars": 3797,
    "preview": "Bit Bang I2C library\n--------------------\nCopyright (c) 2018 BitBank Software, Inc.\nWritten by Larry Bank (bitbank@pobox"
  },
  {
    "path": "lib/CRC32/src/CRC32.cpp",
    "chars": 844,
    "preview": "//\n// Copyright (c) 2013 Christopher Baker <https://christopherbaker.net>\n//\n// SPDX-License-Identifier:\tMIT\n//\n\n#includ"
  },
  {
    "path": "lib/CRC32/src/CRC32.h",
    "chars": 1844,
    "preview": "//\n// Copyright (c) 2013 Christopher Baker <https://christopherbaker.net>\n//\n// SPDX-License-Identifier:\tMIT\n//\n\n#pragma"
  },
  {
    "path": "lib/FlashPROM/include/FlashPROM.h",
    "chars": 1270,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#if"
  },
  {
    "path": "lib/FlashPROM/library.json",
    "chars": 284,
    "preview": "{\n\t\"name\": \"FlashPROM\",\n\t\"version\": \"0.0.1\",\n\t\"description\": \"Basic EEPROM-like interface for saving to flash memory on "
  },
  {
    "path": "lib/FlashPROM/src/FlashPROM.cpp",
    "chars": 1865,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#in"
  },
  {
    "path": "lib/NeoPico/.editorconfig",
    "chars": 309,
    "preview": "# EditorConfig is awesome: https://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n[*]\nindent_style = space\n"
  },
  {
    "path": "lib/NeoPico/library.json",
    "chars": 274,
    "preview": "{\n\t\"name\": \"NeoPico\",\n\t\"version\": \"0.0.1\",\n\t\"description\": \"WS2812 PIO handling based on official Pico examples\",\n\t\"keyw"
  },
  {
    "path": "lib/NeoPico/src/NeoPico.cpp",
    "chars": 1443,
    "preview": "/**\n * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.\n *\n * Modified by Jonathan Barket - 2021\n * SPDX-License-Identifie"
  },
  {
    "path": "lib/NeoPico/src/NeoPico.hpp",
    "chars": 599,
    "preview": "#ifndef _NEO_PICO_H_\n#define _NEO_PICO_H_\n\n#include \"ws2812.pio.h\"\n#include <vector>\n\ntypedef enum\n{\n  LED_FORMAT_GRB = "
  },
  {
    "path": "lib/NeoPico/src/ws2812.pio",
    "chars": 2466,
    "preview": ";\n; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.\n;\n; SPDX-License-Identifier: BSD-3-Clause\n;\n\n.program ws2812\n.side_se"
  },
  {
    "path": "lib/NeoPico/src/ws2812.pio.h",
    "chars": 3704,
    "preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
  },
  {
    "path": "lib/OneBitDisplay/OneBitDisplay.cpp",
    "chars": 31656,
    "preview": "//\n// OneBitDisplay (OLED+LCD library)\n// Copyright (c) 2020 BitBank Software, Inc.\n// Written by Larry Bank (bitbank@po"
  },
  {
    "path": "lib/OneBitDisplay/OneBitDisplay.h",
    "chars": 13797,
    "preview": "#ifndef __ONEBITDISPLAY__\n#define __ONEBITDISPLAY__\n\n#include <BitBang_I2C.h>\n#include \"hardware/i2c.h\"\n\n// Proportional"
  },
  {
    "path": "lib/OneBitDisplay/README.md",
    "chars": 6630,
    "preview": "OneBitDisplay (1-bpp OLED/LCD library)<br>\n-----------------------------------\nProject started 3/23/2020<br>\nCopyright ("
  },
  {
    "path": "lib/OneBitDisplay/fonts/FreeSerif12pt7b.h",
    "chars": 15315,
    "preview": "const uint8_t FreeSerif12pt7bBitmaps[] = {\n\t0xFF, 0xFE, 0xA8, 0x3F, 0xCF, 0x3C, 0xF3, 0x8A, 0x20, 0x0C, 0x40, 0xC4,\n\t0x0"
  },
  {
    "path": "lib/OneBitDisplay/obd.inl",
    "chars": 103962,
    "preview": "//\n// obd.inl\n// Drawing code for OneBitDisplay library\n//\n\nvoid obdSetDCMode(OBDISP *pOBD, int iMode);\nvoid InvertBytes"
  },
  {
    "path": "lib/PlayerLEDs/include/PlayerLEDs.h",
    "chars": 3116,
    "preview": "#ifndef PLAYER_LEDS_H_\n#define PLAYER_LEDS_H_\n\n#include <cstring>\n#include <stdint.h>\n\n#define PLED_COUNT 4\n#define PLED"
  },
  {
    "path": "lib/PlayerLEDs/src/PlayerLEDs.cpp",
    "chars": 794,
    "preview": "#include \"PlayerLEDs.h\"\n#include \"pico/stdlib.h\"\n#include \"hardware/pwm.h\"\n\nvoid PlayerLEDs::animate(PLEDAnimationState "
  },
  {
    "path": "lib/TinyUSB_Gamepad/include/hid_driver.h",
    "chars": 366,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#pr"
  },
  {
    "path": "lib/TinyUSB_Gamepad/include/net_driver.h",
    "chars": 320,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#pr"
  },
  {
    "path": "lib/TinyUSB_Gamepad/include/usb_driver.h",
    "chars": 386,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#pr"
  },
  {
    "path": "lib/TinyUSB_Gamepad/include/webserver_descriptors.h",
    "chars": 2980,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#pr"
  },
  {
    "path": "lib/TinyUSB_Gamepad/include/xinput_driver.h",
    "chars": 1293,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#pr"
  },
  {
    "path": "lib/TinyUSB_Gamepad/library.json",
    "chars": 403,
    "preview": "{\n\t\"name\": \"tinyusb-gamepad-driver\",\n\t\"version\": \"0.0.1\",\n\t\"description\": \"C library for handling USB HID/XID gamepads v"
  },
  {
    "path": "lib/TinyUSB_Gamepad/src/hid_driver.cpp",
    "chars": 1264,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#in"
  },
  {
    "path": "lib/TinyUSB_Gamepad/src/net_driver.cpp",
    "chars": 362,
    "preview": "#include \"net_driver.h\"\n\nconst usbd_class_driver_t net_driver = {\n#if CFG_TUSB_DEBUG >= 2\n\t.name = \"NET\",\n#endif\n\t.init "
  },
  {
    "path": "lib/TinyUSB_Gamepad/src/tusb_driver.cpp",
    "chars": 3316,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#in"
  },
  {
    "path": "lib/TinyUSB_Gamepad/src/usb_descriptors.cpp",
    "chars": 2153,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n * SPDX-"
  },
  {
    "path": "lib/TinyUSB_Gamepad/src/xinput_driver.cpp",
    "chars": 3255,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#in"
  },
  {
    "path": "lib/httpd/fs.c",
    "chars": 5822,
    "preview": "/*\n * Copyright (c) 2001-2003 Swedish Institute of Computer Science.\n * All rights reserved.\n *\n * Redistribution and us"
  },
  {
    "path": "lib/httpd/fs.h",
    "chars": 5036,
    "preview": "/*\n * Copyright (c) 2001-2003 Swedish Institute of Computer Science.\n * All rights reserved. \n * \n * Redistribution and "
  },
  {
    "path": "lib/httpd/fscustom.h",
    "chars": 239,
    "preview": "#ifndef FSCUSTOM_H_\n#define FSCUSTOM_H_\n\n#include \"fs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint fs_open_custom(str"
  },
  {
    "path": "lib/httpd/fsdata.c",
    "chars": 4580314,
    "preview": "#include \"fsdata.h\"\n\n\n#define file_NULL (struct fsdata_file *) NULL\n\n\n#ifndef FS_FILE_FLAGS_HEADER_INCLUDED\n#define FS_F"
  },
  {
    "path": "lib/httpd/fsdata.h",
    "chars": 1994,
    "preview": "/*\n * Copyright (c) 2001-2003 Swedish Institute of Computer Science.\n * All rights reserved.\n *\n * Redistribution and us"
  },
  {
    "path": "lib/httpd/httpd.c",
    "chars": 82239,
    "preview": "/*\n * Copyright (c) 2001-2003 Swedish Institute of Computer Science.\n * All rights reserved.\n *\n * Redistribution and us"
  },
  {
    "path": "lib/httpd/httpd.h",
    "chars": 10011,
    "preview": "/*\n * Copyright (c) 2001-2003 Swedish Institute of Computer Science.\n * All rights reserved.\n *\n * Redistribution and us"
  },
  {
    "path": "lib/httpd/httpd_structs.h",
    "chars": 4613,
    "preview": "#ifndef __HTTPD_STRUCTS_H__\n#define __HTTPD_STRUCTS_H__\n\n#include \"httpd.h\"\n\n/** This string is passed in the HTTP heade"
  },
  {
    "path": "lib/lwip-port/arch/cc.h",
    "chars": 2443,
    "preview": "/*\n * Copyright (c) 2001-2003 Swedish Institute of Computer Science.\n * All rights reserved. \n * \n * Redistribution and "
  },
  {
    "path": "lib/lwip-port/lwipopts.h",
    "chars": 2915,
    "preview": "/*\n * Copyright (c) 2001-2003 Swedish Institute of Computer Science.\n * All rights reserved.\n *\n * Redistribution and us"
  },
  {
    "path": "lib/lwip-port/server/dhserver.c",
    "chars": 8917,
    "preview": "/*\n * The MIT License (MIT)\n *\n * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>\n * \n * Permission is hereby gr"
  },
  {
    "path": "lib/lwip-port/server/dhserver.h",
    "chars": 1913,
    "preview": "/*\n * The MIT License (MIT)\n *\n * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>\n * \n * Permission is hereby gr"
  },
  {
    "path": "lib/lwip-port/server/dnserver.c",
    "chars": 5024,
    "preview": "/*\n * The MIT License (MIT)\n *\n * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>\n * \n * Permission is hereby gr"
  },
  {
    "path": "lib/lwip-port/server/dnserver.h",
    "chars": 1661,
    "preview": "/*\n * The MIT License (MIT)\n *\n * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>\n * \n * Permission is hereby gr"
  },
  {
    "path": "lib/rndis/ndis.h",
    "chars": 11150,
    "preview": "/* This file has been prepared for Doxygen automatic documentation generation.*/\n/*! \\file ndis.h **********************"
  },
  {
    "path": "lib/rndis/rndis.c",
    "chars": 7994,
    "preview": "/*\n * The MIT License (MIT)\n *\n * Copyright (c) 2020 Peter Lawrence\n *\n * influenced by lrndis https://github.com/fetiso"
  },
  {
    "path": "lib/rndis/rndis.h",
    "chars": 157,
    "preview": "#ifndef RNDIS_H_\n#define RNDIS_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint rndis_init(void);\nvoid rndis_task(void);\n"
  },
  {
    "path": "lib/rndis/rndis_protocol.h",
    "chars": 9393,
    "preview": "/**\n * \\file rndis_protocol.h\n *         RNDIS Defines\n *\n * \\author\n *         Colin O'Flynn <coflynn@newae.com>\n *\n * "
  },
  {
    "path": "lib/rndis/rndis_reports.c",
    "chars": 11515,
    "preview": "/*\n  The original version of this code was lrndis/usbd_rndis_core.c from https://github.com/fetisov/lrndis\n  It has sinc"
  },
  {
    "path": "platformio.ini",
    "chars": 599,
    "preview": "[platformio]\ndefault_envs = raspberry-pi-pico\nextra_configs = configs/*/env.ini\n\n[env]\nplatform = wizio-pico\nboard = ras"
  },
  {
    "path": "src/display.cpp",
    "chars": 7805,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#in"
  },
  {
    "path": "src/gamepad.cpp",
    "chars": 4526,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#in"
  },
  {
    "path": "src/leds.cpp",
    "chars": 15192,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#in"
  },
  {
    "path": "src/main.cpp",
    "chars": 3418,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#de"
  },
  {
    "path": "src/pleds.cpp",
    "chars": 4002,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#in"
  },
  {
    "path": "src/storage.cpp",
    "chars": 4300,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#in"
  },
  {
    "path": "src/webserver.cpp",
    "chars": 18341,
    "preview": "/*\n * SPDX-License-Identifier: MIT\n * SPDX-FileCopyrightText: Copyright (c) 2021 Jason Skuby (mytechtoybox.com)\n */\n\n#in"
  },
  {
    "path": "www/.gitignore",
    "chars": 310,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": "www/README.md",
    "chars": 2575,
    "preview": "# GP2040 Web Configurator\n\nSimple web application for gamepad configuration.\n\n## Requirements\n\n* PlatformIO with the [Wi"
  },
  {
    "path": "www/package.json",
    "chars": 1218,
    "preview": "{\n  \"name\": \"gp2040-configurator\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"dependencies\": {\n    \"axios\": \"^0.24.0\",\n"
  },
  {
    "path": "www/public/index.html",
    "chars": 1267,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"%PUBLIC_URL%/favicon.ico\" /"
  },
  {
    "path": "www/public/manifest.json",
    "chars": 312,
    "preview": "{\n  \"short_name\": \"GP2040 Configurator\",\n  \"name\": \"GP2040 Configurator\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\","
  },
  {
    "path": "www/server/app.js",
    "chars": 2603,
    "preview": "/**\n * GP2040 Configurator Development Server\n */\n\nconst express = require('express');\nconst cors = require('cors');\n\nco"
  },
  {
    "path": "www/server/docs/GP2040 (Dev).postman_environment.json",
    "chars": 317,
    "preview": "{\n\t\"id\": \"d2a5e53c-ff1a-43b5-8d9b-d3e32f3077c0\",\n\t\"name\": \"GP2040 (Dev)\",\n\t\"values\": [\n\t\t{\n\t\t\t\"key\": \"baseUrl\",\n\t\t\t\"valu"
  },
  {
    "path": "www/server/docs/GP2040.postman_collection.json",
    "chars": 2579,
    "preview": "{\n\t\"info\": {\n\t\t\"_postman_id\": \"f14b64b0-eae6-4bbe-b8fc-edc35a7f64bb\",\n\t\t\"name\": \"GP2040\",\n\t\t\"schema\": \"https://schema.ge"
  },
  {
    "path": "www/src/App.js",
    "chars": 1443,
    "preview": "import React, { useState } from 'react';\nimport { BrowserRouter as Router, Route, Switch } from \"react-router-dom\";\nimpo"
  },
  {
    "path": "www/src/App.scss",
    "chars": 151,
    "preview": ".body-content {\n\tmin-height: calc(100vh - 56px);\n\tmin-width: 400px;\n\tdisplay: flex;\n\tmargin-top: 56px;\n\tflex-direction: "
  },
  {
    "path": "www/src/Components/DangerSection.js",
    "chars": 311,
    "preview": "import React from 'react';\nimport Section from './Section';\n\nconst DangerSection = ({ className, titleClassName, ...prop"
  },
  {
    "path": "www/src/Components/DraggableListGroup.js",
    "chars": 3525,
    "preview": "import React, { useEffect, useState } from \"react\";\nimport { DragDropContext, Droppable, Draggable } from \"react-beautif"
  },
  {
    "path": "www/src/Components/DraggableListGroup.scss",
    "chars": 334,
    "preview": ".draggable-list-group {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\n\t.list-group {\n\t\tflex: 1 1 auto;\n\t}\n}\n\n.dragga"
  },
  {
    "path": "www/src/Components/FormCheck.js",
    "chars": 407,
    "preview": "import React from 'react';\nimport { Form } from 'react-bootstrap';\n\nimport './FormCheck.scss'\n\nconst FormControl = ({ la"
  },
  {
    "path": "www/src/Components/FormCheck.scss",
    "chars": 38,
    "preview": ".form-check-input {\n\tmargin-top: 0;\n}\n"
  },
  {
    "path": "www/src/Components/FormControl.js",
    "chars": 382,
    "preview": "import React from 'react';\nimport { Form } from 'react-bootstrap';\n\nconst FormControl = ({ label, error, groupClassName,"
  },
  {
    "path": "www/src/Components/FormSelect.js",
    "chars": 390,
    "preview": "import React from 'react';\nimport { Form } from 'react-bootstrap';\n\nconst FormSelect = ({ label, error, groupClassName, "
  },
  {
    "path": "www/src/Components/Navigation.js",
    "chars": 2319,
    "preview": "import React, { useContext } from 'react';\nimport { Nav, NavDropdown, Navbar } from 'react-bootstrap';\nimport { NavLink "
  },
  {
    "path": "www/src/Components/Navigation.scss",
    "chars": 538,
    "preview": ".title-logo {\n\theight: 24px;\n\tmargin: -6px 8px 0 12px;\n}\n\n.nav-link, .nav-item * {\n\tfont-size: 0.875rem;\n}\n\n.navbar-tool"
  },
  {
    "path": "www/src/Components/Section.js",
    "chars": 357,
    "preview": "import React from 'react';\nimport './Section.scss';\n\nconst Section = ({ children, title, ...props }) => {\n\treturn (\n\t\t<d"
  },
  {
    "path": "www/src/Components/Section.scss",
    "chars": 32,
    "preview": ".card {\n\tmargin-bottom: 10px;\n}\n"
  },
  {
    "path": "www/src/Contexts/AppContext.js",
    "chars": 158,
    "preview": "import { createContext } from 'react';\n\nexport const defaultAppData = {\n\tbuttonLabels: 'gp2040',\n};\n\nexport const AppCon"
  },
  {
    "path": "www/src/Data/Boards.json",
    "chars": 109,
    "preview": "{\n\t\"pico\": {\n\t\t\"minPin\": 0,\n\t\t\"maxPin\": 28,\n\t\t\"analogPins\": [26, 27, 28],\n\t\t\"invalidPins\": [23, 24, 25]\n\t}\n}\n"
  },
  {
    "path": "www/src/Data/Buttons.json",
    "chars": 1994,
    "preview": "{\n\t\"gp2040\": {\n\t\t\"label\": \"GP2040\",\n\t\t\"value\": \"gp2040\",\n\t\t\"Up\": \"Up\",\n\t\t\"Down\": \"Down\",\n\t\t\"Left\": \"Left\",\n\t\t\"Right\": \"R"
  },
  {
    "path": "www/src/Data/Controllers.json",
    "chars": 570,
    "preview": "{\n\t\"pico\": {\n\t\t\"board\": \"pico\",\n\t\t\"Up\": \"2\",\n\t\t\"Down\": \"3\",\n\t\t\"Left\": \"5\",\n\t\t\"Right\": \"4\",\n\t\t\"B1\": \"6\",\n\t\t\"B2\": \"7\",\n\t\t\""
  },
  {
    "path": "www/src/Pages/DisplayConfig.js",
    "chars": 7629,
    "preview": "import React, { useEffect, useState } from 'react';\nimport { Button, Form, Row, Col } from 'react-bootstrap';\nimport { F"
  },
  {
    "path": "www/src/Pages/HomePage.js",
    "chars": 1312,
    "preview": "import React, { useEffect, useState } from 'react';\nimport axios from 'axios';\nimport { orderBy } from 'lodash';\n\nimport"
  },
  {
    "path": "www/src/Pages/LEDConfigPage.js",
    "chars": 7554,
    "preview": "import React, { useContext, useEffect, useState } from 'react';\nimport { Button, Form, Row } from 'react-bootstrap';\nimp"
  },
  {
    "path": "www/src/Pages/PinMapping.js",
    "chars": 4286,
    "preview": "import React, { useContext, useEffect, useState } from 'react';\nimport { NavLink } from \"react-router-dom\";\nimport { But"
  },
  {
    "path": "www/src/Pages/PinMappings.scss",
    "chars": 460,
    "preview": "table.pin-mapping-table {\n\twidth: 100%;\n\tvertical-align: middle;\n\n\t.table-header-button-label {\n\t\twidth: 150px;\n\t}\n\n\tinp"
  },
  {
    "path": "www/src/Pages/ResetSettingsPage.js",
    "chars": 932,
    "preview": "import React from 'react';\nimport DangerSection from '../Components/DangerSection';\nimport WebApi from '../Services/WebA"
  },
  {
    "path": "www/src/Pages/SettingsPage.js",
    "chars": 3760,
    "preview": "import React, { useEffect, useState } from 'react';\nimport { Button, Form } from 'react-bootstrap';\nimport { Formik, use"
  },
  {
    "path": "www/src/Services/Storage.js",
    "chars": 274,
    "preview": "const STORAGE_BUTTON_LABELS = 'buttonLabels';\n\nconst loadButtonLabels = () => localStorage.getItem(STORAGE_BUTTON_LABELS"
  },
  {
    "path": "www/src/Services/WebApi.js",
    "chars": 3206,
    "preview": "import axios from 'axios';\n\nconst baseUrl = process.env.NODE_ENV === 'production' ? '' : 'http://localhost:8080';\n\nexpor"
  },
  {
    "path": "www/src/index.js",
    "chars": 317,
    "preview": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport { BrowserRouter } from 'react-router-dom';\nimport Ap"
  },
  {
    "path": "www/src/index.scss",
    "chars": 1389,
    "preview": "// Override default variables before the import\n$body-bg: #e9ecef;\n$primary: #495057;\n$danger: #7b2d26;\n\n@import '~boots"
  }
]

// ... and 7 more files (download for full content)

About this extraction

This page contains the full source code of the FeralAI/GP2040 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 170 files (5.0 MB), approximately 1.3M tokens, and a symbol index with 386 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!