Рассчитать уровень заряда Li-Po батареи в ESP-Wroom-02
Я новичок в микроконтроллерах и схемах. Я вообще не в этой сфере. Но пытаюсь создать домашний проект, к которому ниже прикреплено изображение устройства.
Это устройство с питанием от аккумулятора 18650 Lipo. Здесь Я хочу рассчитать уровень заряда батареи. Проведя небольшое исследование в Google, я обнаружил, что мне нужен делитель напряжения, который, кажется, я уже включил сюда с сопротивлением 220 кОм и 100 кОм
Люди используют разные способы его расчета. Что я нашел в нескольких примерах. Я вообще не могу понять, какую формулу или значения они имеют в виду для расчета.
Если кто-то сможет помочь понять, это будет полезно. Вот как я это кодирую, что я видел где-то в Интернете.
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
//#include "DHTesp.h"
#define DHT_PIN 16
//SSID и пароль вашего Wi-Fi-роутера
const char* ssid = "Asus";
const char* password = "Xmv02488!!**";
ESP8266WebServer server(80); //Сервер на порту 80
/***************************************************************
* SETUP
**************************************************************/
void setup(void){
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password); //Подключаемся к Wi-Fi роутеру
Serial.println("");
// Ожидаем соединения
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
//Если соединение успешно, отобразить IP-адрес на последовательном мониторе
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP()); //IP-адрес, назначенный вашему ESP
server.on("/", handleRoot); //Какую подпрограмму обрабатывать в корневом месте
server.begin(); //Запускаем сервер
Serial.println("HTTP server started");
pinMode(LED_BUILTIN, OUTPUT);
pinMode(A0, INPUT);
}
/***************************************************************
* LOOP
**************************************************************/
void loop(void){
server.handleClient(); //Обработка клиентских запросов
}
/***************************************************************
* This function converts IPAddress struct to a String
**************************************************************/
String IpAddress2String(const IPAddress& ipAddress)
{
return String(ipAddress[0]) + String(".") +\
String(ipAddress[1]) + String(".") +\
String(ipAddress[2]) + String(".") +\
String(ipAddress[3]) ;
}
/***************************************************************
* This rutine is exicuted when you open its IP in browser
**************************************************************/
void handleRoot() {
IPAddress ip_address = WiFi.localIP();
String ip_str = IpAddress2String(ip_address);
int nVoltageRaw = analogRead(A0);
float fVoltage = (float)nVoltageRaw * 0.00486;
float fVoltageMatrix[22][2] = {
{4.2, 100},
{4.15, 95},
{4.11, 90},
{4.08, 85},
{4.02, 80},
{3.98, 75},
{3.95, 70},
{3.91, 65},
{3.87, 60},
{3.85, 55},
{3.84, 50},
{3.82, 45},
{3.80, 40},
{3.79, 35},
{3.77, 30},
{3.75, 25},
{3.73, 20},
{3.71, 15},
{3.69, 10},
{3.61, 5},
{3.27, 0},
{0, 0}
};
int i, perc;
perc = 100;
for(i=20; i>=0; i--) {
if(fVoltageMatrix[i][0] >= fVoltage) {
perc = fVoltageMatrix[i + 1][1];
break;
}
}
server.send(200, "text/plain", "Hello from esp8266!\n\rIP: " + ip_str +
".\n\rTemp: " + "NO" +
", Hum: " + "NO" +
"\n\r" + "NO" +
"\n\r" + "Voltage: " + fVoltage +
"\n\r" + "Charge: " + perc + '%');
}
Это скопированный код. Я не уверен, откуда они взяли эту формулу и как они получили значение 0,00486
Я скопировал этот код из разных источников. Но я надеюсь, что это поможет вам разобраться в вопросе.
Любые предложения будут полезны!
Спасибо! (заранее)
@user3201500, 👍0
Обсуждение4 ответа
Лучший ответ:
Проблема LiPo-аккумуляторов в том, что кривая разряда очень пологая. Вот пример:
Как видите, в течение примерно 95 % срока службы аккумулятора напряжение меняется очень незначительно. Вы не можете просто взять напряжение между «полным» и «пустым» и получить от этого процент. Вместо этого вам придется сопоставить точки на этой кривой с разными процентами.
Самый простой способ — просто иметь набор интересующих вас процентов — каждые 5%, как в приведенном выше коде. Затем вы говорите: «Если напряжение выше этого значения, то оно составляет 100%. Если оно выше следующего значения вниз, это 95%. Если оно выше следующего значения вниз, то это 90%». и т. д.
Массив fVoltageMatrix
содержит это сопоставление. Для 100% напряжения должно быть 4,2 В или выше. Для 95% оно должно быть 4,15 В или выше. И так далее.
Если мы возьмем эти значения напряжения из массива и нанесем их на график, они будут выглядеть так:
Как видите, это похоже. Достаточно ровный, с резким обвалом.
Код, получающий процентный заряд, просто считывает значение из АЦП и преобразует его в напряжение, умножая его на 0,004861, а затем последовательно проходит по элементу массива. В первый раз, когда он находит тот, который не проходит тест «Порог этого процента меньше напряжения», он принимает предыдущий в списке в качестве процента. Лично я считаю, что это обратный и плохой способ сделать это. Вместо этого он должен быть первым, прошедшим тест «Напряжение больше или равно этому процентному порогу», который следует пройти. Я бы переписал цикл так:
perc = 0;
for(i=0; fVoltageMatrix[i][0] > 0; i++) {
if(fVoltage >= fVoltageMatrix[i][0]) {
perc = fVoltageMatrix[i][1];
break;
}
}
1 Число 0,00486 — это количество вольт, подаваемых на вход, чтобы выдать 1 от АЦП. Кстати, если я использую правильные цифры, то по моим расчетам оно должно быть 0,003125. 1 * (1/1024) дает 0,000976563 (это максимальный диапазон АЦП 1 В, умноженный на один бит - 1024-й, поскольку на ESP8266 разрешение составляет 10 бит). Умножьте это на соотношение резисторов ((R1 + R2)/R2), которое равно 3,2, и это даст 0,003125. Чтобы сжать его, вы получите: x = (R1 + R2) / R2 * 1/1024
= 320000 / 100000 * 0.000976563
= 0.003125
< /п>
Однако мои цифры могут быть неверными...
Спасибо большое за такое подробное объяснение. Это помогло многое понять. Вот если я использую это float fVoltage = (float)nVoltageRaw * 0.0041015625;
и получаю значение напряжения 4,2, что потрясающе. Я добавил резистор сопротивлением 100 кОм к A0 к плюсу моей батареи. Теперь это имеет большой смысл! :), @user3201500
Одна из проблем, с которой я столкнулся при работе с ESP8266, — это плохие результаты работы аналого-цифрового преобразователя. Есть ряд препятствий, которые вам нужно пройти, чтобы планеты выровнялись и получили последовательные показания. См. раздел «Флуктуирующий АЦП со стабилизированным источником» https://github.com/esp8266/Arduino/issues/2070
Другая проблема, с которой я столкнулся, связана с предположением, что диапазон работы АЦП составляет 0–1,0 В, а это, конечно, не так.
По моим расчетам, оно должно быть 0,003125. 1 * (1/1024) дает 0,000976563. (это максимальный диапазон АЦП 1 В
Вышеупомянутое показывает, насколько точными могут быть наши расчеты, но, к сожалению, шестизначная точность теряется, если предполагаемый верхний предел в 1 В имеет отклонения в 5 %. К сожалению, в миниатюрной спецификации не указаны технические характеристики аналого-цифрового преобразователя.
Да, аналого-цифровой преобразователь ESP8266 можно калибровать, а измерения проводить в контролируемых условиях. Но прежде чем доверять своим результатам, убедитесь, что вы выполнили необходимую работу и проверили результаты при всех условиях эксплуатации.
Еще одна проблема, с которой вы можете столкнуться, заключается в том, что многие аналого-цифровые преобразователи используют VCC в качестве источника опорного напряжения. Это означает, что по мере того, как ваше входное напряжение падает (то, что вы пытаетесь измерить), опорное напряжение также падает, а это означает, что записываемые вами значения не будут меняться или, по крайней мере, не будут давать точных эталонных значений.
Ниже вы можете найти цифровой плотномер, который я сделал для пива, и созданное мной программное обеспечение для отслеживания температуры и заряда батареи.
1 . Эта часть процесса — это процесс, который я проделал, чтобы уравнять напряжение, поступающее на делитель напряжения, с напряжением батареи.
float fVoltage = (float)nVoltageRaw * 1,23;)
Вы можете рассчитать и найти это значение (1,23) в соответствии с вашим делителем напряжения.
Но по какой-то причине состояние батареи немного колеблется
2 . Я использовал 2 транзистора BC238 для датчиков и делителя напряжения, чтобы ESP не потреблял энергию, когда не считывает. Напряжение проходит только при срабатывании транзисторов.
3 . float pot = map(a.acceleration.x, yogunlukreset0, yogunlukreset1, 997, 1100 );
Эта деталь использовалась для калибровки устройства.
#include <ESP32Firebase.h>
#include "I2Cdev.h"
//#include "MPU6050.h"
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include "Adafruit_MCP9808.h"
#define _SSID ""
#define _PASSWORD ""
#define REFERENCE_URL ""
Firebase firebase(REFERENCE_URL);
Adafruit_MCP9808 tempsensor = Adafruit_MCP9808();
Adafruit_MPU6050 mpu;
sensors_event_t a, g, temp;
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 3600 /* Time ESP32 will go to sleep (in seconds) */
#define bc1 33
#define bc2 16
RTC_DATA_ATTR int bootCount = 0;
const int potPin = 34;
#include <Preferences.h>
Preferences pref;
namespace{
float yogunlukreset0;
float yogunlukreset1;
}
void wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
}
void setup() {
Serial.begin(115200);
delay(100);
pinMode(bc1, OUTPUT);
pinMode(bc2, OUTPUT);
delay(100);
digitalWrite(bc1, HIGH);
digitalWrite(bc2, HIGH);
delay(100);
Wire.begin();
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(1000);
Serial.println();
Serial.println();
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.print("http://");
Serial.print(WiFi.localIP());
Serial.println("/");
if (!tempsensor.begin(0x18)) {
Serial.println("Couldn't find MCP9808! Check your connections and verify the address is correct.");
while (1);
}
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1) {
delay(10);
}
}
++bootCount;
Serial.flush();
pref.begin("Relay-State", false);
yogunlukreset0 = pref.getFloat("namespace", false);
yogunlukreset1 = pref.getFloat("namespa", false);
}
void loop() {
mpu.setCycleRate(MPU6050_CYCLE_20_HZ);
mpu.getEvent(&a, &g, &temp);
tempsensor.wake();
pref.putFloat("namespace", yogunlukreset0);
pref.putFloat("namespa", yogunlukreset1);
float c = tempsensor.readTempC();
String reset2 = firebase.getString("Data/RT2");
if (reset2 == "1"){
String reset0 = firebase.getString("Data/RT0");
String reset1 = firebase.getString("Data/RT1");
String reset3 = firebase.getString("Data/RT3");
if (reset0=="1") {
yogunlukreset0 = (a.acceleration.x);
}
if (reset1=="1") {
yogunlukreset1 = (a.acceleration.x);
}
if (reset3 == "1"){
pref.clear();
bootCount = 0;
}
}
if (reset2 == "0"){
float pot = map(a.acceleration.x, yogunlukreset0, yogunlukreset1, 997, 1100 );
int nVoltageRaw = analogRead(potPin);
float fVoltage = (float)nVoltageRaw * 1.23;
float fVoltageMatrix[22][2] = {
{4150, 100},
{4130, 95},
{4110, 90},
{4080, 85},
{4040, 80},
{4010, 75},
{3970, 70},
{3930, 65},
{3890, 60},
{3850, 55},
{3810, 50},
{3760, 45},
{3710, 40},
{3660, 35},
{3610, 30},
{3550, 25},
{3490, 20},
{3430, 15},
{3370, 10},
{3320, 5},
{3270, 0},
{0, 0}
};
int i, perc;
perc = 100;
for(i=20; i>=0; i--) {
if(fVoltageMatrix[i][0] >= fVoltage) {
perc = fVoltageMatrix[i + 1][1];
break;
}
}
firebase.setFloat("Data/YG", pot);
firebase.setFloat("Data/TM", c);
firebase.setFloat("Data/SYC", bootCount);
firebase.setFloat("Data/VOLT", fVoltage);
firebase.setFloat("Data/ORAN", perc);
digitalWrite(bc1, LOW);
digitalWrite(bc2, LOW);
wakeup_reason();
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
esp_deep_sleep_start();
delay(50);
}
}
- Отображение уровня заряда батареи Lipo с использованием nodemcu
- Можно ли напрямую питать DHT11+ESP8266 от зарядного устройства Adafruit USB LiPo?
- Как читать и записывать EEPROM в ESP8266
- Как сделать выводы Tx и Rx на ESP-8266-01 в выводах GPIO?
- Как навсегда изменить скорость передачи данных ESP8266 (12e)?
- Как заставить 5-вольтовое реле работать с NodeMCU
- Как исправить: Invalid conversion from 'const char*' to 'char*' [-fpermissive]
- ESP8266 не подключается к Wi-Fi
Самая большая проблема в Интернете — это количество людей, которые думают, что знают, что делают, но на самом деле понятия не имеют, но все же чувствуют необходимость публиковать обучающие материалы о том, о чем они ничего не знают… вероятность «0,00486» была либо результат "проб и ошибок", либо случай "китайского шепота".. код копировал и копировал и еще раз копировал, и где-то по ходу опечатка, или две, или три... и вам конец вверх с *посудомойкой Purple Monkey* (отсылка к Симпсонам), @Jaromanda X