💡 Your First Project: Plug-and-Play Blink
Welcome to Nextino! Let's build the "Hello, World!" of the embedded world—a blinking LED—but with the power and structure of the Nextino framework. This tutorial will demonstrate the core Plug-and-Play, Configuration-Driven, and Multiple Instance philosophies of Nextino.
By the end, you will have a fully functional, modular application with an incredibly minimal main.cpp, and you'll understand how Nextino automatically discovers and manages your hardware.
🎯 The Goal
We will create a simple project that blinks a single LED. However, instead of writing the logic directly in main.cpp, we will create a completely self-contained, reusable LedModule.
Step 1: Project Setup
If you haven't already, create a new PlatformIO project and follow the Installation guide to add the Nextino framework.
Your platformio.ini should look something like this:
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
https://github.com/magradze/Nextino.git
Step 2: Create the LedModule 💡
Modules in Nextino are just standard PlatformIO libraries. They live in your project's lib folder.
- Inside your project's
libfolder, create a new folder namedLedFlasher. - This new library needs three key files to become a Nextino module.
2.1. The Configuration (config.json)
This file defines the default instance(s) for our module. As per Nextino's best practice, the root element is always an array [].
[
{
"type": "LedModule",
"instance_name": "onboard_led",
"config": {
"resource": {
"type": "gpio",
"pin": 2
},
"blink_interval_ms": 500
}
}
]
"type": The C++ class name of our module."instance_name": A unique name for this specific LED."resource": The hardware this LED needs. TheResourceManagerwill protect this pin for us!
2.2. The Manifest (library.json)
This is the module's "passport," telling PlatformIO about it. The special "nextino-module" keyword makes it discoverable by our build script.
{
"name": "LedFlasher",
"version": "1.0.0",
"description": "A simple LED flasher module for the Nextino framework.",
"keywords": "nextino-module, led, flasher",
"dependencies": { "Nextino": "*" }
}
2.3. The Code (LedModule.h & LedModule.cpp)
Create a src folder inside lib/LedFlasher and add the following files.
#pragma once
#include <Nextino.h>
class LedModule : public BaseModule {
private:
int _pin;
unsigned long _interval;
bool _ledState;
public:
// Constructor must accept an instanceName for multiple-instance support
LedModule(const char* instanceName, const JsonObject& config);
// Static create function must match the new ModuleFactory signature
static BaseModule* create(const char* instanceName, const JsonObject& config) {
return new LedModule(instanceName, config);
}
const char* getName() const override;
void init() override;
void start() override;
};
#include "LedModule.h"
// The constructor receives the unique instanceName and passes it to the parent BaseModule
LedModule::LedModule(const char* instanceName, const JsonObject& config)
: BaseModule(instanceName) {
// Read pin from the "resource" object for ResourceManager compatibility
_pin = config["resource"]["pin"];
_interval = config["blink_interval_ms"] | 1000;
_ledState = false; // Start with the LED off
}
// getName() returns the generic TYPE of the module
const char* LedModule::getName() const {
return "LedModule";
}
void LedModule::init() {
pinMode(_pin, OUTPUT);
digitalWrite(_pin, _ledState);
// Use getInstanceName() for logs to show WHICH instance is talking!
NEXTINO_LOGI(getInstanceName(), "Initialized on pin %d.", _pin);
}
void LedModule::start() {
NextinoScheduler().scheduleRecurring(_interval, [this]() {
_ledState = !_ledState;
digitalWrite(_pin, _ledState);
});
NEXTINO_LOGI(getInstanceName(), "Blink task scheduled every %lu ms.", _interval);
}
Step 3: The Minimal main.cpp ✨
Now for the best part. Your main application file is incredibly clean and simple.
#include <Arduino.h>
#include <Nextino.h>
// This header is auto-generated by Nextino's build script!
// It contains the aggregated configuration and the `registerAllModuleTypes()` function.
#include "generated_config.h"
void setup() {
// 1. Initialize the Logger (always first!)
Logger::getInstance().begin(LogLevel::Debug);
// A small delay to ensure you can open the Serial Monitor in time to see boot messages.
delay(2000);
NEXTINO_LOGI("Main", "--- Nextino Blink Demo ---");
// 2. Register all discovered module types with the factory.
registerAllModuleTypes();
// 3. Start the system by passing the auto-generated config.
// The SystemManager handles everything else: resource locking, instantiation, and lifecycles.
NextinoSystem().begin(projectConfigJson);
NEXTINO_LOGI("Main", "System is running.");
}
void loop() {
// Just let the Nextino system do its thing.
NextinoSystem().loop();
}
Notice that main.cpp has no direct reference to LedModule. It doesn't know what hardware is in the project—and it doesn't need to!
Step 4: Build and Upload 🚀
-
Add the
LedFlashermodule to your project's dependencies inplatformio.ini.platformio.ini; ... other settings
lib_deps =
https://github.com/magradze/Nextino.git
lib/LedFlasher ; Tells PIO to find the module in the local lib folder -
Click the "Upload and Monitor" button in PlatformIO.
The build script will run, find your LedModule, generate the configuration, and your main.cpp will bring it to life. You should see the logs in the Serial Monitor and your LED will start blinking!
Sometimes, your device boots and prints logs in milliseconds, before you can open the Serial Monitor. If you miss the boot messages:
- Keep the Serial Monitor window open.
- Press the physical RESET button on your board.
Your device will restart, and you'll see all messages from the beginning, including any errors from the ResourceManager. The delay(2000) in our main.cpp helps prevent this.
Next Steps
Congratulations! You've now experienced the power of Nextino's core philosophy. Now let's dive deeper into what you can do.