查看“项目二--云端计步器”的源代码
←
项目二--云端计步器
跳转至:
导航
、
搜索
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
{| style="width: 800px;" |- | 本教程也是一个microduino和microWRT结合的项目。主要是通过microduino和传感器模块来检测被测对象的运动量,然后通过蓝牙模块将数据传送到microWRT上的microduino core模块。 Core模块进而将数据通过串口发送给microWRT。最后microWRT会把数据上传到yeelink,用户在任何地方只要能够访问网络,就可以看到运动量。 本教程有microWRT玩家“while(高群)”在microWRT内测活动中完成的。非常感谢“while”。最终解释权归“while”所有。 本教程中的所有代码可以通过下面链接直接下载。 源程序: '''[[media:pedometer.zip|所有源程序]]''' ==硬件设备== *microWRT 一个 *Microduino-Core 二个 *Microduino-BLE 二个 *Microduino-USBTTL 一个 *ADXL345传感器 一个 ==ADXL34传感器介绍== 项目初始设计阶段是计划使用MPU6050加速度传感器来监测运动数据,但MP6050在省电方面太难调试,之后就把传感器换成了ADXL345加速度传感器。 ADXL345是一款小而薄的低功耗3轴加速度计,分辨率高(13位),测量范围达±16g。数字输出数据为16位二进制补码格式,可通过SPI(3线或4线)或I2C数字接口访问。 ADXL345非常适合移动设备应用。它可以在倾斜检测应用中测量静态重力加速度,还可以测量运动或冲击导致的动态加速度。其高分辨率(4 mg/LSB),能够测量不到1.0°的倾斜角度变化。 该器件提供多种特殊检测功能。活动和非活动检测功能检测有无运动发生,以及任意轴上的加速度是否超过用户设置的限值。敲击检测功能可以检测单击和双击动作。自由落体检测功能可以检测器件是否正在掉落。这些功能可以映射到两个中断输出引脚中的一个。正在申请专利的32级先进先出(FIFO)缓冲器可用于存储数据,最大程度地减少主机处理器的干预。 低功耗模式支持基于运动的智能电源管理,从而以极低的功耗进行阈值感测和运动加速度测量。 ADXL345采用3 mm × 5 mm × 1 mm、14引脚小型超薄塑料封装。 选择ADXL345主要是看重了它的省电特性"测量模式下低至40 μA,待机模式下为0.1 μA",还配备了32组X,Y,Z FIFO, 功耗对于这个项目是非常重要的,谁也不想几天就去换一块电池! ADXL345 说明书: '''[[media:中文说明书.pdf|ADXL345 中文说明书]]''' ADXL345 使用参考: '''[[media:使用参考.pdf|ADXL345 使用参考]]''' ==计步器原理介绍== 计步器是一种颇受欢迎的日常锻炼进度监控器,可以激励人们挑战自己,增强体质,帮助瘦身。早期设计利用加重的机械开关检测步伐,并带有一个简单的计数器。 晃动这些装置时,可以听到有一个金属球来回滑动,或者一个摆锤左右摆动敲击挡块。如今,先进的计步器利用MEMS(微机电系统)惯性传感器和复杂的软件来精确检测真实的步伐。 MEMS惯性传感器可以更准确地检测步伐,误检率更低。MEMS惯性传感器具有低成本、小尺寸和低功耗的特点,因此越来越多的便携式消费电子设备开始集成计步器功能, 如音乐播放器和手机等。ADI公司的3轴加速度计ADXL335, ADXL345和ADXL346小巧纤薄,功耗极低,非常适合这种应用。 本文以对步伐特征的研究为基础,描述一个采用3轴加速度计ADXL345的全功能计步器参考设计,它能辨别并计数步伐,测量距离、速度甚至所消耗的卡路里。 ADXL345专有的片内32级先进先出(FIFO)缓冲器可以存储数据,并执行计步器应用的相关操作,从而最大程度地减少主处理器干预,为便携式设备节省宝贵的系统功率。 其13位分辨率(4 mg/LSB)甚至允许计步器以合理的精度测量超低速步行(每步加速度变化约55 mg)。了解模型在可用于分析跑步或步行的特征当中, 我们选择“加速度”作为相关参数。个体(及其相关轴)的运动包括三个分量,分别是前向(“滚动”)、竖向(“偏航”)和侧向(“俯仰”),如图所示。 ADXL345检测其三个轴——x、y和z上的加速度。计步器处于未知方向,因此测量精度不应严重依赖于运动轴与加速度计测量轴之间的关系。 [[File:yuanlitu.jpg|600px|center|加速度传感器]] ==系统设计方案== 1.设置ADXL345传感器为50Hz采样率,因为配备了FIFO,可以存储32组X,Y,Z数据,所以0.64秒才需要读取一次数据,其他时间arduino可以进入休眠模式 2.当ADXL345把FIFO数据填充好以后会触发中断,把arduino从休眠模式唤醒处理获取到的32组X,Y,Z传感器数据,判断用户(小动物)是否迈出了一步, 具体算法请参考analog公司计步器原理.(http://www.analog.com/library/analogDialogue/china/archives/44-06/pedometer.html) 3.统计用户(小动物)每10分钟走了多少步,唤醒机制是使用了arduino的看门狗模块,把数据通过蓝牙透明串口模块发送给MicroWRT模块。 4.MicroWRT获取到的数据通过上传到Yeelink,我们可以通过Yeelink网页或者客户端查看到运动数据。 ==Microduino端设计== 1. 硬件连接如下表所示。 引脚表 {| class="wikitable" |- ! Microduino引脚 !! ADXL345引脚 |- | 10 || CS || |- | 11 || SDA || |- | 12 || SDO || |- | 13 || SCL || |- | 3V3|| VCC || |- | GND|| GND || |} 2. Microduino Core 代码 <source lang="cpp"> #include <avr/sleep.h> #include <avr/wdt.h> #include <Wire.h> //调用arduino自带的I2C库 #define ADXL345_REG_DEVID 0x00 // Device ID #define ADXL345_REG_THRESH_TAP 0x1D // Tap threshold #define ADXL345_REG_OFSX 0x1E // X-axis offset #define ADXL345_REG_OFSY 0x1F // Y-axis offset #define ADXL345_REG_OFSZ 0x20 // Z-axis offset #define ADXL345_REG_DUR 0x21 // Tap duration #define ADXL345_REG_LATENT 0x22 // Tap latency #define ADXL345_REG_WINDOW 0x23 // Tap window #define ADXL345_REG_THRESH_ACT 0x24 // Activity threshold #define ADXL345_REG_THRESH_INACT 0x25 // Inactivity threshold #define ADXL345_REG_TIME_INACT 0x26 // Inactivity time #define ADXL345_REG_ACT_INACT_CTL 0x27 // Axis enable control for activity and inactivity detection #define ADXL345_REG_THRESH_FF 0x28 // Free-fall threshold #define ADXL345_REG_TIME_FF 0x29 // Free-fall time #define ADXL345_REG_TAP_AXES 0x2A // Axis control for single/double tap #define ADXL345_REG_ACT_TAP_STATUS 0x2B // Source for single/double tap #define ADXL345_REG_BW_RATE 0x2C // Data rate and power mode control #define ADXL345_REG_POWER_CTL 0x2D // Power-saving features control #define ADXL345_REG_INT_ENABLE 0x2E // Interrupt enable control #define ADXL345_REG_INT_MAP 0x2F // Interrupt mapping control #define ADXL345_REG_INT_SOURCE 0x30 // Source of interrupts #define ADXL345_REG_DATA_FORMAT 0x31 // Data format control #define ADXL345_REG_DATAX0 0x32 // X-axis data 0 #define ADXL345_REG_DATAX1 0x33 // X-axis data 1 #define ADXL345_REG_DATAY0 0x34 // Y-axis data 0 #define ADXL345_REG_DATAY1 0x35 // Y-axis data 1 #define ADXL345_REG_DATAZ0 0x36 // Z-axis data 0 #define ADXL345_REG_DATAZ1 0x37 // Z-axis data 1 #define ADXL345_REG_FIFO_CTL 0x38 // FIFO control #define ADXL345_REG_FIFO_STATUS 0x39 // FIFO status int ADXAddress = 0xA7>>1; //转换为7位地址 int X0,X1,X_out; int Y0,Y1,Y_out; int Z1,Z0,Z_out; double Xg,Yg,Zg; boolean fifo=false; //fifo中断标志 int min_x,min_y,min_z; int max_x,max_y,max_z; int dynamic_threshold_x;//动态阀值x int dynamic_threshold_y;//动态阀值y int dynamic_threshold_z;//动态阀值z int dynamic_threshold; int dynamic_accuracy;//动态精度 byte sample_count;//采样计数 int time_1;//间隔计数 int time_2; volatile byte timing;//看门狗计时 byte margin_max;//幅度最大的通道 int data_out; int sample_new; int sample_old; int slope;//斜率变量 int Pedometer2; int Pedometer3;//时间段内的计步数 int test; void setup() { delay(100); Serial.begin(115200,SERIAL_8N1); pinMode(10,OUTPUT); digitalWrite(10,HIGH); Wire.begin(); //初始化I2C delay(100); setup_watchdog(9); // 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms // 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec ACSR |=_BV(ACD);//OFF ACD ADCSRA=0;//OFF ADC Reg(ADXL345_REG_POWER_CTL,0x00);//待机 Reg(ADXL345_REG_BW_RATE,0x09);//数据速率及功率模式控制 Reg(ADXL345_REG_DATA_FORMAT,0x28); //数据格式控制,中断电平 Reg(ADXL345_REG_INT_MAP,0x03);//中断映射 Reg(ADXL345_REG_FIFO_CTL,0xF4);//FIFO控制 Reg(ADXL345_REG_INT_ENABLE,0x03);//中断使能控制 Reg(ADXL345_REG_POWER_CTL,0x08);//测量 Clea(); attachInterrupt(0, Watermark, FALLING); } void Watermark() { fifo=true; } void Clea() { for(int y=0;y<=32;y++) { Wire.beginTransmission(ADXAddress); Wire.write(ADXL345_REG_DATAX0); Wire.write(ADXL345_REG_DATAX1); Wire.endTransmission(); Wire.requestFrom(ADXAddress,2); } } void Reg(byte x1,byte x2) { Wire.beginTransmission(ADXAddress); Wire.write(x1); Wire.write(x2); Wire.endTransmission(); } void setup_watchdog(int ii) { byte bb; if (ii > 9 ) ii=9; bb=ii & 7; if (ii > 7) bb|= (1<<5); bb|= (1<<WDCE); MCUSR &= ~(1<<WDRF); // start timed sequence WDTCSR |= (1<<WDCE) | (1<<WDE); // set new watchdog timeout value WDTCSR = bb; WDTCSR |= _BV(WDIE); } ISR(WDT_vect) { timing++; if(timing>=75)//75*8s=600s { timing=0; //Serial.println(test++); Serial.print(abs( Pedometer2-Pedometer3) ); Pedometer3=Pedometer2; } } void Read_fifo() { Wire.beginTransmission(ADXAddress); Wire.write(ADXL345_REG_DATAX0); Wire.write(ADXL345_REG_DATAX1); Wire.endTransmission(); Wire.requestFrom(ADXAddress,6); if(Wire.available()<=6); { X0 = Wire.read(); X1 = Wire.read(); Y0 = Wire.read(); Y1 = Wire.read(); Z0 = Wire.read(); Z1 = Wire.read(); X1 = X1<<8; X_out = X0+X1; Y1 = Y1<<8; Y_out = Y0+Y1; Z1 = Z1<<8; Z_out = Z0+Z1; } } void Sleep_avr()//睡眠模式 { set_sleep_mode(SLEEP_MODE_PWR_DOWN ); // sleep mode is set here sleep_enable(); sleep_mode(); // System sleeps here } void loop() { if(fifo==true) { fifo=false; for(byte x=0;x<20;x++) { Read_fifo(); sample_count++; if(sample_count<=50) { max_x=max(max_x,X_out);//最大最小值 min_x=min(min_x,X_out); max_y=max(max_y,Y_out); min_y=min(min_y,Y_out); max_z=max(max_z,Z_out); min_z=min(min_z,Z_out); } else { sample_count=0; if( abs(max_x-min_x) > abs(max_y-min_y) && abs(max_x-min_x) > abs(max_z-min_z) ) //动态精度 {margin_max=1;dynamic_accuracy=abs(max_x-min_x)/8;} if( abs(max_y-min_y) > abs(max_x-min_x) && abs(max_y-min_y) > abs(max_z-min_z) ) {margin_max=2;dynamic_accuracy=abs(max_y-min_y)/8;} if( abs(max_z-min_z) > abs(max_x-min_x) && abs(max_y-min_y) > abs(max_y-min_y) ) {margin_max=3;dynamic_accuracy=abs(max_z-min_z)/8;} if(dynamic_accuracy < 10) { dynamic_accuracy=10; } //动态精度 dynamic_threshold_x= (max_x+min_x)/2;//动态阀值 dynamic_threshold_y= (max_y+min_y)/2; dynamic_threshold_z= (max_z+min_z)/2; max_x=-10000;max_y=-10000;max_z=-10000; min_x=10000;min_y=10000;min_z=10000; }//if..else switch(margin_max) { case 1:data_out=X_out; dynamic_threshold=dynamic_threshold_x-10; break; case 2:data_out=Y_out; dynamic_threshold=dynamic_threshold_y-10; break; case 3:data_out=Z_out; dynamic_threshold=dynamic_threshold_z-10; break; } time_1++; // Serial.print(dynamic_threshold); // Serial.print(","); sample_old=sample_new; if( abs(data_out-sample_new) > dynamic_accuracy ) { sample_new=data_out;} // Serial.println(sample_old); if(sample_old>dynamic_threshold && abs(dynamic_threshold-sample_old)>20 ) { slope=1; } if(sample_old<dynamic_threshold && abs(dynamic_threshold-sample_old)>20 ) { if( slope==1 ) { slope=0; if( (time_1 -time_2) >10 && (time_1-time_2)< 100 ) //时间间隔,判断是否是有效步伐 { Pedometer2++; // Serial.println(Pedometer2++); } time_2=time_1; } } }//for Sleep_avr();//Sleep.... }//if else { delay(1); Sleep_avr(); } } </source> 因为使用了ADXL345加速度传感器,所有0.64秒才唤醒arduino读取数据,然后使用低功耗蓝牙4.0透明串口传送数据给MicroWRT上传到Yeelink。 所以microduino端的功耗非常低,睡眠模式下的功耗只需要10ua,所以这个项目的计步器设计使用2032纽扣电池可以提供几个月的使用时间! ==MicroWRT端设计== 1. MicroWRT 代码 <source lang="cpp"> #include <stdio.h> //标准输入输出定义 #include <stdlib.h> //标准函数库定义 #include <unistd.h> //Unix标准函数定义 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> //文件控制定义 #include <termios.h> //POSIX中断控制定义 #include <errno.h> //错误号定义 #include <signal.h> #include <unistd.h> #include<time.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> //using namespace std; const char *YEELINK_IP="42.96.164.52"; //定义波特率数组 int speed_arr[] = {B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300}; int name_arr[] = {115200,38400, 19200, 9600, 4800, 2400, 1200, 300}; //打开串口 int open_port(void) { int fd; //串口的标识符 //O_NOCTTY用来告诉Linux这个程序不会成为“控制终端” //O_NDELAY用来告诉Linux这个程序不关心DCD信号 fd=open("/dev/ttyS0",O_RDWR | O_NOCTTY | O_NDELAY); if(fd == -1) { //不能打开串口 perror("open_port: Unable to open /dev/ttyS0 -"); return(fd); } else { fcntl(fd, F_SETFL, 0); printf("open ttys0 .....\n"); return(fd); } } //设置波特率 void set_speed_and_parity(int fd, int speed) { int i=0; //设置循环标志——注意不要在for内设置,否则会出错 struct termios Opt; //定义termios结构 if(tcgetattr(fd,&Opt)!=0) { perror("tcgetattr fd"); return; } for(i = 0; i < 8 ; i++) { if(speed == name_arr[i]) { tcflush(fd, TCIOFLUSH); cfsetispeed(&Opt, speed_arr[i]); cfsetospeed(&Opt, speed_arr[i]); /*tcsetattr函数标志: TCSANOW:立即执行而不等待数据发送或者接受完成。 TCSADRAIN:等待所有数据传递完成后执行。 TCSAFLUSH:Flush input and output buffers and make the change */ if(tcsetattr(fd, TCSANOW, &Opt) != 0) { perror("tcsetattr fd"); return; } tcflush(fd, TCIOFLUSH); } } //设置奇偶校验——默认8个数据位、没有校验位 Opt.c_cflag &= ~PARENB; Opt.c_cflag &= ~CSTOPB; Opt.c_cflag &= ~CSIZE; Opt.c_cflag |= CS8; } /* //设置奇偶校验——默认8个数据位、没有校验位 int set_parity() { Opt.c_options.c_cflag &= ~PARENB options.c_cflag &= ~CSTOPB options.c_cflag &= ~CSIZE; Opt.c_cflag |= CS8; } */ void yeelink(float data) { char tmp[10]; sprintf(tmp,"%.3f",data); struct sockaddr_in addr ; int t=socket(AF_INET,SOCK_STREAM,0); // TCP bzero(&addr,sizeof(addr)); addr.sin_family =AF_INET; //IPV4 addr.sin_port=htons(80); //PORT addr.sin_addr.s_addr=htonl(INADDR_ANY); addr.sin_addr.s_addr=inet_addr(YEELINK_IP); while(connect(t,(struct sockaddr *)&addr,sizeof(addr))==-1) { printf("connect eero \n"); } char *data1="POST /v1.0/device/5167/sensor/30842/datapoints HTTP/1.0\r\nHost: api.yeelink.net\r\nAccept: */*\r\n"; //5167替换成你自己的device号,30842也需替换成你的sensor号 send(t,data1,strlen(data1),0); char *data2="U-ApiKey: 67f4ce66959dd6d518af60289206f700\r\nContent-Length: ";//替换U-ApiKey send(t,data2,strlen(data2),0); char data3[5]; sprintf(data3,"%d",strlen(tmp)+10); send(t,data3,strlen(data3),0); //写入数据长度 char *data4="\r\nContent-type: application/json;charset=utf-8\r\n\r\n"; send(t,data4,strlen(data4),0); send(t,"{\"value\":",strlen("{\"value\":"),0); send(t,tmp,strlen(tmp),0); send(t,"}\r\n",strlen("}\r\n"),0); close(t); } int main(void) { int fd; int y; int nread,i; char buff[1024]; int test; //打开串口 if((fd=open_port())<0) { perror("open_port error"); return 0; } //设置波特率和校验位 set_speed_and_parity(fd,115200); //设置校验位 //set_parity(); printf("fd=%d\n",fd); while (1) //循环读取数据 { if((nread = read(fd, buff, 512))>0) { //printf("\nLen %d\n",nread); tcflush(fd, TCIOFLUSH); test=atoi(buff); //printf( "%s", buff); printf("%d\n",test); yeelink(test); } } //关闭串口 close(fd); return 0; } </source> 2. 编译源程序 在以前的教程中,我们一般都是把源程序复制到openwrt的编译目录,或者是openwrt SDK的编译目录中去编译。其实我们也有一个简单的方法,就是安装好交叉编译工具链在 HOST主机中,设置环境变量,就可以自由的编译源程序了。具体操作步骤如下: <1> 下载交叉编译工具链。当然你也可以自己用openwrt的source code编译。 下载路径: 链接:http://pan.baidu.com/s/1eQtnfIM 密码:qrye <2> 设置环境变量,登陆系统(非root用户),在终端输入: $ sudo gedit ~/.profile(or .bashrc) <3> 在此文件末尾加入PATH的设置如下,根据自己的路径修改: export STAGING_DIR='/home/while/OpenWrt-Toolchain/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/' <4> 保存文件,注销在登陆,变量将生效。 <5> 安装编译环境软件包: sudo apt-get install gcc g++ binutils patch bzip2 flex bison make autoconf gettext texinfo unzip sharutils ncurses-term zlib1g-dev libncurses5-dev gawk <6> 进入OpenWrt-Toolchain/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin目录,新建文件hello.c <source lang="cpp"> #include<stdio.h> int main() { printf("hello world!"); } </source> <7>然后终端进入OpenWrt-Toolchain/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin 目录,根据你自己的目录修改, 输入./mipsel-openwrt-linux-gcc hello.c -o hello 回车,然后bin目录下就会出现hello文件,复制到openwrt开发板设置下0755权限就可以运行了。 写其他程序只有修改下hello.c的源代码就可以了! 经过上面的步骤,我们只要把microWRT的源程序复制到hello.c 中,就可以很容易的编译出执行文件。然后上传到microWRT去运行。 ==测试== MicroWRT会把前10分钟所走的步数上传到Yeelink上,所以下图中的每个数据都是10分钟的步数和。 [[File:yeelink.png|600px|center|云端数据]] |}
返回至
项目二--云端计步器
。
导航菜单
个人工具
创建账户
登录
名字空间
页面
讨论
变种
视图
阅读
查看源代码
查看历史
更多
搜索
Welcome
首页
创客大赛
大赛详情
3D打印
安装月球车
图形化编程
操控月球车
升级月球车
编程工具下载
软件下载
Arduino
Processing
Mixly
Scratch
模块套件
Microduino 102
mCookie 102
mCookie 202
mCookie 302
IBC
其他
应用套件
四轴飞行器
平衡车
小车CUBE
音乐播放器
刷卡音乐播放器
wifi气象站
彩虹音乐触摸灯
分贝检测仪
迎门汇报
LED点阵时钟
LED点阵屏幕
硬件
mCookie
Sensor
Microduino
MicroWrt
MicroNux
MicroRobot-Core
MicroRobot-CoreESP
ideaBoard
ideaBox
MicroMV
MicroAI
帮助
常见问题
帮助
工具
链入页面
相关更改
特殊页面
页面信息