10514浏览
查看: 10514|回复: 9

[项目] [Hack上海]Project Brother Octopus, 植被自动维护系统

[复制链接]
本帖最后由 放了假的人 于 2015-11-9 22:57 编辑

  被队友安利来了这次的Hack上海,本着蹭吃蹭喝蹭妹子(并没有)的想法我们四个就这么来到了现场。没有想到的是我们有幸借到了设备,惊喜地打开了新世界的大门←_←

[Hack上海]Project Brother Octopus, 植被自动维护系统图1
在没有读取办法USB MODEM的情况下能成功调试真的很开心

  项目假设的应用场景是大家放在寝室放在家里的植物。光照不够番茄不好吃怎么办?寝室太热捕蝇草闷死了怎么办?万一植物逃跑了怎么办?(咳咳,只是在现场看到三轴加速度计开的脑洞)其实对项目加以扩展的话,是可以在一台数据服务器下形成庞大的传感器网络,并且供给产业用途的喔~
  硬件部分的构建比较简单,隔一段时间读取一次传感器数据传到数据服务器,再响应服务器控制信号就可以了。之后网页服务器的队友会用一种叫拉格朗日XXX的机器学习算法处理数据,判断温度是不是过高,光照是不是不够,然后返回相应的控制信号。最后通过网页端和移动端App的页面,用户可以看到实时反馈和数据统计,并且关闭自动控制,人工控制设备(模拟光照强度的灯条,模拟通风系统的电动机,etc)
  如何将开发版和数据服务器连接起来呢?考虑到只有24小时,我们果断选择了比较成熟的WLAN。而具体上传下载的问题,也是选择了一个TRICK,即GET方法利用Query String形式上传数据,再让服务器把控制信号当Response Body回传,就用简单的几秒一次的GET请求解决了数据的传输。
//然而会场WIFI信号实在不稳定,终端和网络都很多,丢包严重,设备之间的链接经常出问题

[Hack上海]Project Brother Octopus, 植被自动维护系统图2
最后长这个样子,传感器加数据线加WIFI天线刚好八根,八爪鱼(章鱼哥)的名字也取自它

[Hack上海]Project Brother Octopus, 植被自动维护系统图4[Hack上海]Project Brother Octopus, 植被自动维护系统图5
网页端实时数据与数据统计页面

[Hack上海]Project Brother Octopus, 植被自动维护系统图3
移动端数据统计页面

感谢Hack[上海]/NYUSH
感谢DFROBOT
感谢相互调用相互传参其乐融融的队友
感谢MTK, node, Java, ionic





Arduino源代码
  1. #include <LWiFi.h>
  2. #include <LWiFiClient.h>
  3. #include <Wire.h>
  4. #include <Suli.h>
  5. #include "Seeed_LED_Bar_Arduino.h"
  6. #include "DHT.h"
  7. #include "Barometer.h"
  8. #include <Servo.h>
  9. #define WIFI_AP "WLAN-SSID"
  10. #define WIFI_PASSWORD "WLAN-PWD"
  11. #define SITE_URL "数据服务器IP地址"
  12. const int PIN_LED= 13;
  13. const int LEF_BAR_CLK = 8;
  14. const int LEF_BAR_DTA = 7;
  15. const int PIN_LIGHT_SENSOR = A0;
  16. #define DHTPIN 2
  17. #define DHTTYPE DHT22
  18. const int PIN_UV_SENSOR = A1;
  19. const int PIN_DUST_SENSOR = 3;
  20. const unsigned long DUST_SENSOR_SAMPLETIME = 60000;
  21. const int PIN_SERVO = 8;
  22. LWiFiClient c;
  23. SeeedLedBar bar(LEF_BAR_CLK, LEF_BAR_DTA);
  24. int controlled_light_level;
  25. DHT dht(DHTPIN, DHTTYPE);
  26. Barometer myBarometer;
  27. int timestamp;
  28. unsigned long dust_duration_sum;
  29. Servo myservo;
  30. void blink(int i){ //控制LED闪烁的函数,没有Modem都是泪
  31.   for(int k=0;k<i;k++){
  32.       digitalWrite(PIN_LED, LOW);    // turn the LED off by making the voltage LOW
  33.       delay(200);              // wait for a second
  34.       digitalWrite(PIN_LED, HIGH);   // turn the LED on (HIGH is the voltage level)
  35.       delay(200);              // wait for a second
  36.   }
  37. }
  38. void setup() {
  39.   pinMode(PIN_LED, OUTPUT);
  40.   digitalWrite(PIN_LED, HIGH);
  41.   Serial1.begin(9600);
  42.   LWiFi.begin();
  43.   Serial1.println();
  44.   Serial1.print("Connecting to AP...");
  45.   if(LWiFi.connectWPA(WIFI_AP, WIFI_PASSWORD) < 0)
  46.   {
  47.     Serial1.println("FAIL!");
  48.     blink(5);
  49.     return;
  50.    }
  51.   blink(1);
  52.   Serial1.println("ok");
  53.   Serial1.print("Connecting to site...");
  54.   bar.begin(LEF_BAR_CLK, LEF_BAR_DTA);
  55.   bar.setLevel(0);
  56.   bar.setLevel(10);
  57.   delay(500);
  58.   bar.setLevel(0);
  59.   myBarometer.init();
  60.   timestamp = millis();
  61.   dust_duration_sum = 0.0;
  62. }
  63. void loop() {
  64.   if(!c.connect(SITE_URL, 4040))
  65.   {
  66.     Serial1.println("FAIL!");
  67.     blink(7);
  68.     bar.setLevel(0);
  69.     bar.setLevel(5);
  70.     delay(500);
  71.     bar.setLevel(0);
  72.     return;
  73.   }
  74.   Serial1.println("ok");
  75.   blink(4);
  76.   Serial1.println("send HTTP GET request");
  77.   int light_value = analogRead(PIN_LIGHT_SENSOR);
  78.   float temperature = 0.0;
  79.   float humidity = 0.0;
  80.   float pressure = myBarometer.bmp085GetPressure(myBarometer.bmp085ReadUP());
  81. //  float altitude = myBarometer.calcAltitude(pressure);
  82.   long  uv_sum = 0;
  83.   for(int i=0; i<32; i++)
  84.   {
  85.         uv_sum += analogRead(PIN_UV_SENSOR);
  86.         delay(10);
  87.   }
  88.   uv_sum = (uv_sum >> 5) * 4980.0 / 1023.0;
  89.   dust_duration_sum += pulseIn(PIN_DUST_SENSOR, LOW);
  90.   c.print("GET /sensor/update?");
  91.   c.print("light=");
  92.   c.print(light_value);
  93.   if(dht.readHT(&temperature, &humidity)){
  94.     c.print("&");
  95.     c.print("temperature=");
  96.     c.print(temperature);
  97.     c.print("&");
  98.     c.print("humidity=");
  99.     c.print(humidity);
  100.   }
  101.   c.print("&");
  102.   c.print("pressure=");
  103.   c.print(pressure);
  104.   c.print("&");
  105.   c.print("uv=");
  106.   c.print(uv_sum);
  107.   if ((millis() - timestamp) > DUST_SENSOR_SAMPLETIME){
  108.     c.print("&");
  109.     c.print("dust=");
  110.     c.print(dust_duration_sum);
  111.     dust_duration_sum = 0;
  112.     timestamp = millis();
  113.   }
  114.   c.println(" HTTP/1.1");
  115.   c.println("Host: " SITE_URL);
  116.   c.println("Connection: close");
  117.   c.println();
  118.   blink(4);
  119.   
  120.   if (c.available()){
  121.     char v = 0;
  122.     while(c.available()){
  123.       v = c.read();
  124.       if(v < 0)
  125.         break;
  126.       else{
  127.         controlled_light_level = v - '0';
  128.       }
  129.     }
  130.     if (controlled_light_level) bar.setLevel(controlled_light_level - 1);
  131.   }   
  132.   delay(1000);
  133. }
复制代码

数据服务器源代码(Node.js)

index.js
  1. /**
  2. * Author: rapidehere@gmail.com
  3. * Maintainer: rapidhere@gmail.com
  4. */
  5. 'use strict';
  6. var server = require('./lib/server').server;
  7. server.listen(4040, '0.0.0.0');
  8. console.log('server start on 0.0.0.0:4040');
  9. console.log('# PRESS CTRL-C TO STOP #');
  10. process.on('SIGINT', function() {
  11.   server.close();
  12.   process.exit(0);
  13. });
复制代码

/lib/Server.js
  1. /**
  2. * Author: rapidhere@gmail.com
  3. * Maintainer: rapidhere@gmail.com
  4. */
  5. 'use strict';
  6. var http = require('http');
  7. var url = require('url');
  8. var util = require('util');
  9. var sensorData = {
  10.   light: 0,
  11.   temperature: 0,
  12.   humidity: 0,
  13.   pressure: 0,
  14.   uv: 0,
  15.   dust: 0,
  16. };
  17. var currentLightLevel = 0;
  18. var nextLightLevel = 0;
  19. var _log = function(msg) {
  20.   var date = new Date();
  21.   console.log('[' + date.toISOString() + ']' + ' ' + msg);
  22. };
  23. var httpLogger = function(req, message) {
  24.   _log(req.socket.remoteAddress + ' ' + req.method + ' ' + req.url + ': ' + message);
  25. };
  26. var send200Body = function(req, res, content) {
  27.     res.writeHead(200, 'OK', {
  28.       'Content-Type': 'application/json',
  29.       'Content-Length': content.length
  30.     });
  31.     res.end(content, function() {
  32.       httpLogger(req, '200 OK: content: ' + content);
  33.     });
  34. };
  35. var server = http.createServer(function(req, res) {
  36.   httpLogger(req, 'get request');
  37.   // only handle GET method
  38.   if(req.method !== 'GET') {
  39.     res.writeHead(405, 'METHOD NOT ALLOWED');
  40.     res.end(function() {
  41.       httpLogger(req, '405 METHOD NOT ALLOWED');
  42.     });
  43.     return;
  44.   }
  45.   // parse url
  46.   var tmp = url.parse(req.url, true, false);
  47.   var pathname = tmp.pathname;
  48.   var query = tmp.query;
  49.   // return sensor data
  50.   if(pathname === '/sensor/get_data') {
  51.     send200Body(req, res, JSON.stringify(sensorData));
  52.   }
  53.   else if(pathname === '/light/level') {
  54.     nextLightLevel = parseInt(query.level || nextLightLevel);
  55.     send200Body(req, res, JSON.stringify({
  56.       'old_light_level': currentLightLevel
  57.     }));
  58.   }
  59.   // echo for debug usage
  60.   else if(pathname === '/sensor/echo') {
  61.     var msg = query.message || 'NO MESSAGE';
  62.     send200Body(req, res, msg);
  63.   }
  64.   // sensor message update
  65.   else if(pathname === '/sensor/update') {
  66.     sensorData.light = parseFloat(query.light || sensorData.light);
  67.     sensorData.humidity = parseFloat(query.humidity || sensorData.humidity);
  68.     sensorData.temperature = parseFloat(query.temperature || sensorData.temperature);
  69.     sensorData.pressure = parseFloat(query.pressure || sensorData.pressure);
  70.     sensorData.uv = parseFloat(query.uv || sensorData.uv);
  71.     sensorData.dust = parseFloat(query.dust || sensorData.dust);
  72.     httpLogger(req, 'update done: sensorData: ' + JSON.stringify(sensorData));
  73.     var returnContent = '0';
  74.     if(nextLightLevel !== currentLightLevel) {
  75.       currentLightLevel = nextLightLevel;
  76.       returnContent = String.fromCharCode(48 + currentLightLevel + 1);
  77.       httpLogger(req, 'turn light level to ' + currentLightLevel);
  78.     }
  79.     send200Body(req, res, returnContent);
  80.   }
  81.   // default url, return 404
  82.   else {
  83.     res.writeHead(404, 'NOT FOUND');
  84.     res.end(function() {
  85.       httpLogger(req, '404 NOT FOUND');
  86.     });
  87.     return ;
  88.   }
  89. });
  90. // server error handling
  91. server.on('clientError', function(err, socket) {
  92.   _log('# HTTP ERROR: ' + ': ' + err + '\nsocket: ' + util.inspect(socket));
  93. });
  94. exports.server = server;
复制代码



服务端代码众多,补充中...

Juice  高级技师 来自手机

发表于 2015-11-9 21:42:33

棒棒哒~~早说蹭妹子,赞助你一打啊
回复

使用道具 举报

放了假的人  见习技师
 楼主|

发表于 2015-11-9 22:53:58

Juice 发表于 2015-11-9 21:42
棒棒哒~~早说蹭妹子,赞助你一打啊

:funk: :funk: :funk:
回复

使用道具 举报

dsweiliang  初级技神

发表于 2015-11-9 23:12:33

好厉害的样子
回复

使用道具 举报

luna  初级技神

发表于 2015-11-11 17:58:19

楼主,你的ID简直拉仇恨啊~555
回复

使用道具 举报

放了假的人  见习技师
 楼主|

发表于 2015-11-11 23:25:06

luna 发表于 2015-11-11 17:58
楼主,你的ID简直拉仇恨啊~555

别误会 楼主学生 哈哈
回复

使用道具 举报

aerospacemaker  高级技师

发表于 2015-11-12 13:16:09

给力
回复

使用道具 举报

luna  初级技神

发表于 2015-11-12 18:15:22

放了假的人 发表于 2015-11-11 23:25
别误会 楼主学生 哈哈

还是不回复比较好~(刚才只是站着哭,现在已经跪着哭了……)
回复

使用道具 举报

Geemi  初级技匠

发表于 2016-1-11 14:45:20

路过………………
回复

使用道具 举报

winisky  学徒

发表于 2016-1-27 13:10:08

楼主,服务端代码能不给我发一下邮箱124044389@qq.com,非常感谢!
回复

使用道具 举报

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

本版积分规则

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

硬件清单

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

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

mail