โšก

Firmware Development Style Guide

Team Guidelines ยท Version 7.0 ยท Last Updated: October 2025

๐Ÿ“„ Source: Based on original style guide

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.

โš ๏ธ Exception: Never break user-visible strings such as printk messages, because that breaks the ability to grep for them.

Indentations

Indentations are 8 characters.

โœ… Correct Indentation
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:

โœ… Non-Function Blocks
if (x is true) {
        we do y
}

switch (action) {
case FOO:
        do_foo();
        break;
case BAR:
        do_bar();
        break;
default:
        break;
}
โœ… Function Blocks
int function(int x)
{
        body of function
        return result;
}
Note: Functions are special - their opening brace goes on the next line.

Spaces

โœ…
Use space after:if, switch, case, for, do, while
โŒ
No space after:sizeof, typeof, alignof, __attribute__
โŒ
No spaces inside parentheses:s = sizeof(struct file); not s = sizeof( struct file );
โœ…
One space around binary/ternary operators
โŒ
No space after unary operators:&, *, +, -, ~, !, ++, --
โŒ
No trailing whitespace

Naming Conventions

๐ŸŒ Global Variables

Use descriptive names (only when necessary)

โœ… Good
int count_active_users;
struct device_info *current_device;

๐Ÿ“ Local Variables

Should be short and to the point

โœ… Good
int i, tmp;
char *p;
size_t len;

Typedefs

Avoid typedefs for structures and pointers. Use plain struct my_structure *a; for clarity.

โœ… Good
struct sensor_data {
        float temperature;
        float humidity;
        uint32_t timestamp;
};

struct sensor_data *data;
โŒ Avoid
typedef struct {
        float temperature;
        float humidity;
} SensorData;  // Hides the fact it's a struct

SensorData *data;  // Less clear
โœ… Exception: Fixed-width typedefs like u8, u16,u32, uint8_t, uint16_t are acceptable and encouraged.

Functions

Function Guidelines

๐Ÿ“
Short and Focused

Functions should be one or two screenfuls maximum

๐ŸŽฏ
Single Responsibility

Each function should do one thing and do it well

๐Ÿ“Š
Few Variables

Preferably under 7โ€“10 local variables

๐Ÿ“
Separation

Separate functions with one blank line

โœ… Good Function Example
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:

0
Success

Operation completed successfully

1
Failed

Operation failed

๐Ÿ“‹ Error Codes: Other error codes should be defined centrally in a header file (e.g., errors.h or error_codes.h).
errors.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_H
โœ… Usage Example
int 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.

โŒ Bad - Scattered #ifdefs
void process_data(void)
{
        prepare_data();
        
#ifdef FEATURE_LOGGING
        log_data();
#endif
        
        send_data();
        
#ifdef FEATURE_LOGGING
        log_sent();
#endif
}
โœ… Good - Stub Functions
// 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();
}
๐Ÿ’ก Pro Tip: Use __maybe_unused attribute for definitions that may be unused in some configurations.

After any non-trivial #endif, add a trailing comment:

โœ… Commented #endif
#ifdef CONFIG_ADVANCED_FEATURES
        enable_advanced_mode();
        configure_advanced_settings();
#endif  // CONFIG_ADVANCED_FEATURES

Centralized Exiting of Functions

Using goto for centralized cleanup is acceptable and often cleanerthan duplicating cleanup logic across multiple return points.

โœ… Good - Centralized Cleanup with goto
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;
}
๐Ÿ“ Label Naming: Use descriptive label names like out_free_buffer:rather than generic err: or error:.
โœ… Multiple Cleanup Levels
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.

โœ… sensor.h
#ifndef SENSOR_H_
#define SENSOR_H_

#include <stdint.h>
#include <stdbool.h>

// Declarations here...

#endif  // SENSOR_H_
โœ… Full Path Guard (for larger projects)
#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.

โœ… Good - Direct Includes
// 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:

1
Related header#include "sensor.h"
2
C system headers#include <string.h>
3
C++ standard headers#include <vector>
4
Other libraries#include <freertos/FreeRTOS.h>
5
Project headers#include "config.h"
โœ… Complete Example
// 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"
๐Ÿ“ Sorting: Sort includes within each group alphabetically for consistency.

Inline Functions

Use inline only for small functions (rough guideline: โ‰ค 10 lines). Avoid inlining large functions or functions with loops/switches unless justified.

โœ… Good - Small Inline Function
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);
}
โŒ Bad - Large Inline
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.

โœ… Good - Narrow Scope
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);
                }
        }
}
โš ๏ธ Performance Note: In tight loops, hoist object creation out of the loop if constructors/initialization are expensive.

Commenting

Comments should explain WHAT the code does, not HOW. Prefer well-structured, self-documenting code over excessive comments.

File Header Comment

โœ… File Header Template
/****************************************************************************
 * 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

โœ… Kernel-Style Comment Block
/*
 * This is a multi-line comment block.
 * It uses the kernel style with asterisks
 * aligned on the left side.
 */

Single-Line Comments

โœ… Good 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;
โŒ Bad Comments
// Delay for 100 milliseconds
delay_ms(100);

// Multiply retry_delay by 2
retry_delay = retry_delay * 2;

Templates

Function Header Template

โœ… Standard Function Header
/******************************************************************************
 * 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

โœ… sensor_driver.c
/****************************************************************************
 * 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

๐Ÿ“˜
Linux Kernel Coding Style

The foundation of this style guide

๐Ÿ“—
Google C++ Style Guide

Modern C++ best practices

๐ŸŽฏ Quick Reference Checklist

โœ… Lines โ‰ค 79 characters
โœ… 8-character indentation
โœ… Opening brace on same line (except functions)
โœ… Descriptive global names
โœ… Short local names
โœ… Avoid typedefs for structs
โœ… Functions do one thing
โœ… Return 0 for success
โœ… Use goto for cleanup
โœ… Include header guards
โœ… Include what you use
โœ… Comment WHAT, not HOW