Firmware Development Style Guide
Team Guidelines ยท Version 7.0 ยท Last Updated: October 2025
Contents
Formatting
Line Size
Keep source lines to 79 characters or less for maximum readability. Statements longer than 80 columns should be broken into sensible chunks, unless exceeding 80 columns significantly increases readability. Descendants are always substantially shorter than the parent and placed substantially to the right.
Indentations
Indentations are 8 characters.
void process_data(void)
{
int count = 0;
for (int i = 0; i < MAX_COUNT; i++) {
if (data[i] > THRESHOLD) {
process_item(data[i]);
count++;
}
}
}Braces
Put the opening brace last on the line, and put the closing brace first:
if (x is true) {
we do y
}
switch (action) {
case FOO:
do_foo();
break;
case BAR:
do_bar();
break;
default:
break;
}int function(int x)
{
body of function
return result;
}Spaces
if, switch, case, for, do, whilesizeof, typeof, alignof, __attribute__s = sizeof(struct file); not s = sizeof( struct file );&, *, +, -, ~, !, ++, --Naming Conventions
๐ Global Variables
Use descriptive names (only when necessary)
int count_active_users;
struct device_info *current_device;๐ Local Variables
Should be short and to the point
int i, tmp;
char *p;
size_t len;Typedefs
Avoid typedefs for structures and pointers. Use plain struct my_structure *a; for clarity.
struct sensor_data {
float temperature;
float humidity;
uint32_t timestamp;
};
struct sensor_data *data;typedef struct {
float temperature;
float humidity;
} SensorData; // Hides the fact it's a struct
SensorData *data; // Less clearu8, u16,u32, uint8_t, uint16_t are acceptable and encouraged.Functions
Function Guidelines
Functions should be one or two screenfuls maximum
Each function should do one thing and do it well
Preferably under 7โ10 local variables
Separate functions with one blank line
void initialize_sensor(void)
{
gpio_set_mode(SENSOR_PIN, GPIO_MODE_INPUT);
i2c_init(I2C_FREQ_100KHZ);
delay_ms(100);
}
void read_sensor_data(struct sensor_data *data)
{
if (!data)
return;
data->temperature = i2c_read_register(TEMP_REG);
data->humidity = i2c_read_register(HUM_REG);
data->timestamp = get_system_time();
}Function Return Values
Standard return conventions for success/failure:
Operation completed successfully
Operation failed
errors.h or error_codes.h).#ifndef ERRORS_H
#define ERRORS_H
#define SUCCESS 0
#define ERROR_FAILED 1
#define ERROR_TIMEOUT 2
#define ERROR_NO_MEM 3
#define ERROR_INVALID 4
#define ERROR_NOT_READY 5
#endif // ERRORS_Hint connect_to_wifi(const char *ssid, const char *password)
{
if (!ssid || !password)
return ERROR_INVALID;
if (!wifi_ready())
return ERROR_NOT_READY;
if (!wifi_connect(ssid, password, TIMEOUT_MS))
return ERROR_TIMEOUT;
return SUCCESS;
}Conditional Compilation
Avoid scattering #ifdef/#if blocks throughout code. Instead, provide stub functions in headers for alternative cases.
void process_data(void)
{
prepare_data();
#ifdef FEATURE_LOGGING
log_data();
#endif
send_data();
#ifdef FEATURE_LOGGING
log_sent();
#endif
}// In header file
#ifdef FEATURE_LOGGING
void log_data(void);
void log_sent(void);
#else
static inline void log_data(void) { }
static inline void log_sent(void) { }
#endif
// In source file - clean code
void process_data(void)
{
prepare_data();
log_data();
send_data();
log_sent();
}__maybe_unused attribute for definitions that may be unused in some configurations.After any non-trivial #endif, add a trailing comment:
#ifdef CONFIG_ADVANCED_FEATURES
enable_advanced_mode();
configure_advanced_settings();
#endif // CONFIG_ADVANCED_FEATURESCentralized Exiting of Functions
Using goto for centralized cleanup is acceptable and often cleanerthan duplicating cleanup logic across multiple return points.
int fun(int a)
{
int result = 0;
char *buffer;
buffer = kmalloc(SIZE, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
if (condition1) {
result = 1;
goto out_free_buffer;
}
if (condition2) {
result = 2;
goto out_free_buffer;
}
// Success path
result = process_buffer(buffer);
out_free_buffer:
kfree(buffer);
return result;
}out_free_buffer:rather than generic err: or error:.int initialize_device(void)
{
int ret = SUCCESS;
ret = allocate_memory();
if (ret != SUCCESS)
goto out;
ret = init_hardware();
if (ret != SUCCESS)
goto out_free_memory;
ret = start_tasks();
if (ret != SUCCESS)
goto out_deinit_hardware;
return SUCCESS;
out_deinit_hardware:
deinit_hardware();
out_free_memory:
free_memory();
out:
return ret;
}Headers
Header Guards
Headers should be self-contained, end in .h, and include header guards. Prefer full-path guards derived from the project path.
#ifndef SENSOR_H_
#define SENSOR_H_
#include <stdint.h>
#include <stdbool.h>
// Declarations here...
#endif // SENSOR_H_#ifndef PROJECT_DRIVERS_SENSOR_H_
#define PROJECT_DRIVERS_SENSOR_H_
// For file: project/drivers/sensor.h
#endif // PROJECT_DRIVERS_SENSOR_H_Include What You Use
Files must directly include the headers that declare the symbols they use. Do not rely on transitive inclusion.
// sensor.c needs string.h functions
#include "sensor.h"
#include <string.h> // Directly included
#include <stdint.h>
void sensor_init(void)
{
memset(&sensor_data, 0, sizeof(sensor_data)); // Uses string.h
}Names and Order of Includes
Preferred order with blank lines separating groups:
#include "sensor.h"#include <string.h>#include <vector>#include <freertos/FreeRTOS.h>#include "config.h"// sensor.c
#include "sensor.h"
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "config.h"
#include "utils.h"
#include "logging.h"Inline Functions
Use inline only for small functions (rough guideline: โค 10 lines). Avoid inlining large functions or functions with loops/switches unless justified.
static inline uint32_t min(uint32_t a, uint32_t b)
{
return (a < b) ? a : b;
}
static inline bool is_valid_pin(uint8_t pin)
{
return (pin < MAX_GPIO_PINS);
}static inline void process_large_buffer(uint8_t *buf, size_t len)
{
// 50+ lines of complex logic...
// Should NOT be inline!
}Local Variables
Declare variables in the narrowest scope possible andinitialize them on declaration when feasible.
void process_array(void)
{
for (int i = 0; i < MAX_COUNT; i++) {
int value = array[i]; // Declared in loop scope
if (value > threshold) {
int processed = process_value(value);
store_result(processed);
}
}
}Commenting
Comments should explain WHAT the code does, not HOW. Prefer well-structured, self-documenting code over excessive comments.
File Header Comment
/****************************************************************************
* File Name: mbr3_api.c
*
* Version 1.00
*
* Author: ip_v1
*
* Description:
* This file contains the code to access CY8CMBR3xxx Touch chips
*
****************************************************************************/Multi-Line Comments
/*
* This is a multi-line comment block.
* It uses the kernel style with asterisks
* aligned on the left side.
*/Single-Line Comments
// Delay required for sensor stabilization after power-on
delay_ms(100);
// Retry with exponential backoff to avoid server overload
retry_delay = retry_delay * 2;// Delay for 100 milliseconds
delay_ms(100);
// Multiply retry_delay by 2
retry_delay = retry_delay * 2;Templates
Function Header Template
/******************************************************************************
* Function Name: bgc_sys_init
******************************************************************************
*
* Summary:
* This function is used to initialize the system
*
* Parameters:
* None
*
* Return:
* int - SUCCESS (0) on success, error code on failure
*
*******************************************************************************/
int bgc_sys_init(void)
{
// Function implementation
}Complete File Example
/****************************************************************************
* File Name: sensor_driver.c
*
* Version 1.0.0
*
* Author: Inderpreet Singh (ip_v1)
*
* Description:
* DHT22 temperature and humidity sensor driver for ESP32
*
****************************************************************************/
#include "sensor_driver.h"
#include <string.h>
#include <stdint.h>
#include <freertos/FreeRTOS.h>
#include <driver/gpio.h>
#include "config.h"
#include "logging.h"
/*
* Private constants
*/
#define SENSOR_TIMEOUT_MS 1000
#define CALIBRATION_OFFSET 0.5f
/*
* Private variables
*/
static bool is_initialized = false;
static struct sensor_data last_reading;
/*
* Private function prototypes
*/
static int verify_sensor(void);
static void apply_calibration(struct sensor_data *data);
/******************************************************************************
* Function Name: sensor_init
******************************************************************************
* Summary:
* Initialize the DHT22 sensor driver
*
* Parameters:
* None
*
* Return:
* int - SUCCESS (0) or error code
******************************************************************************/
int sensor_init(void)
{
int ret;
gpio_set_mode(SENSOR_PIN, GPIO_MODE_INPUT);
ret = verify_sensor();
if (ret != SUCCESS)
goto out;
is_initialized = true;
log_info("Sensor initialized");
out:
return ret;
}
/******************************************************************************
* Function Name: sensor_read
******************************************************************************
* Summary:
* Read temperature and humidity from sensor
*
* Parameters:
* data - Pointer to sensor_data structure to fill
*
* Return:
* int - SUCCESS (0) or error code
******************************************************************************/
int sensor_read(struct sensor_data *data)
{
if (!is_initialized)
return ERROR_NOT_READY;
if (!data)
return ERROR_INVALID;
// Read sensor (implementation details...)
apply_calibration(data);
return SUCCESS;
}
/*
* Private function: Verify sensor is connected and responding
*/
static int verify_sensor(void)
{
// Implementation...
return SUCCESS;
}
/*
* Private function: Apply calibration offset to sensor data
*/
static void apply_calibration(struct sensor_data *data)
{
data->temperature += CALIBRATION_OFFSET;
}References & Further Reading
The foundation of this style guide
Modern C++ best practices
K&R - The classic reference
Kernighan & Pike - Timeless wisdom