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
Note: The current task requires a highly accurate ADC. However, the ESP32’s built-in ADC is non-linear and shows poor accuracy near the voltage edges (close to 0 V and Vref). Due to this limitation, we are not implementing this task using ESP32.
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 messages
Main 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 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.