Skip to content

Contributed by sujingliang, organized by Ai-Thinker

[Ai-WB2 Review] Sampling NTC Thermistor Temperature with ADC and Reporting It over BLE

Using an NTC (negative temperature coefficient) thermistor to measure temperature is a low-cost and widely used approach, especially for scenarios where high precision is not required and the budget is limited.

Features of NTC thermistors

Negative temperature coefficient: resistance decreases as temperature rises (nonlinear relationship).

Low cost: the unit price is typically only a few yuan, far lower than digital temperature sensors (such as the DS18B20).

High sensitivity: resistance changes more significantly in the low-temperature region, making it suitable for detecting small temperature changes.

Nonlinear response: linearization is required via formulas or lookup tables.

Building the circuit with Ai-WB2

  • Principle: the NTC and a fixed resistor form a voltage divider; the ADC reads the divided voltage, converts it to resistance, and then calculates the temperature using a formula.
  • Fixed resistor selection: it is recommended to match the NTC's mid-range resistance within the target temperature range (e.g., if the NTC is 10kΩ at 25°C, choose a 10kΩ fixed resistor) Here a 51K divider resistor was chosen because I mistakenly thought the NTC on hand was 50K; later I found out it was 150K and just went with it. Just note that the lookup table must be calculated for a 150K+50K divider ratio.

Actual circuit:

Software

Based on the applications\ble_slave example, with the following modifications

  1. Add temperature.h to process the ADC-sampled NTC data
Click to expand full code
java
#ifndef __TEMPERATURE_H__
#define __TEMPERATURE_H__

//temperature.h

void

adc_temp_init
(
void
)
;

int

get_temp_value
(
void
)
;

int

get_temperature
(
void
)
;

#endif
Click to expand full code
c
void adc_temp_init(void);//初始化ADC

int get_temp_value(void);//采集ADC数值(0-4095)

int get_temperature(void);//将ADC数值转化为温度
  1. temperature.c implementation
Click to expand full code
java
//

#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <hosal_adc.h>
#include <blog.h>

#define GPIO_ADC_PIN
11

#define ADC_CHANNEL
10

const unsigned
int
 tempTable[][
2
] = {
 {-
30
,
114
},
 {-
29
,
121
},
 {-
28
,
128
},
 {-
27
,
136
},
 {-
26
,
144
},
 {-
25
,
153
},
 {-
24
,
162
},
 {-
23
,
172
},
 {-
22
,
182
},
 {-
21
,
192
},
 {-
20
,
203
},
 {-
19
,
215
},
 {-
18
,
227
},
 {-
17
,
240
},
 {-
16
,
253
},
 {-
15
,
267
},
 {-
14
,
281
},
 {-
13
,
296
},
 {-
12
,
312
},
 {-
11
,
328
},
 {-
10
,
345
},
 {-
9
,
362
},
 {-
8
,
381
},
 {-
7
,
400
},
 {-
6
,
419
},
 {-
5
,
440
},
 {-
4
,
461
},
 {-
3
,
483
},
 {-
2
,
505
},
 {-
1
,
529
},
 {
0
,
553
},
 {
1
,
577
},
 {
2
,
603
},
 {
3
,
629
},
 {
4
,
656
},
 {
5
,
684
},
 {
6
,
713
},
 {
7
,
742
},
 {
8
,
772
},
 {
9
,
803
},
 {
10
,
835
},
 {
11
,
867
},
 {
12
,
900
},
 {
13
,
934
},
 {
14
,
968
},
 {
15
,
1003
},
 {
16
,
1039
},
 {
17
,
1075
},
 {
18
,
1112
},
 {
19
,
1149
},
 {
20
,
1187
},
 {
21
,
1225
},
 {
22
,
1264
},
 {
23
,
1304
},
 {
24
,
1343
},
 {
25
,
1383
},
 {
26
,
1424
},
 {
27
,
1465
},
 {
28
,
1506
},
 {
29
,
1547
},
 {
30
,
1588
},
 {
31
,
1630
},
 {
32
,
1672
},
 {
33
,
1713
},
 {
34
,
1755
},
 {
35
,
1797
},
 {
36
,
1839
},
 {
37
,
1881
},
 {
38
,
1922
},
 {
39
,
1964
},
 {
40
,
2005
},
 {
41
,
2047
},
 {
42
,
2088
},
 {
43
,
2128
},
 {
44
,
2169
},
 {
45
,
2209
},
 {
46
,
2249
},
 {
47
,
2288
},
 {
48
,
2327
},
 {
49
,
2366
},
 {
50
,
2404
},
 {
51
,
2442
},
 {
52
,
2479
},
 {
53
,
2516
},
 {
54
,
2552
},
 {
55
,
2588
},
 {
56
,
2623
},
 {
57
,
2658
},
 {
58
,
2692
},
 {
59
,
2726
},
 {
60
,
2759
},
 {
61
,
2791
},
 {
62
,
2823
},
 {
63
,
2854
},
 {
64
,
2885
},
 {
65
,
2915
},
 {
66
,
2945
},
 {
67
,
2973
},
 {
68
,
3002
},
 {
69
,
3029
},
 {
70
,
3056
},
 {
71
,
3083
},
 {
72
,
3109
},
 {
73
,
3134
},
 {
74
,
3159
},
 {
75
,
3183
},
 {
76
,
3207
},
 {
77
,
3230
},
 {
78
,
3252
},
 {
79
,
3274
},
 {
80
,
3296
},
 {
81
,
3317
},
 {
82
,
3337
},
 {
83
,
3357
},
 {
84
,
3376
},
 {
85
,
3395
},
 {
86
,
3414
},
 {
87
,
3432
},
 {
88
,
3449
},
 {
89
,
3466
},
 {
90
,
3483
},
 {
91
,
3499
},
 {
92
,
3515
},
 {
93
,
3530
},
 {
94
,
3545
},
 {
95
,
3559
},
 {
96
,
3573
},
 {
97
,
3587
},
 {
98
,
3600
},
 {
99
,
3613
},
 {
100
,
3626
},
 {
101
,
3638
},
 {
102
,
3650
},
 {
103
,
3662
},
 {
104
,
3673
},
 {
105
,
3684
},
 {
106
,
3695
},
 {
107
,
3705
},
 {
108
,
3715
},
 {
109
,
3725
},
 {
110
,
3735
},
 {
111
,
3744
},
 {
112
,
3753
},
 {
113
,
3762
},
 {
114
,
3771
},
 {
115
,
3779
},
 {
116
,
3787
},
 {
117
,
3795
},
 {
118
,
3802
},
 {
119
,
3810
},
 {
120
,
3817
},
 {
121
,
3824
},
 {
122
,
3831
},
 {
123
,
3838
},
 {
124
,
3844
},
 {
125
,
3850
},
 {
126
,
3856
},
 {
127
,
3862
},
 {
128
,
3868
},
 {
129
,
3874
},
 {
130
,
3879
},
 {
131
,
3885
},
 {
132
,
3890
},
 {
133
,
3895
},
 {
134
,
3900
},
 {
135
,
3904
},
 {
136
,
3909
},
 {
137
,
3914
},
 {
138
,
3918
},
 {
139
,
3922
},
 {
140
,
3926
},
 {
141
,
3930
},
 {
142
,
3934
},
 {
143
,
3938
},
 {
144
,
3942
},
 {
145
,
3946
},
 {
146
,
3949
},
 {
147
,
3952
},
 {
148
,
3956
},
 {
149
,
3959
},
 {
150
,
3962
},
 {
151
,
3965
},
 {
152
,
3968
},
 {
153
,
3971
},
 {
154
,
3974
},
 {
155
,
3977
},
 {
156
,
3980
},
 {
157
,
3982
},
 {
158
,
3985
},
 {
159
,
3987
},
 {
160
,
3990
},
 {
161
,
3992
},
 {
162
,
3994
},
 {
163
,
3997
},
 {
164
,
3999
},
 {
165
,
4001
},
 {
166
,
4003
},
 {
167
,
4005
},
 {
168
,
4007
},
 {
169
,
4009
},
 {
170
,
4011
},
 {
171
,
4013
},
 {
172
,
4015
},
 {
173
,
4016
},
 {
174
,
4018
},
 {
175
,
4020
},
 {
176
,
4021
},
 {
177
,
4023
},
 {
178
,
4024
},
 {
179
,
4026
},
 {
180
,
4027
},
 {
181
,
4029
},
 {
182
,
4030
},
 {
183
,
4032
},
 {
184
,
4033
},
 {
185
,
4034
},
 {
186
,
4035
},
 {
187
,
4037
},
 {
188
,
4038
},
 {
189
,
4039
},
 {
190
,
4040
},
 {
191
,
4041
},
 {
192
,
4043
},
 {
193
,
4044
},
 {
194
,
4045
},
 {
195
,
4046
},
 {
196
,
4047
},
 {
197
,
4048
},
 {
198
,
4049
},
 {
199
,
4050
},
 {
200
,
4050
},
 {
201
,
4051
},
 {
202
,
4052
},
 {
203
,
4053
},
 {
204
,
4054
},
 {
205
,
4055
},
 {
206
,
4055
},
 {
207
,
4056
},
 {
208
,
4057
},
 {
209
,
4058
},
 {
210
,
4058
},
 {
211
,
4059
},
 {
212
,
4060
},
 {
213
,
4061
},
 {
214
,
4061
},
 {
215
,
4062
},
 {
216
,
4062
},
 {
217
,
4063
},
 {
218
,
4064
},
 {
219
,
4064
},
 {
220
,
4065
},
 {
221
,
4065
},
 {
222
,
4066
},
 {
223
,
4067
},
 {
224
,
4067
},
 {
225
,
4068
},
 {
226
,
4068
},
 {
227
,
4069
},
 {
228
,
4069
},
 {
229
,
4070
},
 {
230
,
4070
},
 {
231
,
4070
},
 {
232
,
4071
},
 {
233
,
4071
},
 {
234
,
4072
},
 {
235
,
4072
},
 {
236
,
4073
},
 {
237
,
4073
},
 {
238
,
4073
},
 {
239
,
4074
},
 {
240
,
4074
},
 };

hosal_adc_dev_t

adc0

=
 {
    .cb = NULL,
    .config = {
        .mode = HOSAL_ADC_ONE_SHOT,
        .pin = GPIO_ADC_PIN,
        .sampling_freq =
340
,
    },
    .dma_chan =
0
,
    .p_arg = NULL,
    .port =
0
,
};

void

adc_temp_init
(
void
)

{
    hosal_adc_init(&adc0);
    hosal_adc_add_channel(&adc0, ADC_CHANNEL);
}

int

get_temp_value
(
void
)

{

int

ret

=
 hosal_adc_value_get(&adc0, ADC_CHANNEL,
100
);
    blog_info(
"ADC = %ld \r\n"
, ret);

return
 ret;

}

int

get_temperature
(
void
)

{

int
 adc_value;
    const unsigned
int

tableSize

=
 sizeof(tempTable) / sizeof(tempTable[
0
]);
    unsigned
int
 i;

    adc_value=get_temp_value();

// 边界检查(注意与采样NTC时方向相反)

if
(adc_value <= tempTable[
0
][
1
]) {

return
 tempTable[
0
][
0
];
// 低于最低温度-30℃

    }

if
(adc_value >= tempTable[tableSize-
1
][
1
]) {

return
 tempTable[tableSize-
1
][
0
];
// 高于最高温度50℃

    }

// 查表(ADC值随温度升高而增大)

for
(i =
0
; i < tableSize-
1
; i++) {

if
(adc_value >= tempTable[i][
1
] && adc_value < tempTable[i+
1
][
1
]) {

// 线性插值计算温度

return
 tempTable[i][
0
] + (
int
)((
float
)(tempTable[i+
1
][
0
] - tempTable[i][
0
]) *
                   (
float
)(adc_value - tempTable[i][
1
]) /
                   (
float
)(tempTable[i+
1
][
1
] - tempTable[i][
1
]));
        }
    }

return

0xFFFF
;
// 错误代码(正常情况不会执行到这里)

}
  1. main.c modification
Click to expand full code
java
void

main
()

{
    bl_sys_init();

    adc_temp_init();
    pwm_led_init();

    puts(
"[OS] proc_main_entry task...\r\n"
);
    xTaskCreate(TaskUart,
"TaskUart"
,
2048
, NULL,
15
, NULL);
    xTaskCreate(TaskTemp,
"TempAdc"
,
1024
, NULL,
15
, NULL);
    xTaskCreate(proc_main_entry, (
char
 *)
"main_entry"
,
1024
, NULL,
15
, NULL);
}

Add adc_temp_init(); //ADC initialization

Create a new RTOS TASK to handle temperature sampling:

xTaskCreate(TaskTemp, "TempAdc", 1024, NULL, 15, NULL);

  1. TaskTemp task
Click to expand full code
java
void

TaskTemp
(
void
 *param)

{

int
 tempval;
    uint8_t data[
5
] = {
0x4e
,
0x4f
,
0x60
,
0x01
,
0x00
};

while
 (
1
)
    {
        tempval=get_temperature();
        data[
4
]=(uint8_t)tempval;
        UUID1_SendNotify(strlen((
char
 *)data), data);
        vTaskDelay(
1000
);
    }

}

The temperature data is collected via get_temperature()

UUID1_SendNotify is called to send the data to the BLE host device (phone) subscribed to UUID1

The data format is

Fixed headerFixed headerIdentifier (indicates temperature follows)UnknownTemperature value
0x4e0x4f0x600x010x00

Running the APP on the phone (third-party software, not Ai-Thinker)

Scan for BLE

After connecting, the reported temperature data can be seen

Released under the MIT License. Build Time 2026-09-11 14:52:23