Mô phỏng thực tế
Thông số cảm biến
Đây là cảm biến nhiệt analog.
Nhiệt độ tính toán
--Luồng Xử Lý Chi Tiết
Kiểm tra giới hạn cực đại/cực tiểu (Clamp)
Sử dụng tham số margin để bảo vệ đầu vào. Nếu ADC nằm quá xa dải dữ liệu thực tế (thí dụ ADC=0 hoặc ADC=5000), thuật toán trả về ngay giá trị nhiệt độ thấp nhất/cao nhất để tránh lỗi tính toán hoặc treo hệ thống.
Xử lý ngoại suy (Extrapolation)
Trong thực tế, linh kiện có thể bị sai số nhỏ (ví dụ ADC thấp hơn 55 một chút). Thuật toán không cắt bỏ mà dùng 2 điểm đầu (hoặc 2 điểm cuối) để tính một đường thẳng kéo dài, giúp giá trị nhiệt độ mượt mà hơn ở các vùng cận biên.
Tìm kiếm điểm kẹp (Binary Search)
Thay vì so sánh từng phần tử, thuật toán nhảy vào giữa mảng và thu hẹp dần vùng chứa ADC. Điều này cực kỳ quan trọng đối với vi điều khiển (như ESP32) để giảm thời gian chiếm dụng CPU. Kết quả trả về là 2 chỉ số i1 và i2 bao vây giá trị ADC.
Nội suy tuyến tính (Linear Interpolation)
Bảng dữ liệu chỉ có các điểm nguyên (°C). Bước cuối cùng tính toán tỷ lệ chính xác của ADC giữa 2 điểm kẹp để tìm ra giá trị nhiệt độ lẻ (ví dụ 25.43°C), tăng độ chính xác lên hàng trăm lần so với việc tra bảng đơn thuần.
Mã nguồn Python
# ===== ADC TABLE (Mỗi loại cảm biến khác nhau sẽ có bảng dữ liệu riêng) ===== ADC_DATA = { "MFP_7_103F3950AF": { "data": [ 55, 59, 64, 70, 76, 82, 88, 94, 106, 118, 130, 142, 154, 166, 180, 195.6, 211.1, 226.6, 242.2, 257.8, 273.3, 290.9, 309.4, 330, 350.29, 370.79, 395, 418, 441, 472, 506, 533, 557, 580, 606.54, 637, 682.15, 758, 813.61, 846.18, 870, 907.02, 931, 968.01, 997, 1010, 1044, 1071, 1100, 1135, 1165, 1195, 1235, 1275, 1313, 1365.11, 1417.72, 1462, 1518.76, 1565.51, 1623.51, 1670.57, 1721.02, 1776, 1830, 1885, 1938.42, 1981, 2028, 2060, 2104, 2140, 2183, 2229, 2260, 2290, 2320.93, 2350.92, 2381, 2410, 2447.08, 2482.23, 2524.07, 2561.47, 2602.57, 2642.47, 2683.56, 2725.95, 2766.1, 2803.85, 2844.91, 2878.75, 2916.59, 2947.67, 2967.67, 3006.96, 3027.71, 3059.71, 3094.1, 3133.15, 3156.8, 3190.08, 3212.21, 3235.4, 3262.43, 3288.91, 3312.9, 3341.81, 3363.02, 3387.59, 3411, 3435, 3459.63, 3483.8, 3504.27, 3524.87, 3544.34, 3564.87, 3584.97, 3608.87, 3633.77, 3653.45, 3675.84, 3698.62, 3720.27, 3740.61, 3761, 3781.43, 3802, 3822.87, 3844, 3865, 3877.39, 3890, 3902.99, 3915.59, 3928.83, 3941.27, 3953.23, 3966, 3978.87, 3991, 4003, 4015, 4027, 4040, ], "min": -40, "max": 105, } } def linear(x, x1, y1, x2, y2): if x2 == x1: return y1 return y1 + (x - x1) * (y2 - y1) / (x2 - x1) def find_bracket(x_data, target): left, right = 0, len(x_data) - 1 if target == x_data[left]: return left, left if target == x_data[right]: return right, right while left <= right: mid = (left + right) // 2 if x_data[mid] == target: return mid, mid elif x_data[mid] < target: left = mid + 1 else: right = mid - 1 return right, left def cal_temperature(adc_value, sensor_type, margin=100): sensor = ADC_DATA[sensor_type] x_data, t_min, t_max = sensor["data"], sensor["min"], sensor["max"] # 1. Clamp if adc_value < x_data[0] - margin: return float(t_min) if adc_value > x_data[-1] + margin: return float(t_max) # 2. Ngoại suy biên if adc_value < x_data[0]: return round(linear(adc_value, x_data[0], t_min, x_data[1], t_min + 1), 2) if adc_value > x_data[-1]: return round(linear(adc_value, x_data[-2], t_max - 1, x_data[-1], t_max), 2) # 3. Tra bảng & Nội suy i1, i2 = find_bracket(x_data, adc_value) if i1 == i2: return float(t_min + i1) return round(linear(adc_value, x_data[i1], t_min + i1, x_data[i2], t_min + i2), 2) # ===== TEST ===== if __name__ == "__main__": tests = [30, 55, 57, 1000, 4040, 4100, 5000] for v in tests: t = cal_temperature(v, "MFP_7_103F3950AF") print(f"ADC={v} => T={t}°C")
Mã nguồn C++ (ESP32 - Full Implementation)
#include <Arduino.h> // Bảng dữ liệu ADC của cảm biến MFP_7_103F3950AF const float ADC_TABLE[] = { 55, 59, 64, 70, 76, 82, 88, 94, 106, 118, 130, 142, 154, 166, 180, 195.6, 211.1, 226.6, 242.2, 257.8, 273.3, 290.9, 309.4, 330, 350.29, 370.79, 395, 418, 441, 472, 506, 533, 557, 580, 606.54, 637, 682.15, 758, 813.61, 846.18, 870, 907.02, 931, 968.01, 997, 1010, 1044, 1071, 1100, 1135, 1165, 1195, 1235, 1275, 1313, 1365.11, 1417.72, 1462, 1518.76, 1565.51, 1623.51, 1670.57, 1721.02, 1776, 1830, 1885, 1938.42, 1981, 2028, 2060, 2104, 2140, 2183, 2229, 2260, 2290, 2320.93, 2350.92, 2381, 2410, 2447.08, 2482.23, 2524.07, 2561.47, 2602.57, 2642.47, 2683.56, 2725.95, 2766.1, 2803.85, 2844.91, 2878.75, 2916.59, 2947.67, 2967.67, 3006.96, 3027.71, 3059.71, 3094.1, 3133.15, 3156.8, 3190.08, 3212.21, 3235.4, 3262.43, 3288.91, 3312.9, 3341.81, 3363.02, 3387.59, 3411, 3435, 3459.63, 3483.8, 3504.27, 3524.87, 3544.34, 3564.87, 3584.97, 3608.87, 3633.77, 3653.45, 3675.84, 3698.62, 3720.27, 3740.61, 3761, 3781.43, 3802, 3822.87, 3844, 3865, 3877.39, 3890, 3902.99, 3915.59, 3928.83, 3941.27, 3953.23, 3966, 3978.87, 3991, 4003, 4015, 4027, 4040 }; const int TABLE_SIZE = sizeof(ADC_TABLE) / sizeof(ADC_TABLE[0]); const int T_MIN = -40; float linear_interp(float x, float x1, float y1, float x2, float y2) { if (abs(x2 - x1) < 0.001) return y1; return y1 + (x - x1) * (y2 - y1) / (x2 - x1); } float calculate_temperature(float adc_val, int margin = 100) { // 1. Clamp if (adc_val < ADC_TABLE[0] - margin) return (float)T_MIN; if (adc_val > ADC_TABLE[TABLE_SIZE - 1] + margin) return (float)(T_MIN + TABLE_SIZE - 1); // 2. Ngoại suy biên if (adc_val < ADC_TABLE[0]) return linear_interp(adc_val, ADC_TABLE[0], T_MIN, ADC_TABLE[1], T_MIN + 1); if (adc_val > ADC_TABLE[TABLE_SIZE - 1]) return linear_interp(adc_val, ADC_TABLE[TABLE_SIZE-2], T_MIN + TABLE_SIZE - 2, ADC_TABLE[TABLE_SIZE-1], T_MIN + TABLE_SIZE - 1); // 3. Binary Search int low = 0, high = TABLE_SIZE - 1; while (low <= high) { int mid = low + (high - low) / 2; if (abs(ADC_TABLE[mid] - adc_val) < 0.001) return (float)(T_MIN + mid); if (ADC_TABLE[mid] < adc_val) low = mid + 1; else high = mid - 1; } // 4. Nội suy (high và low là 2 điểm kẹp) return linear_interp(adc_val, ADC_TABLE[high], T_MIN + high, ADC_TABLE[low], T_MIN + low); } void setup() { Serial.begin(115200); float test_adc = 1000.0; Serial.print("ADC: "); Serial.print(test_adc); Serial.print(" => Temp: "); Serial.println(calculate_temperature(test_adc)); } void loop() {}