38779浏览
查看: 38779|回复: 24

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

[复制链接]
曾几何时
我也是个运动健儿,骑单车、打羽毛球,不落下风
曾几何时
骑着我的捷安特,问道青城山,驰骋都江堰,毫不逊色
而如今
。。。 。。。 。。。
:'(:'(:'(:'(:'(:'(:'(:'(:'(:'(

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


    此神器,可以实时的记录步数(以及消耗的卡路里),可以显示时间,最炫酷的当然是以指针的形式显示时间:
【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教程 汇总贴

luna  初级技神

发表于 2018-12-19 14:29:26

中文版 : ESP32教程汇总贴
回复

使用道具 举报

Chocho2017  初级技匠
 楼主|

发表于 2017-6-14 13:22:09

源代码 发表于 2017-6-14 10:46
喜欢~界面非常酷!我之前也想用Blynk这款蓝牙APP,但注册几次都失败了。。。 ...

你可以能下载的版本不对,我在另外一篇帖子里面提到过的。你可以去看一下。我之前的Blynk软件就是这个问题,一直注册不成功。
回复

使用道具 举报

Chocho2017  初级技匠
 楼主|

发表于 2018-6-4 10:53:27

linzzzzh 发表于 2018-5-29 17:00
请问下,timelib的库文件为什么会出现找不到的情况啊,已经在GitHub上下载过,也放到library里了 ...

是不是你的库文件与其他的库文件冲突了?
回复

使用道具 举报

源代码  中级技匠

发表于 2017-6-14 14:13:45

Chocho2017 发表于 2017-6-14 13:22
你可以能下载的版本不对,我在另外一篇帖子里面提到过的。你可以去看一下。我之前的Blynk软件就是这个问 ...

好的  我去看看
回复

使用道具 举报

Chocho2017  初级技匠
 楼主|

发表于 2017-6-13 15:21:57

照例,自己先抢个沙发。;P;P;P;P;P;P;P;P;P
回复

使用道具 举报

秦皇岛岛主  初级技师

发表于 2017-6-13 15:35:52

楼主焊工牛B 看起来蛮帅的
回复

使用道具 举报

bobo  NPC

发表于 2017-6-13 16:41:18

UI界面不错,顶。
回复

使用道具 举报

Youyou  初级技匠

发表于 2017-6-13 19:02:34

楼主的飞线功底果然扎实,牛逼!
回复

使用道具 举报

Chocho2017  初级技匠
 楼主|

发表于 2017-6-14 08:56:10

Youyou 发表于 2017-6-13 19:02
楼主的飞线功底果然扎实,牛逼!

哈哈,还行吧。谢谢夸奖。
回复

使用道具 举报

源代码  中级技匠

发表于 2017-6-14 10:46:08

喜欢~界面非常酷!我之前也想用Blynk这款蓝牙APP,但注册几次都失败了。。。
回复

使用道具 举报

hnyzcj  版主

发表于 2017-6-14 11:06:55

这个厉害了,哈哈哈
回复

使用道具 举报

Chocho2017  初级技匠
 楼主|

发表于 2017-6-14 13:22:31

hnyzcj 发表于 2017-6-14 11:06
这个厉害了,哈哈哈

呵呵 ,谢谢。
回复

使用道具 举报

Chocho2017  初级技匠
 楼主|

发表于 2017-6-14 15:10:25


:victory::victory::victory::victory::victory::victory::victory::victory::victory::victory::victory:
回复

使用道具 举报

xiaohe9527  高级技师

发表于 2017-6-26 15:35:08

太牛逼了
回复

使用道具 举报

xiaohe9527  高级技师

发表于 2017-6-26 16:00:16

那个10自由度惯性导航模块和OLED的接线没讲清楚啊,大哥请讲一下
回复

使用道具 举报

Chocho2017  初级技匠
 楼主|

发表于 2017-6-27 10:47:53

xiaohe9527 发表于 2017-6-26 16:00
那个10自由度惯性导航模块和OLED的接线没讲清楚啊,大哥请讲一下

是接I2C的,SDA接SDA,SCL接SCL。简单啦。
回复

使用道具 举报

miletian  中级技师

发表于 2017-7-6 15:26:37

虽然硬件不限听那个,打算做下试试
回复

使用道具 举报

gray6666  初级技神

发表于 2017-7-7 10:50:34

请问您的51K上拉电阻是如何测量的?
回复

使用道具 举报

Chocho2017  初级技匠
 楼主|

发表于 2017-7-7 14:55:08

gray6666 发表于 2017-7-7 10:50
请问您的51K上拉电阻是如何测量的?

51K只是个电阻的参考范围,你可以用47K的。
回复

使用道具 举报

linzzzzh  学徒

发表于 2018-5-29 17:00:20

请问下,timelib的库文件为什么会出现找不到的情况啊,已经在GitHub上下载过,也放到library里了
回复

使用道具 举报

12下一页
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail