a vim plugin that displays stuff on an led matrix

Compare changes

Choose any two refs to compare.

Changed files
+308 -68
firmware
lua
braille_indicator
+5 -1
README.md
···
# Usage
-
Eventually you should download the bin from the releases and copy it to your board and then follow the vim plugin steps i will add below.
+
So the orginal goal was for keystrokes to make a nice pattern in your status bar as well as on the led matrix of daedalus but I couldn't get the serial to work from vim; not entirely sure why but it really doesn't like this for some reason.
+
+
What I do have instead is firmware for random led blinking when a serial character is detected and a vim plugin that displays random braille characters in the status bar.
+
+
You can read the [plugin](/lua/braille_indicator/README.md) for more information on installation and for the firmware you can use any serial interface set to a `115200` baud rate (screen for example `screen /dev/tty.usbmodem101 115200`).
<p align="center">
<img src="https://raw.githubusercontent.com/taciturnaxolotl/carriage/main/.github/images/line-break.svg" />
+1
firmware/CMakeLists.txt
···
pico_stdlib
hardware_gpio
hardware_uart
+
hardware_pwm
pico_multicore
)
+50 -4
firmware/README.md
···
## Building
+
### Using the Makefile (Recommended)
+
+
The project includes a Makefile that simplifies the build process:
+
1. Make sure you have the Pico SDK set up (automatically handled by the Nix flake)
-
2. Configure the build:
+
2. Build the firmware:
+
```
+
make
+
```
+
3. The output files will be in the `build` directory
+
+
### Manual Build
+
+
If you prefer to use CMake directly:
+
+
1. Configure the build:
```
cmake -B build -S .
```
-
3. Build the firmware:
+
2. Build the firmware:
```
cmake --build build
```
-
4. The output files will be in the `build` directory:
+
3. The output files will be in the `build` directory:
- `daedalus_firmware.uf2`: UF2 file for drag-and-drop programming
- `daedalus_firmware.elf`: ELF file for debugging
- `daedalus_firmware.hex`: HEX file for programming with external tools
## Flashing
+
### Using the Makefile (Recommended)
+
+
Flash the firmware to the RP2350 board:
+
+
```
+
make flash
+
```
+
+
If the board is already running firmware and needs to be forced into programming mode:
+
+
```
+
make FORCE_FLASH=1 flash
+
```
+
+
### Manual Flashing
+
Connect the RP2350 board in bootloader mode (hold BOOTSEL while connecting USB) and copy the UF2 file:
```
···
```
picotool load -x build/daedalus_firmware.uf2
-
```
+
```
+
+
## Debugging
+
+
Generate disassembly for debugging:
+
+
```
+
make disasm
+
```
+
+
## Development
+
+
The firmware uses both cores of the RP2040:
+
- Core 0: Handles the main application logic
+
- Core 1: Manages the LED matrix display
+
+
The LED matrix is organized in a 4x6 grid, with rows and columns controlled by GPIO pins.
+97 -63
firmware/src/main.cpp
···
#include <stdlib.h>
#include <cstring>
+
#include <stdio.h>
#include "pico/multicore.h"
#include "pico/stdlib.h"
+
#include <time.h>
// Define GPIO pins for rows and columns
-
const uint ROW_PINS[4] = {12, 13, 14, 15};
-
const uint COL_PINS[6] = {17, 18, 19, 20, 21, 22};
-
#define ROWS 4
#define COLS 6
+
const uint ROW_PINS[4] = {12, 13, 14, 15};
+
const uint COL_PINS[6] = {17, 18, 19, 20, 21, 22};
+
// LED state matrix
uint8_t led_states[ROWS][COLS] = {0};
-
void setup_pins() {
-
// Set up row pins
-
for (int i = 0; i < ROWS; i++) {
-
gpio_init(ROW_PINS[i]);
-
gpio_set_dir(ROW_PINS[i], GPIO_OUT);
-
gpio_put(ROW_PINS[i], 0); // Start with LEDs off
-
}
+
// brightness control
+
uint8_t brightness = 100;
-
// Set up column pins
-
for (int j = 0; j < COLS; j++) {
-
gpio_init(COL_PINS[j]);
-
gpio_set_dir(COL_PINS[j], GPIO_OUT);
-
gpio_put(COL_PINS[j], 1); // Start with LEDs off
+
// timing vars
+
bool fading = false;
+
absolute_time_t last_char_time;
+
absolute_time_t fade_start_time;
+
const uint32_t inactivity_timeout_ms = 0;
+
const uint32_t fade_duration_ms = 1200;
+
+
void randomize_matrix() {
+
for (int row = 0; row < ROWS; row++) {
+
for (int col = 0; col < COLS; col++) {
+
led_states[row][col] = rand() % 2;
+
}
}
}
···
last_update = current_time;
-
// Display the current state
-
for (int row = 0; row < ROWS; row++) {
-
// Set row active
-
gpio_put(ROW_PINS[row], 1);
+
uint32_t time_since_last_char = absolute_time_diff_us(last_char_time, get_absolute_time()) / 1000;
-
// Set column states for this row
-
for (int col = 0; col < COLS; col++) {
-
// LED on = column LOW, LED off = column HIGH
-
gpio_put(COL_PINS[col], led_states[row][col] ? 0 : 1);
-
}
+
if (!fading && time_since_last_char > inactivity_timeout_ms) {
+
// start fading
+
fading = true;
+
fade_start_time = get_absolute_time();
+
}
-
// Small delay for this row to be visible
-
sleep_us(100);
+
if (fading) {
+
uint32_t fade_elapsed_ms = absolute_time_diff_us(fade_start_time, get_absolute_time()) / 1000;
-
// Deactivate row
-
gpio_put(ROW_PINS[row], 0);
+
if (fade_elapsed_ms >= fade_duration_ms) {
+
brightness = 0;
+
} else {
+
brightness = 100 - ((fade_elapsed_ms * 100) / fade_duration_ms);
+
}
}
-
}
-
// Function to set a specific LED state
-
void set_led(int row, int col, bool state) {
-
if (row >= 0 && row < ROWS && col >= 0 && col < COLS) {
-
led_states[row][col] = state ? 1 : 0;
+
// Skip display update if brightness is 0
+
if (brightness == 0) {
+
// Turn off all rows and set all columns high to ensure LEDs are off
+
for (int i = 0; i < ROWS; i++) {
+
gpio_put(ROW_PINS[i], 0);
+
}
+
for (int j = 0; j < COLS; j++) {
+
gpio_put(COL_PINS[j], 1);
+
}
+
+
return;
}
-
}
-
// Function to set the entire matrix state
-
void set_matrix_state(const uint8_t new_state[ROWS][COLS]) {
-
memcpy(led_states, new_state, sizeof(led_states));
-
}
+
// Software PWM cycle
+
for (uint8_t pwm_cycle = 0; pwm_cycle < brightness; pwm_cycle++) {
+
// For each row
+
for (int row = 0; row < ROWS; row++) {
+
// Activate row
+
gpio_put(ROW_PINS[row], 1);
-
void random_state(float density) {
-
// clamp
-
if (density > 1.0f)
-
density = 1.0f;
-
if (density < 0.0f)
-
density = 0.0f;
+
// Set column states for this row
+
for (int col = 0; col < COLS; col++) {
+
gpio_put(COL_PINS[col], led_states[row][col] ? 0 : 1);
+
}
-
for (int row = 0; row < ROWS; row++) {
-
for (int col = 0; col < COLS; col++) {
-
float r = (float)rand() / (float)RAND_MAX;
-
led_states[row][col] = (r < density) ? 1 : 0;
+
// Keep row active for a short time
+
sleep_us(50);
+
+
// Deactivate row
+
gpio_put(ROW_PINS[row], 0);
}
}
}
-
-
const uint8_t ALL_ON[ROWS][COLS] = {{1, 1, 1, 1, 1, 1},
-
{1, 1, 1, 1, 1, 1},
-
{1, 1, 1, 1, 1, 1},
-
{1, 1, 1, 1, 1, 1}};
+
void setup_pins() {
+
// Set up row pins
+
for (int i = 0; i < ROWS; i++) {
+
gpio_init(ROW_PINS[i]);
+
gpio_set_dir(ROW_PINS[i], GPIO_OUT);
+
gpio_put(ROW_PINS[i], 0); // Start with LEDs off
+
}
-
const uint8_t ALL_OFF[ROWS][COLS] = {{0, 0, 0, 0, 0, 0},
-
{0, 0, 0, 0, 0, 0},
-
{0, 0, 0, 0, 0, 0},
-
{0, 0, 0, 0, 0, 0}};
+
// Set up column pins
+
for (int j = 0; j < COLS; j++) {
+
gpio_init(COL_PINS[j]);
+
gpio_set_dir(COL_PINS[j], GPIO_OUT);
+
gpio_put(COL_PINS[j], 1); // Start with LEDs off
+
}
+
}
void core1_main() {
while (true) {
-
update_led_display(1000);
+
update_led_display(1000); // Update at 60Hz for smoother display
+
sleep_ms(1); // Small delay to prevent tight loop
}
}
···
stdio_init_all();
setup_pins();
+
// seed the random number generator
+
srand(time_us_32());
+
+
last_char_time = get_absolute_time();
+
multicore_launch_core1(core1_main);
while (true) {
-
random_state(0.5); // Turn random LEDs ON
-
// set_matrix_state(ALL_ON);
-
sleep_ms(200);
-
// set_matrix_state(ALL_OFF);
-
// sleep_ms(500);
+
// check if there are characters available from the input
+
int c = getchar_timeout_us(0);
+
+
if (c != PICO_ERROR_TIMEOUT) {
+
// echo char back
+
printf("%c", c);
+
+
last_char_time = get_absolute_time();
+
+
fading = false;
+
brightness = 100;
+
+
randomize_matrix();
+
+
while (getchar_timeout_us(0) != PICO_ERROR_TIMEOUT) {}
+
}
+
+
sleep_ms(10);
}
}
+78
lua/braille_indicator/README.md
···
+
# Braille Typing Indicator Plugin for Neovim
+
+
A simple plugin that displays a random Braille character in your statusline that changes every time you type.
+
+
## Installation
+
+
### Using a plugin manager (recommended)
+
+
#### [packer.nvim](https://github.com/wbthomason/packer.nvim)
+
+
```lua
+
use {
+
'taciturnaxolotl/daedalus',
+
config = function()
+
require('braille_indicator').setup()
+
end
+
}
+
```
+
+
#### [lazy.nvim](https://github.com/folke/lazy.nvim)
+
+
```lua
+
{
+
'taciturnaxolotl/daedalus',
+
config = function()
+
require('braille_indicator').setup()
+
end
+
}
+
```
+
+
### Manual installation
+
+
1. Clone this repository to your Neovim configuration directory:
+
+
```
+
git clone https://github.com/taciturnaxolotl/daedalus.git ~/.config/nvim/pack/plugins/start/daedalus
+
```
+
+
2. Add the following to your `init.lua`:
+
```lua
+
require('braille_indicator').setup()
+
```
+
+
## Configuration
+
+
The plugin works with minimal configuration, but you can customize it:
+
+
```lua
+
require('braille_indicator').setup({
+
update_statusline = true, -- Automatically update the statusline
+
verbose = true, -- Show setup messages
+
})
+
```
+
+
### Adding to your statusline
+
+
If you're using a custom statusline setup, add the Braille indicator with:
+
+
```lua
+
%{v:lua.getBrailleIndicator()}
+
```
+
+
For example, with lualine:
+
+
```lua
+
require('lualine').setup({
+
sections = {
+
lualine_a = {
+
function()
+
return getBrailleIndicator()
+
end,
+
'mode'
+
},
+
-- other sections...
+
}
+
})
+
```
+
+77
lua/braille_indicator/init.lua
···
+
-- Braille Typing Indicator Plugin for Neovim
+
-- This is a proper plugin structure for installation in your Neovim config
+
+
local M = {}
+
+
-- Braille unicode characters (โ € to โฃฟ)
+
local brailleChars = {
+
'โ €', 'โ ', 'โ ‚', 'โ ƒ', 'โ „', 'โ …', 'โ †', 'โ ‡', 'โ ˆ', 'โ ‰', 'โ Š', 'โ ‹', 'โ Œ', 'โ ', 'โ Ž', 'โ ',
+
'โ ', 'โ ‘', 'โ ’', 'โ “', 'โ ”', 'โ •', 'โ –', 'โ —', 'โ ˜', 'โ ™', 'โ š', 'โ ›', 'โ œ', 'โ ', 'โ ž', 'โ Ÿ',
+
'โ  ', 'โ ก', 'โ ข', 'โ ฃ', 'โ ค', 'โ ฅ', 'โ ฆ', 'โ ง', 'โ จ', 'โ ฉ', 'โ ช', 'โ ซ', 'โ ฌ', 'โ ญ', 'โ ฎ', 'โ ฏ',
+
'โ ฐ', 'โ ฑ', 'โ ฒ', 'โ ณ', 'โ ด', 'โ ต', 'โ ถ', 'โ ท', 'โ ธ', 'โ น', 'โ บ', 'โ ป', 'โ ผ', 'โ ฝ', 'โ พ', 'โ ฟ',
+
'โก€', 'โก', 'โก‚', 'โกƒ', 'โก„', 'โก…', 'โก†', 'โก‡', 'โกˆ', 'โก‰', 'โกŠ', 'โก‹', 'โกŒ', 'โก', 'โกŽ', 'โก',
+
'โก', 'โก‘', 'โก’', 'โก“', 'โก”', 'โก•', 'โก–', 'โก—', 'โก˜', 'โก™', 'โกš', 'โก›', 'โกœ', 'โก', 'โกž', 'โกŸ',
+
'โก ', 'โกก', 'โกข', 'โกฃ', 'โกค', 'โกฅ', 'โกฆ', 'โกง', 'โกจ', 'โกฉ', 'โกช', 'โกซ', 'โกฌ', 'โกญ', 'โกฎ', 'โกฏ',
+
'โกฐ', 'โกฑ', 'โกฒ', 'โกณ', 'โกด', 'โกต', 'โกถ', 'โกท', 'โกธ', 'โกน', 'โกบ', 'โกป', 'โกผ', 'โกฝ', 'โกพ', 'โกฟ',
+
'โข€', 'โข', 'โข‚', 'โขƒ', 'โข„', 'โข…', 'โข†', 'โข‡', 'โขˆ', 'โข‰', 'โขŠ', 'โข‹', 'โขŒ', 'โข', 'โขŽ', 'โข',
+
'โข', 'โข‘', 'โข’', 'โข“', 'โข”', 'โข•', 'โข–', 'โข—', 'โข˜', 'โข™', 'โขš', 'โข›', 'โขœ', 'โข', 'โขž', 'โขŸ',
+
'โข ', 'โขก', 'โขข', 'โขฃ', 'โขค', 'โขฅ', 'โขฆ', 'โขง', 'โขจ', 'โขฉ', 'โขช', 'โขซ', 'โขฌ', 'โขญ', 'โขฎ', 'โขฏ',
+
'โขฐ', 'โขฑ', 'โขฒ', 'โขณ', 'โขด', 'โขต', 'โขถ', 'โขท', 'โขธ', 'โขน', 'โขบ', 'โขป', 'โขผ', 'โขฝ', 'โขพ', 'โขฟ',
+
'โฃ€', 'โฃ', 'โฃ‚', 'โฃƒ', 'โฃ„', 'โฃ…', 'โฃ†', 'โฃ‡', 'โฃˆ', 'โฃ‰', 'โฃŠ', 'โฃ‹', 'โฃŒ', 'โฃ', 'โฃŽ', 'โฃ',
+
'โฃ', 'โฃ‘', 'โฃ’', 'โฃ“', 'โฃ”', 'โฃ•', 'โฃ–', 'โฃ—', 'โฃ˜', 'โฃ™', 'โฃš', 'โฃ›', 'โฃœ', 'โฃ', 'โฃž', 'โฃŸ',
+
'โฃ ', 'โฃก', 'โฃข', 'โฃฃ', 'โฃค', 'โฃฅ', 'โฃฆ', 'โฃง', 'โฃจ', 'โฃฉ', 'โฃช', 'โฃซ', 'โฃฌ', 'โฃญ', 'โฃฎ', 'โฃฏ',
+
'โฃฐ', 'โฃฑ', 'โฃฒ', 'โฃณ', 'โฃด', 'โฃต', 'โฃถ', 'โฃท', 'โฃธ', 'โฃน', 'โฃบ', 'โฃป', 'โฃผ', 'โฃฝ', 'โฃพ', 'โฃฟ'
+
}
+
+
-- Current braille character
+
local currentBraille = brailleChars[1]
+
+
-- Update braille character randomly
+
local function updateBrailleChar()
+
-- Use os.time() for randomness
+
math.randomseed(os.time() * 1000 + os.clock() * 10000)
+
local idx = math.random(1, #brailleChars)
+
currentBraille = brailleChars[idx]
+
return currentBraille
+
end
+
+
-- Get current braille character for statusline
+
function M.getBrailleIndicator()
+
return currentBraille
+
end
+
+
-- Setup function with options
+
function M.setup(opts)
+
opts = opts or {}
+
+
-- Make sure statusline is visible
+
vim.o.laststatus = 2
+
+
-- Key press handler
+
local function onKeyPress()
+
-- Update braille character
+
updateBrailleChar()
+
end
+
+
-- Set up autocommands
+
vim.api.nvim_create_autocmd({"InsertCharPre", "CursorMovedI"}, {
+
callback = onKeyPress,
+
group = vim.api.nvim_create_augroup("BrailleIndicator", { clear = true })
+
})
+
+
-- Make the function available globally for statusline
+
_G.getBrailleIndicator = M.getBrailleIndicator
+
+
-- Add to statusline if requested
+
if opts.update_statusline then
+
vim.o.statusline = "%{v:lua.getBrailleIndicator()} %f %m %r %= %l,%c "
+
end
+
+
-- Print setup message
+
if opts.verbose then
+
print("Braille indicator active. The character will change as you type.")
+
print("Add %{v:lua.getBrailleIndicator()} to your statusline if not already there.")
+
end
+
end
+
+
return M