Skip to content
vchan023Public

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

# IoT ESP32 FreeRTOS Project

This project is an ESP32-based IoT application using PlatformIO and FreeRTOS. It is designed for modular task management, including uptime tracking, button input, and light control. The codebase is structured for clarity, maintainability, and easy extension.

PlatformIO streamlines the setup of the ESP32 development environment, making it efficient and accessible for rapid prototyping and deployment.

## Features

- Modular FreeRTOS tasks for uptime and network management
- Configurable PWM lights with brightness control and synchronized updates
- Debounced button callbacks through Espressif's button component
- Clean separation of application logic and hardware abstraction
- Easy integration of new features via the `lib/` directory
- Example plans and deprecated code for reference

## Directory Structure

| Directory/File  | Description                                              |
| --------------- | -------------------------------------------------------- |
| .github/        | GitHub configuration files                               |
| .gitignore      | Git ignore rules                                         |
| .pio/           | PlatformIO build files                                   |
| .vscode/        | VS Code configuration                                    |
| graveyard/      | Deprecated code for reference                            |
| include/        | Project header files                                     |
| lib/            | Project-specific libraries (uptime, button, light, etc.) |
| plan.excalidraw | Project plan (Excalidraw format)                         |
| platformio.ini  | PlatformIO configuration                                 |
| README          | Project documentation (this file)                        |
| sdkconfig       | ESP32 SDK configuration                                  |
| src/            | Main application source code                             |
| test/           | PlatformIO Test Runner and tests                         |

## Build & Upload Instructions

1. Install [PlatformIO](https://platformio.org/) and [VS Code](https://code.visualstudio.com/).
2. Open the project folder in VS Code.
3. Use PlatformIO commands to build and upload firmware to your ESP32 device:
   - Build: `PlatformIO: Build`
   - Upload: `PlatformIO: Upload`

## Wokwi Simulator

1. Install the [Wokwi for VS Code](https://marketplace.visualstudio.com/items?itemName=wokwi.wokwi-vscode) extension.
2. If a license is requested, open the Command Palette (`Cmd+Shift+P` on macOS),
  run `Wokwi: Request a new License`, and complete activation in your browser.
3. Run `PlatformIO: Build` before starting the simulator. Alternatively, build with
  `~/.platformio/penv/bin/pio run -e esp32dev` on macOS.
4. Open the Command Palette and run `Wokwi: Start Simulator`.
5. Press and release either simulated button to toggle its matching LED.
  Both LEDs start off. Application and uptime messages appear in the serial terminal.

`wokwi.toml` loads `.pio/build/esp32dev/firmware.bin` and its matching ELF file.
The environment is named `esp32dev`, but targets the ESP32-S3-DevKitC-1.
`diagram.json` uses that board with active-low buttons on GPIO 7 and GPIO 16,
and active-high LEDs on GPIO 4 and GPIO 5 through 220-ohm resistors.
The firmware enables internal button pull-ups, so external pull-ups are not needed.
Rebuild and restart the simulator after changing the firmware.

Wi-Fi is initialized, but the current startup does not start station mode or
provisioning. A Wi-Fi connection is not expected when testing the buttons and LEDs.

## Buttons and Lights

Button and light handling has three layers:

- `lib/button`: Reusable `button_create()` / `button_delete()` API with opaque handles,
  configuration, press/release events, and callbacks. Its implementation wraps
  [espressif/button](https://components.espressif.com/components/espressif/button);
  the public header exposes no backend types. It has no light or board dependencies.
- `lib/light`: Reusable create/delete, set/get, atomic toggle, and brightness API with
  opaque handles. Its LEDC backend generates independent PWM outputs without a
  polling task. Each light has its own mutex; wiring, light count, and application
  actions stay outside the library.
- `lib/light_patterns`: Repeating fade, flash, and SOS effects scheduled by a worker
  task per light. It owns effect timing and cancellation through the light API.
- `src/board.h`: GPIO assignments and active levels for this board's buttons and lights.
- `src/controls.c`: Creates the lights and buttons and maps their events to application actions.

GPIO 7 and GPIO 16 are active-low inputs with internal pull-ups, controlling
active-high lights on GPIO 4 and GPIO 5 respectively. Every debounced release toggles the
corresponding light, including releases after long presses.

`main.c` calls `controls_init()`, which creates the lights in the off state before
creating buttons. Repeated initialization is ignored; failures clean up buttons
before lights and return an error.
The IDF Component Manager downloads the backend during the build. The backend
supplies polling, debouncing, and default gesture timing through its timer task.
Callbacks should be short and nonblocking; create/delete buttons from a serialized
application lifecycle, never inside a callback.

`light_create()` takes a GPIO, active level, initial state, and brightness percentage.
An omitted/zero initial brightness defaults to 100%. Use `light_set()`
to apply a state, `light_get()` to read its logical state, and `light_toggle()` to
invert it atomically. `light_delete()` turns the light off, resets the GPIO, and
clears the handle. Each GPIO must have a single owner, and deletion must be
serialized with all users of that light.

Brightness uses a linear PWM duty from 0 to 100%. `light_set_brightness(light, 40)`
turns the light on at 40%; `light_get_brightness()` reports its current percentage
(zero when off). Setting brightness to zero or switching off preserves the last
nonzero brightness, which is restored when switched on again. The buttons keep
their existing toggle behavior, and initially turn the lights on at 100%.

The backend reserves low-speed LEDC timer 0 at 5 kHz with 10-bit resolution. It
allocates one free channel per light, rejects duplicate GPIOs, and releases channels
on deletion. The shared timer remains configured for reuse. Other PWM consumers
must avoid this timer and the light library's allocated channels. PWM updates run
in task context under a mutex; do not call the light API from an ISR.

A future potentiometer belongs in a separate analog-input component. Application
code can map its ADC readings to 0–100 and call `light_set_brightness()` on the
target light. The light library does not need to know about the knob. The knob's
GPIO, ADC handling, and filtering can be added when the hardware is chosen.

## Light Patterns

After `controls_init()`, application code can start an effect without waiting for
the effect to finish:

```c
const light_pattern_options_t sos = {
    .kind = LIGHT_PATTERN_SOS,
    .brightness_percent = 60,
    .interval_ms = 250,
};
ESP_ERROR_CHECK(controls_start_pattern(CONTROLS_LIGHT_ONE, &sos));
// Later, cancel the effect and leave its current output unchanged.
ESP_ERROR_CHECK(controls_stop_pattern(CONTROLS_LIGHT_ONE));
```

`LIGHT_PATTERN_FADE` repeatedly ramps from zero to peak brightness and back;
`interval_ms` sets the duration of each ramp. `LIGHT_PATTERN_FLASH` uses that
interval for each on/off phase. `LIGHT_PATTERN_SOS` uses it as the dot duration,
with dashes three times as long, standard letter gaps, and a seven-dot message gap.
Zero settings select 100% brightness and 1000 ms fade ramps or 250 ms flash/SOS
intervals. Explicit intervals must be at least 20 ms.

Each controller updates approximately every 20 ms while running and sleeps while
idle. Effects repeat until stopped or replaced. A failed PWM update stops the
effect and logs the error. Pressing a button cancels its light's effect before
toggling the current on/off state. Effects are idle at startup.

For another project, create a controller with `light_pattern_create(light, &pattern)`
and use `light_pattern_start()` / `light_pattern_stop()` directly. Create only one
controller per light. Stop it before manual brightness changes, and delete it with
`light_pattern_delete()` before deleting its light. Worker creation/deletion belongs
in application lifecycle code; start/stop may be called from task callbacks.

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

---

For questions or contributions, please open an issue or pull request.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages