|
【TI首届低功耗设计大赛】小结一下,今天比较闲
[复制链接]
恩,大赛基本都快要结束了,参赛帖子没法几篇,趁着要结束了发一篇小结的帖子,针对这个小东西,东拉西扯一下咯。
1)首先说说这个小东西吧,主要是负责测量空气PM2.5、测量温度/湿度,三个量所以后面就叫这个设备为空气质量检测吧,加了一个led模拟实际空气净化开关。
2)测量是主要功能,下面说说通信和人机交互吧,通信采用wifi连接到互联网并接入微信公众平台,最终通过手机等终端的微信来控制和查询设备的状态信息。
3)说说微信公众平台控制原理,首先公众微信平台的接口可以指定为一个80端口的公网服务器地址,微信仅仅支持80端口的接入吧,我这么理解的,微信之所以能火,也是因为他能直接融入web这是他的优势,web嘛当然是80端口了,如果这个web是个后台的服务的话也是没问题的,他会执行这个服务的一系列处理。
4)那么问题来了,我们需要一个服务器来搭建一个服务,并把这个服务器地址给我们申请的公众微信平台的接入口就好了,服务器嘛,免费的很多,比如redhat的openshift这个比较给力,推荐大家折腾,就是个虚拟的linux主机哦,想干啥都可以,太自由了。不过对于我这种没怎么搞过互联网的来说自己重新来还是有点困难的。所以我采用新浪的SAE平台,这玩意还是比较傻瓜的,只要稍微懂一点点,搭建一个应用太容易了。
5)所以我把我的代码部署在SAE平台上面,基本上就是设备需要接入这个服务器(创建了应用就会有个服务器地址)的地址进行交互了,定期的发送我设备采集的数据到这个服务器上面,高些数据库把数据存起来就好了。然后我们的代码呢,需要解析微信发送过来的字符串来匹配等逻辑处理,然后通过微信提供的api回发给微信就好了。
6)关于微信与设备的交互,这个过程也类似,比如控制空气净化开关(采用LED模拟),过程是这样的微信发送开关命令给服务器,服务器存储下来这个值,设备会定期的发送get命令获取这个值,然后更新自己的开关状态。
7)关于get命令的调试,建议采用工具先调试,服务器的公网ip地址可以通过ping SAE应用的域名来获取,看一下是否有返回,下面就是接收到的返回数据,需要提取状态值,这些设备端进行一些简单的string操作即可了。
8)关于CCS基于eclipse的东西,挺好的,基本上eclipse的某些功能都支持的,比如git的代码版本控制
9)在远程端的代码
10)关于一些相关的东西
a、自行实现的一套AT指令,用于查询/控制设备的信息
b、应用了FRAM的频繁写操作特性,实现数据/操作的本地日志模式
c、一键切换wifi配置/设备工作模式,支持设备在新的环境中快速配置wifi接入互联网操作
11)一些主要代码
调试过程
- GET报文
- GET /index.php?token=weixin&TEMPdata=30&HUMIdata=45&PM25data=234 HTTP/1.1
- Host: chipsensor1.sinaapp.com
- User-Agent: MSP430FR5969-LaunchPad
- Connection: close
- RECV报文
- HTTP/1.1 200 OK
- Server: nginx/1.4.4
- Date: Tue, 21 Oct 2014 08:48:23 GMT
- Content-Type: text/html
- Transfer-Encoding: chunked
- Connection: close
- X-Powered-By: PHP/5.3.27
- Via: 10.67.15.48
- Set-Cookie: saeut=101.69.240.98.1413881303666005; path=/; max-age=311040000
- 2
- 30
复制代码
AT指令核心代码
- /**
- * @brief get_line
- * @note 读取命令行
- * @param none
- * @retval none
- */
- char *
- get_line (char *line)
- {
- int pos;
- char ch;
-
- pos = 0;
- ch = (char)in_char();
- while ( (ch != 0x0D ) && (ch != 0x0A ) && (pos < UIF_MAX_LINE))
- {
- switch (ch)
- {
- case 0x08: /* Backspace */
- case 0x7F: /* Delete */
- if (pos > 0)
- {
- pos -= 1;
- out_char(0x08); /* backspace */
- out_char(' ');
- out_char(0x08); /* backspace */
- }
- break;
-
- default:
- if ((pos+1) < UIF_MAX_LINE)
- {
- if ((ch > 0x1f) && (ch < 0x80))
- {
- line[pos++] = (char)ch;
- out_char((char)ch);
- }
- }
- break;
- }
- ch = (char)in_char();
- }
- line[pos] = '\0';
- out_char(0x0D); /* CR */
- out_char(0x0A); /* LF */
-
- return line;
- }
- /**
- * @brief make_argv
- * @note ARGV执行
- * @param none
- * @retval none
- */
- int
- make_argv (char *cmdline, char *argv[])
- {
- char argvTAB;
- int argc, i, in_text;
-
- /*
- * Break cmdline into strings and argv
- * It is permissible for argv to be NULL, in which case
- * the purpose of this routine becomes to count args
- */
- i = 0;
- argc = 0;
- in_text = FALSE;
-
- /* getline() must place 0x00 on end */
- while (cmdline[i] != '\0') {
- if (argc == 1) {
- argvTAB = '=';
- } else {
- argvTAB = ',';
- }
- if ( (cmdline[i] == argvTAB) || (cmdline[i] == '\t') ) {
- if (in_text) {
- /* end of command line argument */
- cmdline[i] = '\0';
- in_text = FALSE;
- } else {
- /* still looking for next argument */
-
- }
- } else {
- /* got non-whitespace character */
- if (in_text){
- } else {
- /* start of an argument */
- in_text = TRUE;
- if (argc < UIF_MAX_ARGS) {
- if (argv != NULL) {
- argv[argc] = &cmdline[i];
- }
- argc++;
- } else {
- /*return argc;*/
- break;
- }
- }
-
- }
- /* proceed to next character */
- i++;
- }
- if (argv != NULL) {
- argv[argc] = NULL;
- }
- return argc;
- }
- /**
- * @brief run_cmd
- * @note 运行命令
- * @param none
- * @retval none
- */
- void
- run_cmd (void)
- {
- /*
- * Global array of pointers to emulate C argc,argv interface
- */
- int argc;
-
- /* one extra for null terminator */
- char *argv[UIF_MAX_ARGS + 1];
-
- get_line(cmdline1);
-
- argc = make_argv(cmdline1,argv);
-
- if (!(argc))
- {
- /* no command entered, just a blank line */
- strcpy(cmdline1, cmdline2);
- argc = make_argv(cmdline1,argv);
- }
- cmdline2[0] = '\0';
-
- if (argc)
- {
- int i;
- for (i = 0; i < UIF_NUM_CMD; i++)
- {
- if (strcasecmp(UIF_CMDTAB[i].cmd, argv[0]) == 0)
- {
- if (((argc-1) >= UIF_CMDTAB[i].min_args) &&
- ((argc-1) <= UIF_CMDTAB[i].max_args))
- {
- if (UIF_CMDTAB[i].flags & UIF_CMD_FLAG_REPEAT)
- {
- strcpy(cmdline2, argv[0]);
- }
- UIF_CMDTAB[i].func(argc, argv);
- return;
- }
- else
- {
- printf(SYNTAX, argv[0]);
- return;
- }
- }
- }
- printf(INVCMD, argv[0]);
- printf(HELPMSG);
- printf("\r\n");
- }
- }
- /**
- * @brief uif_cmd_help
- * @note HELP命令
- * @param none
- * @retval none
- */
- void
- uif_cmd_help (int argc, char **argv)
- {
- int index;
-
- if (argc == 1)
- {
- printf("+ok\r\n\r\n");
- for (index = 0; index < UIF_NUM_CMD; index++)
- {
- printf(HELPFORMAT,
- UIF_CMDTAB[index].cmd,
- UIF_CMDTAB[index].description,
- UIF_CMDTAB[index].cmd,
- UIF_CMDTAB[index].syntax);
- }
- printf("\r\n");
- return;
- }
- printf(INVOPT,argv[1]);
- }
复制代码
相关AT指令实现代码
- /**
- * @brief uif_cmd_mode
- * @note MODE命令
- * @param none
- * @retval none
- */
- void
- uif_cmd_mode (int argc, char **argv)
- {
- char runMode[10];
- char runFreq[10];
-
- if (argc == 1) {
- if (ledMode == 1){
- strcpy(runMode,"off");
- } else if (ledMode == 2){
- strcpy(runMode,"on");
- } else if (ledMode == 3){
- strcpy(runMode,"toggle");
- } else {
- return;
- }
- _itoa(ledFreq, runFreq, 10);
- printf("+ok=%s,%s\r\n\r\n", runMode, runFreq);
- return;
- }
-
- if (argc != 3) {
- printf("Error: Invalid argument list\r\n");
- printf("\r\n");
- return;
- }
- if ( ((argc-1-1) >= UIF_CMDTAB[1].min_args) && ((argc-1-1) <= UIF_CMDTAB[1].max_args) ) {
- if (argv[1] != NULL) {
- if (strcasecmp("off", argv[1]) == 0){
- ledMode = 1;
- } else if(strcasecmp("on", argv[1]) == 0){
- ledMode = 2;
- } else if(strcasecmp("toggle", argv[1]) == 0){
- ledMode = 3;
- } else {
- printf(INVOPT, argv[1]);
- printf("\r\n");
- return;
- }
- }
- if(argv[2] != NULL){
- ledFreq = _atoi((char*)argv[2], 10);
- if (ledFreq > 50) {
- ledFreq = 50;
- printf(INVOPT, argv[2]);
- printf("\r\n");
- return;
- }
- }
- printf("+ok\r\n\r\n");
- return;
- } else {
- printf(INVARG, argv[0]);
- printf("\r\n");
- return;
- }
- }
- /**
- * @brief uif_cmd_mode
- * @note MODE命令
- * @param none
- * @retval none
- */
- void
- uif_cmd_rtcs (int argc, char **argv)
- {
- if (argc == 1) {
- getRTC();
- return;
- }
- if (argc != 3) {
- printf("Error: Invalid argument list\r\n");
- printf("\r\n");
- return;
- }
- if ( ((argc-1-1) >= UIF_CMDTAB[1].min_args) && ((argc-1-1) <= UIF_CMDTAB[1].max_args) ) {
- if (argv[1] != NULL && argv[2] != NULL) {
- setRTC(argv[1], argv[2]);
- printf("Set rtc successful\r\n");
- }
- printf("+ok\r\n\r\n");
- return;
- } else {
- printf(INVARG, argv[0]);
- printf("\r\n");
- return;
- }
- }
- /**
- * @brief uif_cmd_sens
- * @note SENS命令
- * @param none
- * @retval none
- */
- void
- uif_cmd_sens (int argc, char **argv)
- {
- char temp[16],humi[16],pmtf[16];
- memset(temp, 0x00, 16);
- memset(humi, 0x00, 16);
- memset(pmtf, 0x00, 16);
- if (argc != 2) {
- printf("Error: Invalid argument list\r\n");
- printf("\r\n");
- return;
- }
- if ( ((argc-1) >= UIF_CMDTAB[1].min_args) && ((argc-1) <= UIF_CMDTAB[1].max_args) ) {
- if (argv[1] != NULL) {
- _itoa(dht11_humi(), humi, 10);
- _itoa(dht11_temp(), temp, 10);
- _itoa(dsm501a_pm2p5(), pmtf, 10);
- if (strcasecmp("temp", argv[1]) == 0){
- printf("temperature %s℃\r\n", temp);
- } else if(strcasecmp("humi", argv[1]) == 0){
- printf("humidity %s%\r\n", humi);
- } else if(strcasecmp("pm2.5", argv[1]) == 0){
- printf("dsm501ap %sPcs\r\n", pmtf);
- } else {
- printf(INVOPT, argv[1]);
- printf("\r\n");
- return;
- }
- }
- printf("+ok\r\n\r\n");
- return;
- } else {
- printf(INVARG, argv[0]);
- printf("\r\n");
- return;
- }
- }
- /**
- * @brief uif_cmd_fram
- * @note FRAM命令
- * @param none
- * @retval none
- */
- void
- uif_cmd_fram (int argc, char **argv)
- {
- if (argc != 2) {
- printf("Error: Invalid argument list\r\n");
- printf("\r\n");
- return;
- }
- if ( ((argc-1) >= UIF_CMDTAB[1].min_args) && ((argc-1) <= UIF_CMDTAB[1].max_args) ) {
- if (argv[1] != NULL) {
- if (strcasecmp("pm2.5", argv[1]) == 0){
- transmitFRAMData();
- }else if (strcasecmp("cls", argv[1]) == 0){
- clearFRAMData();
- } else {
- printf(INVOPT, argv[1]);
- printf("\r\n");
- return;
- }
- }
- printf("+ok\r\n\r\n");
- return;
- } else {
- printf(INVARG, argv[0]);
- printf("\r\n");
- return;
- }
- }
复制代码
传感器驱动部分代码,PM2.5的细节参考之前的帖子,有详细分析
- /**
- * brief DHT_Read
- * note DHT读取温湿度
- * param None
- * retval None
- */
- void DHT_Read(uint8_t *RHTdat)
- {
- uint8_t timeOut;
- uint8_t checkSum,U8CK_DATA;
- uint8_t U8RH_DATH=0,U8RH_DATL=0;
- uint8_t U8TH_DATH=0,U8TH_DATL=0;
- /* 主机拉低DHT11 18ms */
- DHT11_DAT_DIR |= DHT11_DAT_BIT;
- DHT11_DAT_POUT &= ~DHT11_DAT_BIT;
- /* 延时18ms左右 */
- __delay_cycles(18000);
- /* 主机拉高DHT11 */
- DHT11_DAT_POUT |= DHT11_DAT_BIT;
- /* 总线由上拉电阻拉高 主机延时20us */
- __delay_cycles(20);
- /* 主机设为输入 判断从机响应信号 */
- DHT11_DAT_DIR &= ~DHT11_DAT_BIT;
- /* 判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行 */
- if ( !(DHT11_DAT_PIN&DHT11_DAT_BIT) ) {
- timeOut = 1;
- /* 判断从机发出 80us的低电平响应信号是否结束 */
- while( (!(DHT11_DAT_PIN&DHT11_DAT_BIT)) && timeOut++ );
- timeOut = 1;
- /* 判断从机是否发出 80us的高电平,如发出则进入数据接收状态 */
- while( (DHT11_DAT_PIN&DHT11_DAT_BIT) && timeOut++ );
- /* 数据接收 */
- U8RH_DATH = DHT_ReadByte(); // 读取湿度数据高字节,即整数部分
- U8RH_DATL = DHT_ReadByte(); // 读取湿度数据低字节,即小数部分,DHT11精度有限,小数为空
- U8TH_DATH = DHT_ReadByte(); // 读取温度数据高字节,即整数部分
- U8TH_DATL = DHT_ReadByte(); // 读取温度数据低字节,即小数部分,DHT11精度有限,小数为空
- U8CK_DATA = DHT_ReadByte(); // 读取校验和字节
- /* 数据校验 */
- checkSum = (U8TH_DATH + U8TH_DATL + U8RH_DATH + U8RH_DATL);
- if (checkSum == U8CK_DATA) {
- RHTdat[0] = U8RH_DATH; // 存储湿度数据
- RHTdat[1] = U8TH_DATH; // 存储温度数据
- }
- }
- }
复制代码
PHP服务器相关代码
-
- //传输文本
- private function transmitText($object, $content)
- {
- $textTpl = "<xml>
- <ToUserName><![CDATA[%s]]></ToUserName>
- <FromUserName><![CDATA[%s]]></FromUserName>
- <CreateTime>%s</CreateTime>
- <MsgType><![CDATA[text]]></MsgType>
- <Content><![CDATA[%s]]></Content>
- <FuncFlag>0</FuncFlag>
- </xml>";
- $result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $content);
- return $result;
- }
- //传输NEWS
- private function transmitNews($object, $newsArray)
- {
- if(!is_array($newsArray)){
- return;
- }
- $itemTpl = "<item>
- <Title><![CDATA[%s]]></Title>
- <Description><![CDATA[%s]]></Description>
- <PicUrl><![CDATA[%s]]></PicUrl>
- <Url><![CDATA[%s]]></Url>
- </item>";
- $item_str = "";
-
- foreach ($newsArray as $item){
- $item_str .= sprintf($itemTpl, $item['Title'], $item['Description'], $item['PicUrl'], $item['Url']);
- }
- $newsTpl = "<xml>
- <ToUserName><![CDATA[%s]]></ToUserName>
- <FromUserName><![CDATA[%s]]></FromUserName>
- <CreateTime>%s</CreateTime>
- <MsgType><![CDATA[news]]></MsgType>
- <Content><![CDATA[]]></Content>
- <ArticleCount>%s</ArticleCount>
- <Articles>
- $item_str</Articles>
- </xml>";
- $result = sprintf($newsTpl, $object->FromUserName, $object->ToUserName, time(), count($newsArray));
- return $result;
- }
-
- //接收事件
- private function receiveEvent($object)
- {
- $content = "";
- switch ($object->Event)
- {
- case "subscribe":
- {
- $content = "欢迎关注Chip-Sensor\r\n输入<help>获取帮助";
- }break;
- }
-
- $result = $this->transmitText($object, $content);
- return $result;
- }
- //接收文本
- private function receiveText($object)
- {
- $keyword = trim($object->Content);
- $url = "http://apix.sinaapp.com/weather/?appkey=".$object->ToUserName."&city=".urlencode($keyword);
- $output = file_get_contents($url);
- $content = json_decode($output, true);
- $result = $this->transmitNews($object, $content);
- return $result;
- }
复制代码
- else if($keyword == "switchoff" || $keyword == "关灯")
- {
- $con = mysql_connect(SAE_MYSQL_HOST_M.':'.SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
- // 获取时间戳
- $dates = date("h:i:sa");
- mysql_select_db("app_chipsensor1", $con);
- // 修改开关状态值
- $sql ="UPDATE switch SET timestamp='$dates',value = '1' WHERE ID = '1'";
- if(!mysql_query($sql,$con)){
- die('Error: ' . mysql_error());
- }else{
- mysql_close($con);
- }
- // 返回给微信的信息
- $tips = "灯关闭!";
-
- $resultStr = $resultStr = $this->transmitText($postObj, $tips);
- echo $resultStr;
- }
-
- else if($keyword == "switchon" || $keyword == "开灯")
- {
- $con = mysql_connect(SAE_MYSQL_HOST_M.':'.SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
- // 获取时间戳
- $dates = date("h:i:sa");
- mysql_select_db("app_chipsensor1", $con);
- // 修改开关状态值
- $sql ="UPDATE switch SET timestamp='$dates',value = '2' WHERE ID = '1'";
- if(!mysql_query($sql,$con)){
- die('Error: ' . mysql_error());
- }else{
- mysql_close($con);
- }
- // 返回给微信的信息
- $tips = "灯打开!";
-
- $resultStr = $resultStr = $this->transmitText($postObj, $tips);
- echo $resultStr;
- }
-
- else if($keyword == "switch" || $keyword == "SWITCH" || $keyword == "状态")
- {
- // MySQL连接语句
- $con = mysql_connect(SAE_MYSQL_HOST_M.':'.SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
-
- // 选择数据库进行连接
- mysql_select_db("app_chipsensor1", $con);
- $result = mysql_query("SELECT * FROM switch");
-
- while($arr = mysql_fetch_array($result)){
- if ($arr['ID'] == 1) {
- // 如果数据库连接正常读取value值
- $tempr = $arr['value'];
- }
- }
- // 关闭数据库连接
- mysql_close($con);
- if($tempr == 1){
- $tips="关闭";
- }else if($tempr == 2){
- $tips="打开";
- }
-
- $resultStr = $resultStr = $this->transmitText($postObj, "当前灯状态:".$tips);
- echo $resultStr;
- }
-
复制代码
基本上这些内容我相信大家就能够自己diy一个类似的应用了,有问题欢迎大家一起交流。
|
|