Skip to content

Commit f42f437

Browse files
Merge pull request #21 from FrameworkComputer/demo
2 parents b1b861b + c623ede commit f42f437

File tree

14 files changed

+683
-197
lines changed

14 files changed

+683
-197
lines changed

README.md

Lines changed: 100 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,122 @@
11
# Lotus Input Module Firmware
22

3-
See below sections for LED Matrix, LCD Display and C1 Minimal module details.
3+
This repository contains both the firmware for the Lotus input modules, as well
4+
as the tool to control them.
45

5-
Rust project setup based off of: https://github.com/rp-rs/rp2040-project-template
6+
Rust firmware project setup based off of: https://github.com/rp-rs/rp2040-project-template
67

7-
## Features
8+
## Modules
89

9-
- Reset into bootloader when firmware crashes/panics
10+
See pages of the individual modules for details about how they work and how
11+
they're controlled.
12+
13+
- [LED Matrix](ledmatrix/README.md)
14+
- [2nd Display](b1display/README.md)
15+
- [Minimal C1 Input Module](c1minimal/README.md)
16+
17+
## Generic Features
18+
19+
All modules are built with an RP2040 microcontroller
20+
Features that all modules share
21+
22+
- Firmware written in bare-metal Rust
23+
- Reset into RP2040 bootloader when firmware crashes/panics
24+
- Sleep Mode to save power
1025
- API over USB ACM Serial Port - Requires no Drivers on Windows and Linux
11-
- Display various pre-programmed patterns
12-
- Light up a percentage of the screen
13-
- Change brightness
14-
- Send a black/white image to the display
15-
- Send a greyscale image to the display
1626
- Go to sleep
1727
- Reset into bootloader
18-
- Scroll and loop the display content vertically
19-
- A commandline script and graphical application to control it
20-
- Sleep Mode
21-
- Transition slowly turns off/on the LEDs
22-
- Current hardware does not have the SLEEP# GPIO connected, can't sleep automatically
28+
- Control and read module state (brightness, displayed image, ...)
2329

24-
Future features:
30+
## Control from the host
2531

26-
- API
27-
- Send a greyscale image to display
28-
- Read current system state (brightness, sleeping, ...)
32+
To build your own application see the: [API command documentation](commands.md)
2933

30-
## Control from the host
34+
Or use our `inputmodule-control` app. Optionally there are is also a
35+
[Python script](python.md).
36+
37+
For device specific commands, see their individual documentation pages.
38+
39+
Common commands:
40+
41+
###### Listing available devices
42+
43+
```sh
44+
> inputmodule-control --list
45+
/dev/ttyACM0
46+
VID 0x32AC
47+
PID 0x0020
48+
SN FRAKDEAM0020110001
49+
Product Lotus_LED_Matrix
50+
/dev/ttyACM1
51+
VID 0x32AC
52+
PID 0x0021
53+
SN FRAKDEAM0000000000
54+
Product Lotus_B1_Display
55+
```
3156

32-
Requirements: Python, [PySimpleGUI](https://www.pysimplegui.org) and optionally [pillow](https://pillow.readthedocs.io/en/stable/index.html)
57+
###### Apply command to single device
3358

34-
Use `control.py`. Either the commandline, see `control.py --help` or the graphical version: `control.py --gui`
59+
By default a command will be sent to all devices that can be found, to apply it
60+
to a single device, specify the COM port.
61+
In this example the command is targeted at `b1-display`, so it will only apply
62+
to this module type.
3563

3664
```
37-
options:
38-
-h, --help show this help message and exit
39-
--bootloader Jump to the bootloader to flash new firmware
40-
--sleep, --no-sleep Simulate the host going to sleep or waking up
41-
--brightness BRIGHTNESS
42-
Adjust the brightness. Value 0-255
43-
--animate, --no-animate
44-
Start/stop vertical scrolling
45-
--pattern {full,lotus,gradient,double-gradient,zigzag,panic,lotus2}
46-
Display a pattern
47-
--image IMAGE Display a PNG or GIF image in black and white only)
48-
--image-grey IMAGE_GREY
49-
Display a PNG or GIF image in greyscale
50-
--percentage PERCENTAGE
51-
Fill a percentage of the screen
52-
--clock Display the current time
53-
--string STRING Display a string or number, like FPS
54-
--symbols SYMBOLS [SYMBOLS ...]
55-
Show symbols (degF, degC, :), snow, cloud, ...)
56-
--gui Launch the graphical version of the program
57-
--blink Blink the current pattern
58-
--breathing Breathing of the current pattern
59-
--eq EQ [EQ ...] Equalizer
60-
--random-eq Random Equalizer
61-
--wpm WPM Demo
62-
--snake Snake
63-
--all-brightnesses Show every pixel in a different brightness
64-
--set-color {white,black,red,green,blue,cyan,yellow,purple}
65-
Set RGB color (C1 Minimal Input Module)
66-
--get-color Get RGB color (C1 Minimal Input Module)
67-
-v, --version Get device version
68-
--serial-dev SERIAL_DEV
69-
Change the serial dev. Probably /dev/ttyACM0 on Linux, COM0 on Windows
65+
# Example on Linux
66+
> inputmodule-control --serial-dev /dev/ttyACM0 b1-display --pattern black
67+
68+
# Example on Windows
69+
> inputmodule-control.exe --serial-dev COM5 b1-display --pattern black
7070
```
7171

72-
Examples
72+
###### Send command when device connects
7373

74-
```sh
75-
# Launch graphical application
76-
./control.py --gui
74+
By default the app tries to connect with the device and aborts if it can't
75+
connect. But you might want to start the app, have it wait until the device is
76+
connected and then send the command.
7777

78-
# Show current time and keep updating it
79-
./control.py --clock
78+
```
79+
> inputmodule-control b1-display --pattern black
80+
Failed to find serial devivce. Please manually specify with --serial-dev
8081
81-
# Draw PNG or GIF
82-
./control.py --image stripe.gif
83-
./control.py --image stripe.png
82+
# No failure, waits until the device is connected, sends command and exits
83+
> inputmodule-control --wait-for-device b1-display --pattern black
8484
85-
# Change brightness (0-255)
86-
./control.py --brightness 50
85+
# If the device is already connected, it does nothing, just wait 1s.
86+
# This means you can run this command by a system service and restart it when
87+
# it finishes. Then it will only ever do anything if the device reconnects.
88+
> inputmodule-control --wait-for-device b1-display --pattern black
89+
Device already present. No need to wait. Not executing command.
8790
```
8891

89-
## Control via Rust binary
92+
## Update the Firmware
9093

91-
Currently have to specify the build target because it's not possible to specify a per package build target.
92-
Tracking issue: https://github.com/rust-lang/cargo/issues/9406
94+
First, put the module into bootloader mode.
95+
96+
This can be done either by pressing the bootsel button while plugging it in or
97+
by using one of the following commands:
9398

99+
```sh
100+
inputmodule-control led-matrix --bootloader
101+
inputmodule-control b1-display --bootloader
102+
inputmodule-control c1-minimal --bootloader
94103
```
95-
> cargo build --target x86_64-unknown-linux-gnu -p inputmodule-control
96-
> cargo run --target x86_64-unknown-linux-gnu -p inputmodule-control
104+
105+
Then the module will present itself in the same way as a USB thumb drive.
106+
Copy the UF2 firmware file onto it and the device will flash and reset automatically.
107+
Alternatively when building from source, run one of the following commands:
108+
109+
```sh
110+
cargo run -p ledmatrix
111+
cargo run -p b1display
112+
cargo run -p c1minimal
97113
```
98114

99-
## Building
115+
## Building the firmware
100116

101117
Dependencies: Rust
102118

103-
Prepare Rust toolchain:
119+
Prepare Rust toolchain (once):
104120

105121
```sh
106122
rustup target install thumbv6m-none-eabi
@@ -116,36 +132,33 @@ cargo build -p b1display
116132
cargo build -p c1minimal
117133
```
118134

119-
Generate UF2 file:
135+
Generate the UF2 update file:
120136

121137
```sh
122138
elf2uf2-rs target/thumbv6m-none-eabi/debug/ledmatrix ledmatrix.uf2
123139
elf2uf2-rs target/thumbv6m-none-eabi/debug/b1display b1dipslay.uf2
124-
elf2uf2-rs target/thumbv6m-none-eabi/debug/b1display c1minimal.uf2
140+
elf2uf2-rs target/thumbv6m-none-eabi/debug/c1minimal c1minimal.uf2
125141
```
126142

127-
## Flashing
143+
## Building the Application
128144

129-
First, put the module into bootloader mode, which will expose a filesystem
145+
Dependencies: Rust, pkg-config, libudev
130146

131-
This can be done by pressing the bootsel button while plugging it in.
147+
Currently have to specify the build target because it's not possible to specify a per package build target.
148+
Tracking issue: https://github.com/rust-lang/cargo/issues/9406
132149

133-
```sh
134-
cargo run -p ledmatrix
135-
cargo run -p b1display
136-
cargo run -p c1minimal
137150
```
138-
139-
Or by copying the above generated UF2 file to the partition mounted when the
140-
module is in the bootloder.
151+
> cargo build --target x86_64-unknown-linux-gnu -p inputmodule-control
152+
> cargo run --target x86_64-unknown-linux-gnu -p inputmodule-control
153+
```
141154

142155
### Check the firmware version of the device
143156

144-
###### In-band using `control.py`
157+
###### In-band using commandline
145158

146159
```sh
147-
> ./control.py --version
148-
Device version: 0.1.2
160+
> inputmodule-control b1-display --version
161+
Device Version: 0.1.3
149162
```
150163

151164
###### By looking at the USB descriptor
@@ -169,30 +182,3 @@ Additionally the panic message is written to flash, which can be read as follows
169182
sudo picotool save -r 0x15000000 0x15004000 message.bin
170183
strings message.bin | head
171184
```
172-
173-
## LED Matrix
174-
175-
It's a 9x34 (306) LED matrix, controlled by RP2040 MCU and IS31FL3741A LED controller.
176-
177-
Connection to the host system is via USB 2.0 and currently there is a USB Serial API to control it without reflashing.
178-
179-
## B1 Display
180-
181-
## C1 Minimal Input Module
182-
183-
It's a very minimal input module. Many GPIO pins are exposed so that headers
184-
can be soldered onto them. Additionally there are pads for a WS2812/Neopixel
185-
compatible RGB LED.
186-
187-
When booting up this LED is lit in green color.
188-
Its color and brightness can be controlled via the commands:
189-
190-
```sh
191-
> ./control.py --brightness 255
192-
> ./control.py --get-brightness
193-
Current brightness: 255
194-
195-
> ./control.py --set-color yellow
196-
> ./control.py --get-color
197-
Current color: RGB:(255, 255, 0)
198-
```

b1display/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
## B1 Display

b1display/src/main.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ fn main() -> ! {
182182

183183
let mut state = B1DIsplayState {
184184
sleeping: SimpleSleepState::Awake,
185+
screen_inverted: false,
186+
screen_on: true,
185187
};
186188

187189
let mut said_hello = false;
@@ -240,21 +242,22 @@ fn main() -> ! {
240242
};
241243
}
242244
(Some(command), SimpleSleepState::Awake) => {
245+
// While sleeping no command is handled, except waking up
246+
if let Some(response) =
247+
handle_command(&command, &mut state, logo_rect, &mut disp)
248+
{
249+
let _ = serial.write(&response);
250+
};
251+
// Must write AFTER writing response, otherwise the
252+
// client interprets this debug message as the response
243253
let mut text: String<64> = String::new();
244254
write!(
245255
&mut text,
246-
"Handling command {}:{}:{}:{}\r\n",
256+
"Handled command {}:{}:{}:{}\r\n",
247257
buf[0], buf[1], buf[2], buf[3]
248258
)
249259
.unwrap();
250260
let _ = serial.write(text.as_bytes());
251-
252-
// While sleeping no command is handled, except waking up
253-
if let Some(response) =
254-
handle_command(&command, &mut state, logo_rect, &mut disp)
255-
{
256-
let _ = serial.write(&response);
257-
};
258261
}
259262
_ => {}
260263
}

c1minimal/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
## C1 Minimal Input Module
2+
3+
It's a very minimal input module. Many GPIO pins are exposed so that headers
4+
can be soldered onto them. Additionally there are pads for a WS2812/Neopixel
5+
compatible RGB LED.
6+
7+
When booting up this LED is lit in green color.
8+
Its color and brightness can be controlled via the commands:
9+
10+
```sh
11+
> ./control.py --brightness 255
12+
> ./control.py --get-brightness
13+
Current brightness: 255
14+
15+
> ./control.py --set-color yellow
16+
> ./control.py --get-color
17+
Current color: RGB:(255, 255, 0)
18+
```

0 commit comments

Comments
 (0)