A production-ready, modular template for ESP32 projects featuring a comprehensive Hardware Abstraction Layer (HAL), extensive test coverage, and a clean architecture that makes it easy to build any IoT device.
This template provides a solid foundation for ESP32 projects with:
- Modular Architecture - Clean separation of concerns with hardware abstraction
- Production Ready - Comprehensive error handling, logging, and testing
- Easy to Extend - Simple patterns for adding new sensors, actuators, and features
- Well Tested - 51+ unit tests covering critical functionality
- Professional CI/CD - GitHub Actions with optimized caching
- OTA Updates - Built-in over-the-air update capability
- Web Flasher - Browser-based firmware upload tool
# Clone the template
git clone https://github.com/ericdahl.dev/ESP32-Lora-Template.git my-iot-device
cd my-iot-device
# Remove the original git history
rm -rf .git
git init
# Install PlatformIO
pip install platformioEdit platformio.ini to match your board:
[env:my_device]
platform = espressif32
board = heltec_wifi_lora_32_V3 ; Change to your board
framework = arduino
monitor_speed = 115200Replace the example application logic in src/app_logic.cpp:
#include "app_logic.h"
#include "hardware/hardware_abstraction.h"
void AppLogic::setup() {
// Initialize your sensors/actuators
HAL::GPIO::pinMode(LED_PIN, OUTPUT);
// Setup your custom hardware
myServo.attach(SERVO_PIN);
mySensor.begin();
}
void AppLogic::loop() {
// Your main application logic
float reading = mySensor.read();
if (reading > THRESHOLD) {
HAL::GPIO::digitalWrite(LED_PIN, HIGH);
myServo.write(90);
}
}ESP32-Lora-Template/
βββ src/ # Source code
β βββ hardware/ # Hardware Abstraction Layer
β β βββ hardware_abstraction.h
β β βββ hardware_abstraction.cpp
β βββ sensors/ # Sensor implementations
β β βββ gps_sensor.h/.cpp
β β βββ lightning_sensor.h
β β βββ sensor_interface.h
β βββ actuators/ # Actuator implementations
β β βββ actuator_interface.h
β βββ communication/ # Communication protocols
β β βββ communication_interface.h
β βββ system/ # System utilities
β β βββ error_handler.h
β β βββ logger.h
β β βββ state_machine.h
β βββ config/ # Configuration files
β β βββ system_config.h
β βββ app_logic.cpp/.h # Main application logic
β βββ wifi_manager.cpp/.h # WiFi management
β βββ main.cpp # Entry point
βββ test/ # Unit tests
βββ examples/ # Example implementations
β βββ environmental_monitor/
βββ docs/ # Documentation
β βββ HAL_GUIDE.md
βββ web-flasher/ # Browser-based flasher
βββ platformio.ini # Build configuration
The HAL provides a clean interface to hardware functionality:
#include "hardware/hardware_abstraction.h"
using namespace HardwareAbstraction;
// Initialize HAL
initialize();
// Configure GPIO
GPIO::pinMode(2, GPIO::Mode::MODE_OUTPUT);
GPIO::digitalWrite(2, GPIO::Level::LEVEL_HIGH);// Initialize I2C for OLED
I2C::initialize(21, 22, 400000); // SDA, SCL, frequency
// Write to device
uint8_t data[] = {0x00, 0xFF};
I2C::beginTransmission(0x3C);
I2C::write(data, sizeof(data));
I2C::endTransmission();// Deep sleep for battery conservation
Power::sleep(Power::Mode::DEEP_SLEEP, 300000); // 5 minutes
// Check battery status
float voltage = Power::getBatteryVoltage();// Get free heap memory
uint32_t free_heap = Memory::getFreeHeap();
uint32_t largest_block = Memory::getLargestFreeBlock();- Create sensor file in
src/sensors/:
// src/sensors/temperature_sensor.h
#pragma once
#include "hardware/hardware_abstraction.h"
#include "sensor_interface.h"
class TemperatureSensor : public SensorInterface {
private:
uint8_t _pin;
public:
TemperatureSensor(uint8_t pin) : _pin(pin) {}
void initialize() override;
SensorData read() override;
bool isReady() const override;
};- Implement the sensor:
// src/sensors/temperature_sensor.cpp
#include "temperature_sensor.h"
using namespace HardwareAbstraction;
void TemperatureSensor::initialize() {
GPIO::pinMode(_pin, GPIO::Mode::MODE_INPUT);
}
SensorData TemperatureSensor::read() {
// Read analog value and convert to temperature
int rawValue = analogRead(_pin);
float celsius = (rawValue * 3.3 / 4095.0) * 100;
SensorData data;
data.value = celsius;
data.unit = "Β°C";
data.timestamp = millis();
return data;
}- Use in your application:
// src/app_logic.cpp
#include "sensors/temperature_sensor.h"
TemperatureSensor tempSensor(34); // GPIO34
void AppLogic::setup() {
HardwareAbstraction::initialize();
tempSensor.initialize();
}
void AppLogic::loop() {
if (tempSensor.isReady()) {
SensorData data = tempSensor.read();
Serial.printf("Temperature: %.2f%s\n", data.value, data.unit.c_str());
}
}Similar pattern for actuators in src/actuators/.
The template includes multi-network WiFi support:
// wifi_networks_example.h
const char* WIFI_SSIDS[] = {"Network1", "Network2", "Network3"};
const char* WIFI_PASSWORDS[] = {"password1", "password2", "password3"};
const int WIFI_NETWORK_COUNT = 3;Enable over-the-air updates:
// In your setup()
WiFiManager::enableOTA("device-name", "ota-password");Run the comprehensive test suite:
./run_tests.shAdd your own tests in test/:
// test/test_my_sensor.cpp
#include <unity.h>
#include "sensors/my_sensor.h"
void test_sensor_initialization() {
MySensor sensor(GPIO_NUM_34);
sensor.begin();
TEST_ASSERT_TRUE(sensor.isReady());
}
void setup() {
UNITY_BEGIN();
RUN_TEST(test_sensor_initialization);
UNITY_END();
}The examples/ directory contains ready-to-use implementations:
examples/environmental_monitor/
βββ README.md
βββ platformio.ini
βββ src/
βββ main.cpp # Temperature, humidity, air quality monitoring
For more examples and detailed implementations, see the Examples README.
Flash firmware directly from a web browser:
- Build the flasher:
cd web-flasher
npm install
npm run build- Serve locally:
npm start- Or deploy to GitHub Pages for easy distribution.
- Heltec WiFi LoRa 32 V3
- ESP32-S3 DevKit
- ESP32-WROOM-32
- Add your board here!
- OLED Displays (SSD1306, SH1106)
- LoRa Modules (SX1262, SX1276)
- GPS Modules (NEO-6M, NEO-8M)
- Various I2C/SPI sensors
- Hardware Abstraction Layer Guide
- Template Configuration Guide
- WiFi Multi-Network Setup
- OTA Update Guide
- Troubleshooting Guide
- Static Analysis Results
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
This template is open source and available under the MIT License.
This template was originally based on a lightning detection system but has been generalized for broader ESP32 development use cases.