Project Contributions
We are excited to see the innovative ways in which you are utilizing SensEdu. By submitting your project, you have the opportunity to have it listed as a showcase for SensEdu functionality and included in the main repository.
Project Submission
- Fork SensEdu repository
- Add a new project folder in your fork
~\projects\my_project\ - Create an Arduino sketch
~\projects\my_project\my_project.ino. Ensure that the sketch filename matches the folder name- If you have additional files (e.g., MATLAB scripts), place them in the subfolder within your project folder, such as
~\projects\my_project\matlab\my_script.m
- If you have additional files (e.g., MATLAB scripts), place them in the subfolder within your project folder, such as
- Create a documentation for your project
- Create a new markdown page
~\docs\projects\my_project.md - Add the following fields to the markdown header:
--- title: My Project layout: default parent: Projects nav_order: 10 --- - The order of projects is defined by
nav_order. Use the next available number for proper website navigation. Customizetitlewith your project name - Follow Documentation Contributions for detailed page creation guidelines
- Share implementation details and nice pictures of your project!
- Create a new markdown page
- Commit all changes to your fork and submit a Pull Request (PR) to the main SensEdu repository
A finished project folder looks like this:
projects/My_Project/
├── .theia/settings.json 4-space indentation for the Arduino editors
├── My_Project.ino entry point, same name as the folder
├── helpers.ino optional, additional sketch files
├── table.h optional, tables or declarations
└── matlab/ matlab host scripts (can be python/)
├── .gitignore ignores generated data (Recordings/, Measurements/, ...)
└── My_Project.m
Never commit generated measurement data, recordings, figures or MATLAB autosave files. Add a small .gitignore next to the host script.
Writing the Project Page
Project pages follow a common layout so that a reader can compare projects without re-learning the structure. Use an existing page such as Audio Recording as the template:
- Introduction: what the project does and which problem it solves
- Background: the theory
- Code Layout: a table of every file with a one-line purpose
- Configuration: a table of every tunable constant with its default and meaning, both firmware and host side
- Implementation: some tricky parts of the firmware and the host script, with short code snippets rather than full source code
- Showcase: plots, photos, audio, or measurements from a real run
- Developer Notes: the non-obvious decisions and the reasoning behind them
The documentation is aimed at beginner students as well as developers. Explain the reasoning behind your design choices and avoid logical skips.
Style Guidelines
Arduino projects are written in C++. A source file is called a “sketch”, it ends with .ino and must be in a folder with the same name. The project folder can contain additional .ino files to split functionality and make the code easier to manage. You can also include traditional .h header files for definitions and declarations.
The rules on this page apply to every sketch in the repository, including the library examples in libraries/SensEdu/examples/.
Indentation
Unlike standard Arduino code, SensEdu uses 4-space indentation. Please include the file .theia/settings.json with the following contents:
{
"editor.tabSize": 4
}
Sketch Skeleton
Every sketch opens with a block comment naming the sketch and describing what it does, what it prints or streams, and any additional notes the reader must know up front:
/*
* ADC_3CH_DMA_Circular
*
* Streams three ADC channels at 44.1 kS/s per channel over USB serial without
* gaps, using circular DMA with a double-buffered half/full transfer.
*
* Data is sent as raw interleaved 16-bit binary - use the MATLAB script in
* matlab/ to receive and plot it. The host pauses/resumes the stream with 'P' / 'S'.
*
* The D86 LED blinks if the board runs into an error.
*/
After the header, the sketch is split into sections with a banner like this:
/* -------------------------------------------------------------------------- */
/* Settings */
/* -------------------------------------------------------------------------- */
Only Settings, Setup, Loop and Functions sections are mandatory, the rest are used when the sketch grows:
| Section | Contents |
|---|---|
Settings
| Tunable constants and the peripheral configuration structs |
Structs
| typedef enum / typedef struct state definitions
|
Globals
| Derived constants and runtime state |
Declarations
| Forward declarations of all private functions |
Setup
| setup() only
|
Loop
| loop() only
|
Functions
| Private function definitions, in declaration order |
Settings Block
Put everything a user might want to change into the settings section. Name those values as constants and comment the non-obvious ones:
/* -------------------------------------------------------------------------- */
/* Settings */
/* -------------------------------------------------------------------------- */
static const uint8_t ERROR_LED_PIN = D86;
static ADC_TypeDef* adc = ADC1;
static const uint16_t SAMPLING_RATE_PER_CH = 44100;
static const uint16_t CHANNEL_NUM_PER_ADC = 3;
static uint8_t adc_pins[CHANNEL_NUM_PER_ADC] = {A0, A1, A2};
Loop
Keep loop() short and non-blocking. In a finished project it usually reads as a list of steps:
void loop() {
process_command();
process_capture();
process_usb_transfer();
}
Naming Convention
- Macros:
SCREAMING_SNAKE_CASE - Constants:
SCREAMING_SNAKE_CASE - Variables:
snake_case - Functions:
snake_case - Structs and enum types:
PascalCase, withSCREAMING_SNAKE_CASEmembers
#define AIR_SPEED 343
static const uint16_t ADC_MIC_NUM = 4;
static uint16_t buf;
static void process_data(uint16_t* buf);
typedef enum {
STATE_IDLE = 0,
STATE_RECORDING = 1
} FwState;
Error Handling
Every sketch must check the library error container. Call check_lib_errors() after the setup sequence and once per loop iteration. If the sketch prints to the Serial Monitor, report the code there:
// Checks if the library has raised any internal errors
// Prints the error code to the Serial Monitor
void check_lib_errors() {
lib_error = SensEdu_GetError();
while (lib_error != 0) {
delay(1000);
Serial.print("Error: 0x");
Serial.println(lib_error, HEX);
}
}
If the serial link is busy streaming data, blink an error LED instead, so the stream is not corrupted by log messages:
// Checks if the library has raised any internal errors
// Serial is busy streaming, so the error LED is used instead
static void check_lib_errors(uint8_t error_led) {
uint32_t lib_error = SensEdu_GetError();
while (lib_error != 0) {
fatal_error(error_led);
}
}
// Halts the system and blinks the error LED
static void fatal_error(uint8_t error_led) {
digitalWrite(error_led, !digitalRead(error_led));
delay(200);
}
Use static_assert to verify user settings or internal variables during compilation.
static_assert(sizeof(SegmentHeader) == 20, "Unexpected SegmentHeader layout.");
Host Scripts
Most projects ship a host script that receives the data over USB or Wi-Fi and processes it. MATLAB is the default, Python can also be used. Host scripts live in a matlab/ or python/ subfolder of the project and are named after the project.
Keep the wire-protocol constants in one block and state that they must match the firmware.
MATLAB
- Open with a
%%header comment naming the file and what it does, thenclear;andclose all; - Split the script into
%%cells - Local helper functions go at the end of the file. If several scripts need the same helper, move it into a subfolder and pull it in with
addpath(genpath('./processing/'))
%% Audio_Recording.m
% Host script: drive the start/stop handshake, read framed segments over
% USB CDC from Audio_Recording.ino, save WAV, plot waveform + FFT.
clear;
close all;
%% User settings
ARDUINO_PORT = 'COM16';
ARDUINO_BAUDRATE = 2000000;
%% Firmware constants
Fs = 44100;
SEGMENT_SECONDS = 30;
%% Arduino Setup
arduino = serialport(ARDUINO_PORT, ARDUINO_BAUDRATE);
The serial port stays locked if a script errors out without closing it, or if the execution has been stopped manually. This prevents any new firmware uploads. Clear the arduino variable in MATLAB to free up the port.
Python
- Open with a module docstring naming the file, what it does, and what must match the firmware
- Follow a structure similar to the MATLAB scripts
- Use PEP 8 as a code styling reference
Braces and Spaces
- Place the opening brace on the same line as the function name
- Put one space before the opening brace
- Put one space after the keywords
if,switch,case,for,do,while - Do not put a space after the function in both calls and declarations
- Preferred use of pointer’s
*is adjacent to the data type
function(char* ptr) {
if (this_is_true) {
do_something(ptr);
} else {
do_something_else(ptr);
}
}
- Put spaces around most operators
- It is okay to omit spaces around factors for readability
// Good examples
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);
// Bad examples
v = x+y; // use spaces around operators
v = w*x + y / z; // don't mix styles
v = w * ( x + z ); // no internal padding for parentheses
Supplementary Resources
- If a topic isn’t covered here, refer to the Google C++ Style Guide. Local rules in this document take precedence (e.g., 4-space indentation).