【FireBeetle Board-ESP32应用教程】跑步运动的计步神器

2017-06-135.5万
精华项目
AI 快速预览详细 收起
本项目介绍了一款基于FireBeetle Board-ESP32的跑步计步器。硬件包括FireBeetle Board-ESP32、OLED12864显示屏、加速度计模块等。通过Blynk可以实时记录步数和卡路里,并上传数据。适合健身爱好者和初学者使用。
曾几何时
我也是个运动健儿,骑单车、打羽毛球,不落下风
曾几何时
骑着我的捷安特,问道青城山,驰骋都江堰,毫不逊色
而如今
。。。 。。。 。。。
:'(:'(:'(:'(:'(:'(:'(:'(:'(:'(

    于此,为了昔日健硕的身躯,我下定决心重新出发,锻炼身体。每天坚持跑步两公里。咦?得有一个计步器记录每天的步数。于是,就有了这个计步神器的诞生,先来一发视频:


    此神器,可以实时的记录步数(以及消耗的卡路里),可以显示时间,最炫酷的当然是以指针的形式显示时间:
【FireBeetle Board-ESP32应用教程】跑步运动的计步神器图10

还可以将数据上传到网络,通过手机软件(Blynk)显示数据,同时获取网络时间(就不用担心电池没电了时间跑飞)。

所需硬件:
     DFR0478 FireBeetle Board-ESP32
     DFR0486  OLED12864显示屏
     SEN0140 加速度计模块
      3.7V锂电池(这个是在网上买的,容量是600mAH)
      3个按键(网上买的)
手机软件用的是Blynk,搭建起来方便,关于如何使用Blynk建立IoT项目,可以参考我之前的帖子,点击链接

1、创建一个Blynk项目
    添加如下两个控件:
      Value Display × 1
      Real-time clock × 1
    其中Real-time clock控件的属性不需要任何设置,Value Display控件名称设置成steps,INPUT引脚选择V1。然后调整两个控件的布局,如下图所示:
【FireBeetle Board-ESP32应用教程】跑步运动的计步神器图9


2、下载程序到FireBeetle Board-ESP32
    程序源码,点击下载,源码中还包括了库文件和3D打印文件,需要将库文件放到Arduino IDE的libraries下面,3D文件可以直接打印出外壳。
    主程序代码如下:
  1. #include <TimeLib.h>
  2. #include <Wire.h>  // Only needed for Arduino 1.6.5 and earlier
  3. #include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
  4. #include "OLEDDisplayUi.h"
  5. #include "images.h"
  6. #include <SimpleTimer.h>
  7. #include <WiFi.h>
  8. #include <WiFiClient.h>
  9. #include <BlynkSimpleEsp32.h>
  10. #include <WidgetRTC.h>
  11. #define POWER_KEY 1
  12. #define MENU_KEY  2
  13. #define UPLOAD_KEY 3
  14. boolean upload = false;
  15. SSD1306  display(0x3c, 18, 0);
  16. OLEDDisplayUi ui ( &display );
  17. SimpleTimer timer;
  18. WidgetRTC rtc;
  19. int screenW = 128;
  20. int screenH = 64;
  21. int clockCenterX = screenW/2;
  22. int clockCenterY = ((screenH-16)/2)+16;   // top yellow part is 16 px height
  23. int clockRadius = 23;
  24. #define DEVICE (0x53)      //ADXL345 device address
  25. #define TO_READ (6)        //num of bytes we are going to read each time (two bytes for each axis)
  26. byte buff[TO_READ] ;        //6 bytes buffer for saving data read from the device
  27. char str[100];              //string buffer to transform data before sending it to the serial port
  28. int regAddress = 0x32;      //first axis-acceleration-data register on the ADXL345
  29. int xx, yy, zz;                //three axis acceleration data
  30. static int currentValue = 0;
  31. static unsigned long stepsSum=0;
  32. char auth[] = "YourAuthToken";
  33. // Your WiFi credentials.
  34. // Set password to "" for open networks.
  35. char ssid[] = "YourNetworkName";
  36. char pass[] = "YourPassword";
  37. const char running_Logo_bits[] PROGMEM = {
  38.   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  39.   0x64,0x03,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0x00,0xFC,
  40.   0x01,0x00,0x00,0x00,0xFC,0x05,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0x00,0xFC,0x00,
  41.   0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0x00,0xE0,0x03,0x00,
  42.   0x00,0x60,0xF1,0x07,0x00,0x00,0x20,0xF8,0x17,0x00,0x00,0xC0,0xF8,0x0F,0x00,0x00,
  43.   0xE0,0xFB,0x17,0x00,0x00,0xC0,0xFF,0x13,0x00,0x00,0x00,0xFF,0x03,0x00,0x00,0x80,
  44.   0xFE,0x03,0x00,0x00,0x00,0xF9,0x03,0x00,0x00,0x00,0xFA,0x03,0x00,0x00,0x00,0xF8,
  45.   0x03,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0x00,0xF4,0x07,0x00,0x00,0x00,0xF4,0x0F,
  46.   0x00,0x00,0x00,0xF9,0x0F,0x00,0x00,0x00,0xFC,0x1F,0x00,0x00,0x80,0xFE,0x1F,0x00,
  47.   0x00,0x00,0xFF,0x1F,0x00,0x00,0xA0,0xFF,0x5F,0x00,0x00,0xC0,0x3F,0x3F,0x00,0x00,
  48.   0xE8,0x1F,0x3F,0x00,0x00,0xE8,0xA7,0x3E,0x00,0x00,0xF0,0x03,0x7C,0x00,0x00,0xE0,
  49.   0x05,0x7C,0x00,0x00,0xE0,0x05,0xF8,0x01,0x00,0xC0,0x01,0xF0,0x03,0x00,0xC0,0x03,
  50.   0xE8,0x07,0x00,0xC0,0x03,0x88,0x6F,0x00,0x80,0x03,0x40,0x1E,0x00,0xA0,0x03,0x40,
  51.   0xFC,0x00,0x80,0x03,0x00,0xF8,0x01,0x00,0x07,0x00,0xF4,0x00,0x00,0x07,0x00,0xE8,
  52.   0x00,0x80,0x0F,0x00,0xE8,0x00,0x90,0x0F,0x00,0xE0,0x00,0xE8,0x0F,0x00,0xE8,0x00,
  53.   0xF0,0x09,0x00,0x60,0x01,0xF0,0x04,0x00,0x00,0x00,
  54. };
  55. // utility function for digital clock display: prints leading 0
  56. String twoDigits(int digits){
  57.   if(digits < 10) {
  58.     String i = '0'+String(digits);
  59.     return i;
  60.   }
  61.   else {
  62.     return String(digits);
  63.   }
  64. }
  65. void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
  66.   if((hour()==0) && (minute()==0) && (second()==0))
  67.     stepsSum = 0;
  68. }
  69. void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  70.   display->drawCircle(clockCenterX + x, clockCenterY + y, 2);
  71.   //hour ticks
  72.   for( int z=0; z < 360;z= z + 30 ){
  73.     float angle = z ;
  74.     angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
  75.     int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) );
  76.     int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) );
  77.     int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
  78.     int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
  79.     display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y);
  80.   }
  81.   // display second hand
  82.   float angle = second() * 6 ;
  83.   angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
  84.   int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
  85.   int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
  86.   display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
  87.   
  88.   // display minute hand
  89.   angle = minute() * 6 ;
  90.   angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
  91.   x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
  92.   y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
  93.   display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
  94.   // display hour hand
  95.   angle = hour() * 30 + int( ( minute() / 12 ) * 6 )   ;
  96.   angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
  97.   x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
  98.   y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
  99.   display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
  100. }
  101. void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  102.   String date = String(year())+"/"+twoDigits(month())+"/"+twoDigits(day());
  103.   String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
  104.   
  105.   display->setTextAlignment(TEXT_ALIGN_CENTER);
  106.   display->setFont(ArialMT_Plain_24);
  107.   display->drawString(clockCenterX + x , 20, timenow);
  108.   display->setFont(ArialMT_Plain_16);
  109.   display->drawString(60 , 45, date);
  110. }
  111. void writeTo(int device, byte address, byte val) {
  112.   Wire.beginTransmission(device); //start transmission to device
  113.   Wire.write(address);        // send register address
  114.   Wire.write(val);        // send value to write
  115.   Wire.endTransmission(); //end transmission
  116. }
  117. //reads num bytes starting from address register on device in to buff array
  118. void readFrom(int device, byte address, int num, byte buff[]) {
  119.   Wire.beginTransmission(device); //start transmission to device
  120.   Wire.write(address);        //sends address to read from
  121.   Wire.endTransmission(); //end transmission
  122.   Wire.beginTransmission(device); //start transmission to device
  123.   Wire.requestFrom(device, num);    // request 6 bytes from device
  124.   int i = 0;
  125.   while(Wire.available())    //device may send less than requested (abnormal)
  126.   {
  127.     buff= Wire.read(); // receive a byte
  128.     i++;
  129.   }
  130.   Wire.endTransmission(); //end transmission
  131. }
  132. void runningFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  133.   float calValue = stepsSum*0.4487;
  134.   
  135.   display->setTextAlignment(TEXT_ALIGN_CENTER);
  136.   display->setFont(ArialMT_Plain_24);
  137.   display->drawString(clockCenterX , clockCenterY, str);
  138.   sprintf(str,"%.2fcal",calValue);
  139.   display->setTextAlignment(TEXT_ALIGN_CENTER);
  140.   display->setFont(ArialMT_Plain_10);
  141.   display->drawString(100 , 20, str);
  142.   
  143.   display->drawXbm(10, 14, 34, 50, running_Logo_bits);
  144. }
  145. void uploadFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  146.   display->setFont(ArialMT_Plain_16);
  147.   display->drawString(60 , 45, "upload data ...");
  148. }
  149. // This array keeps function pointers to all frames
  150. // frames are the single views that slide in
  151. FrameCallback frames[] = { analogClockFrame, digitalClockFrame, runningFrame, uploadFrame};
  152. // how many frames are there?
  153. int frameCount = 4;
  154. // Overlays are statically drawn on top of a frame eg. a clock
  155. OverlayCallback overlays[] = { clockOverlay };
  156. int overlaysCount = 1;
  157. void uploadToBlynk(void){
  158.   if(upload == true){
  159.     Blynk.virtualWrite(V0,stepsSum);
  160.     Blynk.virtualWrite(V1,stepsSum);
  161.   }
  162. }
  163. void uiInit(void){
  164.   ui.setTargetFPS(30);
  165.   //ui.setActiveSymbol(activeSymbol);
  166.   //ui.setInactiveSymbol(inactiveSymbol);
  167.   ui.setIndicatorPosition(TOP);
  168.   ui.setIndicatorDirection(LEFT_RIGHT);
  169.   ui.setFrameAnimation(SLIDE_LEFT);
  170.   ui.setFrames(frames, frameCount);
  171.   ui.setOverlays(overlays, overlaysCount);
  172.   ui.disableAutoTransition();
  173.   ui.switchToFrame(2);
  174.   ui.init();
  175.   display.flipScreenVertically();
  176. }
  177. void adxl345Init(void){
  178.   writeTo(DEVICE, 0x2D, 0);      
  179.   writeTo(DEVICE, 0x2D, 16);
  180.   writeTo(DEVICE, 0x2D, 8);
  181. }
  182. void updateAdxl345(void){
  183.   readFrom(DEVICE, regAddress, TO_READ, buff); //read the acceleration data from the ADXL345
  184.   xx = (((int)buff[1]) << 8) | buff[0];   
  185.   yy = (((int)buff[3])<< 8) | buff[2];
  186.   zz = (((int)buff[5]) << 8) | buff[4];
  187.   
  188.   if(xx < 100){
  189.     sprintf(str, "%d", stepsSum);  
  190.     return;
  191.   }
  192.   if(fabs(xx - currentValue) > 80){
  193.     if(xx < currentValue){
  194.       stepsSum++;
  195.     }
  196.     currentValue = xx;
  197.   }
  198.   sprintf(str, "%d", stepsSum);  
  199. }
  200. int getKeys(void){
  201.     if(digitalRead(D2) == LOW){
  202.       delay(5);
  203.       if(digitalRead(D2) == LOW){
  204.         while(digitalRead(D2) == LOW);
  205.         return POWER_KEY;
  206.       }
  207.     }
  208.      if(digitalRead(D3) == LOW){
  209.       delay(5);
  210.       if(digitalRead(D3) == LOW){
  211.         while(digitalRead(D3) == LOW);
  212.         return MENU_KEY;
  213.       }
  214.     }
  215.      if(digitalRead(D4) == LOW){
  216.       delay(5);
  217.       if(digitalRead(D4) == LOW){
  218.         while(digitalRead(D4) == LOW);
  219.         return UPLOAD_KEY;
  220.       }
  221.     }
  222.     return 0;
  223. }
  224. void doKeysFunction(void){
  225.   static int uiFrameIndex = 2;
  226.   int keys = getKeys();
  227.   if(keys == POWER_KEY){
  228.     static char i = 0;
  229.     if(i){
  230.       ui.init();
  231.      display.flipScreenVertically();
  232.      display.displayOn();
  233.     }else{
  234.      display.displayOff();
  235.     }
  236.     i = ~i;
  237.   }
  238.   if(keys == MENU_KEY){
  239.     if(upload == false){
  240.       uiFrameIndex++;
  241.       if(uiFrameIndex == 3)
  242.        uiFrameIndex = 0;     
  243.       ui.switchToFrame(uiFrameIndex);
  244.     }else{
  245.       ui.switchToFrame(3);
  246.     }
  247.   }
  248.   if(keys == UPLOAD_KEY){
  249.     if(upload == true){
  250.       upload = false;
  251.       ui.switchToFrame(uiFrameIndex);
  252.     }else{
  253.       upload = true;
  254.       ui.switchToFrame(3);
  255.     }
  256.   }
  257. }
  258. void setup() {
  259.   pinMode(D2,INPUT);
  260.   pinMode(D3,INPUT);
  261.   pinMode(D4,INPUT);
  262.   Blynk.begin(auth, ssid, pass);
  263.   rtc.begin();
  264.   uiInit();
  265.   adxl345Init();
  266.   timer.setInterval(30,updateAdxl345);
  267.   timer.setInterval(100,uploadToBlynk);
  268. }
  269. void loop() {
  270.   int remainingTimeBudget = ui.update();
  271.   static int testSum = 0;
  272.   if((testSum < 100) || (upload == true)){
  273.    Blynk.run();
  274.    testSum++;
  275.   }
  276.   if (remainingTimeBudget > 0) {
  277.     delay(remainingTimeBudget);
  278.   }
  279.   doKeysFunction();
  280.   timer.run();
  281. }
复制代码


    需要注意的是,将源码中的WiFi、密码,以及AUTHTOKENS改成你自己的,修改部分如下:

  1. char auth[] = "YourAuthToken";
  2. // Your WiFi credentials.
  3. // Set password to "" for open networks.
  4. char ssid[] = "YourNetworkName";
  5. char pass[] = "YourPassword";
复制代码


3、硬件组装
    OLED12864显示屏和加速度计模块都连接到I2C,按键分别连接到D2、D3、D4,其中,按键需要加51K上拉电阻,上拉到3.3V,如下图所示:

【FireBeetle Board-ESP32应用教程】跑步运动的计步神器图3

注意:图中上拉接到AREF是错的,需要连接到3.3V

    硬件焊接图,如下所示:

【FireBeetle Board-ESP32应用教程】跑步运动的计步神器图1【FireBeetle Board-ESP32应用教程】跑步运动的计步神器图2


    硬件焊接完成后,将硬件模块组装到外壳中,如下所示:

【FireBeetle Board-ESP32应用教程】跑步运动的计步神器图4【FireBeetle Board-ESP32应用教程】跑步运动的计步神器图5【FireBeetle Board-ESP32应用教程】跑步运动的计步神器图6


    整体效果展示:

【FireBeetle Board-ESP32应用教程】跑步运动的计步神器图7【FireBeetle Board-ESP32应用教程】跑步运动的计步神器图8

查看更多ESP32/ESP8266教程和项目,请点击 : ESP32教程 汇总贴

创作许可协议

本项目采用 None(不开放任何权利,保留所有权利) 进行许可。

评论(16)
Gin66
Gin66
焊接功力很強大,專業的高手
派大星ym
派大星ym
。。。。。
派大星ym
派大星ym
学习了学习了
DFr15BEux-H
DFr15BEux-H
学习中,希望自己能成功做出一个来!谢谢分享!
luna
luna
[align=left]中文版 : ESP32教程汇总贴[/align]
linzzzzh
linzzzzh
请问下,timelib的库文件为什么会出现找不到的情况啊,已经在GitHub上下载过,也放到library里了
Chocho2017
Chocho2017
作者
回复linzzzzh
是不是你的库文件与其他的库文件冲突了?
gray6666
gray6666
请问您的51K上拉电阻是如何测量的?
Chocho2017
Chocho2017
作者
回复gray6666
51K只是个电阻的参考范围,你可以用47K的。
miletian
miletian
虽然硬件不限听那个,打算做下试试
xiaohe9527
xiaohe9527
那个10自由度惯性导航模块和OLED的接线没讲清楚啊,大哥请讲一下
Chocho2017
Chocho2017
作者
回复xiaohe9527
是接I2C的,SDA接SDA,SCL接SCL。简单啦。
xiaohe9527
xiaohe9527
太牛逼了
hnyzcj
hnyzcj
这个厉害了,哈哈哈
Chocho2017
Chocho2017
作者
回复hnyzcj
呵呵 ,谢谢。
源代码
源代码
喜欢~界面非常酷!我之前也想用Blynk这款蓝牙APP,但注册几次都失败了。。。
Chocho2017
Chocho2017
作者
回复源代码
:victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory:
源代码
源代码回复Chocho2017
好的 我去看看
共3条回复,已加载2条,点击查看更多共3条回复,已加载全部
Youyou
Youyou
楼主的飞线功底果然扎实,牛逼!
Chocho2017
Chocho2017
作者
回复Youyou
哈哈,还行吧。谢谢夸奖。
BoBo
BoBo
UI界面不错,顶。
秦皇岛岛主
秦皇岛岛主
楼主焊工牛B 看起来蛮帅的
Chocho2017
Chocho2017
作者
照例,自己先抢个沙发。;P;P;P;P;P;P;P;P;P
- 没有更多了 -