This document defines the coding style and best practices used at EmbSysTech for all embedded C projects and libraries.
Consistency and readability are our top priorities—following these guidelines ensures clean, maintainable, and professional code.
- Introduction
- C Standard Libraries
- Code Formatting Rules
- Variables
- Constants
- Structures
- Enums
- Conditional Flow
- Macros
- Functions
- Header & Source Files
- Comments & Documentation
We encourage everyone to strictly follow this style guide to maintain:
- Clarity → readable and self-explanatory code.
- Consistency → uniform coding across all projects.
- Collaboration → easier reviews, onboarding, and teamwork.
Mistakes happen—even seasoned developers sometimes forget these rules.
If you find deviations:
- Correct them
- Leave a comment (so others learn and avoid repeating the mistake)
Our collective goal is professional, maintainable code.
Use standard C libraries whenever possible instead of reinventing functionality.
The most commonly used are:
<stdio.h>– Input/Output<string.h>– String handling<stdbool.h>– Boolean types<stdint.h>– Fixed-width integers<stdarg.h>– Variable argument handling<math.h>– Mathematical functions<complex.h>– Complex number arithmetic<ctype.h>– Character classification<limits.h>– Type limits<stdlib.h>– General utilities (memory, conversion, random)<time.h>– Time and date utilities
- Standard: Use
C11 - Indentation: 4 spaces (never tabs)
- Line length: Max 120 characters
- Braces: Always on the next line for functions, blocks,
if/else, loops,switch, etc. - Whitespace:
- Space after
,,;in for-loops, and around operators (=,+,-,*, etc.) - No space between keywords and
(→ e.g.,if(condition)
- Space after
✅ Example:
if(condition)
{
for(int i = 0; i < 10; i++)
{
sum += i;
}
}❌ Wrong:
if ( condition ) {
for(int i=0;i<10;i++){
sum+=i;
}}- Line wrapping:
- Indent wrapped lines 2 levels
- Always wrap enums
- Wrap long expressions and function arguments cleanly
- Naming convention:
camelCase - Descriptive, no abbreviations (e.g.,
averageTemperature, notavgTemp) - One declaration per line for readability
- Alignment: Align names and values when grouped
uint16_t averageTemperature = 15;
uint16_t totalRPM = 17;
float averageSpeed = 12.56f;-
Pointer notation:
*sticks to type, not variableint *ptrValue; // ✅ int* ptrValue; // ❌
-
Prefixes:
ptr→ pointerpp→ double pointerglb→ global variablegptr→ global pointer
-
Volatile: Always mark ISR-shared variables or memory-mapped registers as
volatile.
volatile uint32_t glbTickCounter = 0;
volatile uint8_t *ptrGpioInput = (uint8_t*)(GPIOA->IDR);- Declaration order (inside function):
- Custom structures/enums
- Integers (global → local)
- Floats/doubles (global → local)
- Always use
constfor unmodifiable values and pointer protection. - Prefer
constover#definewhen possible.
✅ Examples:
void processBuffer(const void *ptrBuffer, const size_t length);
const float piValue = 3.14159f;- Use
typedefwith PascalCase +_tsuffix - Structure variables →
camelCase - Use C99 style initialization
typedef struct
{
float temperature;
float humidity;
float pressure;
} Sensor_Data_t;
Sensor_Data_t sensorData = {
.temperature = 25.3f,
.humidity = 45.8f,
.pressure = 1013.25f,
};- Use
typedefwith PascalCase +_esuffix - Enum members prefixed with
<EnumName_>
typedef enum
{
Mpu6050_FullScale_2g = 0,
Mpu6050_FullScale_4g,
Mpu6050_FullScale_8g,
Mpu6050_FullScale_16g,
} Mpu6050_FullScale_e;- Always use braces
{}(even for single statements) - Constants first in comparisons:
if(0 == value) - Switch on enums when possible
- Add
defaultin switch unless enum covers all cases - Use named constants instead of magic numbers
#define MAX_RETRIES 3
for(int i = 0; i < MAX_RETRIES; i++)
{
// retry logic
}- Uppercase with
_separator - Double underscore
__for grouped sets - Always wrap expressions and parameters in parentheses
#define GPIO__PIN_0 (1 << 0)
#define LED__ON() (PORTB |= GPIO__PIN_0)
#define DELAY__MS(x) (_delay_ms((x)))
#define TIME__DOUBLE(t) ((t) * 2)- Naming convention:
camelCase - Use library/module prefix for clarity (e.g.,
adc_init()) - Boolean-returning → prefix
is - Callback → prefix
cb - Prefer single exit point (
returnat end) - Doxygen documentation required
/*****************************************************************************
* @brief Initializes the ADC module.
* @param channel ADC channel number (0–15)
* @param resolution ADC resolution (8/10/12 bits)
* @return 0 on success, -1 on error
*****************************************************************************/
int adc_init(uint8_t channel, uint8_t resolution);-
File names: lowercase, short, descriptive
-
Header guard:
#ifndef FILE_H / #define FILE_H / #endif -
Header content order:
- File comment
- Include guard
- Includes (minimal, prefer forward declarations)
- Types (structs, enums)
- Function prototypes
- Callback prototypes
-
Source content order:
- File comment
- Includes
- Macros / defines
- Variables
- Private prototypes
- Private functions
- Public functions
- Callbacks
- Use English always
- Use
//for short inline comments - Use
/* ... */for section comments - Use
TODO:for unfinished code - All public functions → documented with Doxygen
✅ Example:
// Initialize system tick
// TODO: Add support for low-power mode