To connect and use the LM35 temperature sensor with a microcontroller, enabling accurate temperature measurement across the sensor’s full rated range.

Why Shift the Output?
How to Shift the Output?
Using a Voltage Divider
Vshift = Vcc × (R2 / (R1+R2))
Choosing R1 and R2
Circuit Connections Overview

Measurement Process
Vsensor = (VLM35 OUT) − Vshift
Temperature Calculation
Temperature (°C) = Voltage difference (mV) / 10
Notes
Negative temperature detection
So, by considering the above points, we can implement the task. Below are the solutions to the given task using different microcontrollers
We’re using an STM32 NUCLEO-F103RB board, which runs at a 3.3V logic level.
Circuit Diagram

Project Setup in STM32CubeIDE
SystemClock_Config).ADC_CHANNEL_0 (PA0).ADC_CHANNEL_1 (PA1).HAL_Init() → Initializes HAL and system tick.SystemClock_Config() → Configures system clock (HSI + PLL).MX_GPIO_Init() → Initializes GPIO ports.MX_USART2_UART_Init() → Configures UART2.MX_ADC1_Init() → Configures ADC1 for analog input.MX_ADC2_Init() → Configures ADC2 for analog input.ADC Initialization (MX_ADC1_Init and MX_ADC2_Init):
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}Similarly, for ADC2 also
This configures ADC1 and ADC2 for single-channel operation with right-aligned 12-bit results.
UART2 Initialization (MX_USART2_UART_Init):
static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
}This configures USART2 for communication at a 115200 baud rate with 8 data bits, no parity, and 1 stop bit, supporting both transmit and receive.
Header includes:
#include<string.h>
#include<stdio.h>Private defines and variables:
#define ADC_SAMPLES 100 // Number of samples for averaging
#define ADC_RESOLUTION 4096 // 12-bit ADC resolution
#define REFERENCE_VOLTAGE 3.28f //Measured Reference voltage
uint32_t adcValues[2]; // Buffer for two channels
char uartBuffer[64]; // Buffer for UART messagesMain Loop - Reading and Transmitting temperature
while (1) {
uint32_t sumCh0 = 0, sumCh1 = 0;
uint8_t validSamplesCh0 = 0, validSamplesCh1 = 0;
float voltageDiff = 0;
int16_t tempValue = 0;
int16_t tempDecimalValue = 0;
// Take multiple samples for averaging
for (int i = 0; i < ADC_SAMPLES; i++) {
// Read channel 0
HAL_ADC_Start(&hadc1);
if (HAL_ADC_PollForConversion(&hadc1, 20) == HAL_OK) {
sumCh0 += HAL_ADC_GetValue(&hadc1);
validSamplesCh0++;
}
// Read channel 1
HAL_ADC_Start(&hadc2);
if (HAL_ADC_PollForConversion(&hadc2, 20) == HAL_OK) {
sumCh1 += HAL_ADC_GetValue(&hadc2);
validSamplesCh1++;
}
}
// Calculate average values only if we have valid samples
if (validSamplesCh0 > 0 && validSamplesCh1 > 0) {
adcValues[0] = sumCh0 / validSamplesCh0;
adcValues[1] = sumCh1 / validSamplesCh1;
// Calculate absolute difference once
uint32_t adcDiff =
(adcValues[0] >= adcValues[1]) ? (adcValues[0] - adcValues[1]) : (adcValues[1] - adcValues[0]);
if (adcDiff != 0) {
voltageDiff = (REFERENCE_VOLTAGE * adcDiff) / ADC_RESOLUTION;
}
if (voltageDiff != 0) {
tempValue = voltageDiff * 100;
tempDecimalValue = (int16_t)(voltageDiff * 10000) % 100;
}
// Format message based on which channel is higher
if (adcValues[0] >= adcValues[1]) {
sprintf(uartBuffer, "Temperature: %u.%u Degree Celsius \r\n",
tempValue, tempDecimalValue);
} else {
sprintf(uartBuffer, "Temperature: -%u.%u Degree Celsius \r\n",
tempValue, tempDecimalValue);
}
// Send Temperature value on UART
HAL_UART_Transmit(&huart2, (uint8_t*)uartBuffer,
strlen(uartBuffer), HAL_MAX_DELAY);
} else {
// Handle error case if needed
sprintf(uartBuffer, "ADC read error\r\n");
HAL_UART_Transmit(&huart2, (uint8_t*)uartBuffer,
strlen(uartBuffer), HAL_MAX_DELAY);
}
HAL_Delay(1000);
}Main Loop Workflow
while (1))ADC1) and Channel 1 (ADC2).voltageDiff).tempValue = voltageDiff * 100).voltageDiff * 10000) % 100).25.5°C").-10.2°C").HAL_Delay(1000)) before repeating.The complete STM32CubeIDE project (including .ioc configuration, main.c, and HAL files) is available here:
We are using the ESP32 DevKitC v4 development board and programming it using the Arduino IDE
Let’s do the hardware connection.
LM35 Sensor connection:
Circuit connection

Let’s write code,
const int lm35_pin = 34; // LM35 output
const int offset_pin = 35; // "Ground" offset reading
float offset_voltage = 0;
int temp_adc_val;
float temp_val;
void setup() {
Serial.begin(115200);
}
void loop() {
// Read temperature sensor
temp_adc_val = analogRead(lm35_pin);
// Read offset pin (same logic as original code)
offset_voltage = analogRead(offset_pin) * (3300.0 / 4095.0);
// Convert to millivolts (ESP32 = 3.3V, 12-bit ADC)
// Convert LM35 ADC reading to millivolts
temp_val = temp_adc_val * (3300.0 / 4095.0);
// Subtract offset
temp_val = temp_val - offset_voltage;
// LM35 scale: 10 mV per °C
temp_val = temp_val / 10.0;
Serial.print("Temperature = ");
Serial.print(temp_val);
Serial.println(" Degree Celsius");
delay(1000);
}float temp_val = adcValue * (3300 / 4095);temp_val = temp_val - offset_voltage;temp_val = temp_val / 10;We are using the Arduino UNO development board and programming it using the Arduino IDE.
Let’s do the hardware connection.
LM35 Sensor connection:

Let’s write code
const int lm35_pin = A0; /* LM35 O/P pin */
const int offset_pin = A1; /* LM35 GND pin */
float offset_voltage = 0; /* offset voltage stored in milli-volts */
int temp_adc_val;
float temp_val;
void setup() {
Serial.begin(9600);
}
void loop() {
temp_adc_val = analogRead(lm35_pin);
offset_voltage = analogRead(offset_pin) * (5000 / 1024); /* read offset voltage on A1 pin and convert it to milli-volts */
temp_val = temp_adc_val * (5000 / 1024); /* Convert adc value to equivalent voltage (milli-volts) */
temp_val = temp_val - offset_voltage; /* remove the offset voltage */
temp_val = (temp_val / 10); /* LM35 gives output of 10mv/°C */
Serial.print("Temperature = "); /* Print the temprature*/
Serial.print(temp_val);
Serial.println(" Degree Celsius");
delay(1000);
}
float temp_val = adcValue * (5000 / 1024) : converts ADC_value to voltage (milli volts).temp_val = temp_val - offset_voltage: removes the offset voltage, that is given to the GND pin.temp_val = (temp_val/10) : converts into degree Celsius (1 degree Celsius = 10mV).Hardware Setup


As we can see in the video, the temperature change is successfully detected and printed on the Serial monitor.