One of the first things I built was a temperature monitoring system. I had a temperature sensor and a microcontroller. I assumed connecting them would be straightforward. It was — once I understood how they communicate.
Sensors and microcontrollers talk to each other through communication protocols. Understanding these protocols is fundamental to embedded systems work.
Analog sensors: the simple ones
Some sensors output a voltage that changes with what they are measuring. A simple temperature sensor might output 0 volts at 0°C and 3.3 volts at 100°C. The microcontroller reads this voltage with its Analog-to-Digital Converter (ADC) and converts it to a number.
// Read the ADC (12-bit, so values from 0 to 4095)
uint16_t raw = ADC_Read(ADC_CHANNEL_0);
// Convert to voltage (assuming 3.3V reference)
float voltage = (raw / 4095.0f) * 3.3f;
// Convert to temperature (for our example sensor)
float temperature_celsius = voltage * 100.0f;Analog sensors are simple but have a problem: they are sensitive to electrical noise. Long wires pick up interference and your readings become inaccurate. For anything more than a few centimeters, digital sensors are better.
Digital sensors: more accurate, more complex
Digital sensors do the analog-to-digital conversion themselves and send the result as digital data. They use communication protocols — standardized ways of sending bits between chips.
The three protocols you will use most often are I2C, SPI, and UART.
I2C: two wires, many devices
I2C uses just two wires: SDA (data) and SCL (clock). The clever part is that many sensors can share the same two wires. Each sensor has a unique address, so the microcontroller can talk to each one individually.
// Read temperature from a sensor at I2C address 0x48
uint8_t data[2];
HAL_I2C_Master_Receive(&hi2c1, 0x48 << 1, data, 2, 100);
// The sensor sends 2 bytes. Combine them and convert.
int16_t raw_temp = (data[0] << 8) | data[1];
float temperature = raw_temp * 0.0625f;I2C is the most common protocol for sensors. Temperature sensors, humidity sensors, accelerometers, barometric pressure sensors — most of them use I2C.
SPI: faster, more wires
SPI uses four wires: MOSI (data out), MISO (data in), SCK (clock), and CS (chip select). It is faster than I2C but uses more pins. Each device needs its own CS line.
SPI is used for things that need speed: SD cards, displays, high-speed ADCs.
UART: the simplest serial protocol
UART uses two wires: TX (transmit) and RX (receive). It is point-to-point — one device to one device. GPS modules, Bluetooth modules, and WiFi modules typically use UART.
Choosing the right protocol
For most sensors on a PCB: use I2C. It is simple, uses few pins, and supports multiple devices.
For high-speed data (displays, SD cards): use SPI.
For modules with their own processor (GPS, Bluetooth): use UART.
A practical tip
When a sensor is not responding, the first thing I check is the pull-up resistors. I2C requires pull-up resistors on both SDA and SCL lines. Without them, the bus does not work. Many development boards include them, but if you are designing your own PCB, do not forget them.




