以前的文章间接的出了好几期关于555定时器测量电容,电感,电阻的方法。
本期我们将制作一款其整合版,利用555定时器分别测量电阻,电感,电容的电路,并且利用ESP32上传至百度云的物联网,利用物可视快速在网页上显示。
这里我们利用ESP32的外部计数器配合定时器准确的采集频率,利用公式计算出实际的电容/电阻/电感的值。不过由于公式计算太过于理想,因此我们测量多组数据,利用Matlab进行函数拟合。
例如电容的测量所得频率和电容值之间的曲线就非常完美,0.9998的R,如此,我们可以将RCL的频率和电阻利用函数拟合一一对应。
可以给大家看看效果,需要注意的是,这里的电容值和电感值由于博主手上并没有电桥,因此无法测量其实际值,只能利用标称来进行拟合,而这种电容,本身既有10%左右的误差,所以实际使用过程前,需利用数字电桥等精确的测量电容和电感的值来对电容和电感的计算函数进行矫正。
原理图如上,具体可以看之前的文章查看其原理。
关于ESP3 2的代码不做过多,需要注意的是这里采用的是MQTT协议进行通讯,并且使用的是百度云的物可视技术。
如果有哪里不明白的朋友可以加QQ群交流~~
#include <math.h>#include <WiFi.h> #include <PubSubClient.h> // WiFi设置 const char* ssid = "***************"; const char* password = "***************"; // MQTT服务器设置 const char* mqtt_server = "*********************"; const int mqtt_port = 1883; const char* mqtt_client_id = "ESP32_1"; const char* mqtt_username = "**************************"; // MQTT服务器需要用户名const char* mqtt_password = "***********************"; // MQTT服务器需要密码 WiFiClient espClient; PubSubClient client(espClient); const int PULSE_PIN = 19; // ESP32的D19引脚 volatile long pulseCount = 0; // 脉冲计数器,volatile关键字确保在多线程环境中正确访问 // 定义按键引脚和中断编号 #define BUTTON_PIN_33 33 #define BUTTON_PIN_32 32 #define BUTTON_PIN_25 25 // 定义中断服务程序(ISR)的函数原型 void IRAM_ATTR onButtonPress33(); void IRAM_ATTR onButtonPress32(); void IRAM_ATTR onButtonPress25(); // 按键状态变量,用于记录按键是否被按下 int flag;void setup_wifi() { delay(10); // 连接到WiFi网络 Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected."); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect(mqtt_client_id, mqtt_username, mqtt_password)) { Serial.println("connected"); // Once connected, subscribe to all topics client.subscribe("Value"); } else { delay(5000); } } }
void setup() { char buffer[50]; Serial.begin(115200); // 初始化串口通信 // 配置按键引脚为输入模式,并启用内部上拉电阻 pinMode(BUTTON_PIN_33, INPUT_PULLUP); pinMode(BUTTON_PIN_32, INPUT_PULLUP); pinMode(BUTTON_PIN_25, INPUT_PULLUP); // 设置中断,下降沿触发 attachInterrupt(digitalPinToInterrupt(BUTTON_PIN_33), onButtonPress33, FALLING); attachInterrupt(digitalPinToInterrupt(BUTTON_PIN_32), onButtonPress32, FALLING); attachInterrupt(digitalPinToInterrupt(BUTTON_PIN_25), onButtonPress25, FALLING); pinMode(PULSE_PIN, INPUT); // 设置D19引脚为输入模式,并启用内部上拉电阻 pinMode(26, OUTPUT); // 设置D26引脚为输出模式 pinMode(27, OUTPUT); // 设置D27引脚为输出模式 setup_wifi(); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); sprintf(buffer, "{"Type":0,"Va":%lf}", 0); // %f是double的格式化占位符 if (client.connected()) { client.publish("Value", buffer); } attachInterrupt(digitalPinToInterrupt(PULSE_PIN), countPulses, CHANGE); // 设置中断,检测电平变化 } float t;void loop() { delay(1000); // 每秒更新一次 String str; char buffer[50]; //Serial.println(pulseCount); // 打印脉冲数 if(flag == 3) { //Serial.println(fC(pulseCount)); // 发布消息到MQTT服务器 // 足够大的缓冲区来存储转换后的字符串 sprintf(buffer, "{"Type":3,"Va":%lf}", fC(pulseCount)); // %f是double的格式化占位符 if (client.connected()) { client.publish("Value", buffer); } flag = 0; } if(flag == 2) { sprintf(buffer, "{"Type":2,"Va":%lf}", fL(pulseCount)); // %f是double的格式化占位符 if(fL(pulseCount)< 3000000) { if (client.connected()) { client.publish("Value", buffer); } } flag = 0; } if(flag == 1) { sprintf(buffer, "{"Type":1,"Va":%lf}", fR(pulseCount)); // %f是double的格式化占位符 if(fR(pulseCount)<30000) { if (client.connected()) { client.publish("Value", buffer); } } flag = 0; } pulseCount = 0; if (!client.connected()) { reconnect(); } client.loop();
} void countPulses() { pulseCount++; // 每次中断被触发时,增加计数器 }
void IRAM_ATTR onButtonPress33() { Serial.println("电容"); flag = 3; ModeSelection(3);} void IRAM_ATTR onButtonPress32() { Serial.println("电感"); flag = 2; ModeSelection(2);} void IRAM_ATTR onButtonPress25() { Serial.println("电阻"); flag = 1; ModeSelection(1);}
void ModeSelection(int m){ switch(m) { case 0: digitalWrite(27, 1); // 设置D27引脚输出高电平 digitalWrite(26, 1); // 设置D26引脚输出低电平 break; case 1: //R测量 digitalWrite(27, 0); // 设置D27引脚输出高电平 digitalWrite(26, 0); // 设置D26引脚输出低电平 break; case 2: //L测量 digitalWrite(27, 0); // 设置D27引脚输出高电平 digitalWrite(26, 1); // 设置D26引脚输出低电平 break; case 3: //C测量 digitalWrite(27, 1); // 设置D27引脚输出高电平 digitalWrite(26, 0); // 设置D26引脚输出低电平 break; }}
// 定义系数a和b,使用置信区间的中值 double aC = 1.313e+09; // 注意:Arduino可能不支持科学记数法初始化,可能需要手动转换为普通小数 double bC = -1.002; // 定义函数f(x) double fC(double x) { return aC * pow(x, bC); } double aL = 3.312e+12; // 注意:Arduino可能不支持科学记数法初始化,可能需要手动转换为普通小数 double bL = -2.04; // 定义函数f(x) double fL(double x) { return aL * pow(x, bL); }
double p1 = 9.795e-08; double p2 = 0.0228; double p3 = -1026; double p4 = 1.982e+07; double q1 = 434.6; // 定义函数f(x) double fR(double x) { // 避免除以零的情况 if (fabs(x + q1) < 1e-6) { // 使用一个非常小的数来避免直接除以零 return NAN; // 返回“非数字”值,表示结果未定义 } // 计算分子和分母 double numerator = p1 * pow(x, 3) + p2 * pow(x, 2) + p3 * x + p4; double denominator = x + q1; // 返回函数值 return numerator / denominator; }