Part A
Introduction on Embedded C
Embedded C programming is crucial for developing firmware in various systems such as IoT
devices and industrial controllers. As we progress through 2024, it’s important to refine our
approach to mastering this skill. This guide explores core concepts, modern practices, and
advanced topics relevant to embedded C programming this year.
Embedded C programming involves utilizing the C language to develop firmware for
microcontrollers, the brains of embedded systems. These microcontrollers operate within a
constrained environment with limited resources (memory, processing power). Embedded C
provides a powerful yet efficient language for manipulating hardware registers, interfacing with
peripherals, and implementing real-time control logic.
The continued relevance of embedded C in 2024 can be attributed to several factors:
• Growth of the IoT: The proliferation of connected devices necessitates compact, low-
power firmware, making embedded C an ideal choice for resource-constrained IoT nodes.
• Focus on Security: As embedded systems become more interconnected, security
vulnerabilities pose a significant threat. Understanding secure coding practices in
embedded C is paramount.
• Hardware Advancements: Modern microcontrollers offer a plethora of new features
like hardware accelerators and security modules. Effective embedded C code leverages
these features for improved performance and security.
Core Concepts
A solid foundation in C programming fundamentals is crucial for mastering embedded C. This
section revisits essential concepts with an embedded systems perspective:
• Data Types: Grasping the nuances of data types like char, int, long, and structures is vital
for memory management and efficient data manipulation.
• Operators: Operators like bitwise operators (&, |, ^), arithmetic operators (+, -, *, /), and
comparison operators (==, !=, <, >) are fundamental for interacting with hardware
registers and performing calculations.
• Control Flow: Control flow statements like if, else, for, while, and switch enable
decision-making and program execution flow control, essential for implementing control
algorithms.
• Functions: Functions promote code modularity, reusability, and maintainability.
Understanding function prototypes, arguments, return values, and recursion is crucial.
• Pointers: Pointers provide a powerful mechanism for memory management and low-
level hardware interaction. Mastering pointer arithmetic and avoiding dangling pointers is
essential for safe and efficient code.
Memory Management in Embedded Systems
Memory management in embedded systems is a critical aspect distinct from traditional C
programming. This section dives deeper:
• Memory Organization: Embedded systems typically have segmented memory with
distinct regions for program, data, stack, and heap. Understanding these segments and
their usage patterns is crucial.
• Memory Allocation Strategies: Techniques like static allocation, dynamic allocation
using malloc and free, and memory pool management are explored for efficient memory
usage.
• Bit Manipulation: Embedded systems often require bit-level operations. Bit fields and
bitwise operations are crucial for interacting with hardware registers.
Input/Output (I/O) Programming
Interfacing with the physical world is a core function of embedded systems. This section covers
various I/O mechanisms and their C programming aspects:
• General Purpose Input/Output (GPIO): Configuring GPIO pins as inputs or outputs,
reading digital data, and driving output signals are fundamental for sensor interfacing and
actuator control.
• Timers and Counters: Timers and counters are essential for real-time operations.
Programming timers for periodic tasks, pulse-width modulation (PWM), and delay
generation is covered.
• Analog-to-Digital Converters (ADCs) and Digital-to-Analog Converters (DACs):
Interfacing with analog sensors and actuators using ADCs and DACs requires
understanding conversion techniques and programming considerations.
• Communication Protocols: Mastering serial communication protocols like I2C, SPI,
and UART is essential for interfacing with external devices and modules.
Interrupts and Real-Time Programming
Embedded systems often respond to external events. This section explores interrupts and real-
time concepts:
• Interrupt Handling Mechanisms: Understanding interrupt service routines (ISRs),
interrupt priority levels, and interrupt nesting is crucial for real-time response.
• Real-Time Concepts: Concepts like latency (time between event and response) and jitter
(variation in latency) are critical for real-time systems. Techniques for minimizing these
factors are explored.
• Efficient ISR Routines: Writing concise and efficient ISRs that minimize interrupt
latency is essential for maintaining real-time performance.
Embedded Development Tools
The selection of development tools significantly impacts the embedded C development
experience. Here’s a breakdown of essential tools:
• Debuggers: Debuggers allow for step-by-step code execution, variable inspection, and
breakpoint setting, crucial for troubleshooting and code optimization.
• Emulators and Simulators: Emulators mimic the behavior of a microcontroller on a
host computer, while simulators model the microcontroller’s instruction set. These tools
are valuable for pre-silicon development and testing.
• Integrated Development Environments (IDEs): IDEs combine editing, compiling,
debugging, and project management functionalities in a single interface, streamlining the
development workflow. Popular embedded C IDEs include Keil MDK-ARM, IAR
Embedded Workbench, and Eclipse with CDT plugin.
Modern Practices and Considerations
As the embedded systems landscape evolves, best practices and considerations need to be
adapted. Here are key areas to focus on in 2024:
• Security in Embedded Systems: Security vulnerabilities in embedded C code can have
catastrophic consequences.
o Secure coding practices like input validation, buffer overflow protection, and
memory access control are crucial.
o Static code analysis tools can help identify potential security risks in the code.
• Power Efficiency: With the rise of battery-powered IoT devices, optimizing code for low
power consumption is essential.
o Techniques like clock gating, peripheral power management, and low-power
sleep modes need to be carefully considered during development.
• Leveraging Hardware Features: Modern microcontrollers offer a plethora of new
features:
o Hardware accelerators for tasks like cryptography or signal processing can
significantly improve performance when utilized effectively in C code.
o Security modules like secure boot and hardware encryption engines can enhance
system security.
• Integration with IoT and Cloud Platforms (if applicable): For embedded systems
participating in the IoT ecosystem:
o Understanding data serialization formats like JSON or message queuing protocols
(MQTT) is necessary for communication with cloud platforms.
o Security considerations for data transmission over networks become paramount.
Advanced Topics
This section explores advanced concepts for those seeking to delve deeper:
• Embedded Linux Programming: Embedded Linux offers a rich operating system
environment for complex embedded systems. Understanding how C code interacts with
the Linux kernel and device drivers is valuable for certain applications.
• Real-Time Operating Systems (RTOS): Real-time operating systems (RTOS) provide a
layer of abstraction for managing tasks, resources, and communication in complex
embedded systems. Familiarity with concepts like threads, semaphores, and mutexes is
beneficial for RTOS-based development.
• Bare-Metal vs. RTOS Development: Choosing between bare-metal programming
(direct hardware interaction) and using an RTOS depends on project requirements.
Understanding the trade-offs between control, complexity, and development time is
crucial.
Part B
Embedded C/C
Introduction
Embedded C Course is a set of language extensions for the C programming language by the C
Standards Committee to statement commonality issues that exist between C extensions for
different embedded systems. It is used to develop microcontroller programming applications.
It includes features not available in standard C like fixed-point arithmetic, named address spaces,
and necessary I/O hardware addressing. Cell phones, MP3 players are some examples of
embedded systems in which embedded C is used to program and control these devices.
What is a program in C?
A computer program written in C is a human readable and ordered set of instructions that a
computer executes. It aims to provide a solution to a specific computing problem and tell the
computer to perform a certain task with a sequence of instructions that it needs to follow.
Essentially all programs are just plain text files stored on your computer’s hard drive that use a
special syntax which is defined by the programming language you're using.
Each language has its own rules that dictate what you can write and what's considered valid, and
what is not.
A program has keywords, which are specific words that are reserved and are part of the
language. It also has literal pieces of data like strings and numbers. And it has words that follow
the language’s rules, which we define and introduce to the language that don’t already exist (like
variables or methods).
What is a compiler?
Programs are written by us and for us. They are meant to be understood by humans.
When we write programs in human readable form, we can understand them – but the computer
may not be able to. Computers don’t directly understand programming languages, they only
understand binary. So programs need to be translated into this other form so the computer can
actually understand our program's instructions.
Programs in high level languages can be either compiled or interpreted. They use special pieces
of software called compilers and interpreters, respectively.
What's the difference between a compiler and an interpreter?
Both compilers and interpreters are programs, but they're far more complex ones, and they act as
translators. They take a program that's written in a human readable form and turn it into
something that computers can make sense of. And they make it possible to run and execute
programs on different computer systems.
Compiled programs are first converted into machine-readable form which means they are
translated into machine code before they run. Machine code is a numerical language – binary
instructions composed of sequences of 0s and 1s.
This compilation produces an executable program, that is a file containing the code in the
machine language that the CPU (Central Processing Unit) will be able to read, understand, and
execute directly.
After this, the program can run and the computer does what the program tells it to do. Compiled
programs have a stronger correspondence with the underlying hardware and can more easily
manipulate the computer's CPU and memory.
Interpreted programs, on the other hand, are not directly executed by the machine nor do they
need to be translated into a machine language program. Instead, they use an interpreter that
automatically and directly translates and executes each statement and instruction in the code line
by line during run time.
C is a compiled programming language. This means that it uses a compiler to analyse the source
code written in C and then turns it into a binary file that the computer's hardware can directly
execute. This will be specific for each particular machine.
Structure of Embedded C Program
With these data types in mind, let's take a look at the structure of a program in Embedded C.
Preprocessor Directives
Preprocessor directives are not normal code statements. They are lines of code that begin with
the character "#" and appear in Embedded C programming before the main function. At runtime,
the compiler looks for preprocessor directives within the code and resolves them completely
before resolving any of the functions within the code itself. Some preprocessor directives can
skip part of the main code based on certain conditions, while others may ask the preprocessor to
replace the directive with information from a separate file before executing the main code or to
behave differently based on the hardware resources available. Many types of preprocessor
directives are available in the Embedded C language.
Global Variable Declaration
Global declarations happen before the main function of the source code. Engineers can declare
global variables that may be called by the main program or any additional functions or sub-
programs within the code. Engineers may also define functions here that will be accessible
anywhere in the code.
Main Program
The main part of the program begins with main(). If the main function is expected to return an
integer value, we would write int main(). If no return is expected, convention dictates that we
should write void main(void).
• Declaration of local variables - Unlike global variables, these ones can only be called
by the function in which they are declared.
• Initializing variables/devices - A portion of code that includes instructions for
initializating variables, I/O ports, devices, function registers, and anything else needed for
the program to execute
• Program body - Includes the functions, structures, and operations needed to do
something useful with our embedded system
Subprograms
An embedded C program file is not limited to a single function. Beyond the main() function,
programmers can define additional functions that will execute following the main function when
the code is compiled.
Variables and Types
Variables are containers for storing data values, like numbers and characters.
In C, there are different types of variables (defined with different keywords), for example:
• int - stores integers (whole numbers), without decimals, such as 123 or -123
• float - stores floating point numbers, with decimals, such as 19.99 or -19.99
• char - stores single characters, such as 'a' or 'B'. Char values are surrounded by single quotes
Declaring (Creating) Variables
To create a variable, specify the type and assign it a value:
Syntax
type variableName = value;
Where type is one of C types (such as int), and variableName is the name of the variable (such
as x or myName). The equal sign is used to assign a value to the variable.
So, to create a variable that should store a number, look at the following example:
Example
Create a variable called myNum of type int and assign the value 15 to it:
int myNum = 15;
You can also declare a variable without assigning the value, and assign the value later:
Example
// Declare a variable
int myNum;
// Assign a value to the variable
myNum = 15;
Output Variables
Example
int myNum = 15;
[Link](myNum); // Outputs 15
To print other types, use %c for char and %f for float:
Example
// Create variables
int myNum = 15; // Integer (whole number)
float myFloatNum = 5.99; // Floating point number
char myLetter = 'D'; // Character
// Print variables
[Link]("%d\n", myNum);
[Link]("%f\n", myFloatNum);
[Link]("%c\n", myLetter);
To combine both text and a variable, separate them with a comma inside the [Link]()
function:
Example
int myNum = 15;
[Link]("My favorite number is: “ , myNum);
To print different types in a single [Link]() function, you can use the following:
Example
int myNum = 15;
char myLetter = 'D';
Change Variable Values
Note: If you assign a new value to an existing variable, it will overwrite the previous value:
Example
int myNum = 15; // myNum is 15
myNum = 10; // Now myNum is 10
You can also assign the value of one variable to another:
Example
int myNum = 15;
int myOtherNum = 23;
// Assign the value of myOtherNum (23) to myNum
myNum = myOtherNum;
// myNum is now 23, instead of 15
[Link](myNum);
Or copy values to empty variables:
Example
// Create a variable and assign the value 15 to it
int myNum = 15;
// Declare a variable without assigning it a value
int myOtherNum;
// Assign the value of myNum to myOtherNum
myOtherNum = myNum;
// myOtherNum now has 15 as a value
[Link](myOtherNum);
Add Variables Together
To add a variable to another variable, you can use the + operator:
Example
int x = 5;
int y = 6;
int sum = x + y;
[Link](sum);
Declare Multiple Variables
To declare more than one variable of the same type, use a comma-separated list:
Example
int x = 5, y = 6, z = 50;
You can also assign the same value to multiple variables of the same type:
Example
int x, y, z;
x = y = z = 50;
C Variable Names
All C variables must be identified with unique names.
These unique names are called identifiers.
Identifiers can be short names (like x and y) or more descriptive names (age, sum, totalVolume).
Note: It is recommended to use descriptive names in order to create understandable and
maintainable code:
Example
// Good
int minutesPerHour = 60;
// OK, but not so easy to understand what m actually is
int m = 60;
The general rules for naming variables are:
• Names can contain letters, digits and underscores
• Names must begin with a letter or an underscore (_)
• Names are case sensitive (myVar and myvar are different variables)
• Names cannot contain whitespaces or special characters like !, #, %, etc.
• Reserved words (such as int) cannot be used as names
Real-Life Example
Often in our examples, we simplify variable names to match their data type (myInt or myNum
for int types, myChar for char types etc). This is done to avoid confusion.
However, if you want a real-life example on how variables can be used, take a look at the
following, where we have made a program that stores different data of a college student:
Example
// Student data
int studentID = 15;
int studentAge = 23;
float studentFee = 75.25;
char studentGrade = 'B';
// Print variables
[Link](“ Student ID: “);
[Link](studentID);
[Link](“ Student Age: “);
[Link](studentAge);
[Link](“ Student Fee: “);
[Link](studentFee);
[Link](“ Student Grade: “);
[Link](studentGrade);
Basic Data Types
The data type specifies the size and type of information the variable will store.
Data Type Size Description
int 2 or 4 bytes Stores whole numbers, without decimals
Stores fractional numbers, containing one or more decimals. Sufficient
float 4 bytes
for storing 6-7 decimal digits
Stores fractional numbers, containing one or more decimals. Sufficient
double 8 bytes
for storing 15 decimal digits
char 1 byte Stores a single character/letter/number, or ASCII values
Set Decimal Precision
You have probably already noticed that if you print a floating-point number, the output will show
many digits after the decimal point:
Example
float myFloatNum = 3.5;
double myDoubleNum = 19.99;
[Link](myFloatNum); // Outputs 3.500000
[Link](myDoubleNum); // Outputs 19.990000
If you want to remove the extra zeros (set decimal precision),
Example
float myFloatNum = 3.5;
[Link](myFloatNum); // Default will show 6 digits after the decimal point
[Link](myFloatNum, 1); // Only show 1 digit
[Link](myFloatNum, 2); // Only show 2 digits
[Link](myFloatNum, 4); // Only show 4 digits
Constants
If you don't want others (or yourself) to change existing variable values, you can use the const
keyword.
This will declare the variable as "constant", which means unchangeable and read-only:
Example
const int myNum = 15; // myNum will always be 15
myNum = 10; // error: assignment of read-only variable 'myNum'
You should always declare the variable as constant when you have values that are unlikely to
change:
Example
const int minutesPerHour = 60;
const float PI = 3.14;
Notes On Constants
When you declare a constant variable, it must be assigned with a value:
Example
Like this:
const int minutesPerHour = 60;
This however, will not work:
const int minutesPerHour;
minutesPerHour = 60; // error
Good Practice
Another thing about constant variables, is that it is considered good practice to declare them with
uppercase. It is not required, but useful for code readability and common for C programmers:
Example
const int BIRTHYEAR = 1980;
➢ Operators
Operators are used to perform operations on variables and values.
In the example below, we use the + operator to add together two values:
Example
int myNum = 100 + 50;
Although the + operator is often used to add together two values, like in the example above, it
can also be used to add together a variable and a value, or a variable and another variable:
Example
int sum1 = 100 + 50; // 150 (100 + 50)
int sum2 = sum1 + 250; // 400 (150 + 250)
int sum3 = sum2 + sum2; // 800 (400 + 400)
C divides the operators into the following groups:
• Arithmetic operators
• Assignment operators
• Comparison operators
• Logical operators
• Bitwise operators
Arithmetic Operators
Arithmetic operators are used to perform common mathematical operations.
Operator Name Description Example Try it
+ Addition Adds together two values x+y
- Subtraction Subtracts one value from another x-y
* Multiplication Multiplies two values x*y
/ Division Divides one value by another x/y
% Modulus Returns the division remainder x%y
++ Increment Increases the value of a variable by 1 ++x
-- Decrement Decreases the value of a variable by 1 --x
Assignment Operators
Assignment operators are used to assign values to variables.
In the example below, we use the assignment operator (=) to assign the value 10 to a variable
called x:
Example
int x = 10;
The addition assignment operator (+=) adds a value to a variable:
Example
int x = 10;
x += 5;
A list of all assignment operators:
Operator Example Same As
= x=5 x=5
+= x += 3 x=x+3
-= x -= 3 x=x-3
*= x *= 3 x=x*3
/= x /= 3 x=x/3
%= x %= 3 x=x%3
&= x &= 3 x=x&3
|= x |= 3 x=x|3
^= x ^= 3 x=x^3
>>= x >>= 3 x = x >> 3
<<= x <<= 3 x = x << 3
Comparison Operators
Comparison operators are used to compare two values (or variables). This is important in
programming, because it helps us to find answers and make decisions.
The return value of a comparison is either 1 or 0, which means true (1) or false (0). These values
are known as Boolean values, and you will learn more about them in the Booleans and If..Else
chapter.
In the following example, we use the greater than operator (>) to find out if 5 is greater than 3:
Example
int x = 5;
int y = 3;
[Link]("%d", x > y); // returns 1 (true) because 5 is greater than 3
A list of all comparison operators:
Operator Name Example Try it
== Equal to x == y
!= Not equal x != y
> Greater than x > y
< Less than x<y
Greater than
>= x >= y
or equal to
Less than or
<= x <= y
equal to
Logical Operators
You can also test for true or false values with logical operators.
Logical operators are used to determine the logic between variables or values:
Operator Name Description Example
Returns true if both statements are
&& Logical and x < 5 && x < 10
true
|| Logical or Returns true if one of the statements is true x < 5 || x < 4
! Logical not Reverse the result, returns false if the result is true !(x < 5 && x < 10)
Sizeof Operator
The memory size (in bytes) of a data type or a variable can be found with the sizeof operator:
Example
int myInt;
float myFloat;
double myDouble;
char myChar;
[Link](sizeof(myInt));
[Link]("%lu\n", sizeof(myFloat));
[Link](sizeof(myDouble));
[Link](sizeof(myChar));
➢ Booleans
Very often, in programming, you will need a data type that can only have one of two values, like:
• YES / NO
• ON / OFF
• TRUE / FALSE
For this, C has a bool data type, which is known as booleans.
Booleans represent values that are either true or false.
Boolean Variables
In C, the bool type is not a built-in data type, like int or char.
It was introduced in C99, and you must import the following header file to use it:
#include <stdbool.h>
A boolean variable is declared with the bool keyword and can only take the values true or
false:
bool isProgrammingFun = true;
bool isFishTasty = false;
Before trying to print the boolean variables, you should know that boolean values are returned as
integers:
• 1 (or any other number that is not 0) represents true
• 0 represents false
Therefore, you must use the %d format specifier to print a boolean value:
Example
// Create boolean variables
bool isProgrammingFun = true;
bool isFishTasty = false;
// Return boolean values
[Link]("%d", isProgrammingFun); // Returns 1 (true)
[Link]("%d", isFishTasty); // Returns 0 (false)
However, it is more common to return a boolean value by comparing values and variables.
Comparing Values and Variables
Comparing values are useful in programming, because it helps us to find answers and make
decisions.
For example, you can use a comparison operator, such as the greater than (>) operator, to
compare two values:
Example
[Link]("%d", 10 > 9); // Returns 1 (true) because 10 is greater than 9
From the example above, you can see that the return value is a boolean value (1).
You can also compare two variables:
Example
int x = 10;
int y = 9;
[Link]("%d", x > y);
In the example below, we use the equal to (==) operator to compare different values:
Example
[Link]("%d", 10 == 10); // Returns 1 (true), because 10 is equal to 10
[Link]("%d", 10 == 15); // Returns 0 (false), because 10 is not equal to 15
[Link]("%d", 5 == 55); // Returns 0 (false) because 5 is not equal to 55
You are not limited to only compare numbers. You can also compare boolean variables, or even
special structures, like arrays (which you will learn more about in a later chapter):
Example
bool isHamburgerTasty = true;
bool isPizzaTasty = true;
// Find out if both hamburger and pizza is tasty
[Link]("%d", isHamburgerTasty == isPizzaTasty);
Remember to include the <stdbool.h> header file when working with bool variables.
Real Life Example
Let's think of a "real life example" where we need to find out if a person is old enough to vote.
In the example below, we use the >= comparison operator to find out if the age (25) is greater
than OR equal to the voting age limit, which is set to 18:
Example
int myAge = 25;
int votingAge = 18;
[Link]("%d", myAge >= votingAge); // Returns 1 (true), meaning 25 year olds are allowed to vote!
Cool, right? An even better approach (since we are on a roll now), would be to wrap the code
above in an if...else statement, so we can perform different actions depending on the result:
Example
Output "Old enough to vote!" if myAge is greater than or equal to 18. Otherwise output "Not
old enough to vote.":
int myAge = 25;
int votingAge = 18;
if (myAge >= votingAge) {
[Link]("Old enough to vote!");
} else {
[Link]("Not old enough to vote.");
}
C If ... Else
Conditions and If Statements
You have already learned that C supports the usual logical conditions from mathematics:
• Less than: a < b
• Less than or equal to: a <= b
• Greater than: a > b
• Greater than or equal to: a >= b
• Equal to a == b
• Not Equal to: a != b
You can use these conditions to perform different actions for different decisions.
C has the following conditional statements:
• Use if to specify a block of code to be executed, if a specified condition is true
• Use else to specify a block of code to be executed, if the same condition is false
• Use else if to specify a new condition to test, if the first condition is false
• Use switch to specify many alternative blocks of code to be executed
The if Statement
Use the if statement to specify a block of code to be executed if a condition is true.
Syntax
if (condition) {
// block of code to be executed if the condition is true
}
Note that if is in lowercase letters. Uppercase letters (If or IF) will generate an error.
In the example below, we test two values to find out if 20 is greater than 18. If the condition is
true, print some text:
Example
if (20 > 18) {
[Link]("20 is greater than 18");
}
We can also test variables:
Example
int x = 20;
int y = 18;
if (x > y) {
[Link]("x is greater than y");
}
Example explained
In the example above we use two variables, x and y, to test whether x is greater than y (using the
> operator). As x is 20, and y is 18, and we know that 20 is greater than 18, we print to the screen
that "x is greater than y".
The else Statement
Use the else statement to specify a block of code to be executed if the condition is false.
Syntax
if (condition) {
// block of code to be executed if the condition is true
} else {
// block of code to be executed if the condition is false
}
Example
int time = 20;
if (time < 18) {
[Link]("Good day.");
} else {
[Link]("Good evening.");
}
// Outputs "Good evening."
Example explained
In the example above, time (20) is greater than 18, so the condition is false. Because of this, we
move on to the else condition and print to the screen "Good evening". If the time was less than
18, the program would print "Good day".
The else if Statement
Use the else if statement to specify a new condition if the first condition is false.
Syntax
if (condition1) {
// block of code to be executed if condition1 is true
} else if (condition2) {
// block of code to be executed if the condition1 is false and condition2 is true
} else {
// block of code to be executed if the condition1 is false and condition2 is false
}
Example
int time = 22;
if (time < 10) {
[Link]("Good morning.");
} else if (time < 20) {
[Link]("Good day.");
} else {
[Link]("Good evening.");
}
// Outputs "Good evening."
Example explained
In the example above, time (22) is greater than 10, so the first condition is false. The next
condition, in the else if statement, is also false, so we move on to the else condition since
condition1 and condition2 is both false - and print to the screen "Good evening".
However, if the time was 14, our program would print "Good day."
Another Example
This example shows how you can use if..else to find out if a number is positive or negative:
Example
int myNum = 10; // Is this a positive or negative number?
if (myNum > 0) {
[Link]("The value is a positive number.");
} else if (myNum < 0) {
[Link]("The value is a negative number.");
} else {
[Link]("The value is 0.");
}
Switch Statement
Instead of writing many if..else statements, you can use the switch statement.
The switch statement selects one of many code blocks to be executed:
Syntax
switch(expression) {
case x:
// code block
break;
case y:
// code block
break;
default:
// code block
}
This is how it works:
• The switch expression is evaluated once
• The value of the expression is compared with the values of each case
• If there is a match, the associated block of code is executed
• The break statement breaks out of the switch block and stops the execution
• The default statement is optional, and specifies some code to run if there is no case match
The example below uses the weekday number to calculate the weekday name:
Example
int day = 4;
switch (day) {
case 1:
[Link]("Monday");
break;
case 2:
[Link]("Tuesday");
break;
case 3:
[Link]("Wednesday");
break;
case 4:
[Link]("Thursday");
break;
case 5:
[Link]("Friday");
break;
case 6:
[Link]("Saturday");
break;
case 7:
[Link]("Sunday");
break;
}
// Outputs "Thursday" (day 4)
The break Keyword
When C reaches a break keyword, it breaks out of the switch block.
This will stop the execution of more code and case testing inside the block.
When a match is found, and the job is done, it's time for a break. There is no need for more
testing.
A break can save a lot of execution time because it "ignores" the execution of all the rest of the
code in the switch block.
The default Keyword
The default keyword specifies some code to run if there is no case match:
Example
int day = 4;
switch (day) {
case 6:
[Link]("Today is Saturday");
break;
case 7:
[Link]("Today is Sunday");
break;
default:
[Link]("Looking forward to the Weekend");
}
// Outputs "Looking forward to the Weekend"
Note: The default keyword must be used as the last statement in the switch, and it does not need
a break.
C While Loop
Loops
Loops can execute a block of code as long as a specified condition is reached.
Loops are handy because they save time, reduce errors, and they make code more readable.
While Loop
The while loop loops through a block of code as long as a specified condition is true:
Syntax
while (condition) {
// code block to be executed
}
In the example below, the code in the loop will run, over and over again, as long as a variable (i)
is less than 5:
Example
int i = 0;
while (i < 5) {
[Link]("%d\n", i);
i++;
}
Note: Do not forget to increase the variable used in the condition (i++), otherwise the loop will
never end!
The Do/While Loop
The do/while loop is a variant of the while loop. This loop will execute the code block once,
before checking if the condition is true, then it will repeat the loop as long as the condition is
true.
Syntax
do {
// code block to be executed
}
while (condition);
The example below uses a do/while loop. The loop will always be executed at least once, even
if the condition is false, because the code block is executed before the condition is tested:
Example
int i = 0;
do {
[Link]( i);
i++;
}
while (i < 5);
Do not forget to increase the variable used in the condition, otherwise the loop will never end!
C For Loop
For Loop
When you know exactly how many times you want to loop through a block of code, use the for
loop instead of a while loop:
Syntax
for (statement 1; statement 2; statement 3) {
// code block to be executed
}
Statement 1 is executed (one time) before the execution of the code block.
Statement 2 defines the condition for executing the code block.
Statement 3 is executed (every time) after the code block has been executed.
The example below will print the numbers 0 to 4:
Example
int i;
for (i = 0; i < 5; i++) {
[Link]("%d\n", i);
}
Example explained
Statement 1 sets a variable before the loop starts (int i = 0).
Statement 2 defines the condition for the loop to run (i must be less than 5). If the condition is
true, the loop will start over again, if it is false, the loop will end.
Statement 3 increases a value (i++) each time the code block in the loop has been executed.
Another Example
This example will only print even values between 0 and 10:
Example
for (i = 0; i <= 10; i = i + 2) {
[Link]("%d\n", i);
}
Nested Loops
It is also possible to place a loop inside another loop. This is called a nested loop.
The "inner loop" will be executed one time for each iteration of the "outer loop":
Example
int i, j;
// Outer loop
for (i = 1; i <= 2; ++i) {
[Link]( i); // Executes 2 times
// Inner loop
for (j = 1; j <= 3; ++j) {
[Link](" Inner: %d\n", j); // Executes 6 times (2 * 3)
}
}
C Arrays
Arrays
Arrays are used to store multiple values in a single variable, instead of declaring separate
variables for each value.
To create an array, define the data type (like int) and specify the name of the array followed by
square brackets [].
To insert values to it, use a comma-separated list, inside curly braces:
int myNumbers[] = {25, 50, 75, 100};
We have now created a variable that holds an array of four integers.
Access the Elements of an Array
To access an array element, refer to its index number.
Array indexes start with 0: [0] is the first element. [1] is the second element, etc.
This statement accesses the value of the first element [0] in myNumbers:
Example
int myNumbers[] = {25, 50, 75, 100};
printf("%d", myNumbers[0]);
// Outputs 25
Change an Array Element
To change the value of a specific element, refer to the index number:
Example
myNumbers[0] = 33;
Example
int myNumbers[] = {25, 50, 75, 100};
myNumbers[0] = 33;
printf("%d", myNumbers[0]);
// Now outputs 33 instead of 25
Set Array Size
Another common way to create arrays, is to specify the size of the array, and add elements later:
Example
// Declare an array of four integers:
int myNumbers[4];
// Add elements
myNumbers[0] = 25;
myNumbers[1] = 50;
myNumbers[2] = 75;
myNumbers[3] = 100;
Using this method, you should know the size of the array, in order for the program to store
enough memory.
You are not able to change the size of the array after creation.
C Strings
Strings
Strings are used for storing text/characters.
For example, "Hello World" is a string of characters.
Unlike many other programming languages, C does not have a String type to easily create string
variables. Instead, you must use the char type and create an array of characters to make a string
in C:
char greetings[] = "Hello World!";
Note that you have to use double quotes ("").
To output the string, you can use the printf() function together with the format specifier %s to
tell C that we are now working with strings:
Example
char greetings[] = "Hello World!";
printf("%s", greetings);
Access Strings
Since strings are actually arrays in C, you can access a string by referring to its index number
inside square brackets [].
This example prints the first character (0) in greetings:
Example
char greetings[] = "Hello World!";
printf("%c", greetings[0]);
Note that we have to use the %c format specifier to print a single character.
Modify Strings
To change the value of a specific character in a string, refer to the index number, and use single
quotes:
Example
char greetings[] = "Hello World!";
greetings[0] = 'J';
printf("%s", greetings);
// Outputs Jello World! instead of Hello World!
C Functions
A function is a block of code which only runs when it is called.
You can pass data, known as parameters, into a function.
Functions are used to perform certain actions, and they are important for reusing code: Define
the code once, and use it many times.
Predefined Functions
So it turns out you already know what a function is. You have been using it the whole time while
studying this tutorial!
For example, main() is a function, which is used to execute code, and printf() is a function;
used to output/print text to the screen:
Example
int main() {
printf("Hello World!");
return 0;
}
Create a Function
To create (often referred to as declare) your own function, specify the name of the function,
followed by parentheses () and curly brackets {}:
Syntax
void myFunction() {
// code to be executed
}
Example Explained
• myFunction() is the name of the function
• void means that the function does not have a return value. You will learn more about return
values later in the next chapter
• Inside the function (the body), add code that defines what the function should do
Call a Function
Declared functions are not executed immediately. They are "saved for later use", and will be
executed when they are called.
To call a function, write the function's name followed by two parentheses () and a semicolon ;
In the following example, myFunction() is used to print a text (the action), when it is called:
Example
Inside main, call myFunction():
// Create a function
void myFunction() {
printf("I just got executed!");
}
int main() {
myFunction(); // call the function
return 0;
}
// Outputs "I just got executed!"
A function can be called multiple times:
Example
void myFunction() {
printf("I just got executed!");
}
int main() {
myFunction();
myFunction();
myFunction();
return 0;
}
// I just got executed!
// I just got executed!
// I just got executed!
C Function Declaration and Definition
Function Declaration and Definition
You just learned from the previous chapters that you can create and call a function in the
following way:
Example
// Create a function
void myFunction() {
printf("I just got executed!");
}
int main() {
myFunction(); // call the function
return 0;
}
A function consist of two parts:
• Declaration: the function's name, return type, and parameters (if any)
• Definition: the body of the function (code to be executed)
void myFunction() { // declaration
// the body of the function (definition)
}
For code optimization, it is recommended to separate the declaration and the definition of the
function.
You will often see C programs that have function declaration above main(), and function
definition below main(). This will make the code better organized and easier to read:
Example
// Function declaration
void myFunction();
// The main method
int main() {
myFunction(); // call the function
return 0;
}
// Function definition
void myFunction() {
printf("I just got executed!");
}
Another Example
If we use the example from the previous chapter regarding function parameters and return
values:
Example
int myFunction(int x, int y) {
return x + y;
}
int main() {
int result = myFunction(5, 3);
printf("Result is = %d", result);
return 0;
}
// Outputs 8 (5 + 3)
It is considered good practice to write it like this instead:
Example
// Function declaration
int myFunction(int, int);
// The main method
int main() {
int result = myFunction(5, 3); // call the function
printf("Result is = %d", result);
return 0;
}
// Function definition
int myFunction(int x, int y) {
return x + y;
}
C Comments
Comments in C
Comments can be used to explain code, and to make it more readable. It can also be used to
prevent execution when testing alternative code.
Comments can be singled-lined or multi-lined.
Single-line Comments
Single-line comments start with two forward slashes (//).
Any text between // and the end of the line is ignored by the compiler (will not be executed).
This example uses a single-line comment before a line of code:
Example
// This is a comment
printf("Hello World!");
This example uses a single-line comment at the end of a line of code:
Example
printf("Hello World!"); // This is a comment
C Multi-line Comments
Multi-line comments start with /* and ends with */.
Any text between /* and */ will be ignored by the compiler:
Example
/* The code below will print the words Hello World!
to the screen, and it is amazing */
printf("Hello World!");
PART C
Introduction to C Programming Language with Arduino
Examples
1.1 Syntax and Structure
1.1.1 Basic Syntax
Arduino Sketch Structure:
// Include necessary libraries
#include <Arduino.h>
// Setup function: runs once at the start
void setup() {
// Initialization code here
// Loop function: runs repeatedly
void loop() {
// Code to execute repeatedly
#include <Arduino.h> includes Arduino core functions.
void setup() initializes settings, runs once.
void loop() runs continuously.
Comments:
// Single line comment
/* Multi-line
comment */
1.1.2 Data Types
Basic Data Types:
int: Integer numbers
int ledPin = 13; // Pin number for an LED
float: Floating-point numbers
float temperature = 25.6;
double: Double-precision floating-point numbers (same as float in Arduino)
double temperature = 25.62789;
char: Single characters
char mode = 'A';
Modifiers:
unsigned int: Positive integers
unsigned int count = 0;
long: Larger integers
long bigNumber = 1000000L;
1.1.3 Operators
Arithmetic Operators:
int sum = 5 + 3; // 8
int product = 5 * 3; // 15
Relational Operators:
if (sensorValue > 500)
// Code if sensorValue is greater than 500
Logical Operators:
if (temperature > 20.0 && humidity < 50.0)
// Code if both conditions are true
Assignment Operators:
ledState = !ledState; // Toggle the state of ledState
1.2 Control Structures
1.2.1 if-else Statement
Example:
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // Set LED pin as output
}
void loop() {
int sensorValue = analogRead(A0); // Read sensor value
if (sensorValue > 512) {
digitalWrite(LED_BUILTIN, HIGH); // Turn LED on
} else {
digitalWrite(LED_BUILTIN, LOW); // Turn LED off
delay(1000); // Wait for 1 second
1.2.2 switch-case Statement
Example:
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // Set LED pin as output
void loop() {
int sensorValue = analogRead(A0); // Read sensor value
switch (sensorValue / 256) {
case 0:
digitalWrite(LED_BUILTIN, LOW); // Low light level
break;
case 1:
digitalWrite(LED_BUILTIN, HIGH); // High light level
break;
default:
digitalWrite(LED_BUILTIN, LOW); // Default case
delay(1000); // Wait for 1 second
1.2.3 Loops
for Loop:
Example:
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // Set LED pin as output
void loop() {
for (int i = 0; i < 10; i++) {
digitalWrite(LED_BUILTIN, HIGH); // Turn LED on
delay(500); // Wait for 500 milliseconds
digitalWrite(LED_BUILTIN, LOW); // Turn LED off
delay(500); // Wait for 500 milliseconds
}
delay(2000); // Wait for 2 seconds before repeating
while Loop:
Example:
void setup()
pinMode(LED_BUILTIN, OUTPUT); // Set LED pin as output
void loop() {
int count = 0;
while (count < 10) {
digitalWrite(LED_BUILTIN, HIGH); // Turn LED on
delay(500); // Wait for 500 milliseconds
digitalWrite(LED_BUILTIN, LOW); // Turn LED off
delay(500); // Wait for 500 milliseconds
count++;
delay(2000); // Wait for 2 seconds before repeating
do-while Loop:
Example:
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // Set LED pin as output
void loop() {
int count = 0;
do {
digitalWrite(LED_BUILTIN, HIGH); // Turn LED on
delay(500); // Wait for 500 milliseconds
digitalWrite(LED_BUILTIN, LOW); // Turn LED off
delay(500); // Wait for 500 milliseconds
count++;
} while (count < 10);
delay(2000); // Wait for 2 seconds before repeating
1.3 Functions
1.3.1 Declaration
Syntax:
return_type function_name(parameters);
Example: int calculateSum(int a, int b);
1.3.2 Definition
Syntax:
return_type function_name(parameters) {
// Code
return value;
}
Example:
int calculateSum(int a, int b) {
return a + b;
}
1.3.3 Usage
Example:
void setup() {
[Link](9600); // Initialize serial communication
void loop() {
int result = calculateSum(5, 3); // Call function
[Link](result); // Print result to Serial Monitor
delay(1000); // Wait for 1 second
}
int calculateSum(int a, int b) {
return a + b;
1.4 Pointers
1.4.1 Basics
Definition:
A pointer is a variable that stores the address of another variable.
Syntax:
type *pointer_name;
Example:
int value = 10;
int *ptr = &value; // Pointer to an integer
1.4.2 Usage
Dereferencing a Pointer:
Access the value at the address pointed to by the pointer.
Example:
void setup() {
[Link](9600);
int value = 42;
int *ptr = &value;
[Link](*ptr); // Print value (42)
void loop() {
// Nothing to do here
Pointer Arithmetic:
Incrementing a pointer to point to the next memory location.
Example:
void setup() {
[Link](9600);
int arr[] = {10, 20, 30, 40, 50};
int *p = arr;
[Link](*(p + 2)); // Access third element (30)
void loop() {
// Nothing to do here
}
Passing Pointers to Functions:
Syntax:
void function_name(type *param);
Example:
void increment(int *p) {
(*p)++;
void setup() {
[Link](9600);
int num = 10;
increment(&num); // Pass address of num
[Link](num); // Print incremented value (11)
void loop() {
// Nothing to do here
This detailed context integrates the basic concepts of C programming with practical
Arduino examples, illustrating how these concepts apply in an embedded environment.
………………………………………………………………………………………………