wing 发表于 2018-4-29 23:01:34

OCROBOT ALPHA ESP8285 + LI POWER 电池电量监视器


之前那个开箱帖做了很多的程序测试,看上比较凌乱。
这次单独发一帖整理一个使用 ESP8285 + ADS1015 + 锂电模块 的应用实例程序:电池电量监视器



除了上面说那套OC开发板之外还需要一个台PC电脑,chrome浏览器65版,一个和PC联网的带WIFI功能路由器。
8285的主程序代码需要自行修改填上WIFI SSID和密码,8285的IP是自动获取的,要访问到监视器页面就需要知道IP地址,我是在路由查出来的。
监视器页面有一个gogogo的按钮,点击后就会开始记录电池电量变化,每隔10分钟刷新一次,同时绘制柱状图表
从满电开始到完全放电可能会超过10小时,如果电脑进入休眠,定时记录也会停止,所以最好把休眠禁止掉

这里是主程序代码
//8285链接wifi路由 IP是动态获取的 至于怎么获得这个IP自己想办法 访问 http://x.x.x.x/read_adc_chart.htm 可见网页
//页面使用SPIFFS文件保存,页面定时异步更新当前所有adc数值,并且显示电量图表


#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h>      //文件系统库SPIFFS

#include <Wire.h>               //iic接口库
#include <Adafruit_ADS1015.h>   //adc库,居然是出自Ada的

const char *ssid = "你的WIFI";         //wifi ssid
const char *password = "你的WIFI密码";   //wifi 密码

Adafruit_ADS1015 ads;    //定义adc变量

ESP8266WebServer server ( 80 );   //定义web服务

void handleNotFound() {//404页面
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";

for ( uint8_t i = 0; i < server.args(); i++ ) {
    message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
}

server.send ( 404, "text/plain", message );// 返回信息给浏览器(状态码,Content-type, 内容)
}


void ra_page() {//读取adc数值页面
if(SPIFFS.exists("/read_adc_chart.htm")){//如果文件存在
    File file = SPIFFS.open("/read_adc_chart.htm", "r");            //打开文件
    size_t sent = server.streamFile(file, "text/html");//发送内容
    file.close();          //关闭文件
    return;
}
else
{
    handleNotFound(); //404
}
}

void adc_values() {//返回adc值
String p= "";
int adc;
unsigned long systime;

systime=millis();       //系统时间
p= String(systime);

adc = ads.readADC_SingleEnded(0);   //获取各路adc的数值
p=p+","+String(adc);
adc = ads.readADC_SingleEnded(1);
p=p+","+String(adc);
adc = ads.readADC_SingleEnded(2);
p=p+","+String(adc);
adc = ads.readADC_SingleEnded(3);
p=p+","+String(adc);

server.send(200, "text/plain", p);//抛出返回信息如果整个过程都没出错,应该返回这样的信息   系统运行毫秒数,0路电压,1路电压,2路电压,3路电压所有数字之间均以逗号分隔
}


void setup() {
WiFi.begin ( ssid, password );//wifi链接
delay(3500);

SPIFFS.begin();//这个文件系统实例好像在头文件里已经定义好了,无需声明直接开始工作吧

//web服务请求响应对象
server.on ( "/inline", []() {server.send ( 200, "text/plain", "this works as well" );} ); //这句意义重大:当主页面文件无法顺利上存时,至少能通过inline显示一个“好”页面
server.on ( "/read_adc_chart.htm", ra_page );
server.on ( "/adc", adc_values );
server.onNotFound ( handleNotFound );
server.begin();    //启动web服务

ads.begin();//adc开始工作吧
}

void loop() {
server.handleClient();
}





由于这个程序是在Arduino 1.6.12 + Esp866 2.3.0版本下写的,上存程序和文件都需要手动把GPIO 0接地
如果想更加了解这个问题 请看这里 http://www.geek-workshop.com/thread-37411-1-1.html


接着是页面代码,页面文件的位置在主程序目录的data文件夹下


<html>
    <head>
      <title>read adc values show chart</title>
      <!--ESP8285 + ads1015-->
      
         <style>
             canvas {
               cursor: pointer;
               border: 1px solid black;
             }
         </style>      
      
      <script type="text/javascript">
      var ar_time = new Array();
      var ar_volt = new Array();   
            
      function read_adc()//get adc values
      {
            var url;
            url ="/adc";

            var xhr = new XMLHttpRequest();
            xhr.open("GET", url, true);
            xhr.onreadystatechange = function(e){
            if (xhr.readyState == 4) {
            var rtn = xhr.responseText;
            document.getElementById("textarea").value+= rtn+"\n";   
               
            var ar_str = rtn.split(",");   
            ar_time.push(ar_str);   
            ar_volt.push(ar_str);   
            }
            };   

            xhr.send();
            draw_chart();
      }
            
      function draw_chart() {

      var canvas;
      var context;

      canvas = document.getElementById("canvas");
      context = canvas.getContext("2d");      

      context.clearRect(0, 0, canvas.width, canvas.height);
            
      context.beginPath();         
      context.lineWidth = 25;
      context.strokeStyle = "#cd2828";   
            
      var i,v;
      for (i = 0; i < ar_volt.length; i++) {
          context.moveTo(65+i*25, 400);   
          v=ar_volt;
          context.lineTo(65+i*25, 1400-v);
            
          context.save();
          context.translate(65+i*25,410);
          context.rotate(Math.PI / 2);         
         
          var n,h;
          n=Math.floor(ar_time / (1000*60) );
          h=Math.floor(n / 60);
          n= n % 60;   
          context.fillText(h+" : "+n,0,0);
         
          var m=ar_volt*0.003;   
          context.fillText(m.toFixed(3)+'v',0,13);
            
          context.restore();
      }      
      context.stroke();   
      }
               

      function button_gogogo() //btn_go make anything start once
      {
            document.getElementById("btn_go").disabled = true;
            read_adc();
            self.setInterval('read_adc()',600000);
      }            
      </script>
      
    </head>
    <body>
      <textarea id="textarea" rows=5 cols=100></textarea>
      <input id="btn_go" type="button" value="gogogo" onclick="button_gogogo()" />   
      <div>
      <canvas id="canvas" width="1300" height="500"></canvas>
      </div>
    </body>
</html>   


页面没写什么注释,因为整个文件会原原本本地上存到8285,就算注释也需要占用8285宝贵的存储空间,而且读取文件的速度也不怎么快。
其实注释也没什么用的,无论我怎么写也无法说清楚这个页面为什么只能在chrome下正常工作,而Internet Explorer则不行。
所以如果前端出了什么问题,一定就是浏览器的错

如果你想更加了解SPIFFS文件 请参阅这里 https://github.com/esp8266/arduino-esp8266fs-plugin

到目前为止我未能完完整整地玩一次从满电倒完全放电的测试,那么你想试试么?

wing 发表于 2018-4-29 23:17:59


由于我手上那块8285的丝印并没有很清晰地标出ADC信息,所以这里补充说明一下接线
ADC2要接上18650的正极,就是下图那个绿圈

https://raw.githubusercontent.com/FreezingEye/pedrail-1.5/master/forum_image/20180429/ADS1015.png

wing 发表于 2018-5-2 21:40:28


今日测试了8个小时,情况如下



弘毅 发表于 2018-5-3 08:34:27

wing 发表于 2018-5-2 21:40
今日测试了8个小时,情况如下

还是挺耗电的。。。

wing 发表于 2018-5-5 13:09:59

本帖最后由 wing 于 2018-5-5 13:38 编辑

终于完成了一次从满电到终止放电的过程了,历时超过15小时



图表改进了,蓝色顺眼些吧,而且看起来不那么梯级了
每50秒获取一次电压,然后绘制宽度为1像素的柱状图,其实就是一条线,这样看起来比较平滑
每12次取值绘制下面的文字,也就是每隔10分钟有一次文字记录

主程序还是一样,下面是改进后的页面代码

<html>
    <head>
      <title>battery chart</title>
      <!--ESP8285 + ads1015-->
      
         <style>
             canvas {
               cursor: pointer;
               border: 1px solid black;
             }
         </style>      
      
      <script type="text/javascript">
      var ar_time = new Array();
      var ar_volt = new Array();   
            
      function read_adc()//get adc values
      {
            var url;
            url ="/adc";

            var xhr = new XMLHttpRequest();
            xhr.open("GET", url, true);
            xhr.onreadystatechange = function(e){
            if (xhr.readyState == 4) {
            var rtn = xhr.responseText;
            document.getElementById("textarea").value+= rtn+"\n";   
               
            var ar_str = rtn.split(",");   
            ar_time.push(ar_str);   
            ar_volt.push(ar_str);   
            }
            };   

            xhr.send();
            draw_chart();
      }
            
      function draw_chart() {

      var canvas;
      var context;

      canvas = document.getElementById("canvas");
      context = canvas.getContext("2d");      

      context.clearRect(0, 0, canvas.width, canvas.height);
            
      context.beginPath();         
      context.lineWidth = 1;
      context.strokeStyle = "#2828cd";   
            
      var i,v;
      var vtime;   
      for (i = 0; i < ar_volt.length; i++) {
          context.moveTo(35+i, 650);   
          v=ar_volt;
          context.lineTo(35+i, 1400-v);
         
          if (i % 12 ==0) {
            context.save();
            context.translate(35+i,660);
            context.rotate(Math.PI / 2);         

            var s,n,h;
            s=Math.floor(ar_time / 1000 );
            n=Math.floor(s / 60 );
            h=Math.floor(n / 60);
            if (h<10){
                vtime='0'+h;
            }else{
                vtime=h;         
            }
            n= n % 60;
            if (n<10){
                vtime=vtime+' : 0'+n;
            }else{
                vtime=vtime+' : '+n;   
            }
            s= s % 60;
            if (s<10){
                vtime=vtime+' : 0'+s;
            }else{
                vtime=vtime+' : '+s;   
            }            
            
            var m=ar_volt*0.003;   
            context.fillText(m.toFixed(3)+'V '+vtime,0,0);

            context.restore();
          }
      }      
      context.stroke();   
      }
               

      function button_gogogo() //btn_go make anything start once
      {
            document.getElementById("btn_go").disabled = true;
            read_adc();
            self.setInterval('read_adc()',50000);//50s once
      }            
      </script>
      
    </head>
    <body>
      <textarea id="textarea" rows=5 cols=100></textarea>
      <input id="btn_go" type="button" value="gogogo" onclick="button_gogogo()" />   
      <div>
      <canvas id="canvas" width="1800" height="750"></canvas>
      </div>
    </body>
</html>   

整体来看分三段
从满电到3.7V,以一个基本相等的速度放电,这种状态维持6个多小时
3.7~3.4,以另一个基本相等的速度放电,这种状态维持8个小时
3.4之后就开始跳水了,一个小时不到就放尽了

来分析一下,最后10分钟的获取的电压数据

55806894,1010
55856890,1004
55906899,997
55956898,990
56006887,983
56056895,974
56106892,964
56156893,953
56206899,938
56256895,919
56306895,895
56356900,862

基本上就是到了3V就开始断崖式下跌,最后获取到的电压是 2.586V。
毅神说过保护电压大概在2.5左右,从实验数据来看基本吻合

迷你强 发表于 2018-6-28 09:16:22

:o有意思。。。按照理论推算,此电池标称容量为2AH,实际测试容量约为1.8~1.9Ah左右(5A负载), 理论容量为3.7X2=7.4Wh 。 ESP8285的待机功耗约为23ma,加上外设 ADC usb等 估计会有40-50ma的负载, 也就是说5x0.045=0.225W(取5V不取3.3V 因为板载使用的是线性稳压器,多余的能量是耗散掉的)
考虑升压器综合效率85%计算7.4x0.85 = 6.29
6.29÷0.225 = 约为27小时与实际测试值接近

误差主要来源就是wifi通讯时的发射功耗 会增加到200-300ma

迷你强 发表于 2018-6-28 09:17:59

:lol根据实验数据,以后可以在理论值除以2的基础上推算wifi通讯情况下的实际耗电了

wing 发表于 2018-9-28 12:02:20



测试一下,大家能不能看到下面的图

https://raw.githubusercontent.com/FreezingEye/pedrail-1.5/master/forum_image/20180928/图表版本电池用量.webp
页: [1]
查看完整版本: OCROBOT ALPHA ESP8285 + LI POWER 电池电量监视器