How to read internal temperature sensor on the photon?

It would be nice if the firmware had an API call to easily read the temperature from the internal temperature sensor.

Short of that, does anybody have code they’d be willing to share how to do that?

The usage of the STM32 internal temperature sensor is not recommended. It is quite inaccurate and should be used only to detect temperature changes and not to take absolute measurements.

This limitation is mainly linked to process variations that usually happen in the fab when the chips are manufactured.

The information is on page 236 of the ST32F205_Ref_Manual pdf.

As @embedded said above, the ref manual specifically says that values of two different chips may differ by up to 45 C due to process differences, so it is not useful for making temperature measurements, just changes in temperature.

I understand that. The plan is only to use it to figure out whether the device overheats (customer put it into an enclosed space with no ventilation…). So we don’t need much precision, and the plan is to track the delta from the value at boot (assuming that is room temperature).

Photon does not provide any API call that could be used to read the temperature by using the internal sensor.

However you can always try to modify the source code by yourself ! :wink:

Temperature sensing is covered by the ADC peripheral driver you can find in the bootloader source code.

Have a look here: .\firmware\bootloader\src\photon\stdperiphdriver\stm32f2xx_adc.c

2 Likes

Hello,
I wrote the following code for reading the Photon’s internal temperature sensor

/************************************************************************************************/
const uint16_t V25 = 943;    //(0.76*4096)/3.3
const float Avg_Slope = 2.5; //From electrical characteristics 

void readT()
{
    readTEMP();
}

void readT2()
{
    Serial.print("AnalogVal5: "); Serial.println(analogRead(A5));
}


Timer readMCUTemp(1, &readT);    //7307 works fine, works best with prime numbers, 19 too
Timer readMCUTemp2(10, &readT2);

void setup() {
    pinMode(A5, INPUT);
    
    /*Init */
    ADC_InitTypeDef ADC_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    
    ADC_InitStructure.ADC_Resolution = 12;
    //We will convert single channel only
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    //we will convert one time
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    //select no external triggering
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;

    //right 12-bit data alignment in ADC data register
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    //single channel conversion
    ADC_InitStructure.ADC_NbrOfConversion = 1;
    //load structure values to control and status registers
    ADC_Init(ADC1, &ADC_InitStructure);
    

    //According to the reference manual
    ADC_TempSensorVrefintCmd(ENABLE);  //Enable the temperature sensor and Vrefint channel
    ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_480Cycles); //MIN SAMPLETIME IS 10us

    readMCUTemp.start();
    readMCUTemp2.start();
 }


void readTEMP(){
    ADC_Cmd(ADC1, ENABLE); //power on the ADC
    ADC_SoftwareStartConv(ADC1); //start the conversion, SWSTART

    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
    
    //read ADC value
    uint16_t AD_value=ADC_GetConversionValue(ADC1);
    
    //clear EOC flag
    ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
    
    //calculate temperature from the formula in the reference manual
    uint16_t tempC = (uint16_t)((AD_value - V25)/Avg_Slope) + 25;
    Serial.print("Temperature--: "); Serial.println(tempC);
    
    ADC_Cmd(ADC1, DISABLE); //power on the ADC
}

void loop() {
    delay(2000);
}

Case 1
Comment out the analogRead(A5) line in readT2() and the code works as expected. It reads the internal temperature sensor values. With the analogRead(A5) uncommented. The internal temperature sensor values get corrupted.

I have a situation in which the 3 ADC’s are being read every 10 ms. I also need to read the internal temperature sensor in the same callback. My values are getting corrupted if I try to read in the same callback. How do I solve this? @mdma

Not addressing your actual problem, but for the records:
For analogRead() you should not set pinMode(). analogRead() sets a different input mode implicitly and if you had set another one before hand, that’ll be reactivated after the action. So if you only want to analogRead() from that pin, just save the extra time on each read with not demanding the re-init.
Also the “addressOf” operator (&) for Timer callbacks is not required.

You are controlling the ADC initialization structure yourself for the on-chip temperature readings, but the Particle firmware is also going to set the ADC init settings in its own way. I think you have a conflict of settings.

Why don’t you just take control of the A5 ADC as well and not use analogRead?

Some things are unclear to me to implement that.

Consider analogRead(A5);
From pinmap_hal.h, line 107,
A5 is 15.

In spark_wiring_gpio-cpp,
analogRead() -> HAL_Validate_Pin_Function()

In gpio_hal.c, line64
if (pinFunction==PF_ADC && PIN_MAP[pin].adc_channel!=ADC_CHANNEL_NONE)

From pinmap_hal.c, line 61
Here PIN_MAP[15].adc_channel is ADC_Channel_7

So A5 is reading from this channel. From datasheet, the internal temperature sensor is on ADC_Channel_16.

My questions:

  1. From the Photon pinout diagram, there are 8 ADC’s(ADC0, ADC4, ADC7, ADC6, ADC5, ADC12, ADC13, ADC15) mapped to pins (WKP, DAC, A5, A4, A3, A2, A1, A0) respectively. From the STM32F2xx datasheet, there are 16 ADC channels for each ADC(ADCx_IN[15:0]).

However, in the firmware peripheral declarations(stm32f2xx.h) I can see only 3 ADC’s defined - ADC1, ADC2, ADC3. Out of those 3, only 2 are initialized in HAL_ADC_Read(). How are ADC4, ADC7, ADC6 etc mapping to ADC1, ADC2, ADC3 peripheral address?

  1. From the data sheet, the internal temperature is on ADC1_IN16 which means it’s ADC1 so it makes sense to turn on ADC1. But in adc_hal.c lines 105 and 107, why are both ADC1 and ADC2 being configured? How to know if A5 is on ADC1 or ADC2?

  2. Why enable and disable ADC before and after reading? In my code if don’t do that, then the code gets stuck in the while(ADC_GetStatusFlag()) condition if there are is a second ADC enabled.

  3. Why is there an averaging algorithm implemented in HAL_ADC_Read()? I read that this is due to low impedence on the analog pins but I didn’t understand it.

  4. What is the ADC3 in peripheral declaration for?

Thank you