Compare commits
No commits in common. "pan" and "master" have entirely different histories.
BIN
Blink copy.png
|
Before Width: | Height: | Size: 271 B |
92
README.md
|
|
@ -1,92 +0,0 @@
|
||||||
# Panagotchi
|
|
||||||
|
|
||||||
A Tamagotchi-style virtual pet for the **CH32V003F4P6** badge — a hacker-themed creature that evolves from `proc` → `user` → `root`, then either ascends to **Claude** or degrades into a **glitch** depending on how well you tend to it.
|
|
||||||
|
|
||||||
Fits in ~12 KB of the 16 KB flash budget.
|
|
||||||
|
|
||||||
## Hardware
|
|
||||||
|
|
||||||
| Pin | Function |
|
|
||||||
|------|-----------------|
|
|
||||||
| PA1 | Indicator LED |
|
|
||||||
| PC0 | OK button |
|
|
||||||
| PC1/PC2 | SSD1306 128×64 OLED (I2C1) |
|
|
||||||
| PD2 | Passive piezo buzzer |
|
|
||||||
| PD5 | Right button |
|
|
||||||
| PD6 | Left button |
|
|
||||||
|
|
||||||
Powered by a CR2032 coin cell via a TLV61070A boost converter to 3.3V. See [pinout.md](pinout.md) for the full pin assignment and power architecture.
|
|
||||||
|
|
||||||
## Gameplay
|
|
||||||
|
|
||||||
**Controls:** Left / Right move the menu cursor. OK activates the selected action.
|
|
||||||
|
|
||||||
**Menu actions:**
|
|
||||||
|
|
||||||
| Icon | Action | Effect |
|
|
||||||
|---------|---------|--------|
|
|
||||||
| Apple | FEED | +30 satiety, −5 hygiene |
|
|
||||||
| Ball | PLAY | +25 happy, −12 energy (requires energy ≥ 15) |
|
|
||||||
| Moon | SLEEP | Toggle nap; restores energy |
|
|
||||||
| Drop | CLEAN | Reset hygiene to 100, remove poop piles |
|
|
||||||
| Cross | HEAL | +35 health |
|
|
||||||
| Chart | STATS | Show stat bars + XP readout |
|
|
||||||
| Gear | SETTINGS| Screen timeout, brightness, sound |
|
|
||||||
|
|
||||||
Stats decay on a ~15-second tick. Health responds to overall wellbeing — if any need drops below 15 it bleeds down; if all are above 50 it recovers. Let health reach 0 and the pet dies with a `SEGFAULT (11)`.
|
|
||||||
|
|
||||||
**Evolution** (XP thresholds: 45 / 120 / 240):
|
|
||||||
|
|
||||||
```
|
|
||||||
proc → user → root → Claude (well-kept) / glitch (neglected)
|
|
||||||
```
|
|
||||||
|
|
||||||
Each stage gains a visual accessory: shades at `user`, a gaming headset and mic boom at `root`.
|
|
||||||
|
|
||||||
**Species** (chosen on the welcome screen):
|
|
||||||
|
|
||||||
- **Panko** — the panda; full 7-expression animation set
|
|
||||||
- **Shell** — a friendly CRT terminal with a `> _` prompt mouth
|
|
||||||
- **Tmux** — a split-pane terminal with a status bar
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
Requires [ch32v003fun](https://github.com/cnlohr/ch32v003fun) checked out as a sibling directory.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Generate sprites from source art
|
|
||||||
python3 sprites_gen.py > sprites.h
|
|
||||||
|
|
||||||
# Build and flash
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
Flash via the `minichlink` or `wch-link` programmer. The Makefile targets `cv_flash` / `cv_clean` from ch32fun.
|
|
||||||
|
|
||||||
Check real flash usage with:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
riscv-none-elf-size tama.elf
|
|
||||||
```
|
|
||||||
|
|
||||||
## Power saving
|
|
||||||
|
|
||||||
The OLED is the biggest power draw. When the screen-off timeout fires, the display is blanked and the MCU enters WFI light-sleep. Two wake sources:
|
|
||||||
|
|
||||||
- **Button press** (EXTI falling edge on PC0/PD5/PD6) — lights the screen back up
|
|
||||||
- **TIM2 overflow** (~once per game tick) — runs a background stat update and low-need chirp, without waking the display
|
|
||||||
|
|
||||||
SRAM and all pet state survive WFI; only the CPU clock is gated.
|
|
||||||
|
|
||||||
Brightness and screen-off timeout are adjustable in the settings menu.
|
|
||||||
|
|
||||||
## Files
|
|
||||||
|
|
||||||
| File | Purpose |
|
|
||||||
|------|---------|
|
|
||||||
| [tama.c](tama.c) | Main firmware: game logic, rendering, input, low-power |
|
|
||||||
| [sprites_gen.py](sprites_gen.py) | Python sprite generator — edit art here |
|
|
||||||
| [sprites.h](sprites.h) | Auto-generated 1-bpp sprite data (do not edit) |
|
|
||||||
| [funconfig.h](funconfig.h) | ch32fun config overrides (disables printf backends) |
|
|
||||||
| [pinout.md](pinout.md) | Full pin assignment and power architecture notes |
|
|
||||||
| [OPTIMIZATION.md](OPTIMIZATION.md) | Flash size backlog and optimization notes |
|
|
||||||
BIN
custom_blink.png
|
Before Width: | Height: | Size: 271 B |
BIN
custom_dead.png
|
Before Width: | Height: | Size: 265 B |
BIN
custom_eat.png
|
Before Width: | Height: | Size: 278 B |
BIN
custom_happy.png
|
Before Width: | Height: | Size: 275 B |
BIN
custom_idle.png
|
Before Width: | Height: | Size: 282 B |
BIN
custom_sad.png
|
Before Width: | Height: | Size: 280 B |
BIN
custom_sleep.png
|
Before Width: | Height: | Size: 258 B |
593
sprites.h
|
|
@ -9,254 +9,108 @@
|
||||||
|
|
||||||
// pana_idle: 32x32
|
// pana_idle: 32x32
|
||||||
static const unsigned char pana_idle[] = {
|
static const unsigned char pana_idle[] = {
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x80, 0x00, 0x00, 0x10, 0x88, 0x00, 0x00, 0x11, 0x88, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x31, 0x8C, 0x00, 0x00, 0x39, 0xDC, 0x00, 0x00, 0x3B, 0xDC, 0x00,
|
||||||
0x01, 0x00, 0x00, 0x80,
|
0x00, 0x7F, 0xFE, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0xF8, 0x3F, 0x00,
|
||||||
0x0f, 0xe0, 0x07, 0xf0,
|
0x00, 0xE7, 0xC7, 0x00, 0x00, 0xDF, 0xFB, 0x00, 0x01, 0xBF, 0xFD, 0x80,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xE7, 0xE7, 0xC0, 0x03, 0xE7, 0xE7, 0xC0, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFC, 0x3F, 0x80,
|
||||||
0x3f, 0xf8, 0x9f, 0xfc,
|
0x00, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x01, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xE0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8,
|
||||||
0x0f, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x07, 0xef, 0xfb, 0xf0,
|
|
||||||
0x0f, 0x83, 0xe0, 0xf8,
|
|
||||||
0x0f, 0x93, 0xe4, 0xf8,
|
|
||||||
0x0f, 0x11, 0xc4, 0x78,
|
|
||||||
0x0f, 0x83, 0xe0, 0xf8,
|
|
||||||
0x1f, 0x83, 0xe0, 0xfc,
|
|
||||||
0x0f, 0xef, 0x7b, 0xf8,
|
|
||||||
0x0f, 0xfc, 0x1f, 0xf8,
|
|
||||||
0x0f, 0xff, 0x7f, 0xf8,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x07, 0xf8, 0x0f, 0xf0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x01, 0xff, 0xff, 0xc0,
|
|
||||||
0x00, 0xff, 0xff, 0x80,
|
|
||||||
0x00, 0x7f, 0xff, 0x00,
|
|
||||||
0x00, 0x1f, 0xfc, 0x00,
|
|
||||||
0x00, 0x00, 0x80, 0x00,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// pana_blink: 32x32
|
// pana_blink: 32x32
|
||||||
static const unsigned char pana_blink[] = {
|
static const unsigned char pana_blink[] = {
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x80, 0x00, 0x00, 0x10, 0x88, 0x00, 0x00, 0x11, 0x88, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x31, 0x8C, 0x00, 0x00, 0x39, 0xDC, 0x00, 0x00, 0x3B, 0xDC, 0x00,
|
||||||
0x01, 0x00, 0x00, 0x80,
|
0x00, 0x7F, 0xFE, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0xF8, 0x3F, 0x00,
|
||||||
0x0f, 0xe0, 0x07, 0xf0,
|
0x00, 0xE7, 0xC7, 0x00, 0x00, 0xDF, 0xFB, 0x00, 0x01, 0xBF, 0xFD, 0x80,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xFF, 0xFF, 0xC0, 0x03, 0xC3, 0xC3, 0xC0, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFC, 0x3F, 0x80,
|
||||||
0x3f, 0xf8, 0x9f, 0xfc,
|
0x00, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x01, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xE0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8,
|
||||||
0x0f, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0x01, 0xc0, 0x78,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x1f, 0xff, 0xff, 0xfc,
|
|
||||||
0x0f, 0xff, 0x7f, 0xf8,
|
|
||||||
0x0f, 0xfc, 0x1f, 0xf8,
|
|
||||||
0x0f, 0xff, 0x7f, 0xf8,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x07, 0xf8, 0x0f, 0xf0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x01, 0xff, 0xff, 0xc0,
|
|
||||||
0x00, 0xff, 0xff, 0x80,
|
|
||||||
0x00, 0x7f, 0xff, 0x00,
|
|
||||||
0x00, 0x1f, 0xfc, 0x00,
|
|
||||||
0x00, 0x00, 0x80, 0x00,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// pana_happy: 32x32
|
// pana_happy: 32x32
|
||||||
static const unsigned char pana_happy[] = {
|
static const unsigned char pana_happy[] = {
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x80, 0x00, 0x00, 0x10, 0x88, 0x00, 0x00, 0x11, 0x88, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x31, 0x8C, 0x00, 0x00, 0x39, 0xDC, 0x00, 0x00, 0x3B, 0xDC, 0x00,
|
||||||
0x01, 0x00, 0x00, 0x80,
|
0x00, 0x7F, 0xFE, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0xF8, 0x3F, 0x00,
|
||||||
0x0f, 0xe0, 0x07, 0xf0,
|
0x00, 0xE7, 0xC7, 0x00, 0x00, 0xDF, 0xFB, 0x00, 0x01, 0xBF, 0xFD, 0x80,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xEF, 0xF7, 0xC0, 0x03, 0xE7, 0xEB, 0xC0, 0x03, 0xDB, 0xDB, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xF7, 0xEF, 0x80,
|
||||||
0x3f, 0xf8, 0x9f, 0xfc,
|
0x00, 0xF7, 0xEF, 0x00, 0x00, 0x7B, 0xDE, 0x00, 0x01, 0xFC, 0x3F, 0xC0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x01, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xE0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8,
|
||||||
0x0f, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0xef, 0xfb, 0xf8,
|
|
||||||
0x0f, 0xd7, 0xf5, 0xf8,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x1f, 0xff, 0xff, 0xfc,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0xf8, 0x0f, 0xf8,
|
|
||||||
0x0f, 0xfb, 0xef, 0xf8,
|
|
||||||
0x0f, 0xf7, 0xf7, 0xf8,
|
|
||||||
0x07, 0xfc, 0x1f, 0xf0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x01, 0xff, 0xff, 0xc0,
|
|
||||||
0x00, 0xff, 0xff, 0x80,
|
|
||||||
0x00, 0x7f, 0xff, 0x00,
|
|
||||||
0x00, 0x1f, 0xfc, 0x00,
|
|
||||||
0x00, 0x00, 0x80, 0x00,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// pana_sad: 32x32
|
// pana_sad: 32x32
|
||||||
static const unsigned char pana_sad[] = {
|
static const unsigned char pana_sad[] = {
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x80, 0x00, 0x00, 0x10, 0x88, 0x00, 0x00, 0x11, 0x88, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x31, 0x8C, 0x00, 0x00, 0x39, 0xDC, 0x00, 0x00, 0x3B, 0xDC, 0x00,
|
||||||
0x01, 0x00, 0x00, 0x80,
|
0x00, 0x7F, 0xFE, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0xF8, 0x3F, 0x00,
|
||||||
0x0f, 0xe0, 0x07, 0xf0,
|
0x00, 0xE7, 0xC7, 0x00, 0x00, 0xDF, 0xFB, 0x00, 0x01, 0xBF, 0xFD, 0x80,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x01, 0xFF, 0xFF, 0x80, 0x03, 0x9F, 0xF9, 0xC0, 0x03, 0xE7, 0xE7, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xFF, 0xFF, 0xC0, 0x03, 0xE7, 0xE7, 0xC0, 0x03, 0xE7, 0xE7, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80,
|
||||||
0x3f, 0xf8, 0x9f, 0xfc,
|
0x00, 0xFC, 0x3F, 0x00, 0x00, 0x7B, 0xDE, 0x00, 0x01, 0xF7, 0xEF, 0xC0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x01, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xE0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8,
|
||||||
0x0f, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x0f, 0xef, 0xfb, 0xf8,
|
|
||||||
0x0f, 0x83, 0xe0, 0xf8,
|
|
||||||
0x0f, 0x93, 0xe4, 0xf8,
|
|
||||||
0x0f, 0x11, 0xc4, 0x78,
|
|
||||||
0x1f, 0x83, 0xe0, 0xfc,
|
|
||||||
0x0f, 0x83, 0x60, 0xf8,
|
|
||||||
0x0f, 0xec, 0x1b, 0xf8,
|
|
||||||
0x0f, 0xff, 0x7f, 0xf8,
|
|
||||||
0x0f, 0xfc, 0x1f, 0xf8,
|
|
||||||
0x07, 0xf7, 0xf7, 0xf0,
|
|
||||||
0x07, 0xfb, 0xef, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x01, 0xff, 0xff, 0xc0,
|
|
||||||
0x00, 0xff, 0xff, 0x80,
|
|
||||||
0x00, 0x7f, 0xff, 0x00,
|
|
||||||
0x00, 0x1f, 0xfc, 0x00,
|
|
||||||
0x00, 0x00, 0x80, 0x00,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// pana_eat: 32x32
|
// pana_eat: 32x32
|
||||||
static const unsigned char pana_eat[] ={
|
static const unsigned char pana_eat[] ={
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x80, 0x00, 0x00, 0x10, 0x88, 0x00, 0x00, 0x11, 0x88, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x31, 0x8C, 0x00, 0x00, 0x39, 0xDC, 0x00, 0x00, 0x3B, 0xDC, 0x00,
|
||||||
0x01, 0x00, 0x00, 0x80,
|
0x00, 0x7F, 0xFE, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0xF8, 0x3F, 0x00,
|
||||||
0x0f, 0xe0, 0x07, 0xf0,
|
0x00, 0xE7, 0xC7, 0x00, 0x00, 0xDF, 0xFB, 0x00, 0x01, 0xBF, 0xFD, 0x80,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xE7, 0xE7, 0xC0, 0x03, 0xE7, 0xE7, 0xC0, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xFF, 0xFF, 0xC0, 0x01, 0xFC, 0x3F, 0x80, 0x01, 0xFD, 0x3F, 0x80,
|
||||||
0x3f, 0xf8, 0x9f, 0xfc,
|
0x00, 0xFC, 0xBF, 0x00, 0x00, 0x7C, 0x3E, 0x00, 0x01, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x01, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xE0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8,
|
||||||
0x0f, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x07, 0xef, 0xfb, 0xf0,
|
|
||||||
0x07, 0x83, 0xe0, 0xf0,
|
|
||||||
0x0f, 0x93, 0xe4, 0xf8,
|
|
||||||
0x0f, 0x11, 0xc4, 0x78,
|
|
||||||
0x0f, 0x83, 0xe0, 0xf8,
|
|
||||||
0x0f, 0x83, 0xe0, 0xf8,
|
|
||||||
0x1f, 0xef, 0xfb, 0xfc,
|
|
||||||
0x0f, 0xff, 0x7f, 0xf8,
|
|
||||||
0x0f, 0xfc, 0x1f, 0xf8,
|
|
||||||
0x0f, 0xfc, 0x1f, 0xf8,
|
|
||||||
0x0f, 0xf8, 0x0f, 0xf8,
|
|
||||||
0x07, 0xfc, 0x1f, 0xf0,
|
|
||||||
0x07, 0xfc, 0x1f, 0xf0,
|
|
||||||
0x03, 0xff, 0x7f, 0xe0,
|
|
||||||
0x01, 0xff, 0xff, 0xc0,
|
|
||||||
0x00, 0xff, 0xff, 0x80,
|
|
||||||
0x00, 0x7f, 0xff, 0x00,
|
|
||||||
0x00, 0x1f, 0xfc, 0x00,
|
|
||||||
0x00, 0x00, 0x80, 0x00,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// pana_sleep: 32x32
|
// pana_sleep: 32x32
|
||||||
static const unsigned char pana_sleep[] = {
|
static const unsigned char pana_sleep[] = {
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x80, 0x00, 0x00, 0x10, 0x88, 0x00, 0x00, 0x11, 0x88, 0xF0,
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x31, 0x8C, 0x20, 0x00, 0x39, 0xDC, 0x40, 0x00, 0x3B, 0xDC, 0xF0,
|
||||||
0x01, 0x00, 0x00, 0x80,
|
0x00, 0x7F, 0xFE, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0xF8, 0x3F, 0x00,
|
||||||
0x0f, 0xe0, 0x07, 0xf0,
|
0x00, 0xE7, 0xC7, 0x00, 0x00, 0xDF, 0xFB, 0x00, 0x01, 0xBF, 0xFD, 0x80,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xFF, 0xFF, 0xC0, 0x03, 0xC3, 0xC3, 0xC0, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFE, 0x7F, 0x80,
|
||||||
0x3f, 0xf8, 0x9f, 0xfc,
|
0x00, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x01, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xE0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8,
|
||||||
0x0f, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0x01, 0xc0, 0x78,
|
|
||||||
0x1f, 0xff, 0xff, 0xfc,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0xff, 0x7f, 0xf8,
|
|
||||||
0x0f, 0xfc, 0x1f, 0xf8,
|
|
||||||
0x0f, 0xff, 0x7f, 0xf8,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x01, 0xff, 0xff, 0xc0,
|
|
||||||
0x00, 0xff, 0xff, 0x80,
|
|
||||||
0x00, 0x7f, 0xff, 0x00,
|
|
||||||
0x00, 0x1f, 0xfc, 0x00,
|
|
||||||
0x00, 0x00, 0x80, 0x00,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// pana_dead: 32x32
|
// pana_dead: 32x32
|
||||||
static const unsigned char pana_dead[] = {
|
static const unsigned char pana_dead[] = {
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x80, 0x00, 0x00, 0x10, 0x88, 0x00, 0x00, 0x11, 0x88, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x31, 0x8C, 0x00, 0x00, 0x39, 0xDC, 0x00, 0x00, 0x3B, 0xDC, 0x00,
|
||||||
0x01, 0x00, 0x00, 0x80,
|
0x00, 0x7F, 0xFE, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0xF8, 0x3F, 0x00,
|
||||||
0x0f, 0xe0, 0x07, 0xf0,
|
0x00, 0xE7, 0xC7, 0x00, 0x00, 0xDF, 0xFB, 0x00, 0x01, 0xBF, 0xFD, 0x80,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xDB, 0xDB, 0xC0, 0x03, 0xE7, 0xE7, 0xC0, 0x03, 0xE7, 0xE7, 0xC0,
|
||||||
0x1f, 0xf0, 0x0f, 0xf8,
|
0x03, 0xDB, 0xDB, 0xC0, 0x01, 0xFD, 0xDF, 0x80, 0x01, 0xFA, 0xBF, 0x80,
|
||||||
0x3f, 0xf8, 0x9f, 0xfc,
|
0x00, 0xF7, 0x7F, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0xC0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x01, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xE0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x03, 0xFF, 0xFF, 0xE0, 0x07, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0,
|
||||||
0x1f, 0xff, 0xff, 0xf8,
|
0x0F, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xF8,
|
||||||
0x0f, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x0f, 0xbb, 0xee, 0xf8,
|
|
||||||
0x0f, 0xd7, 0xf5, 0xf8,
|
|
||||||
0x0f, 0xef, 0xfb, 0xf8,
|
|
||||||
0x0f, 0xd7, 0xf5, 0xf8,
|
|
||||||
0x1f, 0xbb, 0xee, 0xfc,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0xff, 0xff, 0xf8,
|
|
||||||
0x0f, 0xf0, 0x07, 0xf8,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x07, 0xff, 0xff, 0xf0,
|
|
||||||
0x03, 0xff, 0xff, 0xe0,
|
|
||||||
0x01, 0xff, 0xff, 0xc0,
|
|
||||||
0x00, 0xff, 0xff, 0x80,
|
|
||||||
0x00, 0x7f, 0xff, 0x00,
|
|
||||||
0x00, 0x1f, 0xfc, 0x00,
|
|
||||||
0x00, 0x00, 0x80, 0x00,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { EXP_IDLE, EXP_BLINK, EXP_HAPPY, EXP_SAD, EXP_EAT, EXP_SLEEP, EXP_DEAD, EXP_COUNT };
|
enum { EXP_IDLE, EXP_BLINK, EXP_HAPPY, EXP_SAD, EXP_EAT, EXP_SLEEP, EXP_DEAD, EXP_COUNT };
|
||||||
|
|
@ -552,276 +406,17 @@ static const unsigned char tmux_sleep[] = {
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
// custom_idle: 32x32
|
#define SPECIES_COUNT 3
|
||||||
static const unsigned char custom_idle[] = {
|
enum { SP_SHELL, SP_PANKO, SP_TMUX };
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
// custom_blink: 32x32
|
|
||||||
static const unsigned char custom_blink[] = {
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
// custom_happy: 32x32
|
|
||||||
static const unsigned char custom_happy[] = {
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
// custom_sad: 32x32
|
|
||||||
static const unsigned char custom_sad[] = {
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
// custom_eat: 32x32
|
|
||||||
static const unsigned char custom_eat[] = {
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
// custom_sleep: 32x32
|
|
||||||
static const unsigned char custom_sleep[] = {
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
// custom_dead: 32x32
|
|
||||||
static const unsigned char custom_dead[] = {
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SPECIES_COUNT 4
|
|
||||||
enum { SP_SHELL, SP_PANKO, SP_TMUX, SP_CUSTOM };
|
|
||||||
static const char *const species_names[SPECIES_COUNT] = {
|
static const char *const species_names[SPECIES_COUNT] = {
|
||||||
"Shell", "Panko", "Tmux", "Custom",
|
"Shell", "Panko", "Tmux",
|
||||||
};
|
};
|
||||||
static const unsigned char *const species_frames[SPECIES_COUNT][EXP_COUNT] = {
|
static const unsigned char *const species_frames[SPECIES_COUNT][EXP_COUNT] = {
|
||||||
{ shell_idle, shell_idle, shell_happy, shell_sad, shell_happy, shell_sleep, shell_sad },
|
{ shell_idle, shell_idle, shell_happy, shell_sad, shell_happy, shell_sleep, shell_sad },
|
||||||
{ pana_idle, pana_blink, pana_happy, pana_sad, pana_eat, pana_sleep, pana_dead },
|
{ pana_idle, pana_blink, pana_happy, pana_sad, pana_eat, pana_sleep, pana_dead },
|
||||||
{ tmux_idle, tmux_idle, tmux_happy, tmux_sad, tmux_happy, tmux_sleep, tmux_sad },
|
{ tmux_idle, tmux_idle, tmux_happy, tmux_sad, tmux_happy, tmux_sleep, tmux_sad },
|
||||||
{ custom_idle, custom_blink, custom_happy, custom_sad, custom_eat, custom_sleep, custom_dead },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Eye centre coords for custom species accessories (shades at user, headset at root).
|
|
||||||
// Pixel positions inside the 32x32 sprite -- adjust to match your art.
|
|
||||||
#define CUSTOM_EYE_CY 17 // eye centre row
|
|
||||||
#define CUSTOM_EYE_LX 11 // left eye centre column
|
|
||||||
#define CUSTOM_EYE_RX 21 // right eye centre column
|
|
||||||
|
|
||||||
// spr_grave: 32x32
|
// spr_grave: 32x32
|
||||||
static const unsigned char spr_grave[] = {
|
static const unsigned char spr_grave[] = {
|
||||||
0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
|
@ -1082,32 +677,12 @@ static const unsigned char icon_settings[] = {
|
||||||
0x03, 0xc0,
|
0x03, 0xc0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// icon_ir: 16x16
|
#define ICON_COUNT 7
|
||||||
static const unsigned char icon_ir[] = {
|
|
||||||
0x00, 0x00,
|
|
||||||
0x00, 0x00,
|
|
||||||
0x00, 0x80,
|
|
||||||
0x01, 0x40,
|
|
||||||
0x02, 0x20,
|
|
||||||
0x04, 0x10,
|
|
||||||
0x00, 0x80,
|
|
||||||
0x09, 0x48,
|
|
||||||
0x02, 0x20,
|
|
||||||
0x10, 0x84,
|
|
||||||
0x00, 0x80,
|
|
||||||
0x00, 0x80,
|
|
||||||
0x00, 0x80,
|
|
||||||
0x00, 0x80,
|
|
||||||
0x01, 0xc0,
|
|
||||||
0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ICON_COUNT 8
|
|
||||||
static const unsigned char *const menu_icons[ICON_COUNT] = {
|
static const unsigned char *const menu_icons[ICON_COUNT] = {
|
||||||
icon_feed, icon_play, icon_sleep, icon_clean, icon_heal, icon_stats, icon_settings, icon_ir,
|
icon_feed, icon_play, icon_sleep, icon_clean, icon_heal, icon_stats, icon_settings,
|
||||||
};
|
};
|
||||||
static const char *const menu_labels[ICON_COUNT] = {
|
static const char *const menu_labels[ICON_COUNT] = {
|
||||||
"FEED", "PLAY", "SLEEP", "CLEAN", "HEAL", "STATS", "SETTINGS", "IR BEAM",
|
"FEED", "PLAY", "SLEEP", "CLEAN", "HEAL", "STATS", "SETTINGS",
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _SPRITES_H
|
#endif // _SPRITES_H
|
||||||
|
|
|
||||||
|
|
@ -52,45 +52,6 @@ def from_art(art):
|
||||||
g.append([1 if c in "#X1" else 0 for c in row.ljust(w)])
|
g.append([1 if c in "#X1" else 0 for c in row.ljust(w)])
|
||||||
return g
|
return g
|
||||||
|
|
||||||
def from_png(path, threshold=128):
|
|
||||||
"""Load a PNG and convert to a 1-bpp grid (white/bright=lit, dark=off).
|
|
||||||
Width is padded to the nearest multiple of 8. Requires Pillow: pip install pillow"""
|
|
||||||
try:
|
|
||||||
from PIL import Image
|
|
||||||
except ImportError:
|
|
||||||
raise SystemExit("from_png() needs Pillow: pip install pillow")
|
|
||||||
img = Image.open(path).convert("L")
|
|
||||||
w, h = img.size
|
|
||||||
wp = (w + 7) // 8 * 8
|
|
||||||
g = []
|
|
||||||
for y in range(h):
|
|
||||||
row = [1 if x < w and img.getpixel((x, y)) > threshold else 0 for x in range(wp)]
|
|
||||||
g.append(row)
|
|
||||||
return g
|
|
||||||
|
|
||||||
def load_species_pngs(prefix, aliases=None):
|
|
||||||
"""Load the 7 expression PNGs for a species from <prefix>_<expr>.png files.
|
|
||||||
Missing files fall back automatically (blink->idle, eat->happy, dead->sad).
|
|
||||||
Pass aliases={"blink": "idle"} to force a specific reuse.
|
|
||||||
Only custom_idle.png is strictly required; others are optional."""
|
|
||||||
import os
|
|
||||||
EXPRS = ["idle", "blink", "happy", "sad", "eat", "sleep", "dead"]
|
|
||||||
FALLBACK = { "blink": "idle", "eat": "happy", "dead": "sad"}
|
|
||||||
if aliases:
|
|
||||||
FALLBACK.update(aliases)
|
|
||||||
cache = {}
|
|
||||||
result = []
|
|
||||||
for name in EXPRS:
|
|
||||||
src = FALLBACK.get(name, name)
|
|
||||||
if src not in cache:
|
|
||||||
path = f"{prefix}_{src}.png"
|
|
||||||
if not os.path.exists(path):
|
|
||||||
path = f"{prefix}_idle.png"
|
|
||||||
cache[src] = from_png(path)
|
|
||||||
cache[name] = cache[src]
|
|
||||||
result.append(cache[name])
|
|
||||||
return result
|
|
||||||
|
|
||||||
def pack(g):
|
def pack(g):
|
||||||
"""Pack to bytes, MSB first, width padded to multiple of 8."""
|
"""Pack to bytes, MSB first, width padded to multiple of 8."""
|
||||||
h = len(g)
|
h = len(g)
|
||||||
|
|
@ -434,23 +395,6 @@ def icon_settings(): # gear
|
||||||
fill_ellipse(g, 8, 8, 2, 2, 0) # hub hole
|
fill_ellipse(g, 8, 8, 2, 2, 0) # hub hole
|
||||||
return g
|
return g
|
||||||
|
|
||||||
def icon_ir(): # wifi-style arcs = IR signal
|
|
||||||
g = grid(16, 16)
|
|
||||||
vline(g, 8, 9, 14) # antenna stem
|
|
||||||
px(g, 7, 14); px(g, 9, 14) # base feet
|
|
||||||
# inner arc
|
|
||||||
px(g, 6, 8); px(g, 10, 8)
|
|
||||||
px(g, 7, 7); px(g, 9, 7)
|
|
||||||
px(g, 8, 6)
|
|
||||||
# outer arc
|
|
||||||
px(g, 3, 9); px(g, 13, 9)
|
|
||||||
px(g, 4, 7); px(g, 12, 7)
|
|
||||||
px(g, 5, 5); px(g, 11, 5)
|
|
||||||
px(g, 6, 4); px(g, 10, 4)
|
|
||||||
px(g, 7, 3); px(g, 9, 3)
|
|
||||||
px(g, 8, 2)
|
|
||||||
return g
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------- main
|
# ---------------------------------------------------------------- main
|
||||||
def main():
|
def main():
|
||||||
print("// Auto-generated by sprites_gen.py -- do not edit by hand.")
|
print("// Auto-generated by sprites_gen.py -- do not edit by hand.")
|
||||||
|
|
@ -498,46 +442,17 @@ def main():
|
||||||
|
|
||||||
# species frame tables, indexed [species][EXP_*]. Shell/Tmux reuse their
|
# species frame tables, indexed [species][EXP_*]. Shell/Tmux reuse their
|
||||||
# four frames across the seven expression slots.
|
# four frames across the seven expression slots.
|
||||||
#
|
print("#define SPECIES_COUNT 3")
|
||||||
# Custom species: drop PNG files named custom_<expr>.png (32x32, white=lit)
|
print("enum { SP_SHELL, SP_PANKO, SP_TMUX };")
|
||||||
# next to this script and re-run it. Required: custom_idle.png.
|
|
||||||
# Optional (fall back automatically): custom_blink, custom_happy, custom_sad,
|
|
||||||
# custom_eat, custom_sleep, custom_dead.
|
|
||||||
import os
|
|
||||||
has_custom = os.path.exists("custom_idle.png")
|
|
||||||
if has_custom:
|
|
||||||
EXPRS = ["idle", "blink", "happy", "sad", "eat", "sleep", "dead"]
|
|
||||||
custom_frames = load_species_pngs("custom")
|
|
||||||
for name, g in zip(EXPRS, custom_frames):
|
|
||||||
emit(f"custom_{name}", g)
|
|
||||||
|
|
||||||
species_count = 4 if has_custom else 3
|
|
||||||
print(f"#define SPECIES_COUNT {species_count}")
|
|
||||||
enum_vals = "SP_SHELL, SP_PANKO, SP_TMUX" + (", SP_CUSTOM" if has_custom else "")
|
|
||||||
print(f"enum {{ {enum_vals} }};")
|
|
||||||
print("static const char *const species_names[SPECIES_COUNT] = {")
|
print("static const char *const species_names[SPECIES_COUNT] = {")
|
||||||
if has_custom:
|
|
||||||
print(' "Shell", "Panko", "Tmux", "Custom",')
|
|
||||||
else:
|
|
||||||
print(' "Shell", "Panko", "Tmux",')
|
print(' "Shell", "Panko", "Tmux",')
|
||||||
print("};")
|
print("};")
|
||||||
print("static const unsigned char *const species_frames[SPECIES_COUNT][EXP_COUNT] = {")
|
print("static const unsigned char *const species_frames[SPECIES_COUNT][EXP_COUNT] = {")
|
||||||
print(" { shell_idle, shell_idle, shell_happy, shell_sad, shell_happy, shell_sleep, shell_sad },")
|
print(" { shell_idle, shell_idle, shell_happy, shell_sad, shell_happy, shell_sleep, shell_sad },")
|
||||||
print(" { pana_idle, pana_blink, pana_happy, pana_sad, pana_eat, pana_sleep, pana_dead },")
|
print(" { pana_idle, pana_blink, pana_happy, pana_sad, pana_eat, pana_sleep, pana_dead },")
|
||||||
print(" { tmux_idle, tmux_idle, tmux_happy, tmux_sad, tmux_happy, tmux_sleep, tmux_sad },")
|
print(" { tmux_idle, tmux_idle, tmux_happy, tmux_sad, tmux_happy, tmux_sleep, tmux_sad },")
|
||||||
if has_custom:
|
|
||||||
print(" { custom_idle, custom_blink, custom_happy, custom_sad, custom_eat, custom_sleep, custom_dead },")
|
|
||||||
print("};")
|
print("};")
|
||||||
print()
|
print()
|
||||||
if has_custom:
|
|
||||||
# Emit eye geometry as defines so tama.c can extend its arrays.
|
|
||||||
# Edit these pixel coords to match where your character's eyes sit.
|
|
||||||
print("// Eye centre coords for custom species accessories (shades at user, headset at root).")
|
|
||||||
print("// Pixel positions inside the 32x32 sprite -- adjust to match your art.")
|
|
||||||
print("#define CUSTOM_EYE_CY 17 // eye centre row")
|
|
||||||
print("#define CUSTOM_EYE_LX 11 // left eye centre column")
|
|
||||||
print("#define CUSTOM_EYE_RX 21 // right eye centre column")
|
|
||||||
print()
|
|
||||||
|
|
||||||
emit("spr_grave", grave())
|
emit("spr_grave", grave())
|
||||||
emit("spr_poop", poop())
|
emit("spr_poop", poop())
|
||||||
|
|
@ -552,17 +467,16 @@ def main():
|
||||||
("icon_heal", icon_heal()),
|
("icon_heal", icon_heal()),
|
||||||
("icon_stats", icon_stats()),
|
("icon_stats", icon_stats()),
|
||||||
("icon_settings", icon_settings()),
|
("icon_settings", icon_settings()),
|
||||||
("icon_ir", icon_ir()),
|
|
||||||
]
|
]
|
||||||
for n, g in icons:
|
for n, g in icons:
|
||||||
emit(n, g)
|
emit(n, g)
|
||||||
|
|
||||||
print("#define ICON_COUNT 8")
|
print("#define ICON_COUNT 7")
|
||||||
print("static const unsigned char *const menu_icons[ICON_COUNT] = {")
|
print("static const unsigned char *const menu_icons[ICON_COUNT] = {")
|
||||||
print(" icon_feed, icon_play, icon_sleep, icon_clean, icon_heal, icon_stats, icon_settings, icon_ir,")
|
print(" icon_feed, icon_play, icon_sleep, icon_clean, icon_heal, icon_stats, icon_settings,")
|
||||||
print("};")
|
print("};")
|
||||||
print('static const char *const menu_labels[ICON_COUNT] = {')
|
print('static const char *const menu_labels[ICON_COUNT] = {')
|
||||||
print(' "FEED", "PLAY", "SLEEP", "CLEAN", "HEAL", "STATS", "SETTINGS", "IR BEAM",')
|
print(' "FEED", "PLAY", "SLEEP", "CLEAN", "HEAL", "STATS", "SETTINGS",')
|
||||||
print("};")
|
print("};")
|
||||||
print()
|
print()
|
||||||
print("#endif // _SPRITES_H")
|
print("#endif // _SPRITES_H")
|
||||||
|
|
|
||||||
345
tama.c
|
|
@ -32,9 +32,6 @@
|
||||||
#define BTN_RIGHT PD5
|
#define BTN_RIGHT PD5
|
||||||
#define BTN_LEFT PD6
|
#define BTN_LEFT PD6
|
||||||
#define BUZZER PD2
|
#define BUZZER PD2
|
||||||
#define IR_TX PD4 // IR LED, bit-bang 38 kHz carrier
|
|
||||||
#define IR_RX PD3 // IR demodulator data (active-low)
|
|
||||||
#define IR_RX_VCC PC4 // IR receiver power gate (high = on)
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------- tuning
|
// ----------------------------------------------------------------- tuning
|
||||||
#define FRAME_MS 60 // main loop period
|
#define FRAME_MS 60 // main loop period
|
||||||
|
|
@ -46,7 +43,7 @@
|
||||||
// a need empties in ~20 min, after which health bleeds down over ~10 more.
|
// a need empties in ~20 min, after which health bleeds down over ~10 more.
|
||||||
|
|
||||||
// ----------------------------------------------------------------- state
|
// ----------------------------------------------------------------- state
|
||||||
enum { SCR_SELECT, SCR_MAIN, SCR_STATS, SCR_DEAD, SCR_SETTINGS, SCR_IR, SCR_TV };
|
enum { SCR_SELECT, SCR_MAIN, SCR_STATS, SCR_DEAD, SCR_SETTINGS };
|
||||||
|
|
||||||
// notification thresholds: gentle chirp at <50%, urgent at <20%
|
// notification thresholds: gentle chirp at <50%, urgent at <20%
|
||||||
enum { N_FOOD, N_FUN, N_ENGY, N_WASH, N_COUNT };
|
enum { N_FOOD, N_FUN, N_ENGY, N_WASH, N_COUNT };
|
||||||
|
|
@ -110,47 +107,6 @@ static const char *const screen_timeout_names[] = { "5s", "10s", "15s", "30s", "
|
||||||
#define TIMEOUT_OPT_COUNT 6
|
#define TIMEOUT_OPT_COUNT 6
|
||||||
#define SETTINGS_ROWS 4 // screen-off, brightness, sound, back
|
#define SETTINGS_ROWS 4 // screen-off, brightness, sound, back
|
||||||
|
|
||||||
// IR screen state
|
|
||||||
// Modes: 0=ZAPP (send ping), 1=RECV (listen), 2=BEAM (send pet data), 3=TV remote, 4=BACK
|
|
||||||
enum { IR_ZAPP, IR_RECV, IR_BEAM, IR_TV, IR_BACK, IR_MODE_COUNT };
|
|
||||||
static uint8_t ir_mode = 0;
|
|
||||||
// ir_status: 0=idle, 1=sent, 2=got signal, 3=nothing, 4=waiting
|
|
||||||
static uint8_t ir_status = 0;
|
|
||||||
static uint8_t ir_stimer = 0; // frames to show a status message
|
|
||||||
|
|
||||||
// NEC protocol constants for badge-to-badge comms
|
|
||||||
#define PANA_ADDR 0x50 // 'P' for Panagotchi
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------- TV remote
|
|
||||||
// Commands fired at every brand in sequence; TV responds to whichever matches.
|
|
||||||
// Order must match tv_cmd_names[]. BACK is a navigation item, not an IR code.
|
|
||||||
#define TV_CMD_COUNT 6
|
|
||||||
#define TV_NAV_COUNT 7 // TV_CMD_COUNT + 1 (BACK entry)
|
|
||||||
#define TV_BRAND_COUNT 5
|
|
||||||
|
|
||||||
static const char *const tv_cmd_names[TV_NAV_COUNT] = {
|
|
||||||
"POWER", "VOL+", "VOL-", "MUTE", "CH+", "CH-", "BACK",
|
|
||||||
};
|
|
||||||
|
|
||||||
// NEC addresses per brand
|
|
||||||
static const uint8_t tv_addr[TV_BRAND_COUNT] = {
|
|
||||||
0x04, // LG
|
|
||||||
0x40, // Panasonic
|
|
||||||
0xA5, // Pioneer
|
|
||||||
0x05, // Mitsubishi
|
|
||||||
0x00, // Generic / CHUNGHWA
|
|
||||||
};
|
|
||||||
// Commands: [brand][POWER, VOL+, VOL-, MUTE, CH+, CH-]
|
|
||||||
static const uint8_t tv_cmds[TV_BRAND_COUNT][TV_CMD_COUNT] = {
|
|
||||||
{ 0x08, 0x02, 0x03, 0x09, 0x00, 0x01 }, // LG
|
|
||||||
{ 0x3D, 0x20, 0x21, 0x04, 0x34, 0x35 }, // Panasonic
|
|
||||||
{ 0x1C, 0x14, 0x15, 0x19, 0x01, 0x02 }, // Pioneer
|
|
||||||
{ 0x18, 0x07, 0x0B, 0x0D, 0x02, 0x06 }, // Mitsubishi
|
|
||||||
{ 0x45, 0x46, 0x15, 0x09, 0x09, 0x15 }, // Generic
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint8_t tv_cmd = 0; // currently selected command (0..TV_NAV_COUNT-1)
|
|
||||||
|
|
||||||
// display power state
|
// display power state
|
||||||
static uint8_t disp_on = 1;
|
static uint8_t disp_on = 1;
|
||||||
static uint32_t idle_frames = 0; // frames since the last button activity
|
static uint32_t idle_frames = 0; // frames since the last button activity
|
||||||
|
|
@ -159,7 +115,7 @@ static uint32_t idle_frames = 0; // frames since the last button activity
|
||||||
static uint32_t reset_cause = 0;
|
static uint32_t reset_cause = 0;
|
||||||
|
|
||||||
// menu icon order must match sprites.h menu_icons[] / menu_labels[]
|
// menu icon order must match sprites.h menu_icons[] / menu_labels[]
|
||||||
enum { ACT_FEED, ACT_PLAY, ACT_SLEEP, ACT_CLEAN, ACT_HEAL, ACT_STATS, ACT_SETTINGS, ACT_IR };
|
enum { ACT_FEED, ACT_PLAY, ACT_SLEEP, ACT_CLEAN, ACT_HEAL, ACT_STATS, ACT_SETTINGS };
|
||||||
|
|
||||||
// ----------------------------------------------------------------- helpers
|
// ----------------------------------------------------------------- helpers
|
||||||
static int8_t clamp100(int v) { return v < 0 ? 0 : (v > 100 ? 100 : v); }
|
static int8_t clamp100(int v) { return v < 0 ? 0 : (v > 100 ? 100 : v); }
|
||||||
|
|
@ -200,90 +156,6 @@ static void led_blink(void)
|
||||||
funDigitalWrite(LED_PIN, FUN_LOW);
|
funDigitalWrite(LED_PIN, FUN_LOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------- IR TX
|
|
||||||
// Bit-bang 38 kHz carrier on IR_TX for `us` microseconds (mark).
|
|
||||||
static void ir_mark(uint16_t us)
|
|
||||||
{
|
|
||||||
// half-period ≈ 13 µs → cycles ≈ us/26
|
|
||||||
uint16_t cycles = us / 26u + 1u;
|
|
||||||
for (uint16_t i = 0; i < cycles; i++) {
|
|
||||||
funDigitalWrite(IR_TX, FUN_HIGH);
|
|
||||||
Delay_Us(13);
|
|
||||||
funDigitalWrite(IR_TX, FUN_LOW);
|
|
||||||
Delay_Us(13);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ir_space(uint16_t us)
|
|
||||||
{
|
|
||||||
funDigitalWrite(IR_TX, FUN_LOW);
|
|
||||||
Delay_Us(us);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send one NEC frame: 9ms mark + 4.5ms space + 32 bits + stop.
|
|
||||||
static void ir_send_nec(uint8_t addr, uint8_t cmd)
|
|
||||||
{
|
|
||||||
ir_mark(9000); ir_space(4500);
|
|
||||||
uint8_t bytes[4] = { addr, (uint8_t)~addr, cmd, (uint8_t)~cmd };
|
|
||||||
for (uint8_t b = 0; b < 4; b++) {
|
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
|
||||||
ir_mark(562);
|
|
||||||
ir_space((bytes[b] & 1) ? 1687u : 562u);
|
|
||||||
bytes[b] >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ir_mark(562);
|
|
||||||
funDigitalWrite(IR_TX, FUN_LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------- IR RX
|
|
||||||
// Wait up to `wait_us` for IR_RX to reach `lvl`, then measure how long it
|
|
||||||
// holds (up to `hold_us`). Returns ~duration in µs (10 µs resolution), or 0
|
|
||||||
// on timeout. The IR demodulator output is active-low: LOW = carrier present.
|
|
||||||
static uint32_t ir_pulse(uint8_t lvl, uint32_t wait_us, uint32_t hold_us)
|
|
||||||
{
|
|
||||||
uint32_t t;
|
|
||||||
for (t = 0; t < wait_us; t += 10) {
|
|
||||||
if ((uint8_t)funDigitalRead(IR_RX) == lvl) goto measure;
|
|
||||||
Delay_Us(10);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
measure:;
|
|
||||||
for (t = 0; t < hold_us; t += 10) {
|
|
||||||
if ((uint8_t)funDigitalRead(IR_RX) != lvl) return t;
|
|
||||||
Delay_Us(10);
|
|
||||||
}
|
|
||||||
return 0; // pulse held too long
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to receive one NEC frame. Blocks up to ~2 s waiting for the leader.
|
|
||||||
// Returns 1 on a valid frame, 0 otherwise. addr/cmd are set on success.
|
|
||||||
static uint8_t ir_recv_nec(uint8_t *addr_out, uint8_t *cmd_out)
|
|
||||||
{
|
|
||||||
// leader mark ~9000 µs LOW; wait up to 2 s
|
|
||||||
uint32_t t = ir_pulse(FUN_LOW, 2000000u, 12000u);
|
|
||||||
if (t < 7000u) return 0;
|
|
||||||
|
|
||||||
// leader space ~4500 µs HIGH
|
|
||||||
t = ir_pulse(FUN_HIGH, 8000u, 6000u);
|
|
||||||
if (t < 3000u) return 0;
|
|
||||||
|
|
||||||
uint32_t bits = 0;
|
|
||||||
for (uint8_t i = 0; i < 32; i++) {
|
|
||||||
if (!ir_pulse(FUN_LOW, 1500u, 1500u)) return 0;
|
|
||||||
t = ir_pulse(FUN_HIGH, 2500u, 2500u);
|
|
||||||
if (!t) return 0;
|
|
||||||
bits >>= 1;
|
|
||||||
if (t > 1000u) bits |= 0x80000000UL;
|
|
||||||
}
|
|
||||||
ir_pulse(FUN_LOW, 1500u, 1500u); // stop bit
|
|
||||||
|
|
||||||
*addr_out = (uint8_t)(bits & 0xFF);
|
|
||||||
*cmd_out = (uint8_t)((bits >> 16) & 0xFF);
|
|
||||||
return ((bits >> 8) & 0xFF) == (uint8_t)(~*addr_out) &&
|
|
||||||
((bits >> 24) & 0xFF) == (uint8_t)(~*cmd_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
// push the currently selected brightness to the panel's contrast register
|
// push the currently selected brightness to the panel's contrast register
|
||||||
static void apply_brightness(void)
|
static void apply_brightness(void)
|
||||||
{
|
{
|
||||||
|
|
@ -443,14 +315,6 @@ static void do_action(uint8_t act)
|
||||||
set_sel = 0;
|
set_sel = 0;
|
||||||
beep(659, 40);
|
beep(659, 40);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACT_IR:
|
|
||||||
screen = SCR_IR;
|
|
||||||
ir_mode = 0;
|
|
||||||
ir_status = 0;
|
|
||||||
ir_stimer = 0;
|
|
||||||
beep(659, 40);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -514,27 +378,19 @@ static void draw_center_str(int y, const char *s)
|
||||||
|
|
||||||
static void draw_menu_bar(void)
|
static void draw_menu_bar(void)
|
||||||
{
|
{
|
||||||
// 8 icons across the bottom (16 px slots, no gap), selected one boxed
|
// 7 icons across the bottom (18 px slots), selected one boxed
|
||||||
for (uint8_t i = 0; i < ICON_COUNT; i++) {
|
for (uint8_t i = 0; i < ICON_COUNT; i++) {
|
||||||
int x = i * 16;
|
int x = i * 18 + 1;
|
||||||
ssd1306_drawImage(x, 48, menu_icons[i], ICON_W, ICON_H, 0);
|
ssd1306_drawImage(x, 48, menu_icons[i], ICON_W, ICON_H, 0);
|
||||||
if (i == cursor)
|
if (i == cursor)
|
||||||
ssd1306_drawRect(x, 47, ICON_W, ICON_H + 1, 1);
|
ssd1306_drawRect(x - 1, 47, ICON_W + 1, ICON_H + 1, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Per-species eye geometry for accessory rendering (shades at user, headset at root).
|
// Per-species eye geometry, so the shades land on the right spot.
|
||||||
// When a custom species is present, sprites.h emits CUSTOM_EYE_* defines; update
|
static const uint8_t eye_cy[SPECIES_COUNT] = { 11, 17, 12 }; // SP_SHELL,PANKO,TMUX
|
||||||
// those values to match where the eyes sit in your custom_idle.png.
|
|
||||||
#ifdef CUSTOM_EYE_CY
|
|
||||||
static const uint8_t eye_cy[SPECIES_COUNT] = { 11, 17, 12, CUSTOM_EYE_CY };
|
|
||||||
static const uint8_t eye_lx[SPECIES_COUNT] = { 11, 11, 9, CUSTOM_EYE_LX };
|
|
||||||
static const uint8_t eye_rx[SPECIES_COUNT] = { 20, 21, 23, CUSTOM_EYE_RX };
|
|
||||||
#else
|
|
||||||
static const uint8_t eye_cy[SPECIES_COUNT] = { 11, 17, 12 };
|
|
||||||
static const uint8_t eye_lx[SPECIES_COUNT] = { 11, 11, 9 };
|
static const uint8_t eye_lx[SPECIES_COUNT] = { 11, 11, 9 };
|
||||||
static const uint8_t eye_rx[SPECIES_COUNT] = { 20, 21, 23 };
|
static const uint8_t eye_rx[SPECIES_COUNT] = { 20, 21, 23 };
|
||||||
#endif
|
|
||||||
|
|
||||||
// Hacker garb earned as the pet levels up, drawn dark over the lit body so it
|
// Hacker garb earned as the pet levels up, drawn dark over the lit body so it
|
||||||
// reads as black gear.
|
// reads as black gear.
|
||||||
|
|
@ -723,144 +579,6 @@ static void draw_settings(void)
|
||||||
ssd1306_refresh();
|
ssd1306_refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const ir_mode_names[IR_MODE_COUNT] = {
|
|
||||||
"ZAPP", "RECV", "BEAM", "TV", "BACK",
|
|
||||||
};
|
|
||||||
|
|
||||||
static void draw_ir(void)
|
|
||||||
{
|
|
||||||
ssd1306_setbuf(0);
|
|
||||||
draw_center_str(0, "~ IR BEAM ~");
|
|
||||||
ssd1306_drawFastHLine(0, 10, SSD1306_W, 1);
|
|
||||||
|
|
||||||
// mode selector with arrows
|
|
||||||
char mline[17], *p = mline;
|
|
||||||
p = s_cat(p, "< ");
|
|
||||||
p = s_cat(p, ir_mode_names[ir_mode]);
|
|
||||||
p = s_cat(p, " >");
|
|
||||||
*p = 0;
|
|
||||||
draw_center_str(20, mline);
|
|
||||||
|
|
||||||
// status line
|
|
||||||
if (ir_stimer) {
|
|
||||||
const char *msg = "?";
|
|
||||||
if (ir_status == 1) msg = "SENT!";
|
|
||||||
else if (ir_status == 2) msg = "GOT IT! +xp";
|
|
||||||
else if (ir_status == 3) msg = "NOTHING";
|
|
||||||
else if (ir_status == 4) msg = "WAITING...";
|
|
||||||
draw_center_str(35, msg);
|
|
||||||
} else {
|
|
||||||
const char *hint = "?";
|
|
||||||
if (ir_mode == IR_ZAPP) hint = "OK: send ping";
|
|
||||||
else if (ir_mode == IR_RECV) hint = "OK: listen 2s";
|
|
||||||
else if (ir_mode == IR_BEAM) hint = "OK: beam pet";
|
|
||||||
else if (ir_mode == IR_TV) hint = "OK: enter";
|
|
||||||
else if (ir_mode == IR_BACK) hint = "OK: back";
|
|
||||||
draw_center_str(35, hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_center_str(56, "L/R mode OK go");
|
|
||||||
ssd1306_refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when OK is pressed on the IR screen.
|
|
||||||
static void do_ir_action(void)
|
|
||||||
{
|
|
||||||
if (ir_mode == IR_BACK) {
|
|
||||||
screen = SCR_MAIN;
|
|
||||||
beep(880, 30);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ir_mode == IR_ZAPP) {
|
|
||||||
ir_send_nec(PANA_ADDR, 0x01); // fixed ping command
|
|
||||||
beep(988, 40); beep(1319, 60);
|
|
||||||
led_blink();
|
|
||||||
ir_status = 1; ir_stimer = 30;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ir_mode == IR_BEAM) {
|
|
||||||
// encode species in high nibble, level in low nibble
|
|
||||||
uint8_t cmd = (uint8_t)((pet.species << 4) | (pet.level & 0x0F));
|
|
||||||
ir_send_nec(PANA_ADDR, cmd);
|
|
||||||
beep(880, 40); beep(1175, 40); beep(1568, 70);
|
|
||||||
led_blink();
|
|
||||||
ir_status = 1; ir_stimer = 30;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ir_mode == IR_TV) {
|
|
||||||
screen = SCR_TV;
|
|
||||||
tv_cmd = 0;
|
|
||||||
beep(659, 40);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ir_mode == IR_RECV) {
|
|
||||||
// show WAITING... while blocking
|
|
||||||
ir_status = 4; ir_stimer = 255;
|
|
||||||
draw_ir();
|
|
||||||
|
|
||||||
// power up the IR receiver
|
|
||||||
funDigitalWrite(IR_RX_VCC, FUN_HIGH);
|
|
||||||
Delay_Ms(5); // stabilise
|
|
||||||
|
|
||||||
uint8_t addr = 0, cmd = 0;
|
|
||||||
uint8_t ok = ir_recv_nec(&addr, &cmd);
|
|
||||||
|
|
||||||
funDigitalWrite(IR_RX_VCC, FUN_LOW); // power down receiver
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
if (addr == PANA_ADDR) {
|
|
||||||
if (cmd == 0x01) {
|
|
||||||
// ping from another badge: mood boost
|
|
||||||
pet.happy = clamp100(pet.happy + 10);
|
|
||||||
add_xp(5);
|
|
||||||
} else {
|
|
||||||
// BEAM packet: species in high nibble, level in low nibble
|
|
||||||
uint8_t lvl = cmd & 0x0F;
|
|
||||||
if (lvl > pet.level) add_xp(8); else add_xp(3);
|
|
||||||
pet.happy = clamp100(pet.happy + 8);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// generic NEC signal: small happy bump
|
|
||||||
pet.happy = clamp100(pet.happy + 3);
|
|
||||||
}
|
|
||||||
beep(784, 60); beep(988, 60); beep(1175, 90);
|
|
||||||
led_blink();
|
|
||||||
ir_status = 2;
|
|
||||||
} else {
|
|
||||||
beep(330, 150);
|
|
||||||
ir_status = 3;
|
|
||||||
}
|
|
||||||
ir_stimer = 45;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void draw_tv(void)
|
|
||||||
{
|
|
||||||
ssd1306_setbuf(0);
|
|
||||||
draw_center_str(0, "~ TV REMOTE ~");
|
|
||||||
ssd1306_drawFastHLine(0, 10, SSD1306_W, 1);
|
|
||||||
|
|
||||||
// command selector
|
|
||||||
char line[17], *p = line;
|
|
||||||
p = s_cat(p, "< ");
|
|
||||||
p = s_cat(p, tv_cmd_names[tv_cmd]);
|
|
||||||
p = s_cat(p, " >");
|
|
||||||
*p = 0;
|
|
||||||
draw_center_str(22, line);
|
|
||||||
|
|
||||||
if (tv_cmd < TV_CMD_COUNT)
|
|
||||||
draw_center_str(36, "blast 5 brands");
|
|
||||||
// BACK entry has no sub-label
|
|
||||||
|
|
||||||
ssd1306_drawFastHLine(0, 46, SSD1306_W, 1);
|
|
||||||
draw_center_str(50, "L/R cmd OK go");
|
|
||||||
ssd1306_refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------- input
|
// ----------------------------------------------------------------- input
|
||||||
typedef struct { uint8_t ok, left, right; } Buttons;
|
typedef struct { uint8_t ok, left, right; } Buttons;
|
||||||
|
|
||||||
|
|
@ -967,15 +685,6 @@ static void gpio_setup(void)
|
||||||
funPinMode(BUZZER, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP);
|
funPinMode(BUZZER, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP);
|
||||||
funDigitalWrite(BUZZER, FUN_LOW);
|
funDigitalWrite(BUZZER, FUN_LOW);
|
||||||
|
|
||||||
funPinMode(IR_TX, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP);
|
|
||||||
funDigitalWrite(IR_TX, FUN_LOW);
|
|
||||||
|
|
||||||
funPinMode(IR_RX_VCC, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP);
|
|
||||||
funDigitalWrite(IR_RX_VCC, FUN_LOW); // receiver off at boot
|
|
||||||
|
|
||||||
funPinMode(IR_RX, GPIO_CNF_IN_PUPD);
|
|
||||||
funDigitalWrite(IR_RX, FUN_HIGH); // pull-up (idle = HIGH)
|
|
||||||
|
|
||||||
// buttons: input with internal pull-up (ODR high selects pull-up)
|
// buttons: input with internal pull-up (ODR high selects pull-up)
|
||||||
funPinMode(BTN_OK, GPIO_CNF_IN_PUPD);
|
funPinMode(BTN_OK, GPIO_CNF_IN_PUPD);
|
||||||
funPinMode(BTN_LEFT, GPIO_CNF_IN_PUPD);
|
funPinMode(BTN_LEFT, GPIO_CNF_IN_PUPD);
|
||||||
|
|
@ -1142,46 +851,6 @@ int main(void)
|
||||||
}
|
}
|
||||||
draw_dead();
|
draw_dead();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCR_IR:
|
|
||||||
if (left_edge) {
|
|
||||||
ir_mode = (ir_mode + IR_MODE_COUNT - 1) % IR_MODE_COUNT;
|
|
||||||
beep(1047, 25);
|
|
||||||
}
|
|
||||||
if (right_edge) {
|
|
||||||
ir_mode = (ir_mode + 1) % IR_MODE_COUNT;
|
|
||||||
beep(1047, 25);
|
|
||||||
}
|
|
||||||
if (ok_edge) do_ir_action();
|
|
||||||
if (ir_stimer) ir_stimer--;
|
|
||||||
draw_ir();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SCR_TV:
|
|
||||||
if (left_edge) {
|
|
||||||
tv_cmd = (tv_cmd + TV_NAV_COUNT - 1) % TV_NAV_COUNT;
|
|
||||||
beep(1047, 25);
|
|
||||||
}
|
|
||||||
if (right_edge) {
|
|
||||||
tv_cmd = (tv_cmd + 1) % TV_NAV_COUNT;
|
|
||||||
beep(1047, 25);
|
|
||||||
}
|
|
||||||
if (ok_edge) {
|
|
||||||
if (tv_cmd == TV_CMD_COUNT) { // BACK entry
|
|
||||||
screen = SCR_IR;
|
|
||||||
beep(880, 30);
|
|
||||||
} else {
|
|
||||||
// blast selected command at all brands in turn
|
|
||||||
for (uint8_t b = 0; b < TV_BRAND_COUNT; b++) {
|
|
||||||
ir_send_nec(tv_addr[b], tv_cmds[b][tv_cmd]);
|
|
||||||
Delay_Ms(40); // inter-frame gap
|
|
||||||
}
|
|
||||||
beep(988, 40); beep(1319, 80);
|
|
||||||
led_blink();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
draw_tv();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sleep the display after the configured idle period
|
// sleep the display after the configured idle period
|
||||||
|
|
|
||||||