68 Commits

Author SHA1 Message Date
david 3d17e7bab9 refactor(DisplayManager): add hitTestRaw() and remove touch coordinate filtering 2026-02-22 18:43:54 -08:00
david 4030048401 refactor(AGENTS.md): update docs for simplified build commands and board config 2026-02-22 03:19:05 -08:00
david 72636f9ecf feat(Display): Improve touch handling and visual feedback 2026-02-20 04:10:39 -08:00
david 688b1905e5 refactor(DisplayDriverTFT): simplify alert rendering logic for better performance 2026-02-20 03:02:13 -08:00
david b68d36bb85 perf(DisplayDriverTFT): Throttle alert redraws to prevent tearing 2026-02-20 02:41:04 -08:00
david ba8789797c feat: add status screen with system info display 2026-02-20 02:11:21 -08:00
david fd04dea8dd fix(DisplayDriverTFT): right-align WiFi indicator in header 2026-02-20 00:49:57 -08:00
david 9c8f67dccb refactor: improve touch handling and UI element detection 2026-02-20 00:36:29 -08:00
david 6d51234f21 docs: Add justfile for build automation 2026-02-19 23:10:55 -08:00
david 66f09c3468 feat(boards): add device detection and text scrolling support 2026-02-19 21:46:33 -08:00
david 913373ca72 fix(build): change --libraries flag to --library in board configs 2026-02-19 20:48:56 -08:00
david f367bd365b refactor(doorbell): check hold completion before release handling 2026-02-19 19:22:49 -08:00
david dfd511e499 feat: add session notes for 2026-02-19 with build, code, and integration updates 2026-02-19 17:52:08 -08:00
david 4da32466a8 feat: add debug touch drawing function and fix dashboard layout calculations 2026-02-19 17:48:39 -08:00
david a3164d722e docs(AGENTS.md): Update RTK section and add style system docs 2026-02-19 15:38:02 -08:00
david dd1c13fbbc refactor: replace hint feedback with debug crosshair 2026-02-19 15:12:02 -08:00
david ec8ec4cd18 refactor(Style): Add font abstraction and CSS-like styling constants 2026-02-19 14:48:25 -08:00
david 59b2bf01b7 feat(docs): update LSP setup instructions for compile_commands.json 2026-02-19 13:22:19 -08:00
david 3dc66a536e feat(docs): update README and AGENTS.md with RTK instructions 2026-02-19 13:01:55 -08:00
david 16f02ed967 docs(doorbell-touch): update default BOARD to esp32-s3-lcd-43 2026-02-19 10:36:30 -08:00
david d6eb2cd561 feat(esp32-s3-lcd-43): add touch test harness and coordinate transformation 2026-02-18 18:58:37 -08:00
david e4609c6978 refactor(Format code): Improve readability and structure 2026-02-18 17:57:54 -08:00
david c81e23f7cd refactor(sketch): update build configuration and remove obsolete files 2026-02-18 17:23:43 -08:00
david 34ca86d771 feat: update ESP32 board version and add LSP configs 2026-02-18 16:07:52 -08:00
david 9f7a383b38 feat(touch): add press/release detection with touch-down tracking 2026-02-18 13:08:56 -08:00
david 1961631e2c refactor(display): extract tile layout logic to library helper class 2026-02-18 11:43:46 -08:00
david 67613120ad refactor(display): extract dashboard tile grid logic to DisplayManager 2026-02-18 04:28:35 -08:00
david 3b8e54c511 refactor(doorbell-touch): add build harness with monitor agent and board-specific setup 2026-02-18 03:28:38 -08:00
david 4ea7165148 refactor(doorbell-touch): extract lockfile logic to shared script 2026-02-18 02:18:50 -08:00
david 2831ecf43c feat(doorbell-touch): add lockfile to prevent concurrent monitor sessions 2026-02-18 02:05:27 -08:00
david 5ce02a6eff refactor(doorbell-touch): extract board configs and improve logging 2026-02-18 02:02:35 -08:00
david ea8aab0a68 fix(doorbell-touch): kill screen session before upload 2026-02-18 01:14:06 -08:00
david 4c0991b8bd feat(doorbell): add inactivity timeout to turn off screen 2026-02-18 01:12:36 -08:00
david bfba3b02fd feat(doorbell): add staged boot sequence and refactor main loop 2026-02-18 00:35:48 -08:00
david 46289b9d40 refactor(touch): centralize touch handling in DoorbellLogic 2026-02-17 23:35:59 -08:00
david 4f389ac0fe feat(touch): add continuous touch polling and debug logging 2026-02-17 23:20:42 -08:00
david 17c44d9b08 fix(doorbell-touch): add touch debug logging and missing TFT_MISO pin 2026-02-17 22:51:32 -08:00
david cbe7efe883 feat(boards): add ESP32-32E-4 (4" ST7796) board support 2026-02-17 22:07:51 -08:00
david a42415e64e chore(doorbell-touch): add clangd config and project documentation 2026-02-17 21:35:10 -08:00
david d961afd950 fix: Remove vendor directory on clean 2026-02-17 19:43:09 -08:00
david 8e39a1f23c refactor: add active parameter to updateHint method signature
1. **Method Signature Update**: Added `bool active` parameter to `updateHint()` method across the display driver hierarchy:
   - `DisplayManager::updateHint(x, y, active)` - delegates to driver
   - `DisplayDriverTFT::updateHint(x, y, active)` - override implementation
   - `DisplayDriverGFX::updateHint(x, y, active)` - override implementation

2. **Code Formatting**: `DisplayManager.h` reformatted (whitespace/comment style changes only)

- **Breaking Change**: All existing `updateHint(x, y)` calls will fail to compile until updated to include the `active` parameter
- **Enhanced Control**: Callers can now explicitly show/hide touch hints rather than just updating position, enabling better touch feedback UX (e.g., hide hint on touch release)
- **API Consistency**: All implementations in the driver hierarchy now enforce the same signature
2026-02-17 05:23:43 -08:00
david c4adb38576 feat(display): add active state parameter to hint animation
1. **Added `active` parameter to hint animation**
   - `updateHint()` now accepts a boolean `active` parameter across both display drivers (TFT and GFX)
   - When `active=true`: faster pulse animation (500ms period) during active hold
   - When `active=false`: slower pulse animation (2000ms period) during idle state

2. **Improved animation calculations**
   - Replaced modulo operator with `fmodf()` for cleaner float calculations
   - Standardized to `static_cast<uint8_t>()` for type conversions
   - Fixed GFX driver to use `color565()` method instead of manual bit shifting

3. **Updated hint display logic**
   - Now differentiates between "holding" state (fast pulse) and "idle" state (slow pulse)
   - Hint draws at both states when `holdStartX >= 0` (touch position captured)

4. **Added code formatter task**
   - New `mise.toml` task for running clang-format across all source files

- Users get **visual feedback differentiation**: fast pulsing during active hold vs. slow pulsing when idle
- More intuitive UI that clearly indicates whether a long-press is in progress or just waiting
- Cleaner, more maintainable code with standardized calculations and type conversions
2026-02-17 05:11:02 -08:00
david ff073c762b fix(display): correct type cast syntax in pulse calculation 2026-02-17 04:21:05 -08:00
david 8e9bd18676 style: apply consistent code formatting and spacing 2026-02-17 04:15:48 -08:00
david f39364f67f style(esp32-32e): reformat code and fix display dimensions 2026-02-17 04:10:03 -08:00
david 0ace263324 feat(display): draw hint animation at touch position instead of center 2026-02-17 03:49:34 -08:00
david 23712a152b style: fix indentation and formatting in doorbell code 2026-02-17 03:19:12 -08:00
david d61c9c60bf refactor(display): improve touch handling and code formatting 2026-02-17 03:10:52 -08:00
david 2907dac33d refactor(esp32-s3-lcd-43): remove IO expander and use LovyanGFX backlight 2026-02-17 02:13:30 -08:00
david 246bfe0cb9 fix(doorbell-touch): correct LovyanGFX library integration and build 2026-02-17 01:13:28 -08:00
david de45f89f9b refactor(doorbell-touch): simplify LovyanGFX library setup 2026-02-17 01:00:57 -08:00
david 66b4e75174 refactor(display): split render into screen-specific draw methods 2026-02-17 00:06:30 -08:00
david 9f0e603215 chore(examples): add submodule and remove LVGL examples 2026-02-16 23:09:37 -08:00
david 257bc9aa82 refactor(doorbell): unify 2x2 grid touch logic and add progress bar 2026-02-16 22:11:40 -08:00
david b47993b29a refactor(doorbell-touch): remove entire sketch for rebuild 2026-02-16 21:13:31 -08:00
david 7085bfff04 refactor(display): update ScreenState field names and remove time display 2026-02-16 20:59:25 -08:00
david 89f3248747 refactor(doorbell): migrate ESP32-S3 to ESP_IOExpander library 2026-02-16 20:51:56 -08:00
david c7ea8d5817 chore(mise): update PSRAM config and commit task command 2026-02-16 19:15:48 -08:00
david ff197dc889 snapshot 2026-02-16 19:08:47 -08:00
david 838afaa36f consolidate sketches 2026-02-16 19:08:47 -08:00
david 75c3f5706b snapshot 2026-02-16 19:05:13 -08:00
david dc62265fbb snapshot 2026-02-16 19:05:13 -08:00
david fa6bf6fd6a snapshot 2026-02-16 19:05:13 -08:00
david ce413dc20c snapshot 2026-02-16 19:05:13 -08:00
david 6c4cfd3a45 networking broken 2026-02-16 19:05:13 -08:00
david 86b94e3781 snapshot 2026-02-16 19:05:13 -08:00
david da2a077343 snapshot 2026-02-16 19:05:13 -08:00
david 6b2921aa30 snapshot 2026-02-16 19:05:12 -08:00
4547 changed files with 4791 additions and 2115531 deletions
+5
View File
@@ -0,0 +1,5 @@
local M = {}
M.board = 'esp32:esp32:esp32'
M.port = '/dev/ttyUSB0'
M.baudrate = 115200
return M
+26
View File
@@ -0,0 +1,26 @@
# Arduino/C++ friendly format
BasedOnStyle: WebKit
# Indentation
IndentWidth: 4
TabWidth: 4
UseTab: Never
IndentCaseLabels: false
# Braces - keep opening brace on same line
BreakBeforeBraces: Attach
# Spacing (fixed)
SpaceBeforeParens: Never
SpaceAfterCStyleCast: false
PointerAlignment: Left
# Line length
ColumnLimit: 100
# Includes
SortIncludes: true
IncludeBlocks: Regroup
# Functions
KeepEmptyLinesAtTheStartOfBlocks: false
+69
View File
@@ -0,0 +1,69 @@
# Doorbell Touch - Build Harness
## Session Handoff Note
⚠️ ALWAYS check `mise.toml` for available build tasks before compiling/uploading. Run `mise tasks` to list.
## Quick Commands
```bash
BOARD=esp32-32e-4 mise run compile # Compile
BOARD=esp32-32e-4 mise run upload # Upload (auto-kills monitor)
BOARD=esp32-32e-4 mise run monitor # Start JSON monitor daemon
BOARD=esp32-32e-4 mise run log-tail # Watch colored logs
BOARD=esp32-32e-4 mise run cmd COMMAND=dashboard # Send command
BOARD=esp32-32e-4 mise run state # Show device state
BOARD=esp32-32e-4 mise run install # Install libs (shared + board)
```
## Files
- `mise.toml` - Tasks (compile, upload, monitor, log-tail, cmd, state, install, clean)
- `scripts/monitor-agent.py` - Serial monitor with JSON log + command FIFO
- `scripts/install-shared.sh` - Shared Arduino libs (ArduinoJson, NTPClient)
- `boards/{BOARD}/install.sh` - Board-specific vendor libs
## Monitor Daemon
- JSON log: `/tmp/doorbell-$BOARD.jsonl` - `{"ts":123.456,"line":"..."}`
- State: `/tmp/doorbell-$BOARD-state.json` - `{"screen":"DASHBOARD",...}`
- Cmd: `echo 'dashboard' > /tmp/doorbell-$BOARD-cmd.fifo`
## Boards
- esp32-32e (original 2.8" ILI9341)
- esp32-32e-4 (4" ST7796)
- esp32-s3-lcd-43 (4.3" S3 display)
## Known Fixes
- dashboard/off admin commands now reset inactivity timer (DoorbellLogic.cpp:234,240)
## Reverted Changes Log
Track changes that were reverted to avoid flapping:
- 2025-02-18: Initially configured LSP via neovim/mason (`.config/nvim/lua/plugins/arduino.lua`) — user clarified they wanted Crush-native LSP config instead
## Documentation Lookup Rule
When uncertain about CLI tool flags or argument syntax:
1. Run the tool with `-h` or `--help` first
2. If unclear, search for official documentation matching the tool version
3. Prefer checking the tool's own help/docs over guessing
4. For Arduino CLI, check `arduino-cli <command> --help` or official Arduino CLI docs
## Neovim LSP Configuration
For Neovim projects:
- Use Mason + mason-lspconfig to manage LSP installations
- Add LSP configs to `~/.config/nvim/lua/plugins/` as separate plugin files
- Use `on_new_config` for dynamic settings (e.g., BOARD env var → FQBN mapping)
- Example: `~/.config/nvim/lua/plugins/arduino.lua` (arduino-language-server)
## Crush LSP Configuration
- Configure via `.crush.json` in project root (or `$HOME/.config/crush/crush.json` globally)
- Supports env var interpolation: `$BOARD` or `${BOARD}`
- Supports command substitution: `$(cat boards/$BOARD/board-config.sh | grep '^FQBN=' | cut -d'"' -f2)`
- Example for Arduino (auto-detects FQBN from BOARD):
```json
{
"lsp": {
"arduino": {
"command": "arduino-language-server",
"args": ["-fqbn", "$(cat boards/${BOARD:-esp32-32e-4}/board-config.sh | grep '^FQBN=' | cut -d'\"' -f2)"]
}
}
}
```
- Run `mise run gen-crush-config` to regenerate static config
+20
View File
@@ -0,0 +1,20 @@
# Secrets (WiFi passwords etc.)
**/secrets.h
# Build artifacts
**/build/
# Vendored libraries (re-created by install-libs)
vendor/esp32-32e/TFT_eSPI/
vendor/esp32-32e-4/TFT_eSPI/
vendor/esp32-s3-lcd-43/GFX_Library_for_Arduino/
# IDE
.vscode/
*.swp
*.swo
*~
.DS_Store
compile_commands.json
.board-last
.cache/
+5
View File
@@ -0,0 +1,5 @@
{
"MD013": false,
"MD060": false,
"MD025": false
}
+5
View File
@@ -0,0 +1,5 @@
{
"MD013": false,
"MD060": false,
"MD025": false
}
+143
View File
@@ -0,0 +1,143 @@
# AGENTS.md — Klubhaus Doorbell
Multi-target Arduino/ESP32 doorbell alert system using ntfy.sh. Default BOARD: `esp32-s3-lcd-43`.
## Build Commands
```bash
# Set target board
mise set BOARD=esp32-32e-4 # ESP32-32E 4" (320x480 ST7796)
mise set BOARD=esp32-32e # ESP32-32E 3.5" (320x240 ILI9341)
mise set BOARD=esp32-s3-lcd-43 # ESP32-S3-Touch-LCD-4.3 (800x480 RGB)
# Core commands
mise run compile # compile for current BOARD
mise run upload # upload (auto-kills monitor first)
mise run monitor # start JSON monitor daemon
mise run kill # kill monitor/release serial port
# Formatting & cleanup
mise run format # format code with clang-format
mise run clean # remove build artifacts
# Debugging
mise run log-tail # tail colored logs
mise run cmd COMMAND=dashboard # send command to device
mise run state # show device state
mise run monitor-raw # raw serial monitor (115200 baud)
mise run monitor-tio # show tio command for terminal UI
# Install dependencies
mise run install-libs-shared # shared libs (ArduinoJson, NTPClient)
mise run install # shared + board-specific libs
# LSP / IDE
mise run gen-compile-commands # generate compile_commands.json
mise run gen-crush-config # generate .crush.json for BOARD
```
**Serial debug commands** (115200 baud): `alert`, `silence`, `dashboard`, `off`, `status`, `reboot`
**No unit tests exist** — verify changes by compiling and deploying to hardware.
## Code Style
### Formatting (.clang-format)
- BasedOnStyle: WebKit
- 4-space indentation, no tabs
- Column limit: 100
- Opening brace on same line (`BreakBeforeBraces: Attach`)
- Run `mise run format` to format code
### Header Guards
Use `#pragma once` (not `#ifndef` guards).
### Naming Conventions
| Type | Convention | Example |
|------|------------|---------|
| Classes | PascalCase | `DisplayManager`, `IDisplayDriver` |
| Constants/enums | SCREAMING_SNAKE | `POLL_INTERVAL_MS`, `ScreenState::DASHBOARD` |
| Variables/functions | camelCase | `currentState`, `updateDisplay` |
| Member variables | `_` prefix | `_screenWidth`, `_isConnected` |
### Types
- Use fixed-width types for protocol/serialization (`uint8_t`, not `byte`)
- Use `size_t` for sizes and array indices
- Avoid `bool` for pin states — use `uint8_t` or `int`
### Imports Organization (in order)
1. Arduino core (`Arduino.h`)
2. Standard C/C++ (`<cstdint>`, `<String>`, `<vector>`)
3. Third-party libs (`TFT_eSPI.h`, `ArduinoJson.h`)
4. Local project (`"Config.h"`, `"ScreenState.h"`)
### Error Handling
- Serial logging: `Serial.println("[ERROR] message")`
- Use `Serial.printf()` for formatted debug
- Return error codes, not exceptions
- Log state: `[STATE] → DASHBOARD`
## Architecture Patterns
### Display Driver Interface
- Pure virtual `IDisplayDriver` in shared `KlubhausCore`
- Each board implements concrete driver (`DisplayDriverTFT`, `DisplayDriverGFX`)
- `DisplayManager` delegates to `IDisplayDriver` — no display-lib coupling in shared code
### Arduino Patterns
- `setup()` — call `begin()` on managers
- `loop()` — call `update()` on managers
- Use `millis()` for timing (not `delay()`)
- Serial baud: 115200
### Style System
- Style constants in board's `board_config.h`: `STYLE_SPACING_X`, `STYLE_COLOR_BG`, etc.
- Font abstraction via `IDisplayDriver`: `setTitleFont()`, `setBodyFont()`, etc.
- Layout helpers in `KlubhausCore/src/Style.h`
## Key Files
```
libraries/KlubhausCore/src/
├── KlubhausCore.h # Umbrella include
├── Config.h # Timing, WiFiCred struct
├── ScreenState.h # State enums/structs
├── IDisplayDriver.h # Pure virtual interface
├── DisplayManager.h # Delegates to IDisplayDriver
├── NetManager.* # WiFi, HTTP, NTP
└── DoorbellLogic.* # State machine, ntfy polling
boards/{BOARD}/
├── {BOARD}.ino # Main sketch
├── board_config.h # Board-specific config
├── secrets.h # WiFi credentials
├── tft_user_setup.h # TFT_eSPI config (TFT boards)
└── DisplayDriver*.{h,cpp} # Concrete IDisplayDriver
```
## Gotchas
1. **secrets.h**: Boards with `-DLOCAL_SECRETS` use local `secrets.h`; others use `KlubhausCore/src/secrets.h`
2. **Vendored libs**: Each board links only its display lib — never TFT_eSPI + LovyanGFX together
3. **LSP errors**: Run `mise run gen-compile-commands` then restart LSP; build works regardless
4. **Serial port**: `upload`/`monitor` auto-depend on `kill` to release port
5. **State tags**: Use `[STATE] → DASHBOARD`, `[ADMIN]`, `[TOUCH]`, `[ALERT]` for monitor parsing
## Config Constants (Config.h)
| Constant | Default | Description |
|----------|---------|-------------|
| `FW_VERSION` | "5.1" | Firmware version |
| `POLL_INTERVAL_MS` | 15000 | ntfy.sh poll interval |
| `ALERT_TIMEOUT_MS` | 120000 | Auto-clear alert |
| `INACTIVITY_TIMEOUT_MS` | 30000 | Display off timeout |
| `HOLD_TO_SILENCE_MS` | 3000 | Hold to silence |
| `WIFI_CONNECT_TIMEOUT_MS` | 15000 | WiFi timeout |
| `HTTP_TIMEOUT_MS` | 10000 | HTTP request timeout |
## Screen States
- **BOOT** — Initializing
- **DASHBOARD** — Normal operation
- **ALERT** — Doorbell ring detected
- **OFF** — Display backlight off (polling continues)
+103
View File
@@ -0,0 +1,103 @@
# Klubhaus Doorbell
Multi-target doorbell alert system powered by [ntfy.sh](https://ntfy.sh).
## Targets
| Board | Display | Library | Build Command |
|---|---|---|---|
| ESP32-32E | SPI TFT (ILI9341 etc.) | TFT_eSPI | `BOARD=esp32-32e mise run compile` |
| ESP32-32E-4" | SPI TFT 320x480 (ST7796) | TFT_eSPI | `BOARD=esp32-32e-4 mise run compile` |
| ESP32-S3-Touch-LCD-4.3 | 800x480 RGB parallel | LovyanGFX | `BOARD=esp32-s3-lcd-43 mise run compile` |
## Quick Start
1. Install prerequisites: arduino-cli (with esp32:esp32 platform) and mise.
2. Edit WiFi credentials:
cp boards/esp32-32e/secrets.h.example boards/esp32-32e/secrets.h
cp boards/esp32-s3-lcd-43/secrets.h.example boards/esp32-s3-lcd-43/secrets.h
Then edit both secrets.h files with your WiFi SSIDs and passwords.
3. Build and upload:
BOARD=esp32-s3-lcd-43 mise run compile
BOARD=esp32-s3-lcd-43 mise run upload
BOARD=esp32-32e-4 mise run compile
BOARD=esp32-32e-4 mise run upload
BOARD=esp32-32e mise run compile
BOARD=esp32-32e mise run upload
## Project Structure
.
├── libraries/
│ └── KlubhausCore/ Shared Arduino library
│ └── src/
│ ├── KlubhausCore.h Umbrella include
│ ├── Config.h Shared constants
│ ├── ScreenState.h State enums / structs
│ ├── IDisplayDriver.h Abstract display interface
│ ├── DisplayManager.h Thin delegation wrapper
│ ├── NetManager.* WiFi, NTP, HTTP
│ └── DoorbellLogic.* State machine, ntfy polling
├── boards/
│ ├── esp32-32e/ ESP32-32E sketch
│ │ ├── esp32-32e.ino
│ │ ├── board_config.h
│ │ ├── secrets.h (gitignored, copy from .example)
│ │ ├── tft_user_setup.h
│ │ └── DisplayDriverTFT.*
│ ├── esp32-32e-4/ ESP32-32E-4" sketch
│ │ ├── esp32-32e-4.ino
│ │ ├── board_config.h
│ │ ├── secrets.h (gitignored, copy from .example)
│ │ ├── tft_user_setup.h
│ │ └── DisplayDriverTFT.*
│ └── esp32-s3-lcd-43/ ESP32-S3-LCD-4.3 sketch
│ ├── esp32-s3-lcd-43.ino
│ ├── board_config.h
│ ├── secrets.h (gitignored, copy from .example)
│ ├── LovyanPins.h
│ └── DisplayDriverGFX.*
├── vendor/ Per-board vendored display libs
│ ├── esp32-32e/TFT_eSPI/
│ ├── esp32-32e-4/TFT_eSPI/
│ └── esp32-s3-lcd-43/LovyanGFX/
└── mise.toml Build harness
## Serial Commands
Type into the serial monitor at 115200 baud:
| Command | Action |
|---|---|
| alert | Trigger a test alert |
| silence | Silence current alert |
| dashboard | Show dashboard screen |
| off | Turn off display |
| status | Print state + memory info |
| reboot | Restart device |
## Architecture
Each board target gets its own sketch directory with a concrete IDisplayDriver
implementation. The shared KlubhausCore library contains all business logic,
networking, and the abstract interface. Arduino CLI's --libraries flag ensures
each board only links its own vendored display library -- no preprocessor conflicts.
Board sketch (.ino)
|
+-- #include <KlubhausCore.h> (shared library)
| +-- DoorbellLogic (state machine + ntfy polling)
| +-- NetManager (WiFi, HTTP, NTP)
| +-- DisplayManager (delegates to IDisplayDriver)
| +-- IDisplayDriver (pure virtual interface)
|
+-- DisplayDriverXxx (board-specific, concrete driver)
+-- links against vendored display lib
(TFT_eSPI or Arduino_GFX, never both)
+77
View File
@@ -0,0 +1,77 @@
# Session Notes - 2026-02-19
## What's Been Done
### Build System
- Fixed mise.toml compile task (single quotes, proper shell escaping)
- Added upload task back
- All 3 boards now compile successfully
### Code Changes
- Added font abstraction (`setTitleFont`, `setBodyFont`, `setLabelFont`, `setDefaultFont`)
- Added CSS-like styling constants (`STYLE_*` in board_config.h)
- Added Layout helpers in `KlubhausCore/src/Style.h`
- Added `drawDebugTouch()` for red crosshair debug feature
- Added test harness for touch injection (`TEST:touch` commands)
- Fixed dashboard tile layout to use STYLE constants
### Neovim Integration
- Added keymaps in `~/.config/nvim/lua/config/keymaps.lua`
- Board auto-detection from current file path
- Notifications via `vim.notify()` on task completion/failure
- Keybindings: `<leader>mc`, `<leader>mu`, `<leader>mm`, `<leader>ma`, `<leader>mk`
### Documentation
- Updated AGENTS.md with Style System section
- Updated RTK section (slimmed down)
- Added note about multiple mise tasks with `&&`
## Current Issues / TODO
### High Priority
- [ ] Test touch coordinates on esp32-s3-lcd-43 - crosshair should show where user taps
- [ ] Debug why tiles overlap header (should be fixed with STYLE_HEADER_HEIGHT)
### Medium Priority
- [ ] Add theme support (dark/light mode)
- [ ] Fix esp32-32e and esp32-32e-4 dashboard rendering
### Low Priority
- [ ] Add more tile labels for esp32-s3-lcd-43 (currently just "1", "2", "3"...)
## Board Status
| Board | Display | Build | Notes |
|-------|---------|-------|-------|
| esp32-s3-lcd-43 | 800x480 | ✅ 35% | Main development board |
| esp32-32e-4 | 320x480 | ✅ 83% | |
| esp32-32e | 320x240 | ✅ 82% | |
## Style Constants Reference
### Spacing (in board_config.h)
- `STYLE_SPACING_X` - Base horizontal margin
- `STYLE_SPACING_Y` - Base vertical margin
- `STYLE_HEADER_HEIGHT` - Header bar height
- `STYLE_TILE_GAP` - Gap between tiles
- `STYLE_TILE_PADDING` - Tile internal padding
- `STYLE_TILE_RADIUS` - Tile border radius
### Colors
- `STYLE_COLOR_BG` - Screen background
- `STYLE_COLOR_HEADER` - Header background
- `STYLE_COLOR_FG` - Primary text color
- `STYLE_COLOR_ALERT` - Alert screen
- `STYLE_COLOR_TILE_1` through `STYLE_COLOR_TILE_4` - Tile colors
## Useful Commands
```bash
# Compile and upload
mise run compile && mise run upload && mise run monitor
# Debug touch (in serial monitor)
TEST:touch X Y press
TEST:touch X Y release
TEST:touch clear
```
+464
View File
@@ -0,0 +1,464 @@
#include "DisplayDriverTFT.h"
#include <Arduino.h>
#include <KlubhausCore.h>
#include <TFT_eSPI.h>
extern DisplayManager display;
// ── Fonts ───────────────────────────────────────────────────
// TFT_eSPI built-in fonts for 320x480 display (scaled from 800x480)
// Using FreeFonts - scaled bitmap fonts via setTextSize would be too pixelated
// Note: FreeFonts are enabled via LOAD_GFXFF=1 in board-config.sh
void DisplayDriverTFT::setTitleFont() { _tft.setFreeFont(&FreeSansBold18pt7b); }
void DisplayDriverTFT::setBodyFont() { _tft.setFreeFont(&FreeSans12pt7b); }
void DisplayDriverTFT::setLabelFont() { _tft.setFreeFont(&FreeSans9pt7b); }
void DisplayDriverTFT::setDefaultFont() { _tft.setTextFont(2); }
// ── Test harness ───────────────────────────────────────────────
// Test harness: parse serial commands to inject synthetic touches
// Commands:
// TEST:touch x y press - simulate press at (x, y)
// TEST:touch x y release - simulate release at (x, y)
// TEST:touch clear - clear test mode
bool DisplayDriverTFT::parseTestTouch(int* outX, int* outY, bool* outPressed) {
if(!Serial.available())
return false;
if(Serial.peek() != 'T') {
return false;
}
String cmd = Serial.readStringUntil('\n');
cmd.trim();
if(!cmd.startsWith("TEST:touch"))
return false;
int firstSpace = cmd.indexOf(' ');
if(firstSpace < 0)
return false;
String args = cmd.substring(firstSpace + 1);
args.trim();
if(args.equals("clear")) {
_testMode = false;
Serial.println("[TEST] Test mode cleared");
return false;
}
int secondSpace = args.indexOf(' ');
if(secondSpace < 0)
return false;
String xStr = args.substring(0, secondSpace);
String yState = args.substring(secondSpace + 1);
yState.trim();
int x = xStr.toInt();
int y = yState.substring(0, yState.indexOf(' ')).toInt();
String state = yState.substring(yState.indexOf(' ') + 1);
state.trim();
bool pressed = state.equals("press");
Serial.printf("[TEST] Injecting touch: (%d,%d) %s\n", x, y, pressed ? "press" : "release");
if(outX)
*outX = x;
if(outY)
*outY = y;
if(outPressed)
*outPressed = pressed;
_testMode = true;
return true;
}
void DisplayDriverTFT::begin() {
// Backlight
pinMode(PIN_LCD_BL, OUTPUT);
digitalWrite(PIN_LCD_BL, LOW);
_tft.init();
_tft.setRotation(DISPLAY_ROTATION);
_tft.fillScreen(TFT_BLACK);
Serial.printf("[GFX] Display OK: const %dx%d, tft %dx%d\n", DISPLAY_WIDTH, DISPLAY_HEIGHT,
_tft.width(), _tft.height());
Serial.flush();
// Debug: check if touch controller is responding
uint16_t z = _tft.getTouchRawZ();
Serial.printf("[TOUCH] Raw Z=%d (non-zero = controller detected)\n", z);
Serial.flush();
ScreenState st;
st.screen = ScreenID::BOOT;
st.bootStage = BootStage::SPLASH;
drawBoot(st);
digitalWrite(PIN_LCD_BL, HIGH);
Serial.println("[GFX] Backlight ON");
Serial.flush();
}
void DisplayDriverTFT::setBacklight(bool on) { digitalWrite(PIN_LCD_BL, on ? HIGH : LOW); }
// ── Rendering ───────────────────────────────────────────────
void DisplayDriverTFT::render(const ScreenState& st) {
if(st.screen != _lastScreen
|| (st.screen == ScreenID::BOOT && st.bootStage != _lastBootStage)) {
_needsRedraw = true;
_lastScreen = st.screen;
_lastBootStage = st.bootStage;
}
switch(st.screen) {
case ScreenID::BOOT:
if(_needsRedraw) {
drawBoot(st);
_needsRedraw = false;
}
break;
case ScreenID::ALERT:
drawAlert(st);
break;
case ScreenID::DASHBOARD:
if(_needsRedraw) {
drawDashboard(st);
_needsRedraw = false;
}
break;
case ScreenID::STATUS:
if(_needsRedraw) {
drawStatus(st);
_needsRedraw = false;
}
break;
case ScreenID::OFF:
if(_needsRedraw) {
_tft.fillScreen(TFT_BLACK);
_needsRedraw = false;
}
break;
}
}
void DisplayDriverTFT::drawBoot(const ScreenState& st) {
BootStage stage = st.bootStage;
_tft.fillScreen(TFT_BLACK);
_tft.setTextColor(TFT_WHITE, TFT_BLACK);
setTitleFont();
_tft.setCursor(10, 28); // y=28 baseline accounts for ~18px font height above baseline
_tft.print("KLUBHAUS");
setBodyFont();
_tft.setCursor(10, 55); // y adjusted for ~12px font
_tft.print(BOARD_NAME);
// Show boot stage status
setLabelFont();
_tft.setCursor(10, 85); // y adjusted for ~9px label font
switch(stage) {
case BootStage::SPLASH:
_tft.print("Initializing...");
break;
case BootStage::INIT_DISPLAY:
_tft.print("Display OK");
break;
case BootStage::INIT_NETWORK:
_tft.print("Network init...");
break;
case BootStage::CONNECTING_WIFI:
_tft.print("Connecting WiFi...");
break;
case BootStage::READY:
_tft.print("All systems go!");
break;
case BootStage::DONE:
_tft.print("Ready!");
break;
}
}
void DisplayDriverTFT::drawAlert(const ScreenState& st) {
// Static 2-color pulse - alternate every ~2 seconds
uint32_t elapsed = millis() - st.alertStartMs;
bool brightPhase = (elapsed / 2000) % 2 == 0;
// Redraw when phase changes OR when touch was released (to clear fill)
bool needsRedraw = (brightPhase != _lastAlertPhase) || _alertNeedsRedraw;
if(needsRedraw) {
_lastAlertPhase = brightPhase;
_alertNeedsRedraw = false; // clear the flag
uint16_t bg = brightPhase ? TFT_RED : _tft.color565(180, 0, 0);
_tft.fillScreen(bg);
_tft.setTextColor(TFT_WHITE, bg);
setTitleFont();
_tft.setCursor(10, 28);
_tft.print(st.alertTitle.length() > 0 ? st.alertTitle : "ALERT");
setBodyFont();
_tft.setCursor(10, 70);
_tft.print(st.alertBody);
setLabelFont();
_tft.setCursor(10, _tft.height() - 10);
_tft.print("Hold to silence...");
}
// Progressive fill hint - while touch is held
if(_alertTouchDown) {
uint32_t touchElapsed = millis() - _alertTouchStartMs;
float progress = (float)touchElapsed / (float)ALERT_FILL_DURATION_MS;
if(progress > 1.0f)
progress = 1.0f;
int dispH = _tft.height();
int fillHeight = (int)(dispH * progress);
if(fillHeight > 0) {
uint16_t overlay = _tft.color565(80, 0, 0);
_tft.fillRect(0, dispH - fillHeight, _tft.width(), fillHeight, overlay);
}
}
}
void DisplayDriverTFT::drawDashboard(const ScreenState& st) {
_tft.fillScreen(TFT_BLACK);
// Use actual display dimensions (after rotation)
int dispW = _tft.width();
int dispH = _tft.height();
// Header - using standard bitmap font for reliable positioning
_tft.fillRect(0, 0, dispW, STYLE_HEADER_HEIGHT, 0x1A1A); // Dark gray header
_tft.setTextSize(1);
_tft.setTextColor(TFT_WHITE);
_tft.setCursor(5, 20); // y=28 is baseline, text sits above this
_tft.print("KLUBHAUS");
// WiFi indicator - right aligned in header
const char* wifiText = st.wifiSsid.length() > 0 ? "WiFi:ON" : "WiFi:OFF";
int wifiW = _tft.textWidth(wifiText);
_tft.setCursor(dispW - wifiW - 10, 20);
_tft.print(wifiText);
// Get tile layouts from library helper
int tileCount = display.calculateDashboardLayouts(STYLE_HEADER_HEIGHT, STYLE_TILE_GAP);
display.setHeaderHeight(STYLE_HEADER_HEIGHT);
const TileLayout* layouts = display.getTileLayouts();
const char* tileLabels[] = { "Alert", "Silent", "Status", "Reboot" };
const uint16_t tileColors[] = { 0x0280, 0x0400, 0x0440, 0x0100 };
for(int i = 0; i < tileCount && i < 4; i++) {
const TileLayout& lay = layouts[i];
int x = lay.x;
int y = lay.y;
int w = lay.w;
int h = lay.h;
// Tile background
_tft.fillRoundRect(x, y, w, h, 8, tileColors[i]);
// Tile border
_tft.drawRoundRect(x, y, w, h, 8, TFT_WHITE);
// Tile label
setBodyFont();
_tft.setTextColor(TFT_WHITE);
int textLen = strlen(tileLabels[i]);
int textW = textLen * 12;
_tft.setCursor(x + w / 2 - textW / 2, y + h / 2 - 10);
_tft.print(tileLabels[i]);
}
}
void DisplayDriverTFT::drawStatus(const ScreenState& st) {
int dispW = _tft.width();
int dispH = _tft.height();
_tft.fillScreen(TFT_BLACK);
// Header
_tft.fillRect(0, 0, dispW, STYLE_HEADER_HEIGHT, 0x1A1A);
_tft.setTextSize(1);
_tft.setTextColor(TFT_WHITE);
_tft.setCursor(5, 20);
_tft.print("STATUS");
// Back button in lower right
_tft.setCursor(dispW - 60, dispH - 20);
_tft.print("[BACK]");
// Status info
setBodyFont();
int y = STYLE_HEADER_HEIGHT + 20;
// WiFi
_tft.setCursor(10, y);
_tft.printf("WiFi: %s", st.wifiSsid.length() > 0 ? st.wifiSsid.c_str() : "N/A");
y += 20;
_tft.setCursor(10, y);
_tft.printf("RSSI: %d dBm", st.wifiRssi);
y += 20;
_tft.setCursor(10, y);
_tft.printf("IP: %s", st.ipAddr.length() > 0 ? st.ipAddr.c_str() : "N/A");
y += 30;
// Uptime
uint32_t upSec = st.uptimeMs / 1000;
uint32_t upMin = upSec / 60;
uint32_t upHr = upMin / 60;
upSec = upSec % 60;
upMin = upMin % 60;
_tft.setCursor(10, y);
_tft.printf("Uptime: %02lu:%02lu:%02lu", upHr, upMin, upSec);
y += 20;
// Heap
_tft.setCursor(10, y);
_tft.printf("Heap: %d bytes", ESP.getFreeHeap());
y += 20;
// Last poll
uint32_t pollAgo = (millis() - st.lastPollMs) / 1000;
_tft.setCursor(10, y);
_tft.printf("Last poll: %lu sec ago", pollAgo);
}
// ── Touch ───────────────────────────────────────────────────
TouchEvent DisplayDriverTFT::readTouch() {
TouchEvent evt;
// Check for test injection via serial
int testX, testY;
bool testPressed;
if(parseTestTouch(&testX, &testY, &testPressed)) {
if(testPressed && !_touchWasPressed) {
evt.pressed = true;
_touchDownX = testX;
_touchDownY = testY;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
} else if(!testPressed && _touchWasPressed) {
evt.released = true;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
}
if(testPressed) {
evt.x = testX;
evt.y = testY;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
}
_touchWasPressed = testPressed;
return evt;
}
uint16_t tx, ty;
uint8_t touched = _tft.getTouch(&tx, &ty, 100);
// Debug: log touch state changes with transformed coords
if(touched != _touchWasPressed) {
int tx_form = tx, ty_form = ty;
transformTouch(&tx_form, &ty_form);
Serial.printf("[TOUCH] raw touched=%d wasPressed=%d (raw=%d,%d trans=%d,%d)\n", touched,
_touchWasPressed, tx, ty, tx_form, ty_form);
}
// Detect transitions (press/release)
if(touched && !_touchWasPressed) {
// Press transition: finger just touched down
evt.pressed = true;
_touchDownX = tx;
_touchDownY = ty;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
} else if(!touched && _touchWasPressed) {
// Release transition: finger just lifted
evt.released = true;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
}
// Current position if still touched
if(touched) {
evt.x = tx;
evt.y = ty;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
}
// Track previous state for next call
_touchWasPressed = touched;
// Track alert touch for progressive hint
if(evt.pressed) {
_alertTouchDown = true;
_alertTouchStartMs = millis();
} else if(evt.released) {
_alertTouchDown = false;
_alertNeedsRedraw = true; // force redraw to clear fill
}
return evt;
}
void DisplayDriverTFT::transformTouch(int* x, int* y) {
// Resistive touch panel is rotated 90° vs display - swap and adjust
// Touch panel: 320x480 (portrait), Display: 480x320 (landscape)
// This was the original working transform
int temp = *x;
*x = *y;
*y = temp;
}
HoldState DisplayDriverTFT::updateHold(const TouchEvent& evt, unsigned long holdMs) {
HoldState h;
if(evt.pressed) {
if(!_holdActive) {
_holdActive = true;
_holdStartMs = millis();
h.started = true;
}
uint32_t held = millis() - _holdStartMs;
h.active = true;
h.progress = constrain((float)held / (float)holdMs, 0.0f, 1.0f);
h.completed = (held >= holdMs);
// Simple progress bar at bottom of screen
int dispW = _tft.width();
int dispH = _tft.height();
int barW = (int)(dispW * h.progress);
_tft.fillRect(0, dispH - 8, barW, 8, TFT_WHITE);
_tft.fillRect(barW, dispH - 8, dispW - barW, 8, TFT_DARKGREY);
} else {
if(_holdActive) {
// Clear the progress bar when released
_tft.fillRect(0, _tft.height() - 8, _tft.width(), 8, TFT_DARKGREY);
}
_holdActive = false;
}
return h;
}
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include "board_config.h"
#include <KlubhausCore.h>
#include <TFT_eSPI.h>
class DisplayDriverTFT : public IDisplayDriver {
public:
void begin() override;
void setBacklight(bool on) override;
void render(const ScreenState& state) override;
TouchEvent readTouch() override;
HoldState updateHold(const TouchEvent& evt, unsigned long holdMs) override;
int width() override {
// Use TFT_eSPI's dimensions after rotation - it's more reliable
return _tft.width();
}
int height() override { return _tft.height(); }
// Dashboard - uses transform for touch coordinate correction
void transformTouch(int* x, int* y) override;
// Fonts
void setTitleFont() override;
void setBodyFont() override;
void setLabelFont() override;
void setDefaultFont() override;
private:
void drawBoot(const ScreenState& st);
void drawAlert(const ScreenState& st);
void drawDashboard(const ScreenState& st);
void drawStatus(const ScreenState& st);
TFT_eSPI _tft;
bool _holdActive = false;
uint32_t _holdStartMs = 0;
ScreenID _lastScreen = ScreenID::BOOT;
BootStage _lastBootStage = BootStage::SPLASH;
bool _needsRedraw = true;
// Touch hint for alert - progressive fill from bottom
bool _alertTouchDown = false;
bool _alertNeedsRedraw = false; // force redraw after touch release
uint32_t _alertTouchStartMs = 0;
bool _lastAlertPhase = false; // tracks bright/dark phase for 2-color alert
static constexpr uint32_t ALERT_FILL_DURATION_MS = 3000;
// Touch tracking for press/release detection
bool _touchWasPressed = false;
int _touchDownX = -1;
int _touchDownY = -1;
// Test mode for touch injection
bool _testMode = false;
bool parseTestTouch(int* outX, int* outY, bool* outPressed);
};
+4
View File
@@ -0,0 +1,4 @@
FQBN="esp32:esp32:esp32:FlashSize=4M,PartitionScheme=default"
PORT="/dev/ttyUSB0"
LIBS="--library ./vendor/esp32-32e-4/TFT_eSPI"
OPTS="-DDEBUG_MODE -DBOARD_HAS_PSRAM -DLOAD_GFXFF=1"
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#define BOARD_NAME "esp32-32e-4"
// ══════════════════════════════════════════════════════════
// Hosyond ESP32-32E 4" (320x480) with ST7796 + XPT2046
// Pin mapping from lcdwiki.com/4.0inch_ESP32-32E_Display
// ══════════════════════════════════════════════════════════
#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 480
#define DISPLAY_ROTATION 1 // landscape
// Backlight GPIO (HIGH = on)
#define PIN_LCD_BL 27
// Touch — XPT2046 configured in tft_user_setup.h
// Touch CS: GPIO33, Touch IRQ: GPIO36
// ── Style Constants (CSS-like) ────────────────────────────────────────
// Spacing - scaled for 320x480
#define STYLE_SPACING_X 6
#define STYLE_SPACING_Y 6
#define STYLE_HEADER_HEIGHT 24
#define STYLE_TILE_GAP 4
#define STYLE_TILE_PADDING 8
#define STYLE_TILE_RADIUS 4
// Colors
#define STYLE_COLOR_BG TFT_BLACK
#define STYLE_COLOR_HEADER 0x1A1A
#define STYLE_COLOR_FG TFT_WHITE
#define STYLE_COLOR_ALERT TFT_RED
#define STYLE_COLOR_TILE_1 0x0280
#define STYLE_COLOR_TILE_2 0x0400
#define STYLE_COLOR_TILE_3 0x0440
#define STYLE_COLOR_TILE_4 0x0100
+51
View File
@@ -0,0 +1,51 @@
//
// Klubhaus Doorbell — ESP32-32E-4" target
//
#include "DisplayDriverTFT.h"
#include "board_config.h"
// Include local secrets.h if it exists (board-specific credentials),
// otherwise KlubhausCore/src/secrets.h provides defaults.
#ifdef LOCAL_SECRETS
#include "secrets.h"
#else
#include <secrets.h>
#endif
#include <KlubhausCore.h>
DisplayDriverTFT tftDriver;
DisplayManager display(&tftDriver);
DoorbellLogic logic(&display);
void setup() {
Serial.begin(115200);
delay(500);
logic.begin(FW_VERSION, BOARD_NAME, wifiNetworks, wifiNetworkCount);
logic.finishBoot();
}
void loop() {
// Read touch
TouchEvent evt = display.readTouch();
// State machine tick
logic.update();
// Render current screen
display.render(logic.getScreenState());
// Handle tap gestures
logic.handleTouch(evt);
// Handle hold-to-silence gesture
logic.updateHold(evt);
// Serial console
logic.processSerial();
// Yield to WiFi/BT stack
delay(LOOP_YIELD_MS);
}
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Install TFT_eSPI for esp32-32e-4
set -euo pipefail
DIR="$(cd "$(dirname "$0")" && pwd)"
VENDOR_DIR="$DIR/../../vendor/esp32-32e-4"
if [ ! -d "$VENDOR_DIR/TFT_eSPI" ]; then
echo "Cloning TFT_eSPI..."
git clone --depth 1 --branch V2.5.43 \
https://github.com/Bodmer/TFT_eSPI.git \
"$VENDOR_DIR/TFT_eSPI"
fi
cp "$DIR/tft_user_setup.h" "$VENDOR_DIR/TFT_eSPI/User_Setup.h"
echo "[OK] TFT_eSPI vendored for esp32-32e-4"
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <KlubhausCore.h>
// Copy this file to secrets.h and fill in your credentials.
// secrets.h is gitignored.
static const WiFiCred wifiNetworks[] = {
{ "Your_SSID_1", "password1" },
{ "Your_SSID_2", "password2" },
};
static const int wifiNetworkCount = sizeof(wifiNetworks) / sizeof(wifiNetworks[0]);
+45
View File
@@ -0,0 +1,45 @@
// ══════════════════════════════════════════════════════════
// TFT_eSPI User_Setup for ESP32-32E-4" (Hosyond 320x480)
// This file is copied over vendor/esp32-32e-4/TFT_eSPI/User_Setup.h
// by the install-libs-32e-4 task.
// ══════════════════════════════════════════════════════════
#define USER_SETUP_ID 201
// ── Driver ──
#define ST7796_DRIVER
// ── Resolution ──
#define TFT_WIDTH 320
#define TFT_HEIGHT 480
// ── SPI Pins (from lcdwiki.com/4.0inch_ESP32-32E_Display) ──
#define TFT_MISO 12
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST -1 // tied to EN, handled by board
// ── Backlight ──
#define TFT_BL 27
#define TFT_BACKLIGHT_ON HIGH
// ── Touch (XPT2046 resistive) ──
#define TOUCH_CS 33
#define TOUCH_IRQ 36
// ── SPI speed ──
#define SPI_FREQUENCY 40000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
// ── Misc ──
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF
#define SMOOTH_FONT
+333
View File
@@ -0,0 +1,333 @@
#include "DisplayDriverTFT.h"
#include <Arduino.h>
#include <KlubhausCore.h>
#include <TFT_eSPI.h>
extern DisplayManager display;
// ── Fonts ───────────────────────────────────────────────────
// TFT_eSPI built-in fonts for 320x240 display (further scaled)
// Using FreeFonts for better readability
void DisplayDriverTFT::setTitleFont() { _tft.setFreeFont(&FreeSansBold12pt7b); }
void DisplayDriverTFT::setBodyFont() { _tft.setFreeFont(&FreeSans9pt7b); }
void DisplayDriverTFT::setLabelFont() { _tft.setFreeFont(&FreeSans9pt7b); }
void DisplayDriverTFT::setDefaultFont() { _tft.setTextFont(2); }
// ── Test harness ───────────────────────────────────────────────
// Test harness: parse serial commands to inject synthetic touches
// Commands:
// TEST:touch x y press - simulate press at (x, y)
// TEST:touch x y release - simulate release at (x, y)
// TEST:touch clear - clear test mode
bool DisplayDriverTFT::parseTestTouch(int* outX, int* outY, bool* outPressed) {
if(!Serial.available())
return false;
if(Serial.peek() != 'T') {
return false;
}
String cmd = Serial.readStringUntil('\n');
cmd.trim();
if(!cmd.startsWith("TEST:touch"))
return false;
int firstSpace = cmd.indexOf(' ');
if(firstSpace < 0)
return false;
String args = cmd.substring(firstSpace + 1);
args.trim();
if(args.equals("clear")) {
_testMode = false;
Serial.println("[TEST] Test mode cleared");
return false;
}
int secondSpace = args.indexOf(' ');
if(secondSpace < 0)
return false;
String xStr = args.substring(0, secondSpace);
String yState = args.substring(secondSpace + 1);
yState.trim();
int x = xStr.toInt();
int y = yState.substring(0, yState.indexOf(' ')).toInt();
String state = yState.substring(yState.indexOf(' ') + 1);
state.trim();
bool pressed = state.equals("press");
Serial.printf("[TEST] Injecting touch: (%d,%d) %s\n", x, y, pressed ? "press" : "release");
if(outX)
*outX = x;
if(outY)
*outY = y;
if(outPressed)
*outPressed = pressed;
_testMode = true;
return true;
}
void DisplayDriverTFT::begin() {
// Backlight
pinMode(PIN_LCD_BL, OUTPUT);
digitalWrite(PIN_LCD_BL, LOW);
_tft.init();
_tft.setRotation(DISPLAY_ROTATION);
_tft.fillScreen(TFT_BLACK);
Serial.printf("[GFX] Display OK: %dx%d\n", DISPLAY_WIDTH, DISPLAY_HEIGHT);
ScreenState st;
st.screen = ScreenID::BOOT;
st.bootStage = BootStage::SPLASH;
drawBoot(st);
digitalWrite(PIN_LCD_BL, HIGH);
Serial.println("[GFX] Backlight ON");
}
void DisplayDriverTFT::setBacklight(bool on) { digitalWrite(PIN_LCD_BL, on ? HIGH : LOW); }
// ── Rendering ───────────────────────────────────────────────
void DisplayDriverTFT::render(const ScreenState& st) {
if(st.screen != _lastScreen
|| (st.screen == ScreenID::BOOT && st.bootStage != _lastBootStage)) {
_needsRedraw = true;
_lastScreen = st.screen;
_lastBootStage = st.bootStage;
}
switch(st.screen) {
case ScreenID::BOOT:
if(_needsRedraw) {
drawBoot(st);
_needsRedraw = false;
}
break;
case ScreenID::ALERT:
drawAlert(st);
break;
case ScreenID::DASHBOARD:
if(_needsRedraw) {
drawDashboard(st);
_needsRedraw = false;
}
break;
case ScreenID::OFF:
if(_needsRedraw) {
_tft.fillScreen(TFT_BLACK);
_needsRedraw = false;
}
break;
}
}
void DisplayDriverTFT::drawBoot(const ScreenState& st) {
BootStage stage = st.bootStage;
_tft.fillScreen(TFT_BLACK);
_tft.setTextColor(TFT_WHITE, TFT_BLACK);
_tft.setTextSize(2);
_tft.setCursor(10, 10);
_tft.printf("KLUBHAUS v%s", FW_VERSION);
_tft.setTextSize(1);
_tft.setCursor(10, 40);
_tft.print(BOARD_NAME);
// Show boot stage status
_tft.setCursor(10, 70);
switch(stage) {
case BootStage::SPLASH:
_tft.print("Initializing...");
break;
case BootStage::INIT_DISPLAY:
_tft.print("Display OK");
break;
case BootStage::INIT_NETWORK:
_tft.print("Network init...");
break;
case BootStage::CONNECTING_WIFI:
_tft.print("Connecting WiFi...");
break;
case BootStage::READY:
_tft.print("All systems go!");
break;
case BootStage::DONE:
_tft.print("Ready!");
break;
}
}
void DisplayDriverTFT::drawAlert(const ScreenState& st) {
uint32_t elapsed = millis() - st.alertStartMs;
uint8_t pulse = 180 + (uint8_t)(75.0f * sinf(elapsed / 300.0f));
uint16_t bg = _tft.color565(pulse, 0, 0);
_tft.fillScreen(bg);
_tft.setTextColor(TFT_WHITE, bg);
_tft.setTextSize(3);
_tft.setCursor(10, 20);
_tft.print(st.alertTitle.length() > 0 ? st.alertTitle : "ALERT");
_tft.setTextSize(2);
_tft.setCursor(10, 80);
_tft.print(st.alertBody);
_tft.setTextSize(1);
_tft.setCursor(10, DISPLAY_HEIGHT - 20);
_tft.print("Hold to silence...");
}
void DisplayDriverTFT::drawDashboard(const ScreenState& st) {
_tft.fillScreen(TFT_BLACK);
_tft.setTextColor(TFT_WHITE, TFT_BLACK);
_tft.setTextSize(1);
_tft.setCursor(5, 10);
_tft.printf("KLUBHAUS — %s", deviceStateStr(st.deviceState));
int y = 30;
_tft.setCursor(5, y);
y += 18;
_tft.printf("WiFi: %s %ddBm", st.wifiSsid.c_str(), st.wifiRssi);
_tft.setCursor(5, y);
y += 18;
_tft.printf("IP: %s", st.ipAddr.c_str());
_tft.setCursor(5, y);
y += 18;
_tft.printf("Up: %lus Heap: %d", st.uptimeMs / 1000, ESP.getFreeHeap());
_tft.setCursor(5, y);
y += 18;
_tft.printf("Last poll: %lus ago", st.lastPollMs > 0 ? (millis() - st.lastPollMs) / 1000 : 0);
}
// ── Touch ───────────────────────────────────────────────────
TouchEvent DisplayDriverTFT::readTouch() {
TouchEvent evt;
// Check for test injection via serial
int testX, testY;
bool testPressed;
if(parseTestTouch(&testX, &testY, &testPressed)) {
if(testPressed && !_touchWasPressed) {
evt.pressed = true;
_touchDownX = testX;
_touchDownY = testY;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
} else if(!testPressed && _touchWasPressed) {
evt.released = true;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
}
if(testPressed) {
evt.x = testX;
evt.y = testY;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
}
_touchWasPressed = testPressed;
return evt;
}
uint16_t tx, ty;
uint8_t touched = _tft.getTouch(&tx, &ty, 100);
// Detect transitions (press/release)
if(touched && !_touchWasPressed) {
// Press transition: finger just touched down
evt.pressed = true;
_touchDownX = tx;
_touchDownY = ty;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
} else if(!touched && _touchWasPressed) {
// Release transition: finger just lifted
evt.released = true;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
}
// Current position if still touched
if(touched) {
evt.x = tx;
evt.y = ty;
evt.downX = _touchDownX;
evt.downY = _touchDownY;
}
// Track previous state for next call
_touchWasPressed = touched;
return evt;
}
int DisplayDriverTFT::dashboardTouch(int x, int y) {
// 2x2 grid, accounting for 30px header
if(y < 30)
return -1;
int col = (x * 2) / DISPLAY_WIDTH; // 0 or 1
int row = ((y - 30) * 2) / (DISPLAY_HEIGHT - 30); // 0 or 1
if(col < 0 || col > 1 || row < 0 || row > 1)
return -1;
return row * 2 + col; // 0, 1, 2, or 3
}
HoldState DisplayDriverTFT::updateHold(const TouchEvent& evt, unsigned long holdMs) {
HoldState h;
if(evt.pressed) {
if(!_holdActive) {
_holdActive = true;
_holdStartMs = millis();
h.started = true;
}
uint32_t held = millis() - _holdStartMs;
h.active = true;
h.progress = constrain((float)held / (float)holdMs, 0.0f, 1.0f);
h.completed = (held >= holdMs);
// Simple progress bar at bottom of screen
int barW = (int)(DISPLAY_WIDTH * h.progress);
_tft.fillRect(0, DISPLAY_HEIGHT - 8, barW, 8, TFT_WHITE);
_tft.fillRect(barW, DISPLAY_HEIGHT - 8, DISPLAY_WIDTH - barW, 8, TFT_DARKGREY);
} else {
if(_holdActive) {
// Clear the progress bar when released
_tft.fillRect(0, DISPLAY_HEIGHT - 8, DISPLAY_WIDTH, 8, TFT_DARKGREY);
}
_holdActive = false;
}
return h;
}
void DisplayDriverTFT::drawDebugTouch(int x, int y) {
const int size = 20;
_tft.drawLine(x - size, y, x + size, y, TFT_RED);
_tft.drawLine(x, y - size, x, y + size, TFT_RED);
}
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include "board_config.h"
#include <KlubhausCore.h>
#include <TFT_eSPI.h>
class DisplayDriverTFT : public IDisplayDriver {
public:
void begin() override;
void setBacklight(bool on) override;
void render(const ScreenState& state) override;
TouchEvent readTouch() override;
int dashboardTouch(int x, int y);
HoldState updateHold(const TouchEvent& evt, unsigned long holdMs) override;
int width() override { return DISPLAY_WIDTH; }
int height() override { return DISPLAY_HEIGHT; }
// Fonts
void setTitleFont() override;
void setBodyFont() override;
void setLabelFont() override;
void setDefaultFont() override;
// Debug
void drawDebugTouch(int x, int y) override;
private:
void drawBoot(const ScreenState& st);
void drawAlert(const ScreenState& st);
void drawDashboard(const ScreenState& st);
TFT_eSPI _tft;
bool _holdActive = false;
uint32_t _holdStartMs = 0;
ScreenID _lastScreen = ScreenID::BOOT;
BootStage _lastBootStage = BootStage::SPLASH;
bool _needsRedraw = true;
// Touch tracking for press/release detection
bool _touchWasPressed = false;
int _touchDownX = -1;
int _touchDownY = -1;
// Test mode for touch injection
bool _testMode = false;
bool parseTestTouch(int* outX, int* outY, bool* outPressed);
};
+4
View File
@@ -0,0 +1,4 @@
FQBN="esp32:esp32:esp32:FlashSize=4M,PartitionScheme=default"
PORT="/dev/ttyUSB0"
LIBS="--library ./vendor/esp32-32e/TFT_eSPI"
OPTS="-DDEBUG_MODE -DBOARD_HAS_PSRAM -DLOCAL_SECRETS -DLOAD_GFXFF=1"
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#define BOARD_NAME "esp32-32e"
// ══════════════════════════════════════════════════════════
// TODO: Set these to match YOUR display + wiring.
// Defaults below are for a common ILI9341 320×240 SPI TFT.
// The actual pin mapping must also be set in tft_user_setup.h
// (which gets copied into the vendored TFT_eSPI library).
// ══════════════════════════════════════════════════════════
#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 240
#define DISPLAY_ROTATION 1 // landscape
// Backlight GPIO (directly wired)
#define PIN_LCD_BL 22
// Touch — if using XPT2046 via TFT_eSPI, set TOUCH_CS in tft_user_setup.h
// If using capacitive touch (e.g. FT6236), configure I2C pins here:
// #define TOUCH_SDA 21
// #define TOUCH_SCL 22
// ── Style Constants (CSS-like) ────────────────────────────────────────
// Spacing - scaled for 320x240
#define STYLE_SPACING_X 4
#define STYLE_SPACING_Y 4
#define STYLE_HEADER_HEIGHT 20
#define STYLE_TILE_GAP 4
#define STYLE_TILE_PADDING 6
#define STYLE_TILE_RADIUS 4
// Colors
#define STYLE_COLOR_BG TFT_BLACK
#define STYLE_COLOR_HEADER 0x1A1A
#define STYLE_COLOR_FG TFT_WHITE
#define STYLE_COLOR_ALERT TFT_RED
#define STYLE_COLOR_TILE_1 0x0280
#define STYLE_COLOR_TILE_2 0x0400
#define STYLE_COLOR_TILE_3 0x0440
#define STYLE_COLOR_TILE_4 0x0100
+51
View File
@@ -0,0 +1,51 @@
//
// Klubhaus Doorbell — ESP32-32E target
//
#include "DisplayDriverTFT.h"
#include "board_config.h"
// Include local secrets.h if it exists (board-specific credentials),
// otherwise KlubhausCore/src/secrets.h provides defaults.
#ifdef LOCAL_SECRETS
#include "secrets.h"
#else
#include <secrets.h>
#endif
#include <KlubhausCore.h>
DisplayDriverTFT tftDriver;
DisplayManager display(&tftDriver);
DoorbellLogic logic(&display);
void setup() {
Serial.begin(115200);
delay(500);
logic.begin(FW_VERSION, BOARD_NAME, wifiNetworks, wifiNetworkCount);
logic.finishBoot();
}
void loop() {
// Read touch
TouchEvent evt = display.readTouch();
// State machine tick
logic.update();
// Render current screen
display.render(logic.getScreenState());
// Handle tap gestures
logic.handleTouch(evt);
// Handle hold-to-silence gesture
logic.updateHold(evt);
// Serial console
logic.processSerial();
// Yield to WiFi/BT stack
delay(LOOP_YIELD_MS);
}
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Install TFT_eSPI for esp32-32e
set -euo pipefail
DIR="$(cd "$(dirname "$0")" && pwd)"
VENDOR_DIR="$DIR/../../vendor/esp32-32e"
if [ ! -d "$VENDOR_DIR/TFT_eSPI" ]; then
echo "Cloning TFT_eSPI..."
git clone --depth 1 --branch V2.5.43 \
https://github.com/Bodmer/TFT_eSPI.git \
"$VENDOR_DIR/TFT_eSPI"
fi
cp "$DIR/tft_user_setup.h" "$VENDOR_DIR/TFT_eSPI/User_Setup.h"
echo "[OK] TFT_eSPI vendored for esp32-32e"
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <KlubhausCore.h>
// Copy this file to secrets.h and fill in your credentials.
// secrets.h is gitignored.
static const WiFiCred wifiNetworks[] = {
{ "Your_SSID_1", "password1" },
{ "Your_SSID_2", "password2" },
};
static const int wifiNetworkCount = sizeof(wifiNetworks) / sizeof(wifiNetworks[0]);
+44
View File
@@ -0,0 +1,44 @@
// ═══════════════════════════════════════════════════════════
// TFT_eSPI User_Setup for ESP32-32E target
// This file is copied over vendor/esp32-32e/TFT_eSPI/User_Setup.h
// by the install-libs-32e task.
// ═══════════════════════════════════════════════════════════
#define USER_SETUP_ID 200
// ── Driver ──
#define ILI9341_DRIVER
// ── Resolution ──
// FIXED: Match board_config.h (320x240 landscape)
#define TFT_WIDTH 320
#define TFT_HEIGHT 240
// ── SPI Pins ──
#define TFT_MOSI 23
#define TFT_SCLK 18
#define TFT_CS 5
#define TFT_DC 27
#define TFT_RST 33
// ── Backlight (optional, can also use GPIO directly) ──
// #define TFT_BL 22
// #define TFT_BACKLIGHT_ON HIGH
// ── Touch (XPT2046 resistive) ──
#define TOUCH_CS 14
// ── SPI speed ──
#define SPI_FREQUENCY 40000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
// ── Misc ──
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF
#define SMOOTH_FONT
+585
View File
@@ -0,0 +1,585 @@
// boards/esp32-s3-lcd-43/DisplayDriverGFX.cpp
#include "DisplayDriverGFX.h"
#include "LovyanPins.h"
#include "board_config.h"
#include <Arduino.h>
#include <KlubhausCore.h>
// ── Globals ──
static LGFX* _gfx = nullptr;
extern DisplayManager display;
// ── Forward declarations ──
static void initDisplay();
// ── Dimensions ──
static constexpr int DISP_W = 800;
static constexpr int DISP_H = 480;
// ── Display initialization ──
static void initDisplay() {
Serial.println("LovyanGFX init...");
// Note: LovyanGFX handles I2C internally (port 1 for touch, port 0 for CH422G)
// No need to call Wire.begin() or Wire1.begin()
_gfx = new LGFX();
_gfx->init();
_gfx->setRotation(0); // Landscape
_gfx->fillScreen(0x000000);
Serial.println("Display ready");
}
// ── Singleton ──
DisplayDriverGFX& DisplayDriverGFX::instance() {
static DisplayDriverGFX inst;
return inst;
}
// ── IDisplayDriver implementation ──
void DisplayDriverGFX::begin() {
initDisplay();
// Turn on backlight immediately
setBacklight(true);
}
void DisplayDriverGFX::setBacklight(bool on) {
if(_gfx) {
// LovyanGFX handles backlight via setBrightness
_gfx->setBrightness(on ? 255 : 0);
}
}
int DisplayDriverGFX::width() { return _gfx ? _gfx->width() : DISP_W; }
int DisplayDriverGFX::height() { return _gfx ? _gfx->height() : DISP_H; }
// ── Fonts ──
// LovyanGFX built-in fonts for 800x480 display
void DisplayDriverGFX::setTitleFont() { _gfx->setFont(&fonts::FreeSansBold24pt7b); }
void DisplayDriverGFX::setBodyFont() { _gfx->setFont(&fonts::FreeSans18pt7b); }
void DisplayDriverGFX::setLabelFont() { _gfx->setFont(&fonts::FreeSans12pt7b); }
void DisplayDriverGFX::setDefaultFont() { _gfx->setFont(&fonts::Font2); }
// Transform touch coordinates to match display orientation
// GT911 touch panel on this board is rotated 180° relative to display
void DisplayDriverGFX::transformTouch(int* x, int* y) {
if(!x || !y)
return;
// Flip both axes: (0,0) becomes (width, height)
*x = DISP_W - *x;
*y = DISP_H - *y;
}
// Test harness: parse serial commands to inject synthetic touches
// Commands:
// TEST:touch x y press - simulate press at (x, y) [raw panel coords]
// TEST:touch x y release - simulate release at (x, y)
// TEST:touch clear - clear test mode
bool DisplayDriverGFX::parseTestTouch(int* outX, int* outY, bool* outPressed) {
if(!Serial.available())
return false;
// Only consume if it starts with 'T' - don't steal other commands
// Use "TEST:" prefix to avoid conflict with [CMD] echo
if(Serial.peek() != 'T') {
return false;
}
String cmd = Serial.readStringUntil('\n');
cmd.trim();
if(!cmd.startsWith("TEST:touch"))
return false;
// Parse: touch x y press|release
int firstSpace = cmd.indexOf(' ');
if(firstSpace < 0)
return false;
String args = cmd.substring(firstSpace + 1);
args.trim();
if(args.equals("clear")) {
_testMode = false;
Serial.println("[TEST] Test mode cleared");
return false;
}
// Parse x y state
int secondSpace = args.indexOf(' ');
if(secondSpace < 0)
return false;
String xStr = args.substring(0, secondSpace);
String yState = args.substring(secondSpace + 1);
yState.trim();
int x = xStr.toInt();
int y = yState.substring(0, yState.indexOf(' ')).toInt();
String state = yState.substring(yState.indexOf(' ') + 1);
state.trim();
bool pressed = state.equals("press");
Serial.printf("[TEST] Injecting touch: (%d,%d) %s\n", x, y, pressed ? "press" : "release");
if(outX)
*outX = x;
if(outY)
*outY = y;
if(outPressed)
*outPressed = pressed;
_testMode = true;
return true;
}
// Touch handling
TouchEvent DisplayDriverGFX::readTouch() {
TouchEvent evt;
if(!_gfx)
return evt;
// Check for test injection via serial
int testX, testY;
bool testPressed;
if(parseTestTouch(&testX, &testY, &testPressed)) {
// Handle test touch with same logic as real touch
unsigned long now = millis();
if(testPressed && !_lastTouch.pressed) {
evt.pressed = true;
evt.downX = testX;
evt.downY = testY;
_lastTouch.downX = evt.downX;
_lastTouch.downY = evt.downY;
_touchBounced = false;
} else if(!testPressed && _lastTouch.pressed) {
evt.released = true;
evt.downX = _lastTouch.downX;
evt.downY = _lastTouch.downY;
_lastReleaseMs = now;
_touchBounced = true;
}
if(testPressed) {
evt.x = testX;
evt.y = testY;
evt.downX = _lastTouch.downX;
evt.downY = _lastTouch.downY;
_pressStartMs = millis();
}
if(_touchBounced && now - _lastReleaseMs >= TOUCH_DEBOUNCE_MS) {
_touchBounced = false;
}
_lastTouch.pressed = testPressed;
if(testPressed) {
_lastTouch.x = evt.x;
_lastTouch.y = evt.y;
}
return evt;
}
int32_t x, y;
bool pressed = _gfx->getTouch(&x, &y);
// Filter out invalid coordinates (touch panel can return garbage on release)
// Ignore both press and release transitions when coordinates are out of bounds
bool validCoords = !(x < 0 || x > DISP_W || y < 0 || y > DISP_H);
if(!validCoords) {
pressed = false;
// Don't update _lastTouch.pressed - keep previous state to avoid false release
return evt;
}
// Debounce: ignore repeated press events within debounce window after release
unsigned long now = millis();
if(pressed && _touchBounced) {
// Within debounce window - ignore this press
_lastTouch.pressed = pressed;
return evt;
}
// Detect transitions (press/release)
if(pressed && !_lastTouch.pressed) {
// Press transition: finger just touched down
evt.pressed = true;
evt.downX = static_cast<int>(x);
evt.downY = static_cast<int>(y);
_lastTouch.downX = evt.downX;
_lastTouch.downY = evt.downY;
_touchBounced = false;
} else if(!pressed && _lastTouch.pressed) {
// Release transition: finger just lifted
evt.released = true;
evt.downX = _lastTouch.downX;
evt.downY = _lastTouch.downY;
// Start debounce window
_lastReleaseMs = now;
_touchBounced = true;
}
// Current position if still touched
if(pressed) {
evt.x = static_cast<int>(x);
evt.y = static_cast<int>(y);
evt.downX = _lastTouch.downX;
evt.downY = _lastTouch.downY;
_pressStartMs = millis();
}
// Check if debounce window has expired
if(_touchBounced && now - _lastReleaseMs >= TOUCH_DEBOUNCE_MS) {
_touchBounced = false;
}
// Track previous state
_lastTouch.pressed = pressed;
if(pressed) {
_lastTouch.x = evt.x;
_lastTouch.y = evt.y;
}
return evt;
}
int DisplayDriverGFX::dashboardTouch(int x, int y) {
// Dashboard tiles: 2 rows × 4 columns
constexpr int cols = 4;
constexpr int rows = 2;
constexpr int tileW = DISP_W / cols;
constexpr int tileH = DISP_H / rows;
if(x < 0 || x >= DISP_W || y < 0 || y >= DISP_H) {
return -1;
}
int col = x / tileW;
int row = y / tileH;
return row * cols + col;
}
HoldState DisplayDriverGFX::updateHold(const TouchEvent& evt, unsigned long holdMs) {
HoldState state;
if(!evt.pressed) {
_isHolding = false;
return state;
}
unsigned long elapsed = millis() - _pressStartMs;
if(!_isHolding) {
_isHolding = true;
state.started = true;
}
state.active = true;
state.progress = static_cast<float>(elapsed) / static_cast<float>(holdMs);
if(state.progress >= 1.0f) {
state.progress = 1.0f;
state.completed = true;
}
return state;
}
// ── Rendering ──
void DisplayDriverGFX::render(const ScreenState& state) {
if(!_gfx)
return;
// Check if we need full redraw
if(state.screen != _lastScreen
|| (state.screen == ScreenID::BOOT && state.bootStage != _lastBootStage)) {
_needsRedraw = true;
_lastScreen = state.screen;
_lastBootStage = state.bootStage;
}
switch(state.screen) {
case ScreenID::BOOT:
if(_needsRedraw) {
drawBoot(state);
_needsRedraw = false;
}
break;
case ScreenID::OFF:
if(_needsRedraw) {
_gfx->fillScreen(0x000000);
_needsRedraw = false;
}
break;
case ScreenID::ALERT:
// Only redraw on first entry or screen change
if(_needsRedraw) {
drawAlert(state);
_needsRedraw = false;
}
break;
case ScreenID::DASHBOARD:
if(_needsRedraw) {
drawDashboard(state);
_needsRedraw = false;
}
break;
case ScreenID::STATUS:
if(_needsRedraw) {
drawStatus(state);
_needsRedraw = false;
}
break;
}
}
void DisplayDriverGFX::drawBoot(const ScreenState& state) {
BootStage stage = state.bootStage;
_gfx->fillScreen(TFT_BLACK);
_gfx->setTextColor(STYLE_COLOR_FG);
setTitleFont();
_gfx->setCursor(STYLE_SPACING_X, STYLE_SPACING_Y);
_gfx->print("KLUBHAUS");
setBodyFont();
_gfx->setCursor(STYLE_SPACING_X, STYLE_SPACING_Y + STYLE_HEADER_HEIGHT);
_gfx->print(BOARD_NAME);
// Show boot stage status
setLabelFont();
_gfx->setCursor(STYLE_SPACING_X, STYLE_SPACING_Y + STYLE_HEADER_HEIGHT + 30);
switch(stage) {
case BootStage::SPLASH:
_gfx->print("Initializing...");
break;
case BootStage::INIT_DISPLAY:
_gfx->print("Display OK");
break;
case BootStage::INIT_NETWORK:
_gfx->print("Network init...");
break;
case BootStage::CONNECTING_WIFI:
_gfx->print("Connecting WiFi...");
break;
case BootStage::READY:
_gfx->print("All systems go!");
break;
case BootStage::DONE:
_gfx->print("Ready!");
break;
}
}
void DisplayDriverGFX::drawAlert(const ScreenState& state) {
uint32_t elapsed = millis() - state.alertStartMs;
uint8_t pulse = static_cast<uint8_t>(180.0f + 75.0f * sinf(elapsed / 300.0f));
uint16_t bg = _gfx->color565(pulse, 0, 0);
_gfx->fillScreen(bg);
_gfx->setTextColor(STYLE_COLOR_FG, bg);
setTitleFont();
_gfx->setCursor(STYLE_SPACING_X, STYLE_HEADER_HEIGHT);
_gfx->print(state.alertTitle.length() > 0 ? state.alertTitle.c_str() : "ALERT");
setBodyFont();
_gfx->setCursor(STYLE_SPACING_X, STYLE_HEADER_HEIGHT + 50);
_gfx->print(state.alertBody);
setLabelFont();
_gfx->setCursor(STYLE_SPACING_X, DISP_H - STYLE_SPACING_Y);
_gfx->print("Hold to silence...");
}
void DisplayDriverGFX::drawDashboard(const ScreenState& state) {
_gfx->fillScreen(STYLE_COLOR_BG);
// Header - use Layout for safe positioning
_gfx->fillRect(0, 0, DISP_W, STYLE_HEADER_HEIGHT, STYLE_COLOR_HEADER);
Layout header = Layout::header(DISP_W, STYLE_HEADER_HEIGHT);
Layout safeText = header.padded(STYLE_SPACING_X);
setBodyFont();
_gfx->setTextColor(STYLE_COLOR_FG);
// Title with scrolling support
_headerScroller.setText("KLUBHAUS");
_headerScroller.setScrollSpeed(80);
_headerScroller.setPauseDuration(2000);
_headerScroller.render(
[&](int16_t x, const char* s) {
_gfx->setCursor(safeText.x + x, safeText.y + 4);
_gfx->print(s);
},
safeText.w);
// WiFi status - right aligned with scrolling
Layout wifiArea(DISP_W - 150, 0, 140, STYLE_HEADER_HEIGHT);
Layout safeWifi = wifiArea.padded(4);
_wifiScroller.setText(state.wifiSsid.length() > 0 ? state.wifiSsid.c_str() : "WiFi: OFF");
_wifiScroller.setScrollSpeed(60);
_wifiScroller.setPauseDuration(1500);
_wifiScroller.render(
[&](int16_t x, const char* s) {
_gfx->setCursor(safeWifi.x + x, safeWifi.y + 4);
_gfx->print(s);
},
safeWifi.w);
// Get tile layouts from library helper
int tileCount = display.calculateDashboardLayouts(STYLE_HEADER_HEIGHT, STYLE_TILE_GAP);
display.setHeaderHeight(STYLE_HEADER_HEIGHT);
const TileLayout* layouts = display.getTileLayouts();
const char* tileLabels[] = { "Alert", "Silent", "Status", "Reboot" };
const uint16_t tileColors[] = { 0x0280, 0x0400, 0x0440, 0x0100 };
for(int i = 0; i < tileCount && i < 4; i++) {
const TileLayout& lay = layouts[i];
int x = lay.x;
int y = lay.y;
int w = lay.w;
int h = lay.h;
// Tile background
_gfx->fillRoundRect(x, y, w, h, STYLE_TILE_RADIUS, tileColors[i]);
// Tile border
_gfx->drawRoundRect(x, y, w, h, STYLE_TILE_RADIUS, STYLE_COLOR_FG);
// Tile label
_gfx->setTextColor(STYLE_COLOR_FG);
setBodyFont();
int textLen = strlen(tileLabels[i]);
int textW = textLen * 14;
_gfx->setCursor(x + w / 2 - textW / 2, y + h / 2 - 10);
_gfx->print(tileLabels[i]);
}
}
void DisplayDriverGFX::drawStatus(const ScreenState& st) {
_gfx->fillScreen(STYLE_COLOR_BG);
// Header with title and back button
_gfx->fillRect(0, 0, DISP_W, STYLE_HEADER_HEIGHT, STYLE_COLOR_HEADER);
Layout header = Layout::header(DISP_W, STYLE_HEADER_HEIGHT);
Layout safeText = header.padded(STYLE_SPACING_X);
setTitleFont();
_gfx->setTextColor(STYLE_COLOR_FG);
_gfx->setCursor(safeText.x, safeText.y + 4);
_gfx->print("STATUS");
// Back button in lower-right
setLabelFont();
_gfx->setCursor(DISP_W - 60, DISP_H - 20);
_gfx->print("[BACK]");
// Status items in 2-column layout
setBodyFont();
int colWidth = DISP_W / 2;
int startY = STYLE_HEADER_HEIGHT + 30;
int rowHeight = 35;
int labelX = STYLE_SPACING_X + 10;
int valueX = STYLE_SPACING_X + 120;
// Column 1
int y = startY;
// WiFi SSID
_gfx->setTextColor(0x8888);
_gfx->setCursor(labelX, y);
_gfx->print("WiFi:");
_gfx->setTextColor(STYLE_COLOR_FG);
_gfx->setCursor(valueX, y);
_gfx->print(st.wifiSsid.length() > 0 ? st.wifiSsid.c_str() : "N/A");
// RSSI
y += rowHeight;
_gfx->setTextColor(0x8888);
_gfx->setCursor(labelX, y);
_gfx->print("Signal:");
_gfx->setTextColor(STYLE_COLOR_FG);
_gfx->setCursor(valueX, y);
_gfx->printf("%d dBm", st.wifiRssi);
// IP Address
y += rowHeight;
_gfx->setTextColor(0x8888);
_gfx->setCursor(labelX, y);
_gfx->print("IP:");
_gfx->setTextColor(STYLE_COLOR_FG);
_gfx->setCursor(valueX, y);
_gfx->print(st.ipAddr.length() > 0 ? st.ipAddr.c_str() : "N/A");
// Column 2
y = startY;
// Uptime
uint32_t upSec = st.uptimeMs / 1000;
uint32_t upMin = upSec / 60;
uint32_t upHr = upMin / 60;
upSec = upSec % 60;
upMin = upMin % 60;
_gfx->setTextColor(0x8888);
_gfx->setCursor(colWidth + labelX, y);
_gfx->print("Uptime:");
_gfx->setTextColor(STYLE_COLOR_FG);
_gfx->setCursor(colWidth + valueX, y);
_gfx->printf("%02lu:%02lu:%02lu", upHr, upMin, upSec);
// Heap
y += rowHeight;
_gfx->setTextColor(0x8888);
_gfx->setCursor(colWidth + labelX, y);
_gfx->print("Heap:");
_gfx->setTextColor(STYLE_COLOR_FG);
_gfx->setCursor(colWidth + valueX, y);
_gfx->printf("%d KB", ESP.getFreeHeap() / 1024);
// Last Poll
y += rowHeight;
_gfx->setTextColor(0x8888);
_gfx->setCursor(colWidth + labelX, y);
_gfx->print("Last Poll:");
_gfx->setTextColor(STYLE_COLOR_FG);
_gfx->setCursor(colWidth + valueX, y);
uint32_t pollAgo = (millis() - st.lastPollMs) / 1000;
if(pollAgo < 60) {
_gfx->printf("%lu sec", pollAgo);
} else {
_gfx->printf("%lu min", pollAgo / 60);
}
// Footer with firmware version
setLabelFont();
_gfx->setTextColor(0x6666);
_gfx->setCursor(STYLE_SPACING_X, DISP_H - STYLE_SPACING_Y);
_gfx->print("v" FW_VERSION);
}
void DisplayDriverGFX::drawDebugTouch(int x, int y) {
if(!_gfx)
return;
const int size = 20;
_gfx->drawLine(x - size, y, x + size, y, TFT_RED);
_gfx->drawLine(x, y - size, x, y + size, TFT_RED);
}
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include "IDisplayDriver.h"
#include "Style.h"
#include <Arduino.h>
class DisplayDriverGFX : public IDisplayDriver {
public:
// ── IDisplayDriver ──
void begin() override;
void setBacklight(bool on) override;
void render(const ScreenState& state) override;
TouchEvent readTouch() override;
HoldState updateHold(const TouchEvent& evt, unsigned long holdMs) override;
void drawDebugTouch(int x, int y) override;
int width() override;
int height() override;
// Fonts
void setTitleFont() override;
void setBodyFont() override;
void setLabelFont() override;
void setDefaultFont() override;
// Transform touch coordinates (handles rotated touch panels)
void transformTouch(int* x, int* y) override;
// Dashboard tile mapping
int dashboardTouch(int x, int y);
// ── Internal ──
static DisplayDriverGFX& instance();
private:
// Test harness: parse serial commands to inject synthetic touches
bool parseTestTouch(int* outX, int* outY, bool* outPressed);
// Helper rendering functions
void drawBoot(const ScreenState& state);
void drawAlert(const ScreenState& state);
void drawDashboard(const ScreenState& state);
void drawStatus(const ScreenState& state);
// Touch handling
TouchEvent _lastTouch = { false, false, 0, 0, -1, -1 };
unsigned long _pressStartMs = 0;
bool _isHolding = false;
unsigned long _lastReleaseMs = 0;
bool _touchBounced = false;
bool _testMode = false;
// Screen tracking
ScreenID _lastScreen = ScreenID::BOOT;
BootStage _lastBootStage = BootStage::SPLASH;
bool _needsRedraw = true;
// Text scrollers for scrolling elements
TextScroller _headerScroller;
TextScroller _wifiScroller;
};
+118
View File
@@ -0,0 +1,118 @@
#pragma once
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp>
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
// ── Display dimensions ──
#define TFT_HOR_RES 800
#define TFT_VER_RES 480
// ── Touch I2C (from Westcott example) ──
#define TOUCH_SDA 8
#define TOUCH_SCL 9
#define TOUCH_INT 4
#define TOUCH_RST -1
// ── CH422G Expander pins ──
#define TP_RST 1
#define LCD_BL 2
#define LCD_RST 3
#define SD_CS 4
#define USB_SEL 5
class LGFX : public lgfx::LGFX_Device {
public:
lgfx::Bus_RGB _bus_instance;
lgfx::Panel_RGB _panel_instance;
lgfx::Touch_GT911 _touch_instance;
LGFX(void) {
// Panel config
{
auto cfg = _panel_instance.config();
cfg.memory_width = TFT_HOR_RES;
cfg.memory_height = TFT_VER_RES;
cfg.panel_width = TFT_HOR_RES;
cfg.panel_height = TFT_VER_RES;
cfg.offset_x = 0;
cfg.offset_y = 0;
_panel_instance.config(cfg);
}
// RGB parallel bus config (from Westcott)
{
auto cfg = _bus_instance.config();
cfg.panel = &_panel_instance;
// Blue channel
cfg.pin_d0 = 14;
cfg.pin_d1 = 38;
cfg.pin_d2 = 18;
cfg.pin_d3 = 17;
cfg.pin_d4 = 10;
// Green channel
cfg.pin_d5 = 39;
cfg.pin_d6 = 0;
cfg.pin_d7 = 45;
cfg.pin_d8 = 48;
cfg.pin_d9 = 47;
cfg.pin_d10 = 21;
// Red channel
cfg.pin_d11 = 1;
cfg.pin_d12 = 2;
cfg.pin_d13 = 42;
cfg.pin_d14 = 41;
cfg.pin_d15 = 40;
// Timing
cfg.pin_henable = 5;
cfg.pin_vsync = 3;
cfg.pin_hsync = 46;
cfg.pin_pclk = 7;
cfg.freq_write = 14000000;
cfg.hsync_polarity = 0;
cfg.hsync_front_porch = 20;
cfg.hsync_pulse_width = 10;
cfg.hsync_back_porch = 10;
cfg.vsync_polarity = 0;
cfg.vsync_front_porch = 10;
cfg.vsync_pulse_width = 10;
cfg.vsync_back_porch = 10;
cfg.pclk_active_neg = 0;
cfg.de_idle_high = 0;
cfg.pclk_idle_high = 0;
_bus_instance.config(cfg);
}
_panel_instance.setBus(&_bus_instance);
// Touch config (I2C port 1, address 0x14 - from Westcott!)
{
auto cfg = _touch_instance.config();
cfg.x_min = 0;
cfg.x_max = TFT_HOR_RES - 1;
cfg.y_min = 0;
cfg.y_max = TFT_VER_RES - 1;
cfg.pin_int = TOUCH_INT;
cfg.pin_rst = TOUCH_RST;
cfg.bus_shared = false;
cfg.offset_rotation = 0;
cfg.i2c_port = I2C_NUM_1; // IMPORTANT: Port 1, not 0!
cfg.pin_sda = TOUCH_SDA;
cfg.pin_scl = TOUCH_SCL;
cfg.freq = 400000;
cfg.i2c_addr = 0x14; // IMPORTANT: Address 0x14, not 0x5D!
_touch_instance.config(cfg);
_panel_instance.setTouch(&_touch_instance);
}
setPanel(&_panel_instance);
}
};
+4
View File
@@ -0,0 +1,4 @@
FQBN="esp32:esp32:waveshare_esp32_s3_touch_lcd_43:PSRAM=enabled,FlashSize=16M,USBMode=hwcdc,PartitionScheme=app3M_fat9M_16MB"
PORT="/dev/ttyUSB0"
LIBS="--library ./vendor/esp32-s3-lcd-43/LovyanGFX"
OPTS="-DDEBUG_MODE -DBOARD_HAS_PSRAM -DLGFX_USE_V1 -DLOCAL_SECRETS"
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#define BOARD_NAME "esp32-s3-lcd-43"
#define DISPLAY_WIDTH 800
#define DISPLAY_HEIGHT 480
#define DISPLAY_ROTATION 0
// ── RGB parallel bus (from Westcott1 reference) ──
#define LCD_DE 5
#define LCD_VSYNC 3
#define LCD_HSYNC 46
#define LCD_PCLK 7
#define LCD_R0 1
#define LCD_R1 2
#define LCD_R2 42
#define LCD_R3 41
#define LCD_R4 40
#define LCD_G0 39
#define LCD_G1 0
#define LCD_G2 45
#define LCD_G3 48
#define LCD_G4 47
#define LCD_G5 21
#define LCD_B0 14
#define LCD_B1 38
#define LCD_B2 18
#define LCD_B3 17
#define LCD_B4 10
// ── I2C bus (shared: CH422G + GT911) ──
#define I2C_MASTER_NUM ((i2c_port_t)0)
#define I2C_MASTER_SDA 8
#define I2C_MASTER_SCL 9
// ── GT911 Touch ──
#define GT911_ADDR 0x5D
// #define TOUCH_INT -1
// ── Style Constants (CSS-like) ────────────────────────────────────────
// Spacing
#define STYLE_SPACING_X 10
#define STYLE_SPACING_Y 10
#define STYLE_HEADER_HEIGHT 45
#define STYLE_TILE_GAP 8
#define STYLE_TILE_PADDING 16
#define STYLE_TILE_RADIUS 8
// Colors
#define STYLE_COLOR_BG 0x001030 // Dark blue
#define STYLE_COLOR_HEADER 0x1A1A // Dark gray
#define STYLE_COLOR_FG TFT_WHITE
#define STYLE_COLOR_ALERT TFT_RED
#define STYLE_COLOR_TILE_1 0x0280 // Green
#define STYLE_COLOR_TILE_2 0x0400 // Dark green
#define STYLE_COLOR_TILE_3 0x0440 // Teal
#define STYLE_COLOR_TILE_4 0x0100 // Dark red
@@ -0,0 +1,41 @@
//
// Klubhaus Doorbell — ESP32-S3-Touch-LCD-4.3 target
//
#include "DisplayDriverGFX.h"
#include "board_config.h"
#include "secrets.h"
#include <KlubhausCore.h>
DisplayDriverGFX gfxDriver;
DisplayManager display(&gfxDriver);
DoorbellLogic logic(&display);
void setup() {
Serial.begin(115200);
delay(500);
logic.begin(FW_VERSION, BOARD_NAME, wifiNetworks, wifiNetworkCount);
logic.finishBoot();
}
void loop() {
// Read touch (includes test injection via serial "touch x y press/release")
TouchEvent evt = display.readTouch();
// State machine tick
logic.update();
// Render current screen
display.render(logic.getScreenState());
// Handle tap gestures
logic.handleTouch(evt);
// Handle hold-to-silence gesture
logic.updateHold(evt);
// Serial console
logic.processSerial();
}
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Install LovyanGFX for esp32-s3-lcd-43
set -euo pipefail
DIR="$(cd "$(dirname "$0")" && pwd)"
VENDOR_DIR="$DIR/../../vendor/esp32-s3-lcd-43"
LOVGFX_DIR="$VENDOR_DIR/LovyanGFX"
if [ ! -d "$LOVGFX_DIR" ]; then
echo "Cloning LovyanGFX..."
git clone --depth 1 \
https://github.com/lovyan03/LovyanGFX.git \
"$LOVGFX_DIR"
fi
# Create library.properties
cat > "$LOVGFX_DIR/library.properties" << 'EOF'
name=LovyanGFX
version=1.2.0
author=lovyan03
maintainer=lovyan03
sentence=Display and touch driver library for ESP32
paragraph=Universal graphics library for ESP32 with support for various displays and touch controllers
category=Display
url=https://github.com/lovyan03/LovyanGFX
architectures=esp32
includes=LovyanGFX.hpp
EOF
mkdir -p "$LOVGFX_DIR/src"
echo "[OK] LovyanGFX vendored for esp32-s3-lcd-43"
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <KlubhausCore.h>
// Copy this file to secrets.h and fill in your credentials.
// secrets.h is gitignored.
static const WiFiCred wifiNetworks[] = {
{ "Your_SSID_1", "password1" },
{ "Your_SSID_2", "password2" },
};
static const int wifiNetworkCount = sizeof(wifiNetworks) / sizeof(wifiNetworks[0]);
-243
View File
@@ -1,243 +0,0 @@
#include "Display_ST7789.h"
#define SPI_WRITE(_dat) SPI.transfer(_dat)
#define SPI_WRITE_Word(_dat) SPI.transfer16(_dat)
void SPI_Init()
{
SPI.begin(EXAMPLE_PIN_NUM_SCLK,EXAMPLE_PIN_NUM_MISO,EXAMPLE_PIN_NUM_MOSI);
}
void LCD_WriteCommand(uint8_t Cmd)
{
SPI.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, LOW);
SPI_WRITE(Cmd);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
SPI.endTransaction();
}
void LCD_WriteData(uint8_t Data)
{
SPI.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH);
SPI_WRITE(Data);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
SPI.endTransaction();
}
void LCD_WriteData_Word(uint16_t Data)
{
SPI.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH);
SPI_WRITE_Word(Data);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
SPI.endTransaction();
}
void LCD_WriteData_nbyte(uint8_t* SetData,uint8_t* ReadData,uint32_t Size)
{
SPI.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH);
SPI.transferBytes(SetData, ReadData, Size);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
SPI.endTransaction();
}
void LCD_Reset(void)
{
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
delay(50);
digitalWrite(EXAMPLE_PIN_NUM_LCD_RST, LOW);
delay(50);
digitalWrite(EXAMPLE_PIN_NUM_LCD_RST, HIGH);
delay(50);
}
void LCD_Init(void)
{
pinMode(EXAMPLE_PIN_NUM_LCD_CS, OUTPUT);
pinMode(EXAMPLE_PIN_NUM_LCD_DC, OUTPUT);
pinMode(EXAMPLE_PIN_NUM_LCD_RST, OUTPUT);
Backlight_Init();
SPI_Init();
LCD_Reset();
//************* Start Initial Sequence **********//
LCD_WriteCommand(0x11);
delay(120);
LCD_WriteCommand(0x36);
if (HORIZONTAL)
LCD_WriteData(0x00);
else
LCD_WriteData(0x70);
LCD_WriteCommand(0x3A);
LCD_WriteData(0x05);
LCD_WriteCommand(0xB0);
LCD_WriteData(0x00);
LCD_WriteData(0xE8);
LCD_WriteCommand(0xB2);
LCD_WriteData(0x0C);
LCD_WriteData(0x0C);
LCD_WriteData(0x00);
LCD_WriteData(0x33);
LCD_WriteData(0x33);
LCD_WriteCommand(0xB7);
LCD_WriteData(0x35);
LCD_WriteCommand(0xBB);
LCD_WriteData(0x35);
LCD_WriteCommand(0xC0);
LCD_WriteData(0x2C);
LCD_WriteCommand(0xC2);
LCD_WriteData(0x01);
LCD_WriteCommand(0xC3);
LCD_WriteData(0x13);
LCD_WriteCommand(0xC4);
LCD_WriteData(0x20);
LCD_WriteCommand(0xC6);
LCD_WriteData(0x0F);
LCD_WriteCommand(0xD0);
LCD_WriteData(0xA4);
LCD_WriteData(0xA1);
LCD_WriteCommand(0xD6);
LCD_WriteData(0xA1);
LCD_WriteCommand(0xE0);
LCD_WriteData(0xF0);
LCD_WriteData(0x00);
LCD_WriteData(0x04);
LCD_WriteData(0x04);
LCD_WriteData(0x04);
LCD_WriteData(0x05);
LCD_WriteData(0x29);
LCD_WriteData(0x33);
LCD_WriteData(0x3E);
LCD_WriteData(0x38);
LCD_WriteData(0x12);
LCD_WriteData(0x12);
LCD_WriteData(0x28);
LCD_WriteData(0x30);
LCD_WriteCommand(0xE1);
LCD_WriteData(0xF0);
LCD_WriteData(0x07);
LCD_WriteData(0x0A);
LCD_WriteData(0x0D);
LCD_WriteData(0x0B);
LCD_WriteData(0x07);
LCD_WriteData(0x28);
LCD_WriteData(0x33);
LCD_WriteData(0x3E);
LCD_WriteData(0x36);
LCD_WriteData(0x14);
LCD_WriteData(0x14);
LCD_WriteData(0x29);
LCD_WriteData(0x32);
LCD_WriteCommand(0x21);
LCD_WriteCommand(0x11);
delay(120);
LCD_WriteCommand(0x29);
}
/******************************************************************************
function: Set the cursor position
parameter :
Xstart: Start uint16_t x coordinate
Ystart: Start uint16_t y coordinate
Xend : End uint16_t coordinates
Yend : End uint16_t coordinatesen
******************************************************************************/
void LCD_SetCursor(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend)
{
if (HORIZONTAL) {
// set the X coordinates
LCD_WriteCommand(0x2A);
LCD_WriteData(Xstart >> 8);
LCD_WriteData(Xstart + Offset_X);
LCD_WriteData(Xend >> 8);
LCD_WriteData(Xend + Offset_X);
// set the Y coordinates
LCD_WriteCommand(0x2B);
LCD_WriteData(Ystart >> 8);
LCD_WriteData(Ystart + Offset_Y);
LCD_WriteData(Yend >> 8);
LCD_WriteData(Yend + Offset_Y);
}
else {
// set the X coordinates
LCD_WriteCommand(0x2A);
LCD_WriteData(Ystart >> 8);
LCD_WriteData(Ystart + Offset_Y);
LCD_WriteData(Yend >> 8);
LCD_WriteData(Yend + Offset_Y);
// set the Y coordinates
LCD_WriteCommand(0x2B);
LCD_WriteData(Xstart >> 8);
LCD_WriteData(Xstart + Offset_X);
LCD_WriteData(Xend >> 8);
LCD_WriteData(Xend + Offset_X);
}
LCD_WriteCommand(0x2C);
}
/******************************************************************************
function: Refresh the image in an area
parameter :
Xstart: Start uint16_t x coordinate
Ystart: Start uint16_t y coordinate
Xend : End uint16_t coordinates
Yend : End uint16_t coordinates
color : Set the color
******************************************************************************/
void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,uint16_t* color)
{
// uint16_t i,j;
// LCD_SetCursor(Xstart, Ystart, Xend,Yend);
// uint16_t Show_Width = Xend - Xstart + 1;
// uint16_t Show_Height = Yend - Ystart + 1;
// for(i = 0; i < Show_Height; i++){
// for(j = 0; j < Show_Width; j++){
// LCD_WriteData_Word(color[(i*(Show_Width))+j]);
// }
// }
uint16_t Show_Width = Xend - Xstart + 1;
uint16_t Show_Height = Yend - Ystart + 1;
uint32_t numBytes = Show_Width * Show_Height * sizeof(uint16_t);
uint8_t Read_D[numBytes];
LCD_SetCursor(Xstart, Ystart, Xend, Yend);
LCD_WriteData_nbyte((uint8_t*)color, Read_D, numBytes);
}
// backlight
void Backlight_Init(void)
{
ledcAttach(EXAMPLE_PIN_NUM_BK_LIGHT, Frequency, Resolution);
ledcWrite(EXAMPLE_PIN_NUM_BK_LIGHT, 100);
}
void Set_Backlight(uint8_t Light) //
{
if(Light > 100 || Light < 0)
printf("Set Backlight parameters in the range of 0 to 100 \r\n");
else{
uint32_t Backlight = Light*10;
ledcWrite(EXAMPLE_PIN_NUM_BK_LIGHT, Backlight);
}
}
-32
View File
@@ -1,32 +0,0 @@
#pragma once
#include <Arduino.h>
#include <SPI.h>
#define LCD_WIDTH 172 //LCD width
#define LCD_HEIGHT 320 //LCD height
#define SPIFreq 80000000
#define EXAMPLE_PIN_NUM_MISO 5
#define EXAMPLE_PIN_NUM_MOSI 6
#define EXAMPLE_PIN_NUM_SCLK 7
#define EXAMPLE_PIN_NUM_LCD_CS 14
#define EXAMPLE_PIN_NUM_LCD_DC 15
#define EXAMPLE_PIN_NUM_LCD_RST 21
#define EXAMPLE_PIN_NUM_BK_LIGHT 22
#define Frequency 1000
#define Resolution 10
#define VERTICAL 0
#define HORIZONTAL 1
#define Offset_X 34
#define Offset_Y 0
void LCD_SetCursor(uint16_t x1, uint16_t y1, uint16_t x2,uint16_t y2);
void LCD_Init(void);
void LCD_SetCursor(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend);
void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,uint16_t* color);
void Backlight_Init(void);
void Set_Backlight(uint8_t Light);
-26
View File
@@ -1,26 +0,0 @@
#include "SD_Card.h"
#include "Display_ST7789.h"
#include "LVGL_Driver.h"
#include "LVGL_Example.h"
void setup()
{
Flash_test();
LCD_Init();
Lvgl_Init();
SD_Init();
Lvgl_Example1();
// lv_demo_widgets();
// lv_demo_benchmark();
// lv_demo_keypad_encoder();
// lv_demo_music();
// lv_demo_stress();
Wireless_Test2();
}
void loop()
{
Timer_Loop();
delay(5);
}
-84
View File
@@ -1,84 +0,0 @@
/*****************************************************************************
| File : LVGL_Driver.c
| help :
The provided LVGL library file must be installed first
******************************************************************************/
#include "LVGL_Driver.h"
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[ LVGL_BUF_LEN ];
static lv_color_t buf2[ LVGL_BUF_LEN ];
// static lv_color_t* buf1 = (lv_color_t*) heap_caps_malloc(LVGL_BUF_LEN, MALLOC_CAP_SPIRAM);
// static lv_color_t* buf2 = (lv_color_t*) heap_caps_malloc(LVGL_BUF_LEN, MALLOC_CAP_SPIRAM);
/* Serial debugging */
void Lvgl_print(const char * buf)
{
// Serial.printf(buf);
// Serial.flush();
}
/* Display flushing
Displays LVGL content on the LCD
This function implements associating LVGL data to the LCD screen
*/
void Lvgl_Display_LCD( lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p )
{
LCD_addWindow(area->x1, area->y1, area->x2, area->y2, ( uint16_t *)&color_p->full);
lv_disp_flush_ready( disp_drv );
}
/*Read the touchpad*/
void Lvgl_Touchpad_Read( lv_indev_drv_t * indev_drv, lv_indev_data_t * data )
{
// NULL
}
void example_increase_lvgl_tick(void *arg)
{
/* Tell LVGL how many milliseconds has elapsed */
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}
void Lvgl_Init(void)
{
lv_init();
lv_disp_draw_buf_init( &draw_buf, buf1, buf2, LVGL_BUF_LEN);
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init( &disp_drv );
/*Change the following line to your display resolution*/
disp_drv.hor_res = LVGL_WIDTH;
disp_drv.ver_res = LVGL_HEIGHT;
disp_drv.flush_cb = Lvgl_Display_LCD;
disp_drv.full_refresh = 1; /**< 1: Always make the whole screen redrawn*/
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register( &disp_drv );
/*Initialize the (dummy) input device driver*/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init( &indev_drv );
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = Lvgl_Touchpad_Read;
lv_indev_drv_register( &indev_drv );
/* Create simple label */
lv_obj_t *label = lv_label_create( lv_scr_act() );
lv_label_set_text( label, "Hello Ardino and LVGL!");
lv_obj_align( label, LV_ALIGN_CENTER, 0, 0 );
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &example_increase_lvgl_tick,
.name = "lvgl_tick"
};
esp_timer_handle_t lvgl_tick_timer = NULL;
esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer);
esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000);
}
void Timer_Loop(void)
{
lv_timer_handler(); /* let the GUI do its work */
// delay( 5 );
}
-22
View File
@@ -1,22 +0,0 @@
#pragma once
#include <lvgl.h>
#include <lv_conf.h>
#include <demos/lv_demos.h>
#include <esp_heap_caps.h>
#include "Display_ST7789.h"
#define LVGL_WIDTH (LCD_WIDTH )
#define LVGL_HEIGHT LCD_HEIGHT
#define LVGL_BUF_LEN (LVGL_WIDTH * LVGL_HEIGHT / 20)
#define EXAMPLE_LVGL_TICK_PERIOD_MS 5
void Lvgl_print(const char * buf);
void Lvgl_Display_LCD( lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p ); // Displays LVGL content on the LCD. This function implements associating LVGL data to the LCD screen
void Lvgl_Touchpad_Read( lv_indev_drv_t * indev_drv, lv_indev_data_t * data ); // Read the touchpad
void example_increase_lvgl_tick(void *arg);
void Lvgl_Init(void);
void Timer_Loop(void);
-440
View File
@@ -1,440 +0,0 @@
#include "LVGL_Example.h"
/**********************
* TYPEDEFS
**********************/
typedef enum {
DISP_SMALL,
DISP_MEDIUM,
DISP_LARGE,
} disp_size_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void Onboard_create(lv_obj_t * parent);
static void color_changer_create(lv_obj_t * parent);
static void color_changer_event_cb(lv_event_t * e);
static void color_event_cb(lv_event_t * e);
static void ta_event_cb(lv_event_t * e);
static void birthday_event_cb(lv_event_t * e);
static void calendar_event_cb(lv_event_t * e);
void IRAM_ATTR example1_increase_lvgl_tick(lv_timer_t * t);
/**********************
* STATIC VARIABLES
**********************/
static disp_size_t disp_size;
static lv_obj_t * tv;
static lv_obj_t * calendar;
lv_style_t style_text_muted;
lv_style_t style_title;
static lv_style_t style_icon;
static lv_style_t style_bullet;
static lv_obj_t * chart1;
static lv_obj_t * chart2;
static lv_obj_t * chart3;
static lv_chart_series_t * ser1;
static lv_chart_series_t * ser2;
static lv_chart_series_t * ser3;
static lv_chart_series_t * ser4;
static const lv_font_t * font_large;
static const lv_font_t * font_normal;
static lv_timer_t * auto_step_timer;
static lv_color_t original_screen_bg_color;
static lv_timer_t * meter2_timer;
lv_obj_t * SD_Size;
lv_obj_t * FlashSize;
lv_obj_t * Board_angle;
lv_obj_t * RTC_Time;
lv_obj_t * Wireless_Scan;
void IRAM_ATTR auto_switch(lv_timer_t * t)
{
uint16_t page = lv_tabview_get_tab_act(tv);
if (page == 0) {
lv_tabview_set_act(tv, 1, LV_ANIM_ON);
} else if (page == 3) {
lv_tabview_set_act(tv, 2, LV_ANIM_ON);
}
}
void Lvgl_Example1(void){
disp_size = DISP_SMALL;
font_large = LV_FONT_DEFAULT;
font_normal = LV_FONT_DEFAULT;
lv_coord_t tab_h;
tab_h = 45;
#if LV_FONT_MONTSERRAT_18
font_large = &lv_font_montserrat_18;
#else
LV_LOG_WARN("LV_FONT_MONTSERRAT_18 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
#if LV_FONT_MONTSERRAT_12
font_normal = &lv_font_montserrat_12;
#else
LV_LOG_WARN("LV_FONT_MONTSERRAT_12 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
lv_style_init(&style_text_muted);
lv_style_set_text_opa(&style_text_muted, LV_OPA_90);
lv_style_init(&style_title);
lv_style_set_text_font(&style_title, font_large);
lv_style_init(&style_icon);
lv_style_set_text_color(&style_icon, lv_theme_get_color_primary(NULL));
lv_style_set_text_font(&style_icon, font_large);
lv_style_init(&style_bullet);
lv_style_set_border_width(&style_bullet, 0);
lv_style_set_radius(&style_bullet, LV_RADIUS_CIRCLE);
tv = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, tab_h);
lv_obj_set_style_text_font(lv_scr_act(), font_normal, 0);
lv_obj_t * t1 = lv_tabview_add_tab(tv, "Onboard");
Onboard_create(t1);
}
void Lvgl_Example1_close(void)
{
/*Delete all animation*/
lv_anim_del(NULL, NULL);
lv_timer_del(meter2_timer);
meter2_timer = NULL;
lv_obj_clean(lv_scr_act());
lv_style_reset(&style_text_muted);
lv_style_reset(&style_title);
lv_style_reset(&style_icon);
lv_style_reset(&style_bullet);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void Onboard_create(lv_obj_t * parent)
{
/*Create a panel*/
lv_obj_t * panel1 = lv_obj_create(parent);
lv_obj_set_height(panel1, LV_SIZE_CONTENT);
lv_obj_t * panel1_title = lv_label_create(panel1);
lv_label_set_text(panel1_title, "Onboard parameter");
lv_obj_add_style(panel1_title, &style_title, 0);
lv_obj_t * SD_label = lv_label_create(panel1);
lv_label_set_text(SD_label, "SD Card");
lv_obj_add_style(SD_label, &style_text_muted, 0);
SD_Size = lv_textarea_create(panel1);
lv_textarea_set_one_line(SD_Size, true);
lv_textarea_set_placeholder_text(SD_Size, "SD Size");
lv_obj_add_event_cb(SD_Size, ta_event_cb, LV_EVENT_ALL, NULL);
lv_obj_t * Flash_label = lv_label_create(panel1);
lv_label_set_text(Flash_label, "Flash Size");
lv_obj_add_style(Flash_label, &style_text_muted, 0);
FlashSize = lv_textarea_create(panel1);
lv_textarea_set_one_line(FlashSize, true);
lv_textarea_set_placeholder_text(FlashSize, "Flash Size");
lv_obj_add_event_cb(FlashSize, ta_event_cb, LV_EVENT_ALL, NULL);
lv_obj_t * Wireless_label = lv_label_create(panel1);
lv_label_set_text(Wireless_label, "Wireless scan");
lv_obj_add_style(Wireless_label, &style_text_muted, 0);
Wireless_Scan = lv_textarea_create(panel1);
lv_textarea_set_one_line(Wireless_Scan, true);
lv_textarea_set_placeholder_text(Wireless_Scan, "Wireless number");
lv_obj_add_event_cb(Wireless_Scan, ta_event_cb, LV_EVENT_ALL, NULL);
// 器件布局
static lv_coord_t grid_main_col_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
static lv_coord_t grid_main_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST};
lv_obj_set_grid_dsc_array(parent, grid_main_col_dsc, grid_main_row_dsc);
/*Create the top panel*/
static lv_coord_t grid_1_col_dsc[] = {LV_GRID_CONTENT, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
static lv_coord_t grid_1_row_dsc[] = {LV_GRID_CONTENT, /*Avatar*/
LV_GRID_CONTENT, /*Name*/
LV_GRID_CONTENT, /*Description*/
LV_GRID_CONTENT, /*Email*/
LV_GRID_CONTENT, /*Phone number*/
LV_GRID_CONTENT, /*Button1*/
LV_GRID_CONTENT, /*Button2*/
LV_GRID_TEMPLATE_LAST
};
lv_obj_set_grid_dsc_array(panel1, grid_1_col_dsc, grid_1_row_dsc);
static lv_coord_t grid_2_col_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
static lv_coord_t grid_2_row_dsc[] = {
LV_GRID_CONTENT, /*Title*/
5, /*Separator*/
LV_GRID_CONTENT, /*Box title*/
40, /*Box*/
LV_GRID_CONTENT, /*Box title*/
40, /*Box*/
LV_GRID_CONTENT, /*Box title*/
40, /*Box*/
LV_GRID_CONTENT, /*Box title*/
40, /*Box*/
LV_GRID_CONTENT, /*Box title*/
40, /*Box*/
LV_GRID_CONTENT, /*Box title*/
40, /*Box*/
LV_GRID_TEMPLATE_LAST
};
// lv_obj_set_grid_dsc_array(panel2, grid_2_col_dsc, grid_2_row_dsc);
// lv_obj_set_grid_dsc_array(panel3, grid_2_col_dsc, grid_2_row_dsc);
lv_obj_set_grid_cell(panel1, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_START, 0, 1);
lv_obj_set_grid_dsc_array(panel1, grid_2_col_dsc, grid_2_row_dsc);
lv_obj_set_grid_cell(panel1_title, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
lv_obj_set_grid_cell(SD_label, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 2, 1);
lv_obj_set_grid_cell(SD_Size, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 3, 1);
lv_obj_set_grid_cell(Flash_label, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 4, 1);
lv_obj_set_grid_cell(FlashSize, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 5, 1);
lv_obj_set_grid_cell(Wireless_label, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 6, 1);
lv_obj_set_grid_cell(Wireless_Scan, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 7, 1);
// 器件布局 END
auto_step_timer = lv_timer_create(example1_increase_lvgl_tick, 100, NULL);
}
void IRAM_ATTR example1_increase_lvgl_tick(lv_timer_t * t)
{
char buf[100]={0};
snprintf(buf, sizeof(buf), "%d MB\r\n", SDCard_Size);
lv_textarea_set_placeholder_text(SD_Size, buf);
snprintf(buf, sizeof(buf), "%d MB\r\n", Flash_Size);
lv_textarea_set_placeholder_text(FlashSize, buf);
if(Scan_finish)
snprintf(buf, sizeof(buf), "W: %d B: %d OK.\r\n",WIFI_NUM,BLE_NUM);
// snprintf(buf, sizeof(buf), "WIFI: %d ..OK.\r\n",WIFI_NUM);
else
snprintf(buf, sizeof(buf), "W: %d B: %d\r\n",WIFI_NUM,BLE_NUM);
// snprintf(buf, sizeof(buf), "WIFI: %d \r\n",WIFI_NUM);
lv_textarea_set_placeholder_text(Wireless_Scan, buf);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void color_changer_create(lv_obj_t * parent)
{
static lv_palette_t palette[] = {
LV_PALETTE_BLUE, LV_PALETTE_GREEN, LV_PALETTE_BLUE_GREY, LV_PALETTE_ORANGE,
LV_PALETTE_RED, LV_PALETTE_PURPLE, LV_PALETTE_TEAL, _LV_PALETTE_LAST
};
lv_obj_t * color_cont = lv_obj_create(parent);
lv_obj_remove_style_all(color_cont);
lv_obj_set_flex_flow(color_cont, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(color_cont, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_add_flag(color_cont, LV_OBJ_FLAG_FLOATING);
lv_obj_set_style_bg_color(color_cont, lv_color_white(), 0);
lv_obj_set_style_pad_right(color_cont, disp_size == DISP_SMALL ? LV_DPX(47) : LV_DPX(55), 0);
lv_obj_set_style_bg_opa(color_cont, LV_OPA_COVER, 0);
lv_obj_set_style_radius(color_cont, LV_RADIUS_CIRCLE, 0);
if(disp_size == DISP_SMALL) lv_obj_set_size(color_cont, LV_DPX(52), LV_DPX(52));
else lv_obj_set_size(color_cont, LV_DPX(60), LV_DPX(60));
lv_obj_align(color_cont, LV_ALIGN_BOTTOM_RIGHT, - LV_DPX(10), - LV_DPX(10));
uint32_t i;
for(i = 0; palette[i] != _LV_PALETTE_LAST; i++) {
lv_obj_t * c = lv_btn_create(color_cont);
lv_obj_set_style_bg_color(c, lv_palette_main(palette[i]), 0);
lv_obj_set_style_radius(c, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_opa(c, LV_OPA_TRANSP, 0);
lv_obj_set_size(c, 20, 20);
lv_obj_add_event_cb(c, color_event_cb, LV_EVENT_ALL, &palette[i]);
lv_obj_clear_flag(c, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
}
lv_obj_t * btn = lv_btn_create(parent);
lv_obj_add_flag(btn, LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_style_bg_color(btn, lv_color_white(), LV_STATE_CHECKED);
lv_obj_set_style_pad_all(btn, 10, 0);
lv_obj_set_style_radius(btn, LV_RADIUS_CIRCLE, 0);
lv_obj_add_event_cb(btn, color_changer_event_cb, LV_EVENT_ALL, color_cont);
lv_obj_set_style_shadow_width(btn, 0, 0);
lv_obj_set_style_bg_img_src(btn, LV_SYMBOL_TINT, 0);
if(disp_size == DISP_SMALL) {
lv_obj_set_size(btn, LV_DPX(42), LV_DPX(42));
lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, -LV_DPX(15), -LV_DPX(15));
}
else {
lv_obj_set_size(btn, LV_DPX(50), LV_DPX(50));
lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, -LV_DPX(15), -LV_DPX(15));
}
}
static void color_changer_anim_cb(void * var, int32_t v)
{
lv_obj_t * obj =(lv_obj_t *) var;
lv_coord_t max_w = lv_obj_get_width(lv_obj_get_parent(obj)) - LV_DPX(20);
lv_coord_t w;
if(disp_size == DISP_SMALL) {
w = lv_map(v, 0, 256, LV_DPX(52), max_w);
lv_obj_set_width(obj, w);
lv_obj_align(obj, LV_ALIGN_BOTTOM_RIGHT, - LV_DPX(10), - LV_DPX(10));
}
else {
w = lv_map(v, 0, 256, LV_DPX(60), max_w);
lv_obj_set_width(obj, w);
lv_obj_align(obj, LV_ALIGN_BOTTOM_RIGHT, - LV_DPX(10), - LV_DPX(10));
}
if(v > LV_OPA_COVER) v = LV_OPA_COVER;
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_set_style_opa(lv_obj_get_child(obj, i), v, 0);
}
}
static void color_changer_event_cb(lv_event_t * e)
{
if(lv_event_get_code(e) == LV_EVENT_CLICKED) {
lv_obj_t * color_cont = (lv_obj_t *)lv_event_get_user_data(e);
if(lv_obj_get_width(color_cont) < LV_HOR_RES / 2) {
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, color_cont);
lv_anim_set_exec_cb(&a, color_changer_anim_cb);
lv_anim_set_values(&a, 0, 256);
lv_anim_set_time(&a, 200);
lv_anim_start(&a);
}
else {
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, color_cont);
lv_anim_set_exec_cb(&a, color_changer_anim_cb);
lv_anim_set_values(&a, 256, 0);
lv_anim_set_time(&a, 200);
lv_anim_start(&a);
}
}
}
static void color_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_FOCUSED) {
lv_obj_t * color_cont = lv_obj_get_parent(obj);
if(lv_obj_get_width(color_cont) < LV_HOR_RES / 2) {
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, color_cont);
lv_anim_set_exec_cb(&a, color_changer_anim_cb);
lv_anim_set_values(&a, 0, 256);
lv_anim_set_time(&a, 200);
lv_anim_start(&a);
}
}
else if(code == LV_EVENT_CLICKED) {
lv_palette_t * palette_primary = (lv_palette_t *)lv_event_get_user_data(e);
lv_palette_t palette_secondary =(lv_palette_t) ((*palette_primary) + 3); /*Use another palette as secondary*/
if(palette_secondary >= _LV_PALETTE_LAST) palette_secondary =(lv_palette_t)(0);
#if LV_USE_THEME_DEFAULT
lv_theme_default_init(NULL, lv_palette_main(*palette_primary), lv_palette_main(palette_secondary),
LV_THEME_DEFAULT_DARK, font_normal);
#endif
lv_color_t color = lv_palette_main(*palette_primary);
lv_style_set_text_color(&style_icon, color);
lv_chart_set_series_color(chart1, ser1, color);
lv_chart_set_series_color(chart2, ser3, color);
}
}
static void ta_event_cb(lv_event_t * e)
{
}
static void birthday_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * ta = lv_event_get_target(e);
if(code == LV_EVENT_FOCUSED) {
if(lv_indev_get_type(lv_indev_get_act()) == LV_INDEV_TYPE_POINTER) {
if(calendar == NULL) {
lv_obj_add_flag(lv_layer_top(), LV_OBJ_FLAG_CLICKABLE);
calendar = lv_calendar_create(lv_layer_top());
lv_obj_set_style_bg_opa(lv_layer_top(), LV_OPA_50, 0);
lv_obj_set_style_bg_color(lv_layer_top(), lv_palette_main(LV_PALETTE_GREY), 0);
if(disp_size == DISP_SMALL) lv_obj_set_size(calendar, 180, 200);
else if(disp_size == DISP_MEDIUM) lv_obj_set_size(calendar, 200, 220);
else lv_obj_set_size(calendar, 300, 330);
lv_calendar_set_showed_date(calendar, 1990, 01);
lv_obj_align(calendar, LV_ALIGN_CENTER, 0, 30);
lv_obj_add_event_cb(calendar, calendar_event_cb, LV_EVENT_ALL, ta);
lv_calendar_header_dropdown_create(calendar);
}
}
}
}
static void calendar_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * ta = (lv_obj_t *)lv_event_get_user_data(e);
lv_obj_t * obj = (lv_obj_t *)lv_event_get_current_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
lv_calendar_date_t d;
lv_calendar_get_pressed_date(obj, &d);
char buf[32];
lv_snprintf(buf, sizeof(buf), "%02d.%02d.%d", d.day, d.month, d.year);
lv_textarea_set_text(ta, buf);
lv_obj_del(calendar);
calendar = NULL;
lv_obj_clear_flag(lv_layer_top(), LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_style_bg_opa(lv_layer_top(), LV_OPA_TRANSP, 0);
}
}
-10
View File
@@ -1,10 +0,0 @@
#pragma once
#include "LVGL_Driver.h"
#include "SD_Card.h"
#include "Wireless.h"
#define EXAMPLE1_LVGL_TICK_PERIOD_MS 1000
void Lvgl_Example1(void);
-116
View File
@@ -1,116 +0,0 @@
#include "SD_Card.h"
uint16_t SDCard_Size;
uint16_t Flash_Size;
bool SD_Init() {
// SD
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
if (SD.begin(SD_CS, SPI)) {
printf("SD card initialization successful!\r\n");
} else {
printf("SD card initialization failed!\r\n");
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){
printf("No SD card attached\r\n");
return false;
}
else{
printf("SD Card Type: ");
if(cardType == CARD_MMC){
printf("MMC\r\n");
} else if(cardType == CARD_SD){
printf("SDSC\r\n");
} else if(cardType == CARD_SDHC){
printf("SDHC\r\n");
} else {
printf("UNKNOWN\r\n");
}
uint64_t totalBytes = SD.totalBytes();
uint64_t usedBytes = SD.usedBytes();
SDCard_Size = totalBytes/(1024*1024);
printf("Total space: %llu\n", totalBytes);
printf("Used space: %llu\n", usedBytes);
printf("Free space: %llu\n", totalBytes - usedBytes);
}
return true;
}
bool File_Search(const char* directory, const char* fileName)
{
File Path = SD.open(directory);
if (!Path) {
printf("Path: <%s> does not exist\r\n",directory);
return false;
}
File file = Path.openNextFile();
while (file) {
if (strcmp(file.name(), fileName) == 0) {
if (strcmp(directory, "/") == 0)
printf("File '%s%s' found in root directory.\r\n",directory,fileName);
else
printf("File '%s/%s' found in root directory.\r\n",directory,fileName);
Path.close();
return true;
}
file = Path.openNextFile();
}
if (strcmp(directory, "/") == 0)
printf("File '%s%s' not found in root directory.\r\n",directory,fileName);
else
printf("File '%s/%s' not found in root directory.\r\n",directory,fileName);
Path.close();
return false;
}
uint16_t Folder_retrieval(const char* directory, const char* fileExtension, char File_Name[][100],uint16_t maxFiles)
{
File Path = SD.open(directory);
if (!Path) {
printf("Path: <%s> does not exist\r\n",directory);
return false;
}
uint16_t fileCount = 0;
char filePath[100];
File file = Path.openNextFile();
while (file && fileCount < maxFiles) {
if (!file.isDirectory() && strstr(file.name(), fileExtension)) {
strncpy(File_Name[fileCount], file.name(), sizeof(File_Name[fileCount]));
if (strcmp(directory, "/") == 0) {
snprintf(filePath, 100, "%s%s", directory, file.name());
} else {
snprintf(filePath, 100, "%s/%s", directory, file.name());
}
printf("File found: %s\r\n", filePath);
fileCount++;
}
file = Path.openNextFile();
}
Path.close();
if (fileCount > 0) {
printf(" %d <%s> files were retrieved\r\n",fileCount,fileExtension);
return fileCount;
} else {
printf("No files with extension '%s' found in directory: %s\r\n", fileExtension, directory);
return 0;
}
}
void remove_file_extension(char *file_name) {
char *last_dot = strrchr(file_name, '.');
if (last_dot != NULL) {
*last_dot = '\0';
}
}
void Flash_test()
{
printf("/********** RAM Test**********/\r\n");
// Get Flash size
uint32_t flashSize = ESP.getFlashChipSize();
Flash_Size = flashSize/1024/1024;
printf("Flash size: %d MB \r\n", flashSize/1024/1024);
printf("/******* RAM Test Over********/\r\n\r\n");
}
-19
View File
@@ -1,19 +0,0 @@
#pragma once
#include "Arduino.h"
#include <cstring>
#include "Display_ST7789.h"
#include "SD.h"
#include "FS.h"
// Digital I/O used
#define SD_CS 4 // SD_D3:
extern uint16_t SDCard_Size;
extern uint16_t Flash_Size;
bool SD_Init();
void Flash_test();
bool File_Search(const char* directory, const char* fileName);
uint16_t Folder_retrieval(const char* directory, const char* fileExtension, char File_Name[][100],uint16_t maxFiles);
void remove_file_extension(char *file_name);
-79
View File
@@ -1,79 +0,0 @@
#include "Wireless.h"
bool WIFI_Connection = 0;
uint8_t WIFI_NUM = 0;
uint8_t BLE_NUM = 0;
bool Scan_finish = 0;
int wifi_scan_number()
{
printf("/**********WiFi Test**********/\r\n");
// Set WiFi to station mode and disconnect from an AP if it was previously connected.
WiFi.mode(WIFI_STA);
WiFi.setSleep(true);
// WiFi.scanNetworks will return the number of networks found.
int count = WiFi.scanNetworks();
if (count == 0)
{
printf("No WIFI device was scanned\r\n");
}
else{
printf("Scanned %d Wi-Fi devices\r\n",count);
}
// Delete the scan result to free memory for code below.
WiFi.disconnect(true);
WiFi.scanDelete();
WiFi.mode(WIFI_OFF);
vTaskDelay(100);
printf("/*******WiFi Test Over********/\r\n\r\n");
return count;
}
int ble_scan_number()
{
printf("/**********BLE Test**********/\r\n");
BLEDevice::init("ESP32");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setActiveScan(true);
BLEScanResults* foundDevices = pBLEScan->start(5);
int count = foundDevices->getCount();
if (count == 0)
{
printf("No Bluetooth device was scanned\r\n");
}
else{
printf("Scanned %d Bluetooth devices\r\n",count);
}
pBLEScan->stop();
pBLEScan->clearResults();
BLEDevice::deinit(true);
vTaskDelay(100);
printf("/**********BLE Test Over**********/\r\n\r\n");
return count;
}
extern char buffer[128]; /* Make sure buffer is enough for `sprintf` */
void Wireless_Test1(){
BLE_NUM = ble_scan_number(); // !!! Please note that continuing to use Bluetooth will result in allocation failure due to RAM usage, so pay attention to RAM usage when Bluetooth is turned on
WIFI_NUM = wifi_scan_number();
Scan_finish = 1;
}
void WirelessScanTask(void *parameter) {
BLE_NUM = ble_scan_number(); // !!! Please note that continuing to use Bluetooth will result in allocation failure due to RAM usage, so pay attention to RAM usage when Bluetooth is turned on
WIFI_NUM = wifi_scan_number();
Scan_finish = 1;
vTaskDelay(pdMS_TO_TICKS(1000));
vTaskDelete(NULL);
}
void Wireless_Test2(){
xTaskCreatePinnedToCore(
WirelessScanTask,
"WirelessScanTask",
4096,
NULL,
2,
NULL,
0
);
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include "WiFi.h"
#include <BLEDevice.h>
#include <BLEScan.h>
extern bool WIFI_Connection;
extern uint8_t WIFI_NUM;
extern uint8_t BLE_NUM;
extern bool Scan_finish;
int wifi_scan_number();
int ble_scan_number();
void Wireless_Test1();
void Wireless_Test2();
-245
View File
@@ -1,245 +0,0 @@
#include "Display_ST7789.h"
#define SPI_WRITE(_dat) SPI.transfer(_dat)
#define SPI_WRITE_Word(_dat) SPI.transfer16(_dat)
#define SPI_WRITE_nByte(_SetData,_ReadData,_Size) SPI.transferBytes(_SetData,_ReadData,_Size)
void SPI_Init()
{
SPI.begin(EXAMPLE_PIN_NUM_SCLK,EXAMPLE_PIN_NUM_MISO,EXAMPLE_PIN_NUM_MOSI);
}
void LCD_WriteCommand(uint8_t Cmd)
{
SPI.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, LOW);
SPI_WRITE(Cmd);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
SPI.endTransaction();
}
void LCD_WriteData(uint8_t Data)
{
SPI.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH);
SPI_WRITE(Data);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
SPI.endTransaction();
}
void LCD_WriteData_Word(uint16_t Data)
{
SPI.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH);
SPI_WRITE_Word(Data);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
SPI.endTransaction();
}
void LCD_WriteData_nbyte(uint8_t* SetData,uint8_t* ReadData,uint32_t Size)
{
SPI.beginTransaction(SPISettings(SPIFreq, MSBFIRST, SPI_MODE0));
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
digitalWrite(EXAMPLE_PIN_NUM_LCD_DC, HIGH);
SPI_WRITE_nByte(SetData, ReadData, Size);
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, HIGH);
SPI.endTransaction();
}
void LCD_Reset(void)
{
digitalWrite(EXAMPLE_PIN_NUM_LCD_CS, LOW);
delay(50);
digitalWrite(EXAMPLE_PIN_NUM_LCD_RST, LOW);
delay(50);
digitalWrite(EXAMPLE_PIN_NUM_LCD_RST, HIGH);
delay(50);
}
void LCD_Init(void)
{
pinMode(EXAMPLE_PIN_NUM_LCD_CS, OUTPUT);
pinMode(EXAMPLE_PIN_NUM_LCD_DC, OUTPUT);
pinMode(EXAMPLE_PIN_NUM_LCD_RST, OUTPUT);
Backlight_Init();
SPI_Init();
LCD_Reset();
//************* Start Initial Sequence **********//
LCD_WriteCommand(0x11);
delay(120);
LCD_WriteCommand(0x36);
if (HORIZONTAL)
LCD_WriteData(0x00);
else
LCD_WriteData(0x70);
LCD_WriteCommand(0x3A);
LCD_WriteData(0x05);
LCD_WriteCommand(0xB0);
LCD_WriteData(0x00);
LCD_WriteData(0xE8);
LCD_WriteCommand(0xB2);
LCD_WriteData(0x0C);
LCD_WriteData(0x0C);
LCD_WriteData(0x00);
LCD_WriteData(0x33);
LCD_WriteData(0x33);
LCD_WriteCommand(0xB7);
LCD_WriteData(0x35);
LCD_WriteCommand(0xBB);
LCD_WriteData(0x35);
LCD_WriteCommand(0xC0);
LCD_WriteData(0x2C);
LCD_WriteCommand(0xC2);
LCD_WriteData(0x01);
LCD_WriteCommand(0xC3);
LCD_WriteData(0x13);
LCD_WriteCommand(0xC4);
LCD_WriteData(0x20);
LCD_WriteCommand(0xC6);
LCD_WriteData(0x0F);
LCD_WriteCommand(0xD0);
LCD_WriteData(0xA4);
LCD_WriteData(0xA1);
LCD_WriteCommand(0xD6);
LCD_WriteData(0xA1);
LCD_WriteCommand(0xE0);
LCD_WriteData(0xF0);
LCD_WriteData(0x00);
LCD_WriteData(0x04);
LCD_WriteData(0x04);
LCD_WriteData(0x04);
LCD_WriteData(0x05);
LCD_WriteData(0x29);
LCD_WriteData(0x33);
LCD_WriteData(0x3E);
LCD_WriteData(0x38);
LCD_WriteData(0x12);
LCD_WriteData(0x12);
LCD_WriteData(0x28);
LCD_WriteData(0x30);
LCD_WriteCommand(0xE1);
LCD_WriteData(0xF0);
LCD_WriteData(0x07);
LCD_WriteData(0x0A);
LCD_WriteData(0x0D);
LCD_WriteData(0x0B);
LCD_WriteData(0x07);
LCD_WriteData(0x28);
LCD_WriteData(0x33);
LCD_WriteData(0x3E);
LCD_WriteData(0x36);
LCD_WriteData(0x14);
LCD_WriteData(0x14);
LCD_WriteData(0x29);
LCD_WriteData(0x32);
LCD_WriteCommand(0x21);
LCD_WriteCommand(0x11);
delay(120);
LCD_WriteCommand(0x29);
}
/******************************************************************************
function: Set the cursor position
parameter :
Xstart: Start uint16_t x coordinate
Ystart: Start uint16_t y coordinate
Xend : End uint16_t coordinates
Yend : End uint16_t coordinatesen
******************************************************************************/
void LCD_SetCursor(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend)
{
if (HORIZONTAL) {
// set the X coordinates
LCD_WriteCommand(0x2A);
LCD_WriteData(Xstart >> 8);
LCD_WriteData(Xstart + Offset_X);
LCD_WriteData(Xend >> 8);
LCD_WriteData(Xend + Offset_X);
// set the Y coordinates
LCD_WriteCommand(0x2B);
LCD_WriteData(Ystart >> 8);
LCD_WriteData(Ystart + Offset_Y);
LCD_WriteData(Yend >> 8);
LCD_WriteData(Yend + Offset_Y);
}
else {
// set the X coordinates
LCD_WriteCommand(0x2A);
LCD_WriteData(Ystart >> 8);
LCD_WriteData(Ystart + Offset_Y);
LCD_WriteData(Yend >> 8);
LCD_WriteData(Yend + Offset_Y);
// set the Y coordinates
LCD_WriteCommand(0x2B);
LCD_WriteData(Xstart >> 8);
LCD_WriteData(Xstart + Offset_X);
LCD_WriteData(Xend >> 8);
LCD_WriteData(Xend + Offset_X);
}
LCD_WriteCommand(0x2C);
}
/******************************************************************************
function: Refresh the image in an area
parameter :
Xstart: Start uint16_t x coordinate
Ystart: Start uint16_t y coordinate
Xend : End uint16_t coordinates
Yend : End uint16_t coordinates
color : Set the color
******************************************************************************/
void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,uint16_t* color)
{
// uint16_t i,j;
// LCD_SetCursor(Xstart, Ystart, Xend,Yend);
// uint16_t Show_Width = Xend - Xstart + 1;
// uint16_t Show_Height = Yend - Ystart + 1;
// for(i = 0; i < Show_Height; i++){
// for(j = 0; j < Show_Width; j++){
// LCD_WriteData_Word(color[(i*(Show_Width))+j]);
// }
// }
uint16_t Show_Width = Xend - Xstart + 1;
uint16_t Show_Height = Yend - Ystart + 1;
uint32_t numBytes = Show_Width * Show_Height * sizeof(uint16_t);
uint8_t Read_D[numBytes];
LCD_SetCursor(Xstart, Ystart, Xend, Yend);
LCD_WriteData_nbyte((uint8_t*)color, Read_D, numBytes);
}
// backlight
void Backlight_Init(void)
{
ledcAttach(EXAMPLE_PIN_NUM_BK_LIGHT, Frequency, Resolution);
ledcWrite(EXAMPLE_PIN_NUM_BK_LIGHT, 100);
}
void Set_Backlight(uint8_t Light) //
{
if(Light > 100 || Light < 0)
printf("Set Backlight parameters in the range of 0 to 100 \r\n");
else{
uint32_t Backlight = Light*10;
ledcWrite(EXAMPLE_PIN_NUM_BK_LIGHT, Backlight);
}
}
-31
View File
@@ -1,31 +0,0 @@
#pragma once
#include <Arduino.h>
#include <SPI.h>
#define LCD_WIDTH 172 //LCD width
#define LCD_HEIGHT 320 //LCD height
#define SPIFreq 80000000
#define EXAMPLE_PIN_NUM_MISO 5
#define EXAMPLE_PIN_NUM_MOSI 6
#define EXAMPLE_PIN_NUM_SCLK 7
#define EXAMPLE_PIN_NUM_LCD_CS 14
#define EXAMPLE_PIN_NUM_LCD_DC 15
#define EXAMPLE_PIN_NUM_LCD_RST 21
#define EXAMPLE_PIN_NUM_BK_LIGHT 22
#define Frequency 1000
#define Resolution 10
#define VERTICAL 0
#define HORIZONTAL 1
#define Offset_X 34
#define Offset_Y 0
void SPI_Init();
void LCD_Init(void);
void LCD_SetCursor(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend);
void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,uint16_t* color);
void Backlight_Init(void);
void Set_Backlight(uint8_t Light);
-124
View File
@@ -1,124 +0,0 @@
#include "LCD_Image.h"
PNG png;
File Image_file;
uint16_t Image_CNT;
char SD_Image_Name[100][100] ;
char File_Image_Name[100][100] ;
int16_t xpos = 0;
int16_t ypos = 0;
void * pngOpen(const char *filePath, int32_t *size) {
Image_file = SD.open(filePath);
*size = Image_file.size();
return &Image_file;
}
void pngClose(void *handle) {
File Image_file = *((File*)handle);
if (Image_file) Image_file.close();
}
int32_t pngRead(PNGFILE *page, uint8_t *buffer, int32_t length) {
if (!Image_file) return 0;
page = page; // Avoid warning
return Image_file.read(buffer, length);
}
int32_t pngSeek(PNGFILE *page, int32_t position) {
if (!Image_file) return 0;
page = page; // Avoid warning
return Image_file.seek(position);
}
//=========================================v==========================================
// pngDraw
//====================================================================================
// This next function will be called during decoding of the png file to
// render each image line to the TFT. If you use a different TFT library
// you will need to adapt this function to suit.
// Callback function to draw pixels to the display
static uint16_t lineBuffer[MAX_IMAGE_WIDTH];
void pngDraw(PNGDRAW *pDraw) {
png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_BIG_ENDIAN, 0xffffffff);
uint32_t size = pDraw->iWidth;
for (size_t i = 0; i < size; i++) {
lineBuffer[i] = (((lineBuffer[i] >> 8) & 0xFF) | ((lineBuffer[i] << 8) & 0xFF00)); // 所有数据修正
}
LCD_addWindow(xpos, pDraw->y, xpos + pDraw->iWidth, ypos + pDraw->y + 1,lineBuffer); // x_end End index on x-axis (x_end not included)
}
/////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
void Search_Image(const char* directory, const char* fileExtension) {
Image_CNT = Folder_retrieval(directory,fileExtension,SD_Image_Name,100);
if(Image_CNT) {
for (int i = 0; i < Image_CNT; i++) {
strcpy(File_Image_Name[i], SD_Image_Name[i]);
remove_file_extension(File_Image_Name[i]);
}
}
}
void Show_Image(const char * filePath)
{
printf("Currently display picture %s\r\n",filePath);
int16_t ret = png.open(filePath, pngOpen, pngClose, pngRead, pngSeek, pngDraw);
if (ret == PNG_SUCCESS) {
printf("image specs: (%d x %d), %d bpp, pixel type: %d\r\n", png.getWidth(), png.getHeight(), png.getBpp(), png.getPixelType());
uint32_t dt = millis();
if (png.getWidth() > MAX_IMAGE_WIDTH) {
printf("Image too wide for allocated line buffer size!\r\n");
}
else {
ret = png.decode(NULL, 0);
png.close();
}
printf("%d ms\r\n",millis()-dt);
}
}
void Display_Image(const char* directory, const char* fileExtension, uint16_t ID)
{
Search_Image(directory,fileExtension);
if(Image_CNT) {
String FilePath;
if (String(directory) == "/") { // Handle the case when the directory is the root
FilePath = String(directory) + SD_Image_Name[ID];
} else {
FilePath = String(directory) + "/" + SD_Image_Name[ID];
}
const char* filePathCStr = FilePath.c_str(); // Convert String to c_str() for Show_Image function
printf("Show : %s \r\n", filePathCStr); // Print file path for debugging
Show_Image(filePathCStr); // Show the image using the file path
}
else
printf("No files with extension '%s' found in directory: %s\r\n", fileExtension, directory);
}
uint16_t Now_Image = 0;
void Image_Next(const char* directory, const char* fileExtension)
{
if(!digitalRead(BOOT_KEY_PIN)){
while(!digitalRead(BOOT_KEY_PIN));
Now_Image ++;
if(Now_Image == Image_CNT)
Now_Image = 0;
Display_Image(directory,fileExtension,Now_Image);
}
}
void Image_Next_Loop(const char* directory, const char* fileExtension,uint32_t NextTime)
{
static uint32_t NextTime_Now=0;
NextTime_Now++;
if(NextTime_Now == NextTime)
{
NextTime_Now = 0;
Now_Image ++;
if(Now_Image == Image_CNT)
Now_Image = 0;
Display_Image(directory,fileExtension,Now_Image);
}
}
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include <PNGdec.h>
#include "SD_Card.h"
#include "Display_ST7789.h"
#define BOOT_KEY_PIN 9
#define MAX_IMAGE_WIDTH 172 // Adjust for your images
void Search_Image(const char* directory, const char* fileExtension);
void Show_Image(const char * filePath);
void Display_Image(const char* directory, const char* fileExtension, uint16_t ID);
void Image_Next(const char* directory, const char* fileExtension);
void Image_Next_Loop(const char* directory, const char* fileExtension,uint32_t NextTime);
-19
View File
@@ -1,19 +0,0 @@
#include "SD_Card.h"
#include "Display_ST7789.h"
#include "LCD_Image.h"
void setup()
{
Flash_test();
LCD_Init();
SD_Init();
Display_Image("/",".png", 0);
pinMode(BOOT_KEY_PIN, INPUT);
}
void loop()
{
Image_Next_Loop("/",".png",300);
delay(5);
}
-113
View File
@@ -1,113 +0,0 @@
#include "SD_Card.h"
uint16_t SDCard_Size;
uint16_t Flash_Size;
void SD_Init() {
// SD
if (SD.begin(SD_CS, SPI, 80000000, "/sd", 5, true)) {
printf("SD card initialization successful!\r\n");
} else {
printf("SD card initialization failed!\r\n");
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){
printf("No SD card attached\r\n");
return;
}
else{
printf("SD Card Type: ");
if(cardType == CARD_MMC){
printf("MMC\r\n");
} else if(cardType == CARD_SD){
printf("SDSC\r\n");
} else if(cardType == CARD_SDHC){
printf("SDHC\r\n");
} else {
printf("UNKNOWN\r\n");
}
uint64_t totalBytes = SD.totalBytes();
uint64_t usedBytes = SD.usedBytes();
SDCard_Size = totalBytes/(1024*1024);
printf("Total space: %llu\n", totalBytes);
printf("Used space: %llu\n", usedBytes);
printf("Free space: %llu\n", totalBytes - usedBytes);
}
}
bool File_Search(const char* directory, const char* fileName)
{
File Path = SD.open(directory);
if (!Path) {
printf("Path: <%s> does not exist\r\n",directory);
return false;
}
File file = Path.openNextFile();
while (file) {
if (strcmp(file.name(), fileName) == 0) {
if (strcmp(directory, "/") == 0)
printf("File '%s%s' found in root directory.\r\n",directory,fileName);
else
printf("File '%s/%s' found in root directory.\r\n",directory,fileName);
Path.close();
return true;
}
file = Path.openNextFile();
}
if (strcmp(directory, "/") == 0)
printf("File '%s%s' not found in root directory.\r\n",directory,fileName);
else
printf("File '%s/%s' not found in root directory.\r\n",directory,fileName);
Path.close();
return false;
}
uint16_t Folder_retrieval(const char* directory, const char* fileExtension, char File_Name[][100],uint16_t maxFiles)
{
File Path = SD.open(directory);
if (!Path) {
printf("Path: <%s> does not exist\r\n",directory);
return false;
}
uint16_t fileCount = 0;
char filePath[100];
File file = Path.openNextFile();
while (file && fileCount < maxFiles) {
if (!file.isDirectory() && strstr(file.name(), fileExtension)) {
strncpy(File_Name[fileCount], file.name(), sizeof(File_Name[fileCount]));
if (strcmp(directory, "/") == 0) {
snprintf(filePath, 100, "%s%s", directory, file.name());
} else {
snprintf(filePath, 100, "%s/%s", directory, file.name());
}
printf("File found: %s\r\n", filePath);
fileCount++;
}
file = Path.openNextFile();
}
Path.close();
if (fileCount > 0) {
printf(" %d <%s> files were retrieved\r\n",fileCount,fileExtension);
return fileCount;
} else {
printf("No files with extension '%s' found in directory: %s\r\n", fileExtension, directory);
return 0;
}
}
void remove_file_extension(char *file_name) {
char *last_dot = strrchr(file_name, '.');
if (last_dot != NULL) {
*last_dot = '\0';
}
}
void Flash_test()
{
printf("/********** RAM Test**********/\r\n");
// Get Flash size
uint32_t flashSize = ESP.getFlashChipSize();
Flash_Size = flashSize/1024/1024;
printf("Flash size: %d MB \r\n", flashSize/1024/1024);
printf("/******* RAM Test Over********/\r\n\r\n");
}
-19
View File
@@ -1,19 +0,0 @@
#pragma once
#include "Arduino.h"
#include <cstring>
#include "Display_ST7789.h"
#include "SD.h"
#include "FS.h"
// Digital I/O used
#define SD_CS 4 // SD_D3:
extern uint16_t SDCard_Size;
extern uint16_t Flash_Size;
void SD_Init();
void Flash_test();
bool File_Search(const char* directory, const char* fileName);
uint16_t Folder_retrieval(const char* directory, const char* fileExtension, char File_Name[][100],uint16_t maxFiles);
void remove_file_extension(char *file_name);
+189
View File
@@ -0,0 +1,189 @@
# Klubhaus Doorbell - justfile
# Run with: just <recipe>
# Set BOARD: just BOARD=esp32-32e-4 compile
# Default recipe - show help
default:
@just --list
# Set default BOARD
BOARD := "esp32-32e-4"
# Helper to source board config
# just passes args as positional, so we source in each recipe
# Compile firmware
compile:
#!/usr/bin/env bash
set -e
source ./boards/{{BOARD}}/board-config.sh
# Only regenerate compile_commands.json if needed (board changed or first run)
NEED_GEN=false
if [ ! -f compile_commands.json ]; then
NEED_GEN=true
elif [ ! -f .board-last ] || [ "$(cat .board-last)" != "{{BOARD}}" ]; then
NEED_GEN=true
fi
if [ "$NEED_GEN" = "true" ]; then
rm -rf /tmp/arduino-build
arduino-cli compile --only-compilation-database --fqbn "$FQBN" --libraries ./libraries $LIBS --build-property "compiler.cpp.extra_flags=$OPTS" --build-path /tmp/arduino-build ./boards/{{BOARD}}
cp /tmp/arduino-build/compile_commands.json .
echo "{{BOARD}}" > .board-last
echo "[OK] Generated compile_commands.json for {{BOARD}}"
else
echo "[SKIP] compile_commands.json already up to date for {{BOARD}}"
fi
arduino-cli compile --fqbn "$FQBN" --libraries ./libraries $LIBS --build-property "compiler.cpp.extra_flags=$OPTS" --warnings default ./boards/{{BOARD}}
# Upload firmware
upload: kill
#!/usr/bin/env bash
set -e
source ./boards/{{BOARD}}/board-config.sh
arduino-cli upload --fqbn "$FQBN" --port "$PORT" ./boards/{{BOARD}}
# Kill processes using serial port
kill:
#!/usr/bin/env bash
set +e
source ./boards/{{BOARD}}/board-config.sh
PORT="${PORT:-$PORT}"
echo "Killing processes on $PORT..."
fuser -k "$PORT" 2>/dev/null || true
for pid in $(pgrep -f "monitor-agent.py.*{{BOARD}}" 2>/dev/null || true); do
echo "Killing monitor-agent.py (PID: $pid)..."
kill "$pid" 2>/dev/null || true
done
rm -f "/tmp/doorbell-{{BOARD}}.lock" 2>/dev/null || true
sleep 1
echo "[OK] Killed processes for {{BOARD}}"
# Monitor raw serial
monitor-raw: kill
#!/usr/bin/env bash
source ./boards/{{BOARD}}/board-config.sh
PORT="${PORT:-$PORT}"
TARGET="$(readlink -f "$PORT" 2>/dev/null || echo "$PORT")"
arduino-cli monitor -p "$TARGET" --config baudrate=115200
# Show tio command
monitor-tio: kill
#!/usr/bin/env bash
source ./boards/{{BOARD}}/board-config.sh
PORT="${PORT:-$PORT}"
TARGET="$(readlink -f "$PORT" 2>/dev/null || echo "$PORT")"
echo "Run: tio --map INLCRNL $TARGET -e"
# Monitor with JSON logging
monitor: kill
#!/usr/bin/env bash
python3 ./scripts/monitor-agent.py "{{BOARD}}" &
# Tail colored logs
watch:
#!/usr/bin/env bash
tail -f "/tmp/doorbell-{{BOARD}}.jsonl" | while read -r line; do
ts=$(echo "$line" | python3 -c "import sys,json; print(json.load(sys.stdin)['ts'])" 2>/dev/null)
txt=$(echo "$line" | python3 -c "import sys,json; print(json.load(sys.stdin)['line'])" 2>/dev/null)
if [[ "$txt" == *"[STATE]"* ]]; then
echo -e "\033[1;35m[$ts]\033[0m $txt"
elif [[ "$txt" == *"[ADMIN]"* ]]; then
echo -e "\033[1;36m[$ts]\033[0m $txt"
elif [[ "$txt" == *"[TOUCH]"* ]]; then
echo -e "\033[1;33m[$ts]\033[0m $txt"
elif [[ "$txt" == *"ALERT"* ]]; then
echo -e "\033[1;31m[$ts]\033[0m $txt"
else
echo "[$ts] $txt"
fi
done
# Send command to device
cmd command:
#!/usr/bin/env bash
echo -n "{{command}}" > "/tmp/doorbell-{{BOARD}}-cmd.fifo"
echo "[SENT] {{command}}"
# Show device state
state:
@cat "/tmp/doorbell-{{BOARD}}-state.json"
# Detect connected board
detect:
#!/usr/bin/env bash
bash ./scripts/detect-device.sh
# Install shared libraries
install-libs-shared:
#!/usr/bin/env bash
./scripts/install-shared.sh
# Install all libraries
install: install-libs-shared
#!/usr/bin/env bash
./boards/{{BOARD}}/install.sh
arduino-cli core install esp32:esp32
# Clean build artifacts
clean:
#!/usr/bin/env bash
rm -rf vendor/
rm -rf .cache/
rm -rf boards/esp32-32e/build
rm -rf boards/esp32-32e-4/build
rm -rf boards/esp32-s3-lcd-43/build
rm -f .board-last
echo "[OK] Build artifacts cleaned"
# Clean temporary files (monitor logs, FIFOs, state files)
clean-temp:
#!/usr/bin/env bash
rm -f /tmp/doorbell-esp32-32e.jsonl /tmp/doorbell-esp32-32e-state.json /tmp/doorbell-esp32-32e-cmd.fifo
rm -f /tmp/doorbell-esp32-32e-4.jsonl /tmp/doorbell-esp32-32e-4-state.json /tmp/doorbell-esp32-32e-4-cmd.fifo
rm -f /tmp/doorbell-esp32-s3-lcd-43.jsonl /tmp/doorbell-esp32-s3-lcd-43-state.json /tmp/doorbell-esp32-s3-lcd-43-cmd.fifo
rm -f .board-last
echo "[OK] Temp files cleaned"
# Clean Arduino cache
arduino-clean:
#!/usr/bin/env bash
echo "Checking ~/.arduino15..."
du -sh ~/.arduino15/staging 2>/dev/null || echo "No staging folder"
du -sh ~/.arduino15/packages 2>/dev/null || echo "No packages folder"
read -p "Delete staging + packages folders? [y/N] " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
rm -rf ~/.arduino15/staging
rm -rf ~/.arduino15/packages
echo "[OK] Arduino staging + packages cleared"
else
echo "Aborted"
fi
# Format code
format:
#!/usr/bin/env bash
clang-format -i --style=file \
boards/esp32-32e/*.cpp \
boards/esp32-32e/*.h \
boards/esp32-32e/*.ino \
boards/esp32-32e-4/*.cpp \
boards/esp32-32e-4/*.h \
boards/esp32-32e-4/*.ino \
boards/esp32-s3-lcd-43/*.cpp \
boards/esp32-s3-lcd-43/*.h \
boards/esp32-s3-lcd-43/*.ino \
libraries/KlubhausCore/src/*.cpp \
libraries/KlubhausCore/src/*.h \
libraries/KlubhausCore/*.properties
# Generate crush config
gen-crush-config:
#!/usr/bin/env bash
set -e
source ./boards/{{BOARD}}/board-config.sh
printf '{\n "lsp": {\n "arduino": {\n "command": "arduino-language-server",\n "args": ["-fqbn", "%s"]\n },\n "cpp": {\n "command": "clangd"\n }\n }\n}\n' "$FQBN" > .crush.json
echo "[OK] Generated .crush.json with FQBN: $FQBN"
File diff suppressed because it is too large Load Diff
@@ -1,422 +0,0 @@
/*!
* @file Adafruit_NeoPixel.h
*
* This is part of Adafruit's NeoPixel library for the Arduino platform,
* allowing a broad range of microcontroller boards (most AVR boards,
* many ARM devices, ESP8266 and ESP32, among others) to control Adafruit
* NeoPixels, FLORA RGB Smart Pixels and compatible devices -- WS2811,
* WS2812, WS2812B, SK6812, etc.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing products
* from Adafruit!
*
* Written by Phil "Paint Your Dragon" Burgess for Adafruit Industries,
* with contributions by PJRC, Michael Miller and other members of the
* open source community.
*
* This file is part of the Adafruit_NeoPixel library.
*
* Adafruit_NeoPixel is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Adafruit_NeoPixel 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with NeoPixel. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
#ifndef ADAFRUIT_NEOPIXEL_H
#define ADAFRUIT_NEOPIXEL_H
#ifdef ARDUINO
#include <Arduino.h>
#ifdef USE_TINYUSB // For Serial when selecting TinyUSB
#include <Adafruit_TinyUSB.h>
#endif
#endif
#ifdef TARGET_LPC1768
#include <Arduino.h>
#endif
#if defined(ARDUINO_ARCH_RP2040)
#include <stdlib.h>
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "rp2040_pio.h"
#endif
// The order of primary colors in the NeoPixel data stream can vary among
// device types, manufacturers and even different revisions of the same
// item. The third parameter to the Adafruit_NeoPixel constructor encodes
// the per-pixel byte offsets of the red, green and blue primaries (plus
// white, if present) in the data stream -- the following #defines provide
// an easier-to-use named version for each permutation. e.g. NEO_GRB
// indicates a NeoPixel-compatible device expecting three bytes per pixel,
// with the first byte transmitted containing the green value, second
// containing red and third containing blue. The in-memory representation
// of a chain of NeoPixels is the same as the data-stream order; no
// re-ordering of bytes is required when issuing data to the chain.
// Most of these values won't exist in real-world devices, but it's done
// this way so we're ready for it (also, if using the WS2811 driver IC,
// one might have their pixels set up in any weird permutation).
// Bits 5,4 of this value are the offset (0-3) from the first byte of a
// pixel to the location of the red color byte. Bits 3,2 are the green
// offset and 1,0 are the blue offset. If it is an RGBW-type device
// (supporting a white primary in addition to R,G,B), bits 7,6 are the
// offset to the white byte...otherwise, bits 7,6 are set to the same value
// as 5,4 (red) to indicate an RGB (not RGBW) device.
// i.e. binary representation:
// 0bWWRRGGBB for RGBW devices
// 0bRRRRGGBB for RGB
// RGB NeoPixel permutations; white and red offsets are always same
// Offset: W R G B
#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B
#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G
#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B
#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R
#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G
#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R
// RGBW NeoPixel permutations; all 4 offsets are distinct
// Offset: W R G B
#define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3)) ///< Transmit as W,R,G,B
#define NEO_WRBG ((0 << 6) | (1 << 4) | (3 << 2) | (2)) ///< Transmit as W,R,B,G
#define NEO_WGRB ((0 << 6) | (2 << 4) | (1 << 2) | (3)) ///< Transmit as W,G,R,B
#define NEO_WGBR ((0 << 6) | (3 << 4) | (1 << 2) | (2)) ///< Transmit as W,G,B,R
#define NEO_WBRG ((0 << 6) | (2 << 4) | (3 << 2) | (1)) ///< Transmit as W,B,R,G
#define NEO_WBGR ((0 << 6) | (3 << 4) | (2 << 2) | (1)) ///< Transmit as W,B,G,R
#define NEO_RWGB ((1 << 6) | (0 << 4) | (2 << 2) | (3)) ///< Transmit as R,W,G,B
#define NEO_RWBG ((1 << 6) | (0 << 4) | (3 << 2) | (2)) ///< Transmit as R,W,B,G
#define NEO_RGWB ((2 << 6) | (0 << 4) | (1 << 2) | (3)) ///< Transmit as R,G,W,B
#define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B,W
#define NEO_RBWG ((2 << 6) | (0 << 4) | (3 << 2) | (1)) ///< Transmit as R,B,W,G
#define NEO_RBGW ((3 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G,W
#define NEO_GWRB ((1 << 6) | (2 << 4) | (0 << 2) | (3)) ///< Transmit as G,W,R,B
#define NEO_GWBR ((1 << 6) | (3 << 4) | (0 << 2) | (2)) ///< Transmit as G,W,B,R
#define NEO_GRWB ((2 << 6) | (1 << 4) | (0 << 2) | (3)) ///< Transmit as G,R,W,B
#define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B,W
#define NEO_GBWR ((2 << 6) | (3 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,W,R
#define NEO_GBRW ((3 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R,W
#define NEO_BWRG ((1 << 6) | (2 << 4) | (3 << 2) | (0)) ///< Transmit as B,W,R,G
#define NEO_BWGR ((1 << 6) | (3 << 4) | (2 << 2) | (0)) ///< Transmit as B,W,G,R
#define NEO_BRWG ((2 << 6) | (1 << 4) | (3 << 2) | (0)) ///< Transmit as B,R,W,G
#define NEO_BRGW ((3 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G,W
#define NEO_BGWR ((2 << 6) | (3 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,W,R
#define NEO_BGRW ((3 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R,W
// Add NEO_KHZ400 to the color order value to indicate a 400 KHz device.
// All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is
// the default if unspecified. Because flash space is very limited on ATtiny
// devices (e.g. Trinket, Gemma), v1 NeoPixels aren't handled by default on
// those chips, though it can be enabled by removing the ifndef/endif below,
// but code will be bigger. Conversely, can disable the NEO_KHZ400 line on
// other MCUs to remove v1 support and save a little space.
#define NEO_KHZ800 0x0000 ///< 800 KHz data transmission
#ifndef __AVR_ATtiny85__
#define NEO_KHZ400 0x0100 ///< 400 KHz data transmission
#endif
// If 400 KHz support is enabled, the third parameter to the constructor
// requires a 16-bit value (in order to select 400 vs 800 KHz speed).
// If only 800 KHz is enabled (as is default on ATtiny), an 8-bit value
// is sufficient to encode pixel color order, saving some space.
#ifdef NEO_KHZ400
typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
#else
typedef uint8_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
#endif
// These two tables are declared outside the Adafruit_NeoPixel class
// because some boards may require oldschool compilers that don't
// handle the C++11 constexpr keyword.
/* A PROGMEM (flash mem) table containing 8-bit unsigned sine wave (0-255).
Copy & paste this snippet into a Python REPL to regenerate:
import math
for x in range(256):
print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))),
if x&15 == 15: print
*/
static const uint8_t PROGMEM _NeoPixelSineTable[256] = {
128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170,
173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211,
213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240,
241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254,
254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251,
250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 238, 237, 235, 234, 232,
230, 228, 226, 224, 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, 198,
196, 193, 190, 188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155,
152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118, 115, 112, 109,
106, 103, 100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65,
62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, 29,
27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6,
5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11,
12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37,
40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121,
124};
/* Similar to above, but for an 8-bit gamma-correction table.
Copy & paste this snippet into a Python REPL to regenerate:
import math
gamma=2.6
for x in range(256):
print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))),
if x&15 == 15: print
*/
static const uint8_t PROGMEM _NeoPixelGammaTable[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3,
3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17,
17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35,
36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 80, 81,
82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 97, 99, 100, 102,
103, 105, 106, 108, 109, 111, 112, 114, 115, 117, 119, 120, 122, 124, 125,
127, 129, 130, 132, 134, 136, 137, 139, 141, 143, 145, 146, 148, 150, 152,
154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182,
184, 186, 188, 191, 193, 195, 197, 199, 202, 204, 206, 209, 211, 213, 215,
218, 220, 223, 225, 227, 230, 232, 235, 237, 240, 242, 245, 247, 250, 252,
255};
/* Declare external methods required by the Adafruit_NeoPixel implementation
for specific hardware/library versions
*/
#if defined(ESP32)
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
extern "C" void espInit();
#endif
#endif
/*!
@brief Class that stores state and functions for interacting with
Adafruit NeoPixels and compatible devices.
*/
class Adafruit_NeoPixel {
public:
// Constructor: number of LEDs, pin number, LED type
Adafruit_NeoPixel(uint16_t n, int16_t pin = 6,
neoPixelType type = NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel(void);
~Adafruit_NeoPixel();
bool begin(void);
void show(void);
void setPin(int16_t p);
void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w);
void setPixelColor(uint16_t n, uint32_t c);
void fill(uint32_t c = 0, uint16_t first = 0, uint16_t count = 0);
void setBrightness(uint8_t);
void clear(void);
void updateLength(uint16_t n);
void updateType(neoPixelType t);
/*!
@brief Check whether a call to show() will start sending data
immediately or will 'block' for a required interval. NeoPixels
require a short quiet time (about 300 microseconds) after the
last bit is received before the data 'latches' and new data can
start being received. Usually one's sketch is implicitly using
this time to generate a new frame of animation...but if it
finishes very quickly, this function could be used to see if
there's some idle time available for some low-priority
concurrent task.
@return 1 or true if show() will start sending immediately, 0 or false
if show() would block (meaning some idle time is available).
*/
bool canShow(void) {
// It's normal and possible for endTime to exceed micros() if the
// 32-bit clock counter has rolled over (about every 70 minutes).
// Since both are uint32_t, a negative delta correctly maps back to
// positive space, and it would seem like the subtraction below would
// suffice. But a problem arises if code invokes show() very
// infrequently...the micros() counter may roll over MULTIPLE times in
// that interval, the delta calculation is no longer correct and the
// next update may stall for a very long time. The check below resets
// the latch counter if a rollover has occurred. This can cause an
// extra delay of up to 300 microseconds in the rare case where a
// show() call happens precisely around the rollover, but that's
// neither likely nor especially harmful, vs. other code that might
// stall for 30+ minutes, or having to document and frequently remind
// and/or provide tech support explaining an unintuitive need for
// show() calls at least once an hour.
uint32_t now = micros();
if (endTime > now) {
endTime = now;
}
return (now - endTime) >= 300L;
}
/*!
@brief Get a pointer directly to the NeoPixel data buffer in RAM.
Pixel data is stored in a device-native format (a la the NEO_*
constants) and is not translated here. Applications that access
this buffer will need to be aware of the specific data format
and handle colors appropriately.
@return Pointer to NeoPixel buffer (uint8_t* array).
@note This is for high-performance applications where calling
setPixelColor() on every single pixel would be too slow (e.g.
POV or light-painting projects). There is no bounds checking
on the array, creating tremendous potential for mayhem if one
writes past the ends of the buffer. Great power, great
responsibility and all that.
*/
uint8_t *getPixels(void) const { return pixels; };
uint8_t getBrightness(void) const;
/*!
@brief Retrieve the pin number used for NeoPixel data output.
@return Arduino pin number (-1 if not set).
*/
int16_t getPin(void) const { return pin; };
/*!
@brief Return the number of pixels in an Adafruit_NeoPixel strip object.
@return Pixel count (0 if not set).
*/
uint16_t numPixels(void) const { return numLEDs; }
uint32_t getPixelColor(uint16_t n) const;
/*!
@brief An 8-bit integer sine wave function, not directly compatible
with standard trigonometric units like radians or degrees.
@param x Input angle, 0-255; 256 would loop back to zero, completing
the circle (equivalent to 360 degrees or 2 pi radians).
One can therefore use an unsigned 8-bit variable and simply
add or subtract, allowing it to overflow/underflow and it
still does the expected contiguous thing.
@return Sine result, 0 to 255, or -128 to +127 if type-converted to
a signed int8_t, but you'll most likely want unsigned as this
output is often used for pixel brightness in animation effects.
*/
static uint8_t sine8(uint8_t x) {
return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out
}
/*!
@brief An 8-bit gamma-correction function for basic pixel brightness
adjustment. Makes color transitions appear more perceptially
correct.
@param x Input brightness, 0 (minimum or off/black) to 255 (maximum).
@return Gamma-adjusted brightness, can then be passed to one of the
setPixelColor() functions. This uses a fixed gamma correction
exponent of 2.6, which seems reasonably okay for average
NeoPixels in average tasks. If you need finer control you'll
need to provide your own gamma-correction function instead.
*/
static uint8_t gamma8(uint8_t x) {
return pgm_read_byte(&_NeoPixelGammaTable[x]); // 0-255 in, 0-255 out
}
/*!
@brief Convert separate red, green and blue values into a single
"packed" 32-bit RGB color.
@param r Red brightness, 0 to 255.
@param g Green brightness, 0 to 255.
@param b Blue brightness, 0 to 255.
@return 32-bit packed RGB value, which can then be assigned to a
variable for later use or passed to the setPixelColor()
function. Packed RGB format is predictable, regardless of
LED strand color order.
*/
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) {
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
/*!
@brief Convert separate red, green, blue and white values into a
single "packed" 32-bit WRGB color.
@param r Red brightness, 0 to 255.
@param g Green brightness, 0 to 255.
@param b Blue brightness, 0 to 255.
@param w White brightness, 0 to 255.
@return 32-bit packed WRGB value, which can then be assigned to a
variable for later use or passed to the setPixelColor()
function. Packed WRGB format is predictable, regardless of
LED strand color order.
*/
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
static uint32_t ColorHSV(uint16_t hue, uint8_t sat = 255, uint8_t val = 255);
/*!
@brief A gamma-correction function for 32-bit packed RGB or WRGB
colors. Makes color transitions appear more perceptially
correct.
@param x 32-bit packed RGB or WRGB color.
@return Gamma-adjusted packed color, can then be passed in one of the
setPixelColor() functions. Like gamma8(), this uses a fixed
gamma correction exponent of 2.6, which seems reasonably okay
for average NeoPixels in average tasks. If you need finer
control you'll need to provide your own gamma-correction
function instead.
*/
static uint32_t gamma32(uint32_t x);
void rainbow(uint16_t first_hue = 0, int8_t reps = 1,
uint8_t saturation = 255, uint8_t brightness = 255,
bool gammify = true);
static neoPixelType str2order(const char *v);
private:
#if defined(ARDUINO_ARCH_RP2040)
bool rp2040claimPIO(void);
void rp2040releasePIO(void);
void rp2040Show(uint8_t *pixels, uint32_t numBytes);
PIO pio = NULL;
uint pio_sm = -1;
uint pio_program_offset = 0;
#endif
protected:
#ifdef NEO_KHZ400 // If 400 KHz NeoPixel support enabled...
bool is800KHz; ///< true if 800 KHz pixels
#endif
bool begun; ///< true if begin() previously called successfully
uint16_t numLEDs; ///< Number of RGB LEDs in strip
uint16_t numBytes; ///< Size of 'pixels' buffer below
int16_t pin; ///< Output pin number (-1 if not yet set)
uint8_t brightness; ///< Strip brightness 0-255 (stored as +1)
uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each)
uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel
uint8_t gOffset; ///< Index of green byte
uint8_t bOffset; ///< Index of blue byte
uint8_t wOffset; ///< Index of white (==rOffset if no white)
uint32_t endTime; ///< Latch timing reference
#ifdef __AVR__
volatile uint8_t *port; ///< Output PORT register
uint8_t pinMask; ///< Output PORT bitmask
#endif
#if defined(ARDUINO_ARCH_STM32) || \
defined(ARDUINO_ARCH_ARDUINO_CORE_STM32) || \
defined(ARDUINO_ARCH_CH32) || \
defined(_PY32_DEF_)
GPIO_TypeDef *gpioPort; ///< Output GPIO PORT
uint32_t gpioPin; ///< Output GPIO PIN
#endif
};
#endif // ADAFRUIT_NEOPIXEL_H
@@ -1,51 +0,0 @@
#if defined(ARDUINO_ARCH_RP2040)// RP2040 specific driver
#include "Adafruit_NeoPixel.h"
bool Adafruit_NeoPixel::rp2040claimPIO(void) {
// Find a PIO with enough available space in its instruction memory
pio = NULL;
if (! pio_claim_free_sm_and_add_program_for_gpio_range(&ws2812_program,
&pio, &pio_sm, &pio_program_offset,
pin, 1, true)) {
pio = NULL;
pio_sm = -1;
pio_program_offset = 0;
return false; // No PIO available
}
// yay ok!
if (is800KHz) {
// 800kHz, 8 bit transfers
ws2812_program_init(pio, pio_sm, pio_program_offset, pin, 800000, 8);
} else {
// 400kHz, 8 bit transfers
ws2812_program_init(pio, pio_sm, pio_program_offset, pin, 400000, 8);
}
return true;
}
void Adafruit_NeoPixel::rp2040releasePIO(void) {
if (pio == NULL)
return;
pio_remove_program_and_unclaim_sm(&ws2812_program, pio, pio_sm, pio_program_offset);
}
// Private, called from show()
void Adafruit_NeoPixel::rp2040Show(uint8_t *pixels, uint32_t numBytes)
{
// verify we have a valid PIO and state machine
if (! pio || (pio_sm < 0)) {
return;
}
while(numBytes--)
// Bits for transmission must be shifted to top 8 bits
pio_sm_put_blocking(pio, pio_sm, ((uint32_t)*pixels++)<< 24);
}
#endif
@@ -1,13 +0,0 @@
# Contribution Guidelines
This library is the culmination of the expertise of many members of the open source community who have dedicated their time and hard work. The best way to ask for help or propose a new idea is to [create a new issue](https://github.com/adafruit/Adafruit_NeoPixel/issues/new) while creating a Pull Request with your code changes allows you to share your own innovations with the rest of the community.
The following are some guidelines to observe when creating issues or PRs:
- Be friendly; it is important that we can all enjoy a safe space as we are all working on the same project and it is okay for people to have different ideas
- [Use code blocks](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code); it helps us help you when we can read your code! On that note also refrain from pasting more than 30 lines of code in a post, instead [create a gist](https://gist.github.com/) if you need to share large snippets
- Use reasonable titles; refrain from using overly long or capitalized titles as they are usually annoying and do little to encourage others to help :smile:
- Be detailed; refrain from mentioning code problems without sharing your source code and always give information regarding your board and version of the library
-165
View File
@@ -1,165 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
-162
View File
@@ -1,162 +0,0 @@
# Adafruit NeoPixel Library [![Build Status](https://github.com/adafruit/Adafruit_NeoPixel/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_NeoPixel/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_NeoPixel/html/index.html)
Arduino library for controlling single-wire-based LED pixels and strip such as the [Adafruit 60 LED/meter Digital LED strip][strip], the [Adafruit FLORA RGB Smart Pixel][flora], the [Adafruit Breadboard-friendly RGB Smart Pixel][pixel], the [Adafruit NeoPixel Stick][stick], and the [Adafruit NeoPixel Shield][shield].
After downloading, rename folder to 'Adafruit_NeoPixel' and install in Arduino Libraries folder. Restart Arduino IDE, then open File->Sketchbook->Library->Adafruit_NeoPixel->strandtest sketch.
Compatibility notes: Port A is not supported on any AVR processors at this time
[flora]: http://adafruit.com/products/1060
[strip]: http://adafruit.com/products/1138
[pixel]: http://adafruit.com/products/1312
[stick]: http://adafruit.com/products/1426
[shield]: http://adafruit.com/products/1430
---
## Installation
### First Method
![image](https://user-images.githubusercontent.com/36513474/68967967-3e37f480-0803-11ea-91d9-601848c306ee.png)
1. In the Arduino IDE, navigate to Sketch > Include Library > Manage Libraries
1. Then the Library Manager will open and you will find a list of libraries that are already installed or ready for installation.
1. Then search for Neopixel strip using the search bar.
1. Click on the text area and then select the specific version and install it.
### Second Method
1. Navigate to the [Releases page](https://github.com/adafruit/Adafruit_NeoPixel/releases).
1. Download the latest release.
1. Extract the zip file
1. In the Arduino IDE, navigate to Sketch > Include Library > Add .ZIP Library
## Features
- ### Simple to use
Controlling NeoPixels “from scratch” is quite a challenge, so we provide a library letting you focus on the fun and interesting bits.
- ### Give back
The library is free; you dont have to pay for anything. Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
- ### Supported Chipsets
We have included code for the following chips - sometimes these break for exciting reasons that we can't control in which case please open an issue!
- AVR ATmega and ATtiny (any 8-bit) - 8 MHz, 12 MHz and 16 MHz
- Teensy 3.x and LC
- Arduino Due
- Arduino 101
- Arm® Cortex®-M7/M4 - RENESAS/STM (Arduino UNO R4, Arduino Portenta H7, Arduino Giga R1)
- ATSAMD21 (Arduino Zero/M0 and other SAMD21 boards) @ 48 MHz
- ATSAMD51 @ 120 MHz
- Adafruit STM32 Feather @ 120 MHz
- ESP8266 any speed
- ESP32 any speed
- WCH CH32 @ 48 MHz and higher speeds
- Nordic nRF52 (Adafruit Feather nRF52), nRF51 (micro:bit)
- Infineon XMC1100 BootKit @ 32 MHz
- Infineon XMC1100 2Go @ 32 MHz
- Infineon XMC1300 BootKit @ 32 MHz
- Infineon XMC1400 2Go @ 48 MHz
- Infineon XMC4700 RelaxKit, XMC4800 RelaxKit, XMC4800 IoT Amazon FreeRTOS Kit @ 144 MHz
- Sipeed Maix Bit (K210 processor)
Check forks for other architectures not listed here!
- ### GNU Lesser General Public License
Adafruit_NeoPixel is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
## Functions
- begin()
- updateLength()
- updateType()
- show()
- delay_ns()
- setPin()
- setPixelColor()
- fill()
- ColorHSV()
- getPixelColor()
- setBrightness()
- getBrightness()
- clear()
- gamma32()
## Examples
There are many examples implemented in this library. One of the examples is below. You can find other examples [here](https://github.com/adafruit/Adafruit_NeoPixel/tree/master/examples)
### Simple
```Cpp
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 6
#define NUMPIXELS 16
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#define DELAYVAL 500
void setup() {
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
pixels.begin();
}
void loop() {
pixels.clear();
for(int i=0; i<NUMPIXELS; i++) {
pixels.setPixelColor(i, pixels.Color(0, 150, 0));
pixels.show();
delay(DELAYVAL);
}
}
```
## Contributing
If you want to contribute to this project:
- Report bugs and errors
- Ask for enhancements
- Create issues and pull requests
- Tell others about this library
- Contribute new protocols
Please read [CONTRIBUTING.md](https://github.com/adafruit/Adafruit_NeoPixel/blob/master/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
### Roadmap
The PRIME DIRECTIVE is to maintain backward compatibility with existing Arduino sketches -- many are hosted elsewhere and don't track changes here, some are in print and can never be changed!
Please don't reformat code for the sake of reformatting code. The resulting large "visual diff" makes it impossible to untangle actual bug fixes from merely rearranged lines. Also, don't bother with PRs for timing adjustments "to better match the datasheet," because the datasheet isn't really true to begin with.
Things I'd Like To Do But There's No Official Timeline So Please Don't Count On Any Of This Ever Being Canonical:
- 400 KHz support can be removed, turns out it was never actually necessary; even the earliest NeoPixels can ingest 800 KHz data. Of course the #defines should remain so old sketches still compile, but both can be set to 0 and would have no effect on anything.
- For the show() function (with all the delicate pixel timing stuff), break out each architecture into separate source files rather than the current unmaintainable tangle of #ifdef statements!
- Please don't use updateLength() or updateType() in new code. They should not have been implemented this way (use the C++ 'new' operator with the regular constructor instead) and are only sticking around because of the Prime Directive. setPin() is OK for now though, it's a trick we can use to 'recycle' pixel memory across multiple strips.
- In the M0 and M4 code, use the hardware systick counter for bit timing rather than hand-tweaked NOPs (a temporary kludge at the time because I wasn't reading systick correctly). (As of 1.4.2, systick is used on M4 devices and it appears to be overclock-compatible. Not for M0 yet, which is why this item is still here.)
- As currently written, brightness scaling is still a "destructive" operation -- pixel values are altered in RAM and the original value as set can't be accurately read back, only approximated, which has been confusing and frustrating to users. It was done this way at the time because NeoPixel timing is strict, AVR microcontrollers (all we had at the time) are limited, and assembly language is hard. All the 32-bit architectures should have no problem handling nondestructive brightness scaling -- calculating each byte immediately before it's sent out the wire, maintaining the original set value in RAM -- the work just hasn't been done. There's a fair chance even the AVR code could manage it with some intense focus. (The DotStar library achieves nondestructive brightness scaling because it doesn't have to manage data timing so carefully...every architecture, even ATtiny, just takes whatever cycles it needs for the multiply/shift operations.)
## Credits
This library is written by Phil "Paint Your Dragon" Burgess for Adafruit Industries, with contributions by PJRC, Michael Miller and other members of the open source community.
## License
Adafruit_NeoPixel is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Adafruit_NeoPixel 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 Lesser General Public License](https://www.gnu.org/licenses/lgpl-3.0.en.html) for more details.
You should have received a copy of the GNU Lesser General Public License along with NeoPixel. If not, see [this](https://www.gnu.org/licenses/)
-278
View File
@@ -1,278 +0,0 @@
// Implements the RMT peripheral on Espressif SoCs
// Copyright (c) 2020 Lucian Copeland for Adafruit Industries
/* Uses code from Espressif RGB LED Strip demo and drivers
* Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
*
* 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.
*/
#if defined(ESP32)
#include <Arduino.h>
#if defined(ESP_IDF_VERSION)
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
#define HAS_ESP_IDF_4
#endif
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#define HAS_ESP_IDF_5
#endif
#endif
#ifdef HAS_ESP_IDF_5
static SemaphoreHandle_t show_mutex = NULL;
void espShow(uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz) {
// Note: Because rmtPin is shared between all instances, we will
// end up releasing/initializing the RMT channels each time we
// invoke on different pins. This is probably ok, just not
// efficient. led_data is shared between all instances but will
// be allocated with enough space for the largest instance; data
// is not used beyond the mutex lock so this should be fine.
#define SEMAPHORE_TIMEOUT_MS 50
static rmt_data_t *led_data = NULL;
static uint32_t led_data_size = 0;
static int rmtPin = -1;
if (show_mutex && xSemaphoreTake(show_mutex, SEMAPHORE_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
uint32_t requiredSize = numBytes * 8;
if (requiredSize > led_data_size) {
free(led_data);
led_data = (rmt_data_t *)malloc(requiredSize * sizeof(rmt_data_t));
if (led_data != NULL) {
led_data_size = requiredSize;
} else {
led_data_size = 0;
}
} else if (requiredSize == 0) {
// To release RMT resources (RMT channels and led_data), call
// .updateLength(0) to set number of pixels/bytes to zero,
// then call .show() to invoke this code and free resources.
free(led_data);
led_data = NULL;
if (rmtPin >= 0) {
rmtDeinit(rmtPin);
rmtPin = -1;
}
led_data_size = 0;
}
if (led_data_size > 0 && requiredSize <= led_data_size) {
if (pin != rmtPin) {
if (rmtPin >= 0) {
rmtDeinit(rmtPin);
rmtPin = -1;
}
if (!rmtInit(pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) {
log_e("Failed to init RMT TX mode on pin %d", pin);
return;
}
rmtPin = pin;
}
if (rmtPin >= 0) {
int i=0;
for (int b=0; b < numBytes; b++) {
for (int bit=0; bit<8; bit++){
if ( pixels[b] & (1<<(7-bit)) ) {
led_data[i].level0 = 1;
led_data[i].duration0 = 8;
led_data[i].level1 = 0;
led_data[i].duration1 = 4;
} else {
led_data[i].level0 = 1;
led_data[i].duration0 = 4;
led_data[i].level1 = 0;
led_data[i].duration1 = 8;
}
i++;
}
}
rmtWrite(pin, led_data, numBytes * 8, RMT_WAIT_FOR_EVER);
}
}
xSemaphoreGive(show_mutex);
}
}
// To avoid race condition initializing the mutex, all instances of
// Adafruit_NeoPixel must be constructed before launching and child threads
void espInit() {
if (!show_mutex) {
show_mutex = xSemaphoreCreateMutex();
}
}
#else
#include "driver/rmt.h"
// This code is adapted from the ESP-IDF v3.4 RMT "led_strip" example, altered
// to work with the Arduino version of the ESP-IDF (3.2)
#define WS2812_T0H_NS (400)
#define WS2812_T0L_NS (850)
#define WS2812_T1H_NS (800)
#define WS2812_T1L_NS (450)
#define WS2811_T0H_NS (500)
#define WS2811_T0L_NS (2000)
#define WS2811_T1H_NS (1200)
#define WS2811_T1L_NS (1300)
static uint32_t t0h_ticks = 0;
static uint32_t t1h_ticks = 0;
static uint32_t t0l_ticks = 0;
static uint32_t t1l_ticks = 0;
// Limit the number of RMT channels available for the Neopixels. Defaults to all
// channels (8 on ESP32, 4 on ESP32-S2 and S3). Redefining this value will free
// any channels with a higher number for other uses, such as IR send-and-recieve
// libraries. Redefine as 1 to restrict Neopixels to only a single channel.
#define ADAFRUIT_RMT_CHANNEL_MAX RMT_CHANNEL_MAX
#define RMT_LL_HW_BASE (&RMT)
bool rmt_reserved_channels[ADAFRUIT_RMT_CHANNEL_MAX];
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
size_t wanted_num, size_t *translated_size, size_t *item_num)
{
if (src == NULL || dest == NULL) {
*translated_size = 0;
*item_num = 0;
return;
}
const rmt_item32_t bit0 = {{{ t0h_ticks, 1, t0l_ticks, 0 }}}; //Logical 0
const rmt_item32_t bit1 = {{{ t1h_ticks, 1, t1l_ticks, 0 }}}; //Logical 1
size_t size = 0;
size_t num = 0;
uint8_t *psrc = (uint8_t *)src;
rmt_item32_t *pdest = dest;
while (size < src_size && num < wanted_num) {
for (int i = 0; i < 8; i++) {
// MSB first
if (*psrc & (1 << (7 - i))) {
pdest->val = bit1.val;
} else {
pdest->val = bit0.val;
}
num++;
pdest++;
}
size++;
psrc++;
}
*translated_size = size;
*item_num = num;
}
void espShow(uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz) {
// Reserve channel
rmt_channel_t channel = ADAFRUIT_RMT_CHANNEL_MAX;
for (size_t i = 0; i < ADAFRUIT_RMT_CHANNEL_MAX; i++) {
if (!rmt_reserved_channels[i]) {
rmt_reserved_channels[i] = true;
channel = i;
break;
}
}
if (channel == ADAFRUIT_RMT_CHANNEL_MAX) {
// Ran out of channels!
return;
}
#if defined(HAS_ESP_IDF_4)
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(pin, channel);
config.clk_div = 2;
#else
// Match default TX config from ESP-IDF version 3.4
rmt_config_t config = {
.rmt_mode = RMT_MODE_TX,
.channel = channel,
.gpio_num = pin,
.clk_div = 2,
.mem_block_num = 1,
.tx_config = {
.carrier_freq_hz = 38000,
.carrier_level = RMT_CARRIER_LEVEL_HIGH,
.idle_level = RMT_IDLE_LEVEL_LOW,
.carrier_duty_percent = 33,
.carrier_en = false,
.loop_en = false,
.idle_output_en = true,
}
};
#endif
rmt_config(&config);
rmt_driver_install(config.channel, 0, 0);
// Convert NS timings to ticks
uint32_t counter_clk_hz = 0;
#if defined(HAS_ESP_IDF_4)
rmt_get_counter_clock(channel, &counter_clk_hz);
#else
// this emulates the rmt_get_counter_clock() function from ESP-IDF 3.4
if (RMT_LL_HW_BASE->conf_ch[config.channel].conf1.ref_always_on == RMT_BASECLK_REF) {
uint32_t div_cnt = RMT_LL_HW_BASE->conf_ch[config.channel].conf0.div_cnt;
uint32_t div = div_cnt == 0 ? 256 : div_cnt;
counter_clk_hz = REF_CLK_FREQ / (div);
} else {
uint32_t div_cnt = RMT_LL_HW_BASE->conf_ch[config.channel].conf0.div_cnt;
uint32_t div = div_cnt == 0 ? 256 : div_cnt;
counter_clk_hz = APB_CLK_FREQ / (div);
}
#endif
// NS to tick converter
float ratio = (float)counter_clk_hz / 1e9;
if (is800KHz) {
t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
} else {
t0h_ticks = (uint32_t)(ratio * WS2811_T0H_NS);
t0l_ticks = (uint32_t)(ratio * WS2811_T0L_NS);
t1h_ticks = (uint32_t)(ratio * WS2811_T1H_NS);
t1l_ticks = (uint32_t)(ratio * WS2811_T1L_NS);
}
// Initialize automatic timing translator
rmt_translator_init(config.channel, ws2812_rmt_adapter);
// Write and wait to finish
rmt_write_sample(config.channel, pixels, (size_t)numBytes, true);
rmt_wait_tx_done(config.channel, pdMS_TO_TICKS(100));
// Free channel again
rmt_driver_uninstall(config.channel);
rmt_reserved_channels[channel] = false;
gpio_set_direction(pin, GPIO_MODE_OUTPUT);
}
#endif // ifndef IDF5
#endif // ifdef(ESP32)
-86
View File
@@ -1,86 +0,0 @@
// This is a mash-up of the Due show() code + insights from Michael Miller's
// ESP8266 work for the NeoPixelBus library: github.com/Makuna/NeoPixelBus
// Needs to be a separate .c file to enforce ICACHE_RAM_ATTR execution.
#if defined(ESP8266)
#include <Arduino.h>
#ifdef ESP8266
#include <eagle_soc.h>
#endif
static uint32_t _getCycleCount(void) __attribute__((always_inline));
static inline uint32_t _getCycleCount(void) {
uint32_t ccount;
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
return ccount;
}
#ifdef ESP8266
IRAM_ATTR void espShow(
uint8_t pin, uint8_t *pixels, uint32_t numBytes, __attribute__((unused)) boolean is800KHz) {
#else
void espShow(
uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz) {
#endif
#define CYCLES_800_T0H (F_CPU / 2500001) // 0.4us
#define CYCLES_800_T1H (F_CPU / 1250001) // 0.8us
#define CYCLES_800 (F_CPU / 800001) // 1.25us per bit
#define CYCLES_400_T0H (F_CPU / 2000000) // 0.5uS
#define CYCLES_400_T1H (F_CPU / 833333) // 1.2us
#define CYCLES_400 (F_CPU / 400000) // 2.5us per bit
uint8_t *p, *end, pix, mask;
uint32_t t, time0, time1, period, c, startTime;
#ifdef ESP8266
uint32_t pinMask;
pinMask = _BV(pin);
#endif
p = pixels;
end = p + numBytes;
pix = *p++;
mask = 0x80;
startTime = 0;
#ifdef NEO_KHZ400
if(is800KHz) {
#endif
time0 = CYCLES_800_T0H;
time1 = CYCLES_800_T1H;
period = CYCLES_800;
#ifdef NEO_KHZ400
} else { // 400 KHz bitstream
time0 = CYCLES_400_T0H;
time1 = CYCLES_400_T1H;
period = CYCLES_400;
}
#endif
for(t = time0;; t = time0) {
if(pix & mask) t = time1; // Bit high duration
while(((c = _getCycleCount()) - startTime) < period); // Wait for bit start
#ifdef ESP8266
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinMask); // Set high
#else
gpio_set_level(pin, HIGH);
#endif
startTime = c; // Save start time
while(((c = _getCycleCount()) - startTime) < t); // Wait high duration
#ifdef ESP8266
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinMask); // Set low
#else
gpio_set_level(pin, LOW);
#endif
if(!(mask >>= 1)) { // Next bit/byte
if(p >= end) break;
pix = *p++;
mask = 0x80;
}
}
while((_getCycleCount() - startTime) < period); // Wait for last bit
}
#endif // ESP8266
@@ -1,177 +0,0 @@
// NeoPixel test program showing use of the WHITE channel for RGBW
// pixels only (won't look correct on regular RGB NeoPixel strips).
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN 6
// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 60
// NeoPixel brightness, 0 (min) to 255 (max)
#define BRIGHTNESS 50 // Set BRIGHTNESS to about 1/5 (max = 255)
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRBW + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
void setup() {
// These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
// Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
// END of Trinket-specific code.
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(BRIGHTNESS);
}
void loop() {
// Fill along the length of the strip in various colors...
colorWipe(strip.Color(255, 0, 0) , 50); // Red
colorWipe(strip.Color( 0, 255, 0) , 50); // Green
colorWipe(strip.Color( 0, 0, 255) , 50); // Blue
colorWipe(strip.Color( 0, 0, 0, 255), 50); // True white (not RGB white)
whiteOverRainbow(75, 5);
pulseWhite(5);
rainbowFade2White(3, 3, 1);
}
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
void whiteOverRainbow(int whiteSpeed, int whiteLength) {
if(whiteLength >= strip.numPixels()) whiteLength = strip.numPixels() - 1;
int head = whiteLength - 1;
int tail = 0;
int loops = 3;
int loopNum = 0;
uint32_t lastTime = millis();
uint32_t firstPixelHue = 0;
for(;;) { // Repeat forever (or until a 'break' or 'return')
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
if(((i >= tail) && (i <= head)) || // If between head & tail...
((tail > head) && ((i >= tail) || (i <= head)))) {
strip.setPixelColor(i, strip.Color(0, 0, 0, 255)); // Set white
} else { // else set rainbow
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
}
strip.show(); // Update strip with new contents
// There's no delay here, it just runs full-tilt until the timer and
// counter combination below runs out.
firstPixelHue += 40; // Advance just a little along the color wheel
if((millis() - lastTime) > whiteSpeed) { // Time to update head/tail?
if(++head >= strip.numPixels()) { // Advance head, wrap around
head = 0;
if(++loopNum >= loops) return;
}
if(++tail >= strip.numPixels()) { // Advance tail, wrap around
tail = 0;
}
lastTime = millis(); // Save time of last movement
}
}
}
void pulseWhite(uint8_t wait) {
for(int j=0; j<256; j++) { // Ramp up from 0 to 255
// Fill entire strip with white at gamma-corrected brightness level 'j':
strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
strip.show();
delay(wait);
}
for(int j=255; j>=0; j--) { // Ramp down from 255 to 0
strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
strip.show();
delay(wait);
}
}
void rainbowFade2White(int wait, int rainbowLoops, int whiteLoops) {
int fadeVal=0, fadeMax=100;
// Hue of first pixel runs 'rainbowLoops' complete loops through the color
// wheel. Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to rainbowLoops*65536, using steps of 256 so we
// advance around the wheel at a decent clip.
for(uint32_t firstPixelHue = 0; firstPixelHue < rainbowLoops*65536;
firstPixelHue += 256) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
// Offset pixel hue by an amount to make one full revolution of the
// color wheel (range of 65536) along the length of the strip
// (strip.numPixels() steps):
uint32_t pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
// strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
// optionally add saturation and value (brightness) (each 0 to 255).
// Here we're using just the three-argument variant, though the
// second value (saturation) is a constant 255.
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue, 255,
255 * fadeVal / fadeMax)));
}
strip.show();
delay(wait);
if(firstPixelHue < 65536) { // First loop,
if(fadeVal < fadeMax) fadeVal++; // fade in
} else if(firstPixelHue >= ((rainbowLoops-1) * 65536)) { // Last loop,
if(fadeVal > 0) fadeVal--; // fade out
} else {
fadeVal = fadeMax; // Interim loop, make sure fade is at max
}
}
for(int k=0; k<whiteLoops; k++) {
for(int j=0; j<256; j++) { // Ramp up 0 to 255
// Fill entire strip with white at gamma-corrected brightness level 'j':
strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
strip.show();
}
delay(1000); // Pause 1 second
for(int j=255; j>=0; j--) { // Ramp down 255 to 0
strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
strip.show();
}
}
delay(500); // Pause 1/2 second
}
@@ -1,231 +0,0 @@
/****************************************************************************
* This example is based on StrandtestBLE example and adapts it to use
* the new ArduinoBLE library.
*
* https://github.com/arduino-libraries/ArduinoBLE
*
* Supported boards:
* Arduino MKR WiFi 1010, Arduino Uno WiFi Rev2 board, Arduino Nano 33 IoT,
Arduino Nano 33 BLE, or Arduino Nano 33 BLE Sense board.
*
* You can use a generic BLE central app, like LightBlue (iOS and Android) or
* nRF Connect (Android), to interact with the services and characteristics
* created in this sketch.
*
* This example code is in the public domain.
*
*/
#include <Adafruit_NeoPixel.h>
#define PIN 15 // Pin where NeoPixels are connected
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(64, PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
// connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
// a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)
uint8_t rgb_values[3];
#include <ArduinoBLE.h>
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service
// BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
void setup()
{
Serial.begin(115200);
Serial.println("Hello World!");
// custom services and characteristics can be added as well
// begin initialization
if (!BLE.begin())
{
Serial.println("starting BLE failed!");
while (1)
;
}
Serial.print("Peripheral address: ");
Serial.println(BLE.address());
// set advertised local name and service UUID:
BLE.setLocalName("LED");
BLE.setAdvertisedService(ledService);
// add the characteristic to the service
ledService.addCharacteristic(switchCharacteristic);
// add service
BLE.addService(ledService);
// set the initial value for the characeristic:
switchCharacteristic.writeValue(0);
// start advertising
BLE.advertise();
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
pinMode(PIN, OUTPUT);
digitalWrite(PIN, LOW);
}
void loop()
{
BLEDevice central = BLE.central();
// if a central is connected to peripheral:
if (central)
{
Serial.print("Connected to central: ");
// print the central's MAC address:
Serial.println(central.address());
// while the central is still connected to peripheral:
while (central.connected())
{
// if the remote device wrote to the characteristic,
// use the value to control the LED:
if (switchCharacteristic.written())
{
switch (switchCharacteristic.value())
{
case 'a':
colorWipe(strip.Color(255, 0, 0), 20); // Red
break;
case 'b':
colorWipe(strip.Color(0, 255, 0), 20); // Green
break;
case 'c':
colorWipe(strip.Color(0, 0, 255), 20); // Blue
break;
case 'd':
theaterChase(strip.Color(255, 0, 0), 20); // Red
break;
case 'e':
theaterChase(strip.Color(0, 255, 0), 20); // Green
break;
case 'f':
theaterChase(strip.Color(255, 0, 255), 20); // Cyan
break;
case 'g':
rainbow(10);
break;
case 'h':
theaterChaseRainbow(20);
break;
}
}
}
}
}
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait)
{
for (int i = 0; i < strip.numPixels(); i++)
{ // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait)
{
for (int a = 0; a < 10; a++)
{ // Repeat 10 times...
for (int b = 0; b < 3; b++)
{ // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for (int c = b; c < strip.numPixels(); c += 3)
{
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait)
{
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this outer loop:
for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256)
{
for (int i = 0; i < strip.numPixels(); i++)
{ // For each pixel in strip...
// Offset pixel hue by an amount to make one full revolution of the
// color wheel (range of 65536) along the length of the strip
// (strip.numPixels() steps):
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
// strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
// optionally add saturation and value (brightness) (each 0 to 255).
// Here we're using just the single-argument hue variant. The result
// is passed through strip.gamma32() to provide 'truer' colors
// before assigning to each pixel:
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait)
{
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for (int a = 0; a < 30; a++)
{ // Repeat 30 times...
for (int b = 0; b < 3; b++)
{ // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for (int c = b; c < strip.numPixels(); c += 3)
{
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels();
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
@@ -1,239 +0,0 @@
/****************************************************************************
* This example is based on StrandtestArduinoBLE example to make use of
* callbacks features of the ArduinoBLE library.
*
* https://github.com/arduino-libraries/ArduinoBLE
*
* Supported boards:
* Arduino MKR WiFi 1010, Arduino Uno WiFi Rev2 board, Arduino Nano 33 IoT,
Arduino Nano 33 BLE, or Arduino Nano 33 BLE Sense board.
*
* You can use a generic BLE central app, like LightBlue (iOS and Android) or
* nRF Connect (Android), to interact with the services and characteristics
* created in this sketch.
*
* This example code is in the public domain.
*
*/
#include <Adafruit_NeoPixel.h>
#define PIN 15 // Pin where NeoPixels are connected
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(64, PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
// connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
// a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)
uint8_t rgb_values[3];
#include <ArduinoBLE.h>
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service
// BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
void setup()
{
Serial.begin(115200);
Serial.println("Hello World!");
// custom services and characteristics can be added as well
// begin initialization
if (!BLE.begin())
{
Serial.println("starting BLE failed!");
while (1)
;
}
Serial.print("Peripheral address: ");
Serial.println(BLE.address());
// set advertised local name and service UUID:
BLE.setLocalName("LEDCallback");
BLE.setAdvertisedService(ledService);
// add the characteristic to the service
ledService.addCharacteristic(switchCharacteristic);
// add service
BLE.addService(ledService);
// assign event handlers for connected, disconnected to peripheral
BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
// assign event handlers for characteristic
switchCharacteristic.setEventHandler(BLEWritten, switchCharacteristicWritten);
// set the initial value for the characeristic:
switchCharacteristic.writeValue(0);
// start advertising
BLE.advertise();
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
pinMode(PIN, OUTPUT);
digitalWrite(PIN, LOW);
}
void loop()
{
// poll for BLE events
BLE.poll();
}
void blePeripheralConnectHandler(BLEDevice central)
{
// central connected event handler
Serial.print("Connected event, central: ");
Serial.println(central.address());
}
void blePeripheralDisconnectHandler(BLEDevice central)
{
// central disconnected event handler
Serial.print("Disconnected event, central: ");
Serial.println(central.address());
}
void switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic)
{
// central wrote new value to characteristic, update LED
Serial.print("Characteristic event, written: ");
switch (switchCharacteristic.value())
{
case 'a':
colorWipe(strip.Color(255, 0, 0), 20); // Red
break;
case 'b':
colorWipe(strip.Color(0, 255, 0), 20); // Green
break;
case 'c':
colorWipe(strip.Color(0, 0, 255), 20); // Blue
break;
case 'd':
theaterChase(strip.Color(255, 0, 0), 20); // Red
break;
case 'e':
theaterChase(strip.Color(0, 255, 0), 20); // Green
break;
case 'f':
theaterChase(strip.Color(255, 0, 255), 20); // Cyan
break;
case 'g':
rainbow(10);
break;
case 'h':
theaterChaseRainbow(20);
break;
}
}
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait)
{
for (int i = 0; i < strip.numPixels(); i++)
{ // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait)
{
for (int a = 0; a < 10; a++)
{ // Repeat 10 times...
for (int b = 0; b < 3; b++)
{ // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for (int c = b; c < strip.numPixels(); c += 3)
{
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait)
{
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this outer loop:
for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256)
{
for (int i = 0; i < strip.numPixels(); i++)
{ // For each pixel in strip...
// Offset pixel hue by an amount to make one full revolution of the
// color wheel (range of 65536) along the length of the strip
// (strip.numPixels() steps):
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
// strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
// optionally add saturation and value (brightness) (each 0 to 255).
// Here we're using just the single-argument hue variant. The result
// is passed through strip.gamma32() to provide 'truer' colors
// before assigning to each pixel:
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait)
{
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for (int a = 0; a < 30; a++)
{ // Repeat 30 times...
for (int b = 0; b < 3; b++)
{ // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for (int c = b; c < strip.numPixels(); c += 3)
{
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels();
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
@@ -1,133 +0,0 @@
#include "BLESerial.h"
// #define BLE_SERIAL_DEBUG
BLESerial* BLESerial::_instance = NULL;
BLESerial::BLESerial(unsigned char req, unsigned char rdy, unsigned char rst) :
BLEPeripheral(req, rdy, rst)
{
this->_txCount = 0;
this->_rxHead = this->_rxTail = 0;
this->_flushed = 0;
BLESerial::_instance = this;
addAttribute(this->_uartService);
addAttribute(this->_uartNameDescriptor);
setAdvertisedServiceUuid(this->_uartService.uuid());
addAttribute(this->_rxCharacteristic);
addAttribute(this->_rxNameDescriptor);
this->_rxCharacteristic.setEventHandler(BLEWritten, BLESerial::_received);
addAttribute(this->_txCharacteristic);
addAttribute(this->_txNameDescriptor);
}
void BLESerial::begin(...) {
BLEPeripheral::begin();
#ifdef BLE_SERIAL_DEBUG
Serial.println(F("BLESerial::begin()"));
#endif
}
void BLESerial::poll() {
if (millis() < this->_flushed + 100) {
BLEPeripheral::poll();
} else {
flush();
}
}
void BLESerial::end() {
this->_rxCharacteristic.setEventHandler(BLEWritten, NULL);
this->_rxHead = this->_rxTail = 0;
flush();
BLEPeripheral::disconnect();
}
int BLESerial::available(void) {
BLEPeripheral::poll();
int retval = (this->_rxHead - this->_rxTail + sizeof(this->_rxBuffer)) % sizeof(this->_rxBuffer);
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::available() = "));
Serial.println(retval);
#endif
return retval;
}
int BLESerial::peek(void) {
BLEPeripheral::poll();
if (this->_rxTail == this->_rxHead) return -1;
uint8_t byte = this->_rxBuffer[this->_rxTail];
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::peek() = "));
Serial.print((char) byte);
Serial.print(F(" 0x"));
Serial.println(byte, HEX);
#endif
return byte;
}
int BLESerial::read(void) {
BLEPeripheral::poll();
if (this->_rxTail == this->_rxHead) return -1;
this->_rxTail = (this->_rxTail + 1) % sizeof(this->_rxBuffer);
uint8_t byte = this->_rxBuffer[this->_rxTail];
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::read() = "));
Serial.print((char) byte);
Serial.print(F(" 0x"));
Serial.println(byte, HEX);
#endif
return byte;
}
void BLESerial::flush(void) {
if (this->_txCount == 0) return;
this->_txCharacteristic.setValue(this->_txBuffer, this->_txCount);
this->_flushed = millis();
this->_txCount = 0;
BLEPeripheral::poll();
#ifdef BLE_SERIAL_DEBUG
Serial.println(F("BLESerial::flush()"));
#endif
}
size_t BLESerial::write(uint8_t byte) {
BLEPeripheral::poll();
if (this->_txCharacteristic.subscribed() == false) return 0;
this->_txBuffer[this->_txCount++] = byte;
if (this->_txCount == sizeof(this->_txBuffer)) flush();
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::write("));
Serial.print((char) byte);
Serial.print(F(" 0x"));
Serial.print(byte, HEX);
Serial.println(F(") = 1"));
#endif
return 1;
}
BLESerial::operator bool() {
bool retval = BLEPeripheral::connected();
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::operator bool() = "));
Serial.println(retval);
#endif
return retval;
}
void BLESerial::_received(const uint8_t* data, size_t size) {
for (int i = 0; i < size; i++) {
this->_rxHead = (this->_rxHead + 1) % sizeof(this->_rxBuffer);
this->_rxBuffer[this->_rxHead] = data[i];
}
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::received("));
for (int i = 0; i < size; i++) Serial.print((char) data[i]);
Serial.println(F(")"));
#endif
}
void BLESerial::_received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic) {
BLESerial::_instance->_received(rxCharacteristic.value(), rxCharacteristic.valueLength());
}
@@ -1,46 +0,0 @@
#ifndef _BLE_SERIAL_H_
#define _BLE_SERIAL_H_
#include <Arduino.h>
#include <BLEPeripheral.h>
class BLESerial : public BLEPeripheral, public Stream
{
public:
BLESerial(unsigned char req, unsigned char rdy, unsigned char rst);
void begin(...);
void poll();
void end();
virtual int available(void);
virtual int peek(void);
virtual int read(void);
virtual void flush(void);
virtual size_t write(uint8_t byte);
using Print::write;
virtual operator bool();
private:
unsigned long _flushed;
static BLESerial* _instance;
size_t _rxHead;
size_t _rxTail;
size_t _rxCount() const;
uint8_t _rxBuffer[BLE_ATTRIBUTE_MAX_VALUE_LENGTH];
size_t _txCount;
uint8_t _txBuffer[BLE_ATTRIBUTE_MAX_VALUE_LENGTH];
BLEService _uartService = BLEService("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
BLEDescriptor _uartNameDescriptor = BLEDescriptor("2901", "UART");
BLECharacteristic _rxCharacteristic = BLECharacteristic("6E400002-B5A3-F393-E0A9-E50E24DCCA9E", BLEWriteWithoutResponse, BLE_ATTRIBUTE_MAX_VALUE_LENGTH);
BLEDescriptor _rxNameDescriptor = BLEDescriptor("2901", "RX - Receive Data (Write)");
BLECharacteristic _txCharacteristic = BLECharacteristic("6E400003-B5A3-F393-E0A9-E50E24DCCA9E", BLENotify, BLE_ATTRIBUTE_MAX_VALUE_LENGTH);
BLEDescriptor _txNameDescriptor = BLEDescriptor("2901", "TX - Transfer Data (Notify)");
void _received(const uint8_t* data, size_t size);
static void _received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic);
};
#endif
@@ -1,192 +0,0 @@
/****************************************************************************
* This example was developed by the Hackerspace San Salvador to demonstrate
* the simultaneous use of the NeoPixel library and the Bluetooth SoftDevice.
* To compile this example you'll need to add support for the NRF52 based
* following the instructions at:
* https://github.com/sandeepmistry/arduino-nRF5
* Or adding the following URL to the board manager URLs on Arduino preferences:
* https://sandeepmistry.github.io/arduino-nRF5/package_nRF5_boards_index.json
* Then you can install the BLEPeripheral library avaiable at:
* https://github.com/sandeepmistry/arduino-BLEPeripheral
* To test it, compile this example and use the UART module from the nRF
* Toolbox App for Android. Edit the interface and send the characters
* 'a' to 'i' to switch the animation.
* There is a delay because this example blocks the thread of execution but
* the change will be shown after the current animation ends. (This might
* take a couple of seconds)
* For more info write us at: info _at- teubi.co
*/
#include <SPI.h>
#include <BLEPeripheral.h>
#include "BLESerial.h"
#include <Adafruit_NeoPixel.h>
#define PIN 15 // Pin where NeoPixels are connected
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(64, PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
// connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
// a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)
// define pins (varies per shield/board)
#define BLE_REQ 10
#define BLE_RDY 2
#define BLE_RST 9
// create ble serial instance, see pinouts above
BLESerial BLESerial(BLE_REQ, BLE_RDY, BLE_RST);
uint8_t current_state = 0;
uint8_t rgb_values[3];
void setup() {
Serial.begin(115200);
Serial.println("Hello World!");
// custom services and characteristics can be added as well
BLESerial.setLocalName("UART_HS");
BLESerial.begin();
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
//pinMode(PIN, OUTPUT);
//digitalWrite(PIN, LOW);
current_state = 'a';
}
void loop() {
while(BLESerial.available()) {
uint8_t character = BLESerial.read();
switch(character) {
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
current_state = character;
break;
};
}
switch(current_state) {
case 'a':
colorWipe(strip.Color(255, 0, 0), 20); // Red
break;
case 'b':
colorWipe(strip.Color( 0, 255, 0), 20); // Green
break;
case 'c':
colorWipe(strip.Color( 0, 0, 255), 20); // Blue
break;
case 'd':
theaterChase(strip.Color(255, 0, 0), 20); // Red
break;
case 'e':
theaterChase(strip.Color( 0, 255, 0), 20); // Green
break;
case 'f':
theaterChase(strip.Color(255, 0, 255), 20); // Cyan
break;
case 'g':
rainbow(10);
break;
case 'h':
theaterChaseRainbow(20);
break;
}
}
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
for(int a=0; a<10; a++) { // Repeat 10 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this outer loop:
for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
// Offset pixel hue by an amount to make one full revolution of the
// color wheel (range of 65536) along the length of the strip
// (strip.numPixels() steps):
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
// strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
// optionally add saturation and value (brightness) (each 0 to 255).
// Here we're using just the single-argument hue variant. The result
// is passed through strip.gamma32() to provide 'truer' colors
// before assigning to each pixel:
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for(int a=0; a<30; a++) { // Repeat 30 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels();
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
@@ -1,133 +0,0 @@
#include "BLESerial.h"
// #define BLE_SERIAL_DEBUG
BLESerial* BLESerial::_instance = NULL;
BLESerial::BLESerial(unsigned char req, unsigned char rdy, unsigned char rst) :
BLEPeripheral(req, rdy, rst)
{
this->_txCount = 0;
this->_rxHead = this->_rxTail = 0;
this->_flushed = 0;
BLESerial::_instance = this;
addAttribute(this->_uartService);
addAttribute(this->_uartNameDescriptor);
setAdvertisedServiceUuid(this->_uartService.uuid());
addAttribute(this->_rxCharacteristic);
addAttribute(this->_rxNameDescriptor);
this->_rxCharacteristic.setEventHandler(BLEWritten, BLESerial::_received);
addAttribute(this->_txCharacteristic);
addAttribute(this->_txNameDescriptor);
}
void BLESerial::begin(...) {
BLEPeripheral::begin();
#ifdef BLE_SERIAL_DEBUG
Serial.println(F("BLESerial::begin()"));
#endif
}
void BLESerial::poll() {
if (millis() < this->_flushed + 100) {
BLEPeripheral::poll();
} else {
flush();
}
}
void BLESerial::end() {
this->_rxCharacteristic.setEventHandler(BLEWritten, NULL);
this->_rxHead = this->_rxTail = 0;
flush();
BLEPeripheral::disconnect();
}
int BLESerial::available(void) {
BLEPeripheral::poll();
int retval = (this->_rxHead - this->_rxTail + sizeof(this->_rxBuffer)) % sizeof(this->_rxBuffer);
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::available() = "));
Serial.println(retval);
#endif
return retval;
}
int BLESerial::peek(void) {
BLEPeripheral::poll();
if (this->_rxTail == this->_rxHead) return -1;
uint8_t byte = this->_rxBuffer[this->_rxTail];
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::peek() = "));
Serial.print((char) byte);
Serial.print(F(" 0x"));
Serial.println(byte, HEX);
#endif
return byte;
}
int BLESerial::read(void) {
BLEPeripheral::poll();
if (this->_rxTail == this->_rxHead) return -1;
this->_rxTail = (this->_rxTail + 1) % sizeof(this->_rxBuffer);
uint8_t byte = this->_rxBuffer[this->_rxTail];
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::read() = "));
Serial.print((char) byte);
Serial.print(F(" 0x"));
Serial.println(byte, HEX);
#endif
return byte;
}
void BLESerial::flush(void) {
if (this->_txCount == 0) return;
this->_txCharacteristic.setValue(this->_txBuffer, this->_txCount);
this->_flushed = millis();
this->_txCount = 0;
BLEPeripheral::poll();
#ifdef BLE_SERIAL_DEBUG
Serial.println(F("BLESerial::flush()"));
#endif
}
size_t BLESerial::write(uint8_t byte) {
BLEPeripheral::poll();
if (this->_txCharacteristic.subscribed() == false) return 0;
this->_txBuffer[this->_txCount++] = byte;
if (this->_txCount == sizeof(this->_txBuffer)) flush();
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::write("));
Serial.print((char) byte);
Serial.print(F(" 0x"));
Serial.print(byte, HEX);
Serial.println(F(") = 1"));
#endif
return 1;
}
BLESerial::operator bool() {
bool retval = BLEPeripheral::connected();
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::operator bool() = "));
Serial.println(retval);
#endif
return retval;
}
void BLESerial::_received(const uint8_t* data, size_t size) {
for (int i = 0; i < size; i++) {
this->_rxHead = (this->_rxHead + 1) % sizeof(this->_rxBuffer);
this->_rxBuffer[this->_rxHead] = data[i];
}
#ifdef BLE_SERIAL_DEBUG
Serial.print(F("BLESerial::received("));
for (int i = 0; i < size; i++) Serial.print((char) data[i]);
Serial.println(F(")"));
#endif
}
void BLESerial::_received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic) {
BLESerial::_instance->_received(rxCharacteristic.value(), rxCharacteristic.valueLength());
}
@@ -1,46 +0,0 @@
#ifndef _BLE_SERIAL_H_
#define _BLE_SERIAL_H_
#include <Arduino.h>
#include <BLEPeripheral.h>
class BLESerial : public BLEPeripheral, public Stream
{
public:
BLESerial(unsigned char req, unsigned char rdy, unsigned char rst);
void begin(...);
void poll();
void end();
virtual int available(void);
virtual int peek(void);
virtual int read(void);
virtual void flush(void);
virtual size_t write(uint8_t byte);
using Print::write;
virtual operator bool();
private:
unsigned long _flushed;
static BLESerial* _instance;
size_t _rxHead;
size_t _rxTail;
size_t _rxCount() const;
uint8_t _rxBuffer[BLE_ATTRIBUTE_MAX_VALUE_LENGTH];
size_t _txCount;
uint8_t _txBuffer[BLE_ATTRIBUTE_MAX_VALUE_LENGTH];
BLEService _uartService = BLEService("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
BLEDescriptor _uartNameDescriptor = BLEDescriptor("2901", "UART");
BLECharacteristic _rxCharacteristic = BLECharacteristic("6E400002-B5A3-F393-E0A9-E50E24DCCA9E", BLEWriteWithoutResponse, BLE_ATTRIBUTE_MAX_VALUE_LENGTH);
BLEDescriptor _rxNameDescriptor = BLEDescriptor("2901", "RX - Receive Data (Write)");
BLECharacteristic _txCharacteristic = BLECharacteristic("6E400003-B5A3-F393-E0A9-E50E24DCCA9E", BLENotify, BLE_ATTRIBUTE_MAX_VALUE_LENGTH);
BLEDescriptor _txNameDescriptor = BLEDescriptor("2901", "TX - Transfer Data (Notify)");
void _received(const uint8_t* data, size_t size);
static void _received(BLECentral& /*central*/, BLECharacteristic& rxCharacteristic);
};
#endif
@@ -1,198 +0,0 @@
/****************************************************************************
* This example was developed by the Hackerspace San Salvador to demonstrate
* the simultaneous use of the NeoPixel library and the Bluetooth SoftDevice.
* To compile this example you'll need to add support for the NRF52 based
* following the instructions at:
* https://github.com/sandeepmistry/arduino-nRF5
* Or adding the following URL to the board manager URLs on Arduino preferences:
* https://sandeepmistry.github.io/arduino-nRF5/package_nRF5_boards_index.json
* Then you can install the BLEPeripheral library avaiable at:
* https://github.com/sandeepmistry/arduino-BLEPeripheral
* To test it, compile this example and use the UART module from the nRF
* Toolbox App for Android. Edit the interface and send the characters
* 'a' to 'i' to switch the animation.
* There is a no delay because this example does not block the threads execution
* so the change will be shown immediately and will not need to wait for the current
* animation to end.
* For more info write us at: info _at- teubi.co
*/
#include <SPI.h>
#include <BLEPeripheral.h>
#include "BLESerial.h"
#include <Adafruit_NeoPixel.h>
#define PIN 15 // Pin where NeoPixels are connected
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(64, PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
// connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
// a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)
// define pins (varies per shield/board)
#define BLE_REQ 10
#define BLE_RDY 2
#define BLE_RST 9
// create ble serial instance, see pinouts above
BLESerial BLESerial(BLE_REQ, BLE_RDY, BLE_RST);
uint8_t current_state = 0;
uint8_t rgb_values[3];
void setup() {
Serial.begin(115200);
Serial.println("Hello World!");
// custom services and characteristics can be added as well
BLESerial.setLocalName("UART_HS");
BLESerial.begin();
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
//pinMode(PIN, OUTPUT);
//digitalWrite(PIN, LOW);
current_state = 'a';
}
void loop() {
while(BLESerial.available()) {
uint8_t character = BLESerial.read();
switch(character) {
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
current_state = character;
break;
};
}
switch(current_state) {
case 'a':
colorWipe(strip.Color(255, 0, 0), 20); // Red
break;
case 'b':
colorWipe(strip.Color( 0, 255, 0), 20); // Green
break;
case 'c':
colorWipe(strip.Color( 0, 0, 255), 20); // Blue
break;
case 'd':
theaterChase(strip.Color(255, 0, 0), 20); // Red
break;
case 'e':
theaterChase(strip.Color( 0, 255, 0), 20); // Green
break;
case 'f':
theaterChase(strip.Color(255, 0, 255), 20); // Cyan
break;
case 'g':
rainbow(10);
break;
case 'h':
theaterChaseRainbow(20);
break;
}
}
// Some functions of our own for creating animated effects -----------------
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
if(pixelInterval != wait)
pixelInterval = wait; // Update delay time
strip.setPixelColor(pixelCurrent, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
pixelCurrent++; // Advance current pixel
if(pixelCurrent >= pixelNumber) // Loop the pattern from the first LED
pixelCurrent = 0;
}
// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
if(pixelInterval != wait)
pixelInterval = wait; // Update delay time
for(int i = 0; i < pixelNumber; i++) {
strip.setPixelColor(i + pixelQueue, color); // Set pixel's color (in RAM)
}
strip.show(); // Update strip to match
for(int i=0; i < pixelNumber; i+3) {
strip.setPixelColor(i + pixelQueue, strip.Color(0, 0, 0)); // Set pixel's color (in RAM)
}
pixelQueue++; // Advance current pixel
if(pixelQueue >= 3)
pixelQueue = 0; // Loop the pattern from the first LED
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(uint8_t wait) {
if(pixelInterval != wait)
pixelInterval = wait;
for(uint16_t i=0; i < pixelNumber; i++) {
strip.setPixelColor(i, Wheel((i + pixelCycle) & 255)); // Update delay time
}
strip.show(); // Update strip to match
pixelCycle++; // Advance current cycle
if(pixelCycle >= 256)
pixelCycle = 0; // Loop the cycle back to the begining
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
if(pixelInterval != wait)
pixelInterval = wait; // Update delay time
for(int i=0; i < pixelNumber; i+3) {
strip.setPixelColor(i + pixelQueue, Wheel((i + pixelCycle) % 255)); // Update delay time
}
strip.show();
for(int i=0; i < pixelNumber; i+3) {
strip.setPixelColor(i + pixelQueue, strip.Color(0, 0, 0)); // Update delay time
}
pixelQueue++; // Advance current queue
pixelCycle++; // Advance current cycle
if(pixelQueue >= 3)
pixelQueue = 0; // Loop
if(pixelCycle >= 256)
pixelCycle = 0; // Loop
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
@@ -1,164 +0,0 @@
// Simple demonstration on using an input device to trigger changes on your
// NeoPixels. Wire a momentary push button to connect from ground to a
// digital IO pin. When the button is pressed it will change to a new pixel
// animation. Initial state has all pixels off -- press the button once to
// start the first animation. As written, the button does not interrupt an
// animation in-progress, it works only when idle.
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// Digital IO pin connected to the button. This will be driven with a
// pull-up resistor so the switch pulls the pin to ground momentarily.
// On a high -> low transition the button press logic will execute.
#define BUTTON_PIN 2
#define PIXEL_PIN 6 // Digital IO pin connected to the NeoPixels.
#define PIXEL_COUNT 16 // Number of NeoPixels
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
boolean oldState = HIGH;
int mode = 0; // Currently-active animation mode, 0-9
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
strip.begin(); // Initialize NeoPixel strip object (REQUIRED)
strip.show(); // Initialize all pixels to 'off'
}
void loop() {
// Get current button state.
boolean newState = digitalRead(BUTTON_PIN);
// Check if state changed from high to low (button press).
if((newState == LOW) && (oldState == HIGH)) {
// Short delay to debounce button.
delay(20);
// Check if button is still low after debounce.
newState = digitalRead(BUTTON_PIN);
if(newState == LOW) { // Yes, still low
if(++mode > 8) mode = 0; // Advance to next mode, wrap around after #8
switch(mode) { // Start the new animation...
case 0:
colorWipe(strip.Color( 0, 0, 0), 50); // Black/off
break;
case 1:
colorWipe(strip.Color(255, 0, 0), 50); // Red
break;
case 2:
colorWipe(strip.Color( 0, 255, 0), 50); // Green
break;
case 3:
colorWipe(strip.Color( 0, 0, 255), 50); // Blue
break;
case 4:
theaterChase(strip.Color(127, 127, 127), 50); // White
break;
case 5:
theaterChase(strip.Color(127, 0, 0), 50); // Red
break;
case 6:
theaterChase(strip.Color( 0, 0, 127), 50); // Blue
break;
case 7:
rainbow(10);
break;
case 8:
theaterChaseRainbow(50);
break;
}
}
}
// Set the last-read button state to the old state.
oldState = newState;
}
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
for(int a=0; a<10; a++) { // Repeat 10 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
// Hue of first pixel runs 3 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 3*65536. Adding 256 to firstPixelHue each time
// means we'll make 3*65536/256 = 768 passes through this outer loop:
for(long firstPixelHue = 0; firstPixelHue < 3*65536; firstPixelHue += 256) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
// Offset pixel hue by an amount to make one full revolution of the
// color wheel (range of 65536) along the length of the strip
// (strip.numPixels() steps):
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
// strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
// optionally add saturation and value (brightness) (each 0 to 255).
// Here we're using just the single-argument hue variant. The result
// is passed through strip.gamma32() to provide 'truer' colors
// before assigning to each pixel:
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for(int a=0; a<30; a++) { // Repeat 30 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels();
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
@@ -1,50 +0,0 @@
// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// Released under the GPLv3 license to match the rest of the
// Adafruit NeoPixel library
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// Which pin on the Arduino is connected to the NeoPixels?
#define PIN 6 // On Trinket or Gemma, suggest changing this to 1
// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 16 // Popular NeoPixel ring size
// When setting up the NeoPixel library, we tell it how many pixels,
// and which pin to use to send signals. Note that for older NeoPixel
// strips you might need to change the third parameter -- see the
// strandtest example for more information on possible values.
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#define DELAYVAL 500 // Time (in milliseconds) to pause between pixels
void setup() {
// These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
// Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
// END of Trinket-specific code.
pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
}
void loop() {
pixels.clear(); // Set all pixel colors to 'off'
// The first NeoPixel in a strand is #0, second is 1, all the way up
// to the count of pixels minus one.
for(int i=0; i<NUMPIXELS; i++) { // For each pixel...
// pixels.Color() takes RGB values, from 0,0,0 up to 255,255,255
// Here we're using a moderately bright green color:
pixels.setPixelColor(i, pixels.Color(0, 150, 0));
pixels.show(); // Send the updated pixel colors to the hardware.
delay(DELAYVAL); // Pause before next pass through loop
}
}
@@ -1,67 +0,0 @@
// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// Released under the GPLv3 license to match the rest of the
// Adafruit NeoPixel library
// This sketch shows use of the "new" operator with Adafruit_NeoPixel.
// It's helpful if you don't know NeoPixel settings at compile time or
// just want to store this settings in EEPROM or a file on an SD card.
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// Which pin on the Arduino is connected to the NeoPixels?
int pin = 6; // On Trinket or Gemma, suggest changing this to 1
// How many NeoPixels are attached to the Arduino?
int numPixels = 16; // Popular NeoPixel ring size
// NeoPixel color format & data rate. See the strandtest example for
// information on possible values.
int pixelFormat = NEO_GRB + NEO_KHZ800;
// Rather than declaring the whole NeoPixel object here, we just create
// a pointer for one, which we'll then allocate later...
Adafruit_NeoPixel *pixels;
#define DELAYVAL 500 // Time (in milliseconds) to pause between pixels
void setup() {
// These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
// Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
// END of Trinket-specific code.
// Right about here is where we could read 'pin', 'numPixels' and/or
// 'pixelFormat' from EEPROM or a file on SD or whatever. This is a simple
// example and doesn't do that -- those variables are just set to fixed
// values at the top of this code -- but this is where it would happen.
// Then create a new NeoPixel object dynamically with these values:
pixels = new Adafruit_NeoPixel(numPixels, pin, pixelFormat);
// Going forward from here, code works almost identically to any other
// NeoPixel example, but instead of the dot operator on function calls
// (e.g. pixels.begin()), we instead use pointer indirection (->) like so:
pixels->begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
// You'll see more of this in the loop() function below.
}
void loop() {
pixels->clear(); // Set all pixel colors to 'off'
// The first NeoPixel in a strand is #0, second is 1, all the way up
// to the count of pixels minus one.
for(int i=0; i<numPixels; i++) { // For each pixel...
// pixels->Color() takes RGB values, from 0,0,0 up to 255,255,255
// Here we're using a moderately bright green color:
pixels->setPixelColor(i, pixels->Color(0, 150, 0));
pixels->show(); // Send the updated pixel colors to the hardware.
delay(DELAYVAL); // Pause before next pass through loop
}
}
@@ -1,143 +0,0 @@
// A basic everyday NeoPixel strip test program.
// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
// connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
// a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN 6
// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 60
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
// setup() function -- runs once at startup --------------------------------
void setup() {
// These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
// Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
// END of Trinket-specific code.
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
}
// loop() function -- runs repeatedly as long as board is on ---------------
void loop() {
// Fill along the length of the strip in various colors...
colorWipe(strip.Color(255, 0, 0), 50); // Red
colorWipe(strip.Color( 0, 255, 0), 50); // Green
colorWipe(strip.Color( 0, 0, 255), 50); // Blue
// Do a theater marquee effect in various colors...
theaterChase(strip.Color(127, 127, 127), 50); // White, half brightness
theaterChase(strip.Color(127, 0, 0), 50); // Red, half brightness
theaterChase(strip.Color( 0, 0, 127), 50); // Blue, half brightness
rainbow(10); // Flowing rainbow cycle along the whole strip
theaterChaseRainbow(50); // Rainbow-enhanced theaterChase variant
}
// Some functions of our own for creating animated effects -----------------
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
for(int a=0; a<10; a++) { // Repeat 10 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this loop:
for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
// strip.rainbow() can take a single argument (first pixel hue) or
// optionally a few extras: number of rainbow repetitions (default 1),
// saturation and value (brightness) (both 0-255, similar to the
// ColorHSV() function, default 255), and a true/false flag for whether
// to apply gamma correction to provide 'truer' colors (default true).
strip.rainbow(firstPixelHue);
// Above line is equivalent to:
// strip.rainbow(firstPixelHue, 1, 255, 255, true);
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for(int a=0; a<30; a++) { // Repeat 30 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels();
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
@@ -1,200 +0,0 @@
// A non-blocking everyday NeoPixel strip test program.
// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
// connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
// a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#ifdef ESP32
// Cannot use 6 as output for ESP. Pins 6-11 are connected to SPI flash. Use 16 instead.
#define LED_PIN 16
#else
#define LED_PIN 6
#endif
// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 60
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
unsigned long pixelPrevious = 0; // Previous Pixel Millis
unsigned long patternPrevious = 0; // Previous Pattern Millis
int patternCurrent = 0; // Current Pattern Number
int patternInterval = 5000; // Pattern Interval (ms)
bool patternComplete = false;
int pixelInterval = 50; // Pixel Interval (ms)
int pixelQueue = 0; // Pattern Pixel Queue
int pixelCycle = 0; // Pattern Pixel Cycle
uint16_t pixelNumber = LED_COUNT; // Total Number of Pixels
// setup() function -- runs once at startup --------------------------------
void setup() {
// These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
// Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
// END of Trinket-specific code.
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
}
// loop() function -- runs repeatedly as long as board is on ---------------
void loop() {
unsigned long currentMillis = millis(); // Update current time
if( patternComplete || (currentMillis - patternPrevious) >= patternInterval) { // Check for expired time
patternComplete = false;
patternPrevious = currentMillis;
patternCurrent++; // Advance to next pattern
if(patternCurrent >= 7)
patternCurrent = 0;
}
if(currentMillis - pixelPrevious >= pixelInterval) { // Check for expired time
pixelPrevious = currentMillis; // Run current frame
switch (patternCurrent) {
case 7:
theaterChaseRainbow(50); // Rainbow-enhanced theaterChase variant
break;
case 6:
rainbow(10); // Flowing rainbow cycle along the whole strip
break;
case 5:
theaterChase(strip.Color(0, 0, 127), 50); // Blue
break;
case 4:
theaterChase(strip.Color(127, 0, 0), 50); // Red
break;
case 3:
theaterChase(strip.Color(127, 127, 127), 50); // White
break;
case 2:
colorWipe(strip.Color(0, 0, 255), 50); // Blue
break;
case 1:
colorWipe(strip.Color(0, 255, 0), 50); // Green
break;
default:
colorWipe(strip.Color(255, 0, 0), 50); // Red
break;
}
}
}
// Some functions of our own for creating animated effects -----------------
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
static uint16_t current_pixel = 0;
pixelInterval = wait; // Update delay time
strip.setPixelColor(current_pixel++, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
if(current_pixel >= pixelNumber) { // Loop the pattern from the first LED
current_pixel = 0;
patternComplete = true;
}
}
// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
static uint32_t loop_count = 0;
static uint16_t current_pixel = 0;
pixelInterval = wait; // Update delay time
strip.clear();
for(int c=current_pixel; c < pixelNumber; c += 3) {
strip.setPixelColor(c, color);
}
strip.show();
current_pixel++;
if (current_pixel >= 3) {
current_pixel = 0;
loop_count++;
}
if (loop_count >= 10) {
current_pixel = 0;
loop_count = 0;
patternComplete = true;
}
}
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(uint8_t wait) {
if(pixelInterval != wait)
pixelInterval = wait;
for(uint16_t i=0; i < pixelNumber; i++) {
strip.setPixelColor(i, Wheel((i + pixelCycle) & 255)); // Update delay time
}
strip.show(); // Update strip to match
pixelCycle++; // Advance current cycle
if(pixelCycle >= 256)
pixelCycle = 0; // Loop the cycle back to the begining
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
if(pixelInterval != wait)
pixelInterval = wait; // Update delay time
for(int i=0; i < pixelNumber; i+=3) {
strip.setPixelColor(i + pixelQueue, Wheel((i + pixelCycle) % 255)); // Update delay time
}
strip.show();
for(int i=0; i < pixelNumber; i+=3) {
strip.setPixelColor(i + pixelQueue, strip.Color(0, 0, 0)); // Update delay time
}
pixelQueue++; // Advance current queue
pixelCycle++; // Advance current cycle
if(pixelQueue >= 3)
pixelQueue = 0; // Loop
if(pixelCycle >= 256)
pixelCycle = 0; // Loop
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
@@ -1,134 +0,0 @@
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 6
// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);
// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel. Avoid connecting
// on a live circuit...if you must, connect GND first.
void setup() {
// This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
// End of trinket special code
strip.begin();
strip.setBrightness(50);
strip.show(); // Initialize all pixels to 'off'
}
void loop() {
// Some example procedures showing how to display to the pixels:
colorWipe(strip.Color(255, 0, 0), 50); // Red
colorWipe(strip.Color(0, 255, 0), 50); // Green
colorWipe(strip.Color(0, 0, 255), 50); // Blue
//colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW
// Send a theater pixel chase in...
theaterChase(strip.Color(127, 127, 127), 50); // White
theaterChase(strip.Color(127, 0, 0), 50); // Red
theaterChase(strip.Color(0, 0, 127), 50); // Blue
rainbow(20);
rainbowCycle(20);
theaterChaseRainbow(50);
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
for(i=0; i< strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j<10; j++) { //do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, c); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel
for (int q=0; q < 3; q++) {
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, Wheel( (i+j) % 255)); //turn every third pixel on
}
strip.show();
delay(wait);
for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
@@ -1,74 +0,0 @@
// This is a mash-up of the Due show() code + insights from Michael Miller's
// ESP8266 work for the NeoPixelBus library: github.com/Makuna/NeoPixelBus
// Needs to be a separate .c file to enforce ICACHE_RAM_ATTR execution.
#if defined(K210)
#define KENDRYTE_K210 1
#endif
#if defined(KENDRYTE_K210)
#include <Arduino.h>
#include "sysctl.h"
void k210Show(
uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz)
{
#define CYCLES_800_T0H (sysctl_clock_get_freq(SYSCTL_CLOCK_CPU) / 2500000) // 0.4us
#define CYCLES_800_T1H (sysctl_clock_get_freq(SYSCTL_CLOCK_CPU) / 1250000) // 0.8us
#define CYCLES_800 (sysctl_clock_get_freq(SYSCTL_CLOCK_CPU) / 800000) // 1.25us per bit
#define CYCLES_400_T0H (sysctl_clock_get_freq(SYSCTL_CLOCK_CPU) / 2000000) // 0.5uS
#define CYCLES_400_T1H (sysctl_clock_get_freq(SYSCTL_CLOCK_CPU) / 833333) // 1.2us
#define CYCLES_400 (sysctl_clock_get_freq(SYSCTL_CLOCK_CPU) / 400000) // 2.5us per bit
uint8_t *p, *end, pix, mask;
uint32_t t, time0, time1, period, c, startTime;
p = pixels;
end = p + numBytes;
pix = *p++;
mask = 0x80;
startTime = 0;
#ifdef NEO_KHZ400
if (is800KHz)
{
#endif
time0 = CYCLES_800_T0H;
time1 = CYCLES_800_T1H;
period = CYCLES_800;
#ifdef NEO_KHZ400
}
else
{ // 400 KHz bitstream
time0 = CYCLES_400_T0H;
time1 = CYCLES_400_T1H;
period = CYCLES_400;
}
#endif
for (t = time0;; t = time0)
{
if (pix & mask)
t = time1; // Bit high duration
while (((c = read_cycle()) - startTime) < period)
; // Wait for bit start
digitalWrite(pin, HIGH);
startTime = c; // Save start time
while (((c = read_cycle()) - startTime) < t)
; // Wait high duration
digitalWrite(pin, LOW);
if (!(mask >>= 1))
{ // Next bit/byte
if (p >= end)
break;
pix = *p++;
mask = 0x80;
}
}
while ((read_cycle() - startTime) < period)
; // Wait for last bit
}
#endif // KENDRYTE_K210
-72
View File
@@ -1,72 +0,0 @@
#######################################
# Syntax Coloring Map For Adafruit_NeoPixel
#######################################
# Class
#######################################
Adafruit_NeoPixel KEYWORD1
#######################################
# Methods and Functions
#######################################
begin KEYWORD2
show KEYWORD2
setPin KEYWORD2
setPixelColor KEYWORD2
fill KEYWORD2
setBrightness KEYWORD2
clear KEYWORD2
updateLength KEYWORD2
updateType KEYWORD2
canShow KEYWORD2
getPixels KEYWORD2
getBrightness KEYWORD2
getPin KEYWORD2
numPixels KEYWORD2
getPixelColor KEYWORD2
sine8 KEYWORD2
gamma8 KEYWORD2
Color KEYWORD2
ColorHSV KEYWORD2
gamma32 KEYWORD2
#######################################
# Constants
#######################################
NEO_COLMASK LITERAL1
NEO_SPDMASK LITERAL1
NEO_KHZ800 LITERAL1
NEO_KHZ400 LITERAL1
NEO_RGB LITERAL1
NEO_RBG LITERAL1
NEO_GRB LITERAL1
NEO_GBR LITERAL1
NEO_BRG LITERAL1
NEO_BGR LITERAL1
NEO_WRGB LITERAL1
NEO_WRBG LITERAL1
NEO_WGRB LITERAL1
NEO_WGBR LITERAL1
NEO_WBRG LITERAL1
NEO_WBGR LITERAL1
NEO_RWGB LITERAL1
NEO_RWBG LITERAL1
NEO_RGWB LITERAL1
NEO_RGBW LITERAL1
NEO_RBWG LITERAL1
NEO_RBGW LITERAL1
NEO_GWRB LITERAL1
NEO_GWBR LITERAL1
NEO_GRWB LITERAL1
NEO_GRBW LITERAL1
NEO_GBWR LITERAL1
NEO_GBRW LITERAL1
NEO_BWRG LITERAL1
NEO_BWGR LITERAL1
NEO_BRWG LITERAL1
NEO_BRGW LITERAL1
NEO_BGWR LITERAL1
NEO_BGRW LITERAL1
@@ -1,10 +0,0 @@
name=Adafruit NeoPixel
version=1.15.4
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=Arduino library for controlling single-wire-based LED pixels and strip.
paragraph=Arduino library for controlling single-wire-based LED pixels and strip.
category=Display
url=https://github.com/adafruit/Adafruit_NeoPixel
architectures=*
includes=Adafruit_NeoPixel.h
-87
View File
@@ -1,87 +0,0 @@
#if defined(ARDUINO_ARCH_PSOC6)
#include "cyhal_gpio.h"
#include "cyhal_system.h"
#include "cy_syslib.h"
#include "Arduino.h"
void psoc6_show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHZ)
{
if (!pixels) return;
noInterrupts();
GPIO_PRT_Type *base = CYHAL_GET_PORTADDR(mapping_gpio_pin[pin]);
uint32_t pinNum = CYHAL_GET_PIN(mapping_gpio_pin[pin]);
if(is800KHZ){
for (uint16_t i = 0; i < numBytes; i++) {
uint8_t b = pixels[i];
for (uint8_t j = 0; j < 8; j++) {
bool bit = (b & 0x80) != 0;
b <<= 1;
switch (bit) {
case 1:
// Send a 1-bit
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
break;
case 0:
// Send a 0-bit
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
break;
}
}
}
}
else{
for (size_t i = 0; i < numBytes; i++){
uint8_t b = pixels[i];
for (uint8_t j = 0; j < 8; j++) {
bool bit = (b & 0x80) != 0;
b <<= 1;
switch (bit){
case 1:
// Send a 1-bit
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
break;
case 0:
// Send a 0-bit
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 1);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
Cy_GPIO_Write(base, pinNum, 0);
break;
}
}
}
}
interrupts();
delayMicroseconds(50);
return;
}
#endif
-63
View File
@@ -1,63 +0,0 @@
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
// Unless you know what you are doing...
// Lines 47 and 52 have been edited to set transmit bit count
#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, uint bits) {
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,
bits); // <----<<< Length changed to "bits"
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
-5
View File
@@ -1,5 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include "src/ArduinoJson.h"
-255
View File
@@ -1,255 +0,0 @@
ArduinoJson: change log
=======================
v7.4.2 (2025-06-20)
------
* Fix truncated strings on Arduino Due (issue #2181)
v7.4.1 (2025-04-11)
------
* Fix crash with tiny Flash strings (issue #2170)
v7.4.0 (2025-04-09)
------
* Optimize storage of tiny strings (up to 3 characters)
* Fix support for `const char[]` (issue #2166)
v7.3.1 (2025-02-27)
------
* Fix conversion from static string to number
* Slightly reduce code size
v7.3.0 (2024-12-29)
------
* Fix support for NUL characters in `deserializeJson()`
* Make `ElementProxy` and `MemberProxy` non-copyable
* Change string copy policy: only string literal are stored by pointer
* `JsonString` is now stored by copy, unless specified otherwise
* Replace undocumented `JsonString::Ownership` with `bool`
* Rename undocumented `JsonString::isLinked()` to `isStatic()`
* Move public facing SFINAEs to template declarations
> ### BREAKING CHANGES
>
> In previous versions, `MemberProxy` (the class returned by `operator[]`) could lead to dangling pointers when used with a temporary string.
> To prevent this issue, `MemberProxy` and `ElementProxy` are now non-copyable.
>
> Your code is likely to be affected if you use `auto` to store the result of `operator[]`. For example, the following line won't compile anymore:
>
> ```cpp
> auto value = doc["key"];
> ```
>
> To fix the issue, you must append either `.as<T>()` or `.to<T>()`, depending on the situation.
>
> For example, if you are extracting values from a JSON document, you should update like this:
>
> ```diff
> - auto config = doc["config"];
> + auto config = doc["config"].as<JsonObject>();
> const char* name = config["name"];
> ```
>
> However, if you are building a JSON document, you should update like this:
>
> ```diff
> - auto config = doc["config"];
> + auto config = doc["config"].to<JsonObject>();
> config["name"] = "ArduinoJson";
> ```
v7.2.1 (2024-11-15)
------
* Forbid `deserializeJson(JsonArray|JsonObject, ...)` (issue #2135)
* Fix VLA support in `JsonDocument::set()`
* Fix `operator[](variant)` ignoring NUL characters
v7.2.0 (2024-09-18)
------
* Store object members with two slots: one for the key and one for the value
* Store 64-bit numbers (`double` and `long long`) in an additional slot
* Reduce the slot size (see table below)
* Improve message when user forgets third arg of `serializeJson()` et al.
* Set `ARDUINOJSON_USE_DOUBLE` to `0` by default on 8-bit architectures
* Deprecate `containsKey()` in favor of `doc["key"].is<T>()`
* Add support for escape sequence `\'` (issue #2124)
| Architecture | before | after |
|--------------|----------|----------|
| 8-bit | 8 bytes | 6 bytes |
| 32-bit | 16 bytes | 8 bytes |
| 64-bit | 24 bytes | 16 bytes |
> ### BREAKING CHANGES
>
> After being on the death row for years, the `containsKey()` method has finally been deprecated.
> You should replace `doc.containsKey("key")` with `doc["key"].is<T>()`, which not only checks that the key exists but also that the value is of the expected type.
>
> ```cpp
> // Before
> if (doc.containsKey("value")) {
> int value = doc["value"];
> // ...
> }
>
> // After
> if (doc["value"].is<int>()) {
> int value = doc["value"];
> // ...
> }
> ```
v7.1.0 (2024-06-27)
------
* Add `ARDUINOJSON_STRING_LENGTH_SIZE` to the namespace name
* Add support for MsgPack binary (PR #2078 by @Sanae6)
* Add support for MsgPack extension
* Make string support even more generic (PR #2084 by @d-a-v)
* Optimize `deserializeMsgPack()`
* Allow using a `JsonVariant` as a key or index (issue #2080)
Note: works only for reading, not for writing
* Support `ElementProxy` and `MemberProxy` in `JsonDocument`'s constructor
* Don't add partial objects when allocation fails (issue #2081)
* Read MsgPack's 64-bit integers even if `ARDUINOJSON_USE_LONG_LONG` is `0`
(they are set to `null` if they don't fit in a `long`)
v7.0.4 (2024-03-12)
------
* Make `JSON_STRING_SIZE(N)` return `N+1` to fix third-party code (issue #2054)
v7.0.3 (2024-02-05)
------
* Improve error messages when using `char` or `char*` (issue #2043)
* Reduce stack consumption (issue #2046)
* Fix compatibility with GCC 4.8 (issue #2045)
v7.0.2 (2024-01-19)
------
* Fix assertion `poolIndex < count_` after `JsonDocument::clear()` (issue #2034)
v7.0.1 (2024-01-10)
------
* Fix "no matching function" with `JsonObjectConst::operator[]` (issue #2019)
* Remove unused files in the PlatformIO package
* Fix `volatile bool` serialized as `1` or `0` instead of `true` or `false` (issue #2029)
v7.0.0 (2024-01-03)
------
* Remove `BasicJsonDocument`
* Remove `StaticJsonDocument`
* Add abstract `Allocator` class
* Merge `DynamicJsonDocument` with `JsonDocument`
* Remove `JSON_ARRAY_SIZE()`, `JSON_OBJECT_SIZE()`, and `JSON_STRING_SIZE()`
* Remove `ARDUINOJSON_ENABLE_STRING_DEDUPLICATION` (string deduplication cannot be disabled anymore)
* Remove `JsonDocument::capacity()`
* Store the strings in the heap
* Reference-count shared strings
* Always store `serialized("string")` by copy (#1915)
* Remove the zero-copy mode of `deserializeJson()` and `deserializeMsgPack()`
* Fix double lookup in `to<JsonVariant>()`
* Fix double call to `size()` in `serializeMsgPack()`
* Include `ARDUINOJSON_SLOT_OFFSET_SIZE` in the namespace name
* Remove `JsonVariant::shallowCopy()`
* `JsonDocument`'s capacity grows as needed, no need to pass it to the constructor anymore
* `JsonDocument`'s allocator is not monotonic anymore, removed values get recycled
* Show a link to the documentation when user passes an unsupported input type
* Remove `JsonDocument::memoryUsage()`
* Remove `JsonDocument::garbageCollect()`
* Add `deserializeJson(JsonVariant, ...)` and `deserializeMsgPack(JsonVariant, ...)` (#1226)
* Call `shrinkToFit()` in `deserializeJson()` and `deserializeMsgPack()`
* `serializeJson()` and `serializeMsgPack()` replace the content of `std::string` and `String` instead of appending to it
* Replace `add()` with `add<T>()` (`add(T)` is still supported)
* Remove `createNestedArray()` and `createNestedObject()` (use `to<JsonArray>()` and `to<JsonObject>()` instead)
> ### BREAKING CHANGES
>
> As every major release, ArduinoJson 7 introduces several breaking changes.
> I added some stubs so that most existing programs should compile, but I highty recommend you upgrade your code.
>
> #### `JsonDocument`
>
> In ArduinoJson 6, you could allocate the memory pool on the stack (with `StaticJsonDocument`) or in the heap (with `DynamicJsonDocument`).
> In ArduinoJson 7, the memory pool is always allocated in the heap, so `StaticJsonDocument` and `DynamicJsonDocument` have been merged into `JsonDocument`.
>
> In ArduinoJson 6, `JsonDocument` had a fixed capacity; in ArduinoJson 7, it has an elastic capacity that grows as needed.
> Therefore, you don't need to specify the capacity anymore, so the macros `JSON_ARRAY_SIZE()`, `JSON_OBJECT_SIZE()`, and `JSON_STRING_SIZE()` have been removed.
>
> ```c++
> // ArduinoJson 6
> StaticJsonDocument<256> doc;
> // or
> DynamicJsonDocument doc(256);
>
> // ArduinoJson 7
> JsonDocument doc;
> ```
>
> In ArduinoJson 7, `JsonDocument` reuses released memory, so `garbageCollect()` has been removed.
> `shrinkToFit()` is still available and releases the over-allocated memory.
>
> Due to a change in the implementation, it's not possible to store a pointer to a variant from another `JsonDocument`, so `shallowCopy()` has been removed.
>
> In ArduinoJson 6, the meaning of `memoryUsage()` was clear: it returned the number of bytes used in the memory pool.
> In ArduinoJson 7, the meaning of `memoryUsage()` would be ambiguous, so it has been removed.
>
> #### Custom allocators
>
> In ArduinoJson 6, you could specify a custom allocator class as a template parameter of `BasicJsonDocument`.
> In ArduinoJson 7, you must inherit from `ArduinoJson::Allocator` and pass a pointer to an instance of your class to the constructor of `JsonDocument`.
>
> ```c++
> // ArduinoJson 6
> class MyAllocator {
> // ...
> };
> BasicJsonDocument<MyAllocator> doc(256);
>
> // ArduinoJson 7
> class MyAllocator : public ArduinoJson::Allocator {
> // ...
> };
> MyAllocator myAllocator;
> JsonDocument doc(&myAllocator);
> ```
>
> #### `createNestedArray()` and `createNestedObject()`
>
> In ArduinoJson 6, you could create a nested array or object with `createNestedArray()` and `createNestedObject()`.
> In ArduinoJson 7, you must use `add<T>()` or `to<T>()` instead.
>
> For example, to create `[[],{}]`, you would write:
>
> ```c++
> // ArduinoJson 6
> arr.createNestedArray();
> arr.createNestedObject();
>
> // ArduinoJson 7
> arr.add<JsonArray>();
> arr.add<JsonObject>();
> ```
>
> And to create `{"array":[],"object":{}}`, you would write:
>
> ```c++
> // ArduinoJson 6
> obj.createNestedArray("array");
> obj.createNestedObject("object");
>
> // ArduinoJson 7
> obj["array"].to<JsonArray>();
> obj["object"].to<JsonObject>();
> ```
-25
View File
@@ -1,25 +0,0 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
cmake_minimum_required(VERSION 3.15)
if(ESP_PLATFORM)
# Build ArduinoJson as an ESP-IDF component
idf_component_register(INCLUDE_DIRS src)
return()
endif()
project(ArduinoJson VERSION 7.4.2)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)
endif()
add_subdirectory(src)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING)
include(extras/CompileOptions.cmake)
add_subdirectory(extras/tests)
add_subdirectory(extras/fuzzing)
endif()
-10
View File
@@ -1,10 +0,0 @@
# Contribution to ArduinoJson
First, thank you for taking the time to contribute to this project.
You can submit changes via GitHub Pull Requests.
Please:
1. Update the test suite for any change of behavior
2. Use clang-format in "file" mode to format the code
-10
View File
@@ -1,10 +0,0 @@
The MIT License (MIT)
---------------------
Copyright © 2014-2025, Benoit BLANCHON
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.
-153
View File
@@ -1,153 +0,0 @@
<p align="center">
<a href="https://arduinojson.org/"><img alt="ArduinoJson" src="https://arduinojson.org/images/logo.svg" width="200" /></a>
</p>
---
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bblanchon/ArduinoJson/ci.yml?branch=7.x&logo=github)](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A7.x)
[![Continuous Integration](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/7.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/7.x)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
[![Coveralls branch](https://img.shields.io/coveralls/github/bblanchon/ArduinoJson/7.x?logo=coveralls)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=7.x)
[![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github&color=orange)](https://github.com/bblanchon/ArduinoJson/stargazers)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github&color=orange)](https://github.com/sponsors/bblanchon)
ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
## Features
* [JSON deserialization](https://arduinojson.org/v7/api/json/deserializejson/)
* [Optionally decodes UTF-16 escape sequences to UTF-8](https://arduinojson.org/v7/api/config/decode_unicode/)
* [Optionally supports comments in the input](https://arduinojson.org/v7/api/config/enable_comments/)
* [Optionally filters the input to keep only desired values](https://arduinojson.org/v7/api/json/deserializejson/#filtering)
* Supports single quotes as a string delimiter
* Compatible with [NDJSON](http://ndjson.org/) and [JSON Lines](https://jsonlines.org/)
* [JSON serialization](https://arduinojson.org/v7/api/json/serializejson/)
* [Can write to a buffer or a stream](https://arduinojson.org/v7/api/json/serializejson/)
* [Optionally indents the document (prettified JSON)](https://arduinojson.org/v7/api/json/serializejsonpretty/)
* [MessagePack serialization](https://arduinojson.org/v7/api/msgpack/serializemsgpack/)
* [MessagePack deserialization](https://arduinojson.org/v7/api/msgpack/deserializemsgpack/)
* Efficient
* [Twice smaller than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Almost 10% faster than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Deduplicates strings](https://arduinojson.org/news/2020/08/01/version-6-16-0/)
* Versatile
* Supports [custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v7/how-to/use-external-ram-on-esp32/)
* Supports [`String`](https://arduinojson.org/v7/api/config/enable_arduino_string/), [`std::string`](https://arduinojson.org/v7/api/config/enable_std_string/), and [`std::string_view`](https://arduinojson.org/v7/api/config/enable_string_view/)
* Supports [`Stream`](https://arduinojson.org/v7/api/config/enable_arduino_stream/) and [`std::istream`/`std::ostream`](https://arduinojson.org/v7/api/config/enable_std_stream/)
* Supports [Flash strings](https://arduinojson.org/v7/api/config/enable_progmem/)
* Supports [custom readers](https://arduinojson.org/v7/api/json/deserializejson/#custom-reader) and [custom writers](https://arduinojson.org/v7/api/json/serializejson/#custom-writer)
* Supports [custom converters](https://arduinojson.org/news/2021/05/04/version-6-18-0/)
* Portable
* Usable on any C++ project (not limited to Arduino)
* Compatible with C++11, C++14 and C++17
* Support for C++98/C++03 available on [ArduinoJson 6.20.x](https://github.com/bblanchon/ArduinoJson/tree/6.20.x)
* Zero warnings with `-Wall -Wextra -pedantic` and `/W4`
* [Header-only library](https://en.wikipedia.org/wiki/Header-only)
* Works with virtually any board
* Arduino boards: [Uno](https://amzn.to/38aL2ik), [Due](https://amzn.to/36YkWi2), [Micro](https://amzn.to/35WkdwG), [Nano](https://amzn.to/2QTvwRX), [Mega](https://amzn.to/36XWhuf), [Yun](https://amzn.to/30odURc), [Leonardo](https://amzn.to/36XWjlR)...
* Espressif chips: [ESP8266](https://amzn.to/36YluV8), [ESP32](https://amzn.to/2G4pRCB)
* Lolin (WeMos) boards: [D1 mini](https://amzn.to/2QUpz7q), [D1 Mini Pro](https://amzn.to/36UsGSs)...
* Teensy boards: [4.0](https://amzn.to/30ljXGq), [3.2](https://amzn.to/2FT0EuC), [2.0](https://amzn.to/2QXUMXj)
* Particle boards: [Argon](https://amzn.to/2FQHa9X), [Boron](https://amzn.to/36WgLUd), [Electron](https://amzn.to/30vEc4k), [Photon](https://amzn.to/387F9Cd)...
* Texas Instruments boards: [MSP430](https://amzn.to/30nJWgg)...
* Soft cores: [Nios II](https://en.wikipedia.org/wiki/Nios_II)...
* Tested on all major development environments
* [Arduino IDE](https://www.arduino.cc/en/Main/Software)
* [Atmel Studio](http://www.atmel.com/microsite/atmel-studio/)
* [Atollic TrueSTUDIO](https://atollic.com/truestudio/)
* [Energia](http://energia.nu/)
* [IAR Embedded Workbench](https://www.iar.com/iar-embedded-workbench/)
* [Keil uVision](http://www.keil.com/)
* [MPLAB X IDE](http://www.microchip.com/mplab/mplab-x-ide)
* [Particle](https://www.particle.io/)
* [PlatformIO](http://platformio.org/)
* [Sloeber plugin for Eclipse](https://eclipse.baeyens.it/)
* [Visual Micro](http://www.visualmicro.com/)
* [Visual Studio](https://www.visualstudio.com/)
* [Even works with online compilers like wandbox.org](https://wandbox.org/permlink/RlZSKy17DjJ6HcdN)
* [CMake friendly](https://arduinojson.org/v7/how-to/use-arduinojson-with-cmake/)
* Well designed
* [Elegant API](http://arduinojson.org/v7/example/)
* [Thread-safe](https://en.wikipedia.org/wiki/Thread_safety)
* Self-contained (no external dependency)
* `const` friendly
* [`for` friendly](https://arduinojson.org/v7/api/jsonobject/begin_end/)
* [TMP friendly](https://en.wikipedia.org/wiki/Template_metaprogramming)
* Handles [integer overflows](https://arduinojson.org/v7/api/jsonvariant/as/#integer-overflows)
* Well tested
* [Unit test coverage close to 100%](https://coveralls.io/github/bblanchon/ArduinoJson?branch=7.x)
* Continuously tested on
* [Visual Studio 2017, 2019, 2022](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/7.x)
* [GCC 4.8, 5, 6, 7, 8, 9, 10, 11, 12](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
* [Clang 7 to 19](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
* [Continuously fuzzed with Google OSS Fuzz](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
* Passes all default checks of [clang-tidy](https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/)
* Well documented
* [Tutorials](https://arduinojson.org/v7/doc/deserialization/)
* [Examples](https://arduinojson.org/v7/example/)
* [How-tos](https://arduinojson.org/v7/example/)
* [FAQ](https://arduinojson.org/v7/faq/)
* [Troubleshooter](https://arduinojson.org/v7/troubleshooter/)
* [Book](https://arduinojson.org/book/)
* [Changelog](CHANGELOG.md)
* Vibrant user community
* Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories)
* [Used in hundreds of projects](https://www.hackster.io/search?i=projects&q=arduinojson)
* [Responsive support](https://github.com/bblanchon/ArduinoJson/issues?q=is%3Aissue+is%3Aclosed)
## Quickstart
### Deserialization
Here is a program that parses a JSON document with ArduinoJson.
```c++
const char* json = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
JsonDocument doc;
deserializeJson(doc, json);
const char* sensor = doc["sensor"];
long time = doc["time"];
double latitude = doc["data"][0];
double longitude = doc["data"][1];
```
See the [tutorial on arduinojson.org](https://arduinojson.org/v7/doc/deserialization/)
### Serialization
Here is a program that generates a JSON document with ArduinoJson:
```c++
JsonDocument doc;
doc["sensor"] = "gps";
doc["time"] = 1351824120;
doc["data"][0] = 48.756080;
doc["data"][1] = 2.302038;
serializeJson(doc, Serial);
// This prints:
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
```
See the [tutorial on arduinojson.org](https://arduinojson.org/v7/doc/serialization/)
## Sponsors
ArduinoJson is thankful to its sponsors. Please give them a visit; they deserve it!
<p>
<a href="https://github.com/1technophile" rel="sponsored">
<img alt="1technophile" src="https://avatars.githubusercontent.com/u/12672732?s=40&v=4">
</a>
<a href="https://github.com/LArkema" rel="sponsored">
<img alt="LArkema" src="https://avatars.githubusercontent.com/u/38381313?s=40&v=4">
</a>
</p>
If you run a commercial project that embeds ArduinoJson, think about [sponsoring the library's development](https://github.com/sponsors/bblanchon): it ensures the code that your products rely on stays actively maintained. It can also give your project some exposure to the makers' community.
If you are an individual user and want to support the development (or give a sign of appreciation), consider purchasing the book [Mastering ArduinoJson](https://arduinojson.org/book/)&nbsp;❤, or simply [cast a star](https://github.com/bblanchon/ArduinoJson/stargazers)&nbsp;⭐.
-27
View File
@@ -1,27 +0,0 @@
# ArduinoJson Support
First off, thank you very much for using ArduinoJson.
We'll be very happy to help you, but first please read the following.
## Before asking for help
1. Read the [FAQ](https://arduinojson.org/faq/?utm_source=github&utm_medium=support)
2. Search in the [API Reference](https://arduinojson.org/api/?utm_source=github&utm_medium=support)
If you did not find the answer, please create a [new issue on GitHub](https://github.com/bblanchon/ArduinoJson/issues/new).
It is OK to add a comment to a currently opened issue, but please avoid adding comments to a closed issue.
## Before hitting the Submit button
Please provide all the relevant information:
* Good title
* Short description of the problem
* Target platform
* Compiler model and version
* [MVCE](https://stackoverflow.com/help/mcve)
* Compiler output
Good questions get fast answers!
-28
View File
@@ -1,28 +0,0 @@
version: 7.4.2.{build}
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
CMAKE_GENERATOR: Visual Studio 17 2022
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
CMAKE_GENERATOR: Visual Studio 16 2019
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
CMAKE_GENERATOR: Visual Studio 15 2017
- CMAKE_GENERATOR: Ninja
MINGW32: i686-6.3.0-posix-dwarf-rt_v5-rev1 # MinGW-w64 6.3.0 i686
- CMAKE_GENERATOR: Ninja
MINGW64: x86_64-6.3.0-posix-seh-rt_v5-rev1 # MinGW-w64 6.3.0 x86_64
- CMAKE_GENERATOR: Ninja
MINGW64: x86_64-7.3.0-posix-seh-rt_v5-rev0 # MinGW-w64 7.3.0 x86_64
- CMAKE_GENERATOR: Ninja
MINGW64: x86_64-8.1.0-posix-seh-rt_v6-rev0 # MinGW-w64 8.1.0 x86_64
configuration: Debug
before_build:
- set PATH=%PATH:C:\Program Files\Git\usr\bin;=% # Workaround for CMake not wanting sh.exe on PATH for MinGW
- if defined MINGW set PATH=C:\%MINGW%\bin;%PATH%
- if defined MINGW32 set PATH=C:\mingw-w64\%MINGW32%\mingw32\bin;%PATH%
- if defined MINGW64 set PATH=C:\mingw-w64\%MINGW64%\mingw64\bin;%PATH%
- cmake -DCMAKE_BUILD_TYPE=%CONFIGURATION% -G "%CMAKE_GENERATOR%" .
build_script:
- cmake --build . --config %CONFIGURATION%
test_script:
- ctest -C %CONFIGURATION% --output-on-failure .
-1
View File
@@ -1 +0,0 @@
COMPONENT_ADD_INCLUDEDIRS := src
@@ -1,152 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to store your project configuration in a file.
// It uses the SD library but can be easily modified for any other file-system.
//
// The file contains a JSON document with the following content:
// {
// "hostname": "examples.com",
// "port": 2731
// }
//
// To run this program, you need an SD card connected to the SPI bus as follows:
// * MOSI <-> pin 11
// * MISO <-> pin 12
// * CLK <-> pin 13
// * CS <-> pin 4
//
// https://arduinojson.org/v7/example/config/
#include <ArduinoJson.h>
#include <SD.h>
#include <SPI.h>
// Our configuration structure.
struct Config {
char hostname[64];
int port;
};
const char* filename = "/config.txt"; // <- SD library uses 8.3 filenames
Config config; // <- global configuration object
// Loads the configuration from a file
void loadConfiguration(const char* filename, Config& config) {
// Open file for reading
File file = SD.open(filename);
// Allocate a temporary JsonDocument
JsonDocument doc;
// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, file);
if (error)
Serial.println(F("Failed to read file, using default configuration"));
// Copy values from the JsonDocument to the Config
config.port = doc["port"] | 2731;
strlcpy(config.hostname, // <- destination
doc["hostname"] | "example.com", // <- source
sizeof(config.hostname)); // <- destination's capacity
// Close the file (Curiously, File's destructor doesn't close the file)
file.close();
}
// Saves the configuration to a file
void saveConfiguration(const char* filename, const Config& config) {
// Delete existing file, otherwise the configuration is appended to the file
SD.remove(filename);
// Open file for writing
File file = SD.open(filename, FILE_WRITE);
if (!file) {
Serial.println(F("Failed to create file"));
return;
}
// Allocate a temporary JsonDocument
JsonDocument doc;
// Set the values in the document
doc["hostname"] = config.hostname;
doc["port"] = config.port;
// Serialize JSON to file
if (serializeJson(doc, file) == 0) {
Serial.println(F("Failed to write to file"));
}
// Close the file
file.close();
}
// Prints the content of a file to the Serial
void printFile(const char* filename) {
// Open file for reading
File file = SD.open(filename);
if (!file) {
Serial.println(F("Failed to read file"));
return;
}
// Extract each characters by one by one
while (file.available()) {
Serial.print((char)file.read());
}
Serial.println();
// Close the file
file.close();
}
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// Initialize SD library
const int chipSelect = 4;
while (!SD.begin(chipSelect)) {
Serial.println(F("Failed to initialize SD library"));
delay(1000);
}
// Should load default config if run for the first time
Serial.println(F("Loading configuration..."));
loadConfiguration(filename, config);
// Create configuration file
Serial.println(F("Saving configuration..."));
saveConfiguration(filename, config);
// Dump config file
Serial.println(F("Print config file..."));
printFile(filename);
}
void loop() {
// not used in this example
}
// Performance issue?
// ------------------
//
// File is an unbuffered stream, which is not optimal for ArduinoJson.
// See: https://arduinojson.org/v7/how-to/improve-speed/
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization or deserialization problem.
//
// The book "Mastering ArduinoJson" contains a case study of a project that has
// a complex configuration with nested members.
// Contrary to this example, the project in the book uses the SPIFFS filesystem.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤
@@ -1,64 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to use DeserializationOption::Filter
//
// https://arduinojson.org/v7/example/filter/
#include <ArduinoJson.h>
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// The huge input: an extract from OpenWeatherMap response
auto input_json = F(
"{\"cod\":\"200\",\"message\":0,\"list\":[{\"dt\":1581498000,\"main\":{"
"\"temp\":3.23,\"feels_like\":-3.63,\"temp_min\":3.23,\"temp_max\":4.62,"
"\"pressure\":1014,\"sea_level\":1014,\"grnd_level\":1010,\"humidity\":"
"58,\"temp_kf\":-1.39},\"weather\":[{\"id\":800,\"main\":\"Clear\","
"\"description\":\"clear "
"sky\",\"icon\":\"01d\"}],\"clouds\":{\"all\":0},\"wind\":{\"speed\":6."
"19,\"deg\":266},\"sys\":{\"pod\":\"d\"},\"dt_txt\":\"2020-02-12 "
"09:00:00\"},{\"dt\":1581508800,\"main\":{\"temp\":6.09,\"feels_like\":-"
"1.07,\"temp_min\":6.09,\"temp_max\":7.13,\"pressure\":1015,\"sea_"
"level\":1015,\"grnd_level\":1011,\"humidity\":48,\"temp_kf\":-1.04},"
"\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear "
"sky\",\"icon\":\"01d\"}],\"clouds\":{\"all\":9},\"wind\":{\"speed\":6."
"64,\"deg\":268},\"sys\":{\"pod\":\"d\"},\"dt_txt\":\"2020-02-12 "
"12:00:00\"}],\"city\":{\"id\":2643743,\"name\":\"London\",\"coord\":{"
"\"lat\":51.5085,\"lon\":-0.1257},\"country\":\"GB\",\"population\":"
"1000000,\"timezone\":0,\"sunrise\":1581492085,\"sunset\":1581527294}}");
// The filter: it contains "true" for each value we want to keep
JsonDocument filter;
filter["list"][0]["dt"] = true;
filter["list"][0]["main"]["temp"] = true;
// Deserialize the document
JsonDocument doc;
deserializeJson(doc, input_json, DeserializationOption::Filter(filter));
// Print the result
serializeJsonPretty(doc, Serial);
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// deserialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on deserialization.
// It begins with a simple example, like the one above, and then adds more
// features like deserializing directly from a file or an HTTP request.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤
@@ -1,65 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to generate a JSON document with ArduinoJson.
//
// https://arduinojson.org/v7/example/generator/
#include <ArduinoJson.h>
void setup() {
// Initialize Serial port
Serial.begin(9600);
while (!Serial)
continue;
// Allocate the JSON document
JsonDocument doc;
// Add values in the document
doc["sensor"] = "gps";
doc["time"] = 1351824120;
// Add an array
JsonArray data = doc["data"].to<JsonArray>();
data.add(48.756080);
data.add(2.302038);
// Generate the minified JSON and send it to the Serial port
serializeJson(doc, Serial);
// The above line prints:
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
// Start a new line
Serial.println();
// Generate the prettified JSON and send it to the Serial port
serializeJsonPretty(doc, Serial);
// The above line prints:
// {
// "sensor": "gps",
// "time": 1351824120,
// "data": [
// 48.756080,
// 2.302038
// ]
// }
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on serialization.
// It begins with a simple example, like the one above, and then adds more
// features like serializing directly to a file or an HTTP request.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤
@@ -1,125 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to parse a JSON document in an HTTP response.
// It uses the Ethernet library, but can be easily adapted for Wifi.
//
// It performs a GET resquest on https://arduinojson.org/example.json
// Here is the expected response:
// {
// "sensor": "gps",
// "time": 1351824120,
// "data": [
// 48.756080,
// 2.302038
// ]
// }
//
// https://arduinojson.org/v7/example/http-client/
#include <ArduinoJson.h>
#include <Ethernet.h>
#include <SPI.h>
void setup() {
// Initialize Serial port
Serial.begin(9600);
while (!Serial)
continue;
// Initialize Ethernet library
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
if (!Ethernet.begin(mac)) {
Serial.println(F("Failed to configure Ethernet"));
return;
}
delay(1000);
Serial.println(F("Connecting..."));
// Connect to HTTP server
EthernetClient client;
client.setTimeout(10000);
if (!client.connect("arduinojson.org", 80)) {
Serial.println(F("Connection failed"));
return;
}
Serial.println(F("Connected!"));
// Send HTTP request
client.println(F("GET /example.json HTTP/1.0"));
client.println(F("Host: arduinojson.org"));
client.println(F("Connection: close"));
if (client.println() == 0) {
Serial.println(F("Failed to send request"));
client.stop();
return;
}
// Check HTTP status
char status[32] = {0};
client.readBytesUntil('\r', status, sizeof(status));
// It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK"
if (strcmp(status + 9, "200 OK") != 0) {
Serial.print(F("Unexpected response: "));
Serial.println(status);
client.stop();
return;
}
// Skip HTTP headers
char endOfHeaders[] = "\r\n\r\n";
if (!client.find(endOfHeaders)) {
Serial.println(F("Invalid response"));
client.stop();
return;
}
// Allocate the JSON document
JsonDocument doc;
// Parse JSON object
DeserializationError error = deserializeJson(doc, client);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
client.stop();
return;
}
// Extract values
Serial.println(F("Response:"));
Serial.println(doc["sensor"].as<const char*>());
Serial.println(doc["time"].as<long>());
Serial.println(doc["data"][0].as<float>(), 6);
Serial.println(doc["data"][1].as<float>(), 6);
// Disconnect
client.stop();
}
void loop() {
// not used in this example
}
// Performance issue?
// ------------------
//
// EthernetClient is an unbuffered stream, which is not optimal for ArduinoJson.
// See: https://arduinojson.org/v7/how-to/improve-speed/
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on deserialization
// showing how to parse the response from GitHub's API. In the last chapter,
// it shows how to parse the huge documents from OpenWeatherMap
// and Reddit.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤
@@ -1,65 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to deserialize a JSON document with ArduinoJson.
//
// https://arduinojson.org/v7/example/parser/
#include <ArduinoJson.h>
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// Allocate the JSON document
JsonDocument doc;
// JSON input string.
const char* json =
"{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, json);
// Test if parsing succeeds
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
// Fetch the values
//
// Most of the time, you can rely on the implicit casts.
// In other case, you can do doc["time"].as<long>();
const char* sensor = doc["sensor"];
long time = doc["time"];
double latitude = doc["data"][0];
double longitude = doc["data"][1];
// Print the values
Serial.println(sensor);
Serial.println(time);
Serial.println(latitude, 6);
Serial.println(longitude, 6);
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// deserialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on deserialization.
// It begins with a simple example, like the one above, and then adds more
// features like deserializing directly from a file or an HTTP request.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤
@@ -1,118 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to implement an HTTP server that sends a JSON document
// in the response.
// It uses the Ethernet library but can be easily adapted for Wifi.
//
// The JSON document contains the values of the analog and digital pins.
// It looks like that:
// {
// "analog": [0, 76, 123, 158, 192, 205],
// "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0]
// }
//
// https://arduinojson.org/v7/example/http-server/
#include <ArduinoJson.h>
#include <Ethernet.h>
#include <SPI.h>
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
EthernetServer server(80);
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// Initialize Ethernet libary
if (!Ethernet.begin(mac)) {
Serial.println(F("Failed to initialize Ethernet library"));
return;
}
// Start to listen
server.begin();
Serial.println(F("Server is ready."));
Serial.print(F("Please connect to http://"));
Serial.println(Ethernet.localIP());
}
void loop() {
// Wait for an incomming connection
EthernetClient client = server.available();
// Do we have a client?
if (!client)
return;
Serial.println(F("New client"));
// Read the request (we ignore the content in this example)
while (client.available())
client.read();
// Allocate a temporary JsonDocument
JsonDocument doc;
// Create the "analog" array
JsonArray analogValues = doc["analog"].to<JsonArray>();
for (int pin = 0; pin < 6; pin++) {
// Read the analog input
int value = analogRead(pin);
// Add the value at the end of the array
analogValues.add(value);
}
// Create the "digital" array
JsonArray digitalValues = doc["digital"].to<JsonArray>();
for (int pin = 0; pin < 14; pin++) {
// Read the digital input
int value = digitalRead(pin);
// Add the value at the end of the array
digitalValues.add(value);
}
Serial.print(F("Sending: "));
serializeJson(doc, Serial);
Serial.println();
// Write response headers
client.println(F("HTTP/1.0 200 OK"));
client.println(F("Content-Type: application/json"));
client.println(F("Connection: close"));
client.print(F("Content-Length: "));
client.println(measureJsonPretty(doc));
client.println();
// Write JSON document
serializeJsonPretty(doc, client);
// Disconnect
client.stop();
}
// Performance issue?
// ------------------
//
// EthernetClient is an unbuffered stream, which is not optimal for ArduinoJson.
// See: https://arduinojson.org/v7/how-to/improve-speed/
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on serialization.
// It begins with a simple example, then adds more features like serializing
// directly to a file or an HTTP client.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤
@@ -1,106 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to send a JSON document to a UDP socket.
// At regular interval, it sends a UDP packet that contains the status of
// analog and digital pins.
// It looks like that:
// {
// "analog": [0, 76, 123, 158, 192, 205],
// "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0]
// }
//
// If you want to test this program, you need to be able to receive the UDP
// packets.
// For example, you can run netcat on your computer
// $ ncat -ulp 8888
// See https://nmap.org/ncat/
//
// https://arduinojson.org/v7/example/udp-beacon/
#include <ArduinoJson.h>
#include <Ethernet.h>
#include <SPI.h>
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress remoteIp(192, 168, 0, 108); // <- EDIT!!!!
unsigned short remotePort = 8888;
unsigned short localPort = 8888;
EthernetUDP udp;
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// Initialize Ethernet libary
if (!Ethernet.begin(mac)) {
Serial.println(F("Failed to initialize Ethernet library"));
return;
}
// Enable UDP
udp.begin(localPort);
}
void loop() {
// Allocate a temporary JsonDocument
JsonDocument doc;
// Create the "analog" array
JsonArray analogValues = doc["analog"].to<JsonArray>();
for (int pin = 0; pin < 6; pin++) {
// Read the analog input
int value = analogRead(pin);
// Add the value at the end of the array
analogValues.add(value);
}
// Create the "digital" array
JsonArray digitalValues = doc["digital"].to<JsonArray>();
for (int pin = 0; pin < 14; pin++) {
// Read the digital input
int value = digitalRead(pin);
// Add the value at the end of the array
digitalValues.add(value);
}
// Log
Serial.print(F("Sending to "));
Serial.print(remoteIp);
Serial.print(F(" on port "));
Serial.println(remotePort);
serializeJson(doc, Serial);
// Send UDP packet
udp.beginPacket(remoteIp, remotePort);
serializeJson(doc, udp);
udp.println();
udp.endPacket();
// Wait
delay(10000);
}
// Performance issue?
// ------------------
//
// EthernetUDP is an unbuffered stream, which is not optimal for ArduinoJson.
// See: https://arduinojson.org/v7/how-to/improve-speed/
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// serialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on serialization.
// It begins with a simple example, then adds more features like serializing
// directly to a file or any stream.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤
@@ -1,61 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to deserialize a MessagePack document with
// ArduinoJson.
//
// https://arduinojson.org/v7/example/msgpack-parser/
#include <ArduinoJson.h>
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial)
continue;
// Allocate the JSON document
JsonDocument doc;
// The MessagePack input string
uint8_t input[] = {131, 166, 115, 101, 110, 115, 111, 114, 163, 103, 112, 115,
164, 116, 105, 109, 101, 206, 80, 147, 50, 248, 164, 100,
97, 116, 97, 146, 203, 64, 72, 96, 199, 58, 188, 148,
112, 203, 64, 2, 106, 146, 230, 33, 49, 169};
// This MessagePack document contains:
// {
// "sensor": "gps",
// "time": 1351824120,
// "data": [48.75608, 2.302038]
// }
// Parse the input
DeserializationError error = deserializeMsgPack(doc, input);
// Test if parsing succeeded
if (error) {
Serial.print("deserializeMsgPack() failed: ");
Serial.println(error.f_str());
return;
}
// Fetch the values
//
// Most of the time, you can rely on the implicit casts.
// In other case, you can do doc["time"].as<long>();
const char* sensor = doc["sensor"];
long time = doc["time"];
double latitude = doc["data"][0];
double longitude = doc["data"][1];
// Print the values
Serial.println(sensor);
Serial.println(time);
Serial.println(latitude, 6);
Serial.println(longitude, 6);
}
void loop() {
// not used in this example
}
@@ -1,63 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows the different ways you can use Flash strings with
// ArduinoJson.
//
// Use Flash strings sparingly, because ArduinoJson duplicates them in the
// JsonDocument. Prefer plain old char*, as they are more efficient in term of
// code size, speed, and memory usage.
//
// https://arduinojson.org/v7/example/progmem/
#include <ArduinoJson.h>
void setup() {
JsonDocument doc;
// You can use a Flash String as your JSON input.
// WARNING: the strings in the input will be duplicated in the JsonDocument.
deserializeJson(doc, F("{\"sensor\":\"gps\",\"time\":1351824120,"
"\"data\":[48.756080,2.302038]}"));
// You can use a Flash String as a key to get a member from JsonDocument
// No duplication is done.
long time = doc[F("time")];
// You can use a Flash String as a key to set a member of a JsonDocument
// WARNING: the content of the Flash String will be duplicated in the
// JsonDocument.
doc[F("time")] = time;
// You can set a Flash String as the content of a JsonVariant
// WARNING: the content of the Flash String will be duplicated in the
// JsonDocument.
doc["sensor"] = F("gps");
// It works with serialized() too:
doc["sensor"] = serialized(F("\"gps\""));
doc["sensor"] = serialized(F("\xA3gps"), 3);
// You can compare the content of a JsonVariant to a Flash String
if (doc["sensor"] == F("gps")) {
// ...
}
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any memory
// problem.
//
// The book "Mastering ArduinoJson" contains a quick C++ course that explains
// how your microcontroller stores strings in memory. It also tells why you
// should not abuse Flash strings with ArduinoJson.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤
@@ -1,76 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows the different ways you can use String with ArduinoJson.
//
// Use String objects sparingly, because ArduinoJson duplicates them in the
// JsonDocument. Prefer plain old char[], as they are more efficient in term of
// code size, speed, and memory usage.
//
// https://arduinojson.org/v7/example/string/
#include <ArduinoJson.h>
void setup() {
JsonDocument doc;
// You can use a String as your JSON input.
// WARNING: the string in the input will be duplicated in the JsonDocument.
String input =
"{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
deserializeJson(doc, input);
// You can use a String as a key to get a member from JsonDocument
// No duplication is done.
long time = doc[String("time")];
// You can use a String as a key to set a member of a JsonDocument
// WARNING: the content of the String will be duplicated in the JsonDocument.
doc[String("time")] = time;
// You can get the content of a JsonVariant as a String
// No duplication is done, at least not in the JsonDocument.
String sensor = doc["sensor"];
// Unfortunately, the following doesn't work (issue #118):
// sensor = doc["sensor"]; // <- error "ambiguous overload for 'operator='"
// As a workaround, you need to replace by:
sensor = doc["sensor"].as<String>();
// You can set a String as the content of a JsonVariant
// WARNING: the content of the String will be duplicated in the JsonDocument.
doc["sensor"] = sensor;
// It works with serialized() too:
doc["sensor"] = serialized(sensor);
// You can also concatenate strings
// WARNING: the content of the String will be duplicated in the JsonDocument.
doc[String("sen") + "sor"] = String("gp") + "s";
// You can compare the content of a JsonObject with a String
if (doc["sensor"] == sensor) {
// ...
}
// Lastly, you can print the resulting JSON to a String
String output;
serializeJson(doc, output);
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any problem.
//
// The book "Mastering ArduinoJson" contains a quick C++ course that explains
// how your microcontroller stores strings in memory. On several occasions, it
// shows how you can avoid String in your program.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤
@@ -1,4 +0,0 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components("@PROJECT_NAME@")

Some files were not shown because too many files have changed in this diff Show More