放了假的人 发表于 2015-11-9 16:54:31

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

本帖最后由 放了假的人 于 2015-11-9 22:57 编辑

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


在没有读取办法USB MODEM的情况下能成功调试真的很开心

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


最后长这个样子,传感器加数据线加WIFI天线刚好八根,八爪鱼(章鱼哥)的名字也取自它


网页端实时数据与数据统计页面


移动端数据统计页面

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



static/image/hrline/line4.png

Arduino源代码
#include <LWiFi.h>
#include <LWiFiClient.h>

#include <Wire.h>
#include <Suli.h>
#include "Seeed_LED_Bar_Arduino.h"
#include "DHT.h"
#include "Barometer.h"
#include <Servo.h>

#define WIFI_AP "WLAN-SSID"
#define WIFI_PASSWORD "WLAN-PWD"
#define SITE_URL "数据服务器IP地址"

const int PIN_LED= 13;
const int LEF_BAR_CLK = 8;
const int LEF_BAR_DTA = 7;
const int PIN_LIGHT_SENSOR = A0;
#define DHTPIN 2
#define DHTTYPE DHT22
const int PIN_UV_SENSOR = A1;
const int PIN_DUST_SENSOR = 3;
const unsigned long DUST_SENSOR_SAMPLETIME = 60000;
const int PIN_SERVO = 8;

LWiFiClient c;
SeeedLedBar bar(LEF_BAR_CLK, LEF_BAR_DTA);
int controlled_light_level;
DHT dht(DHTPIN, DHTTYPE);
Barometer myBarometer;
int timestamp;
unsigned long dust_duration_sum;
Servo myservo;

void blink(int i){ //控制LED闪烁的函数,没有Modem都是泪
for(int k=0;k<i;k++){
      digitalWrite(PIN_LED, LOW);    // turn the LED off by making the voltage LOW
      delay(200);            // wait for a second
      digitalWrite(PIN_LED, HIGH);   // turn the LED on (HIGH is the voltage level)
      delay(200);            // wait for a second
}
}

void setup() {
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, HIGH);
Serial1.begin(9600);
LWiFi.begin();
Serial1.println();
Serial1.print("Connecting to AP...");
if(LWiFi.connectWPA(WIFI_AP, WIFI_PASSWORD) < 0)
{
    Serial1.println("FAIL!");
    blink(5);
    return;
   }
blink(1);
Serial1.println("ok");
Serial1.print("Connecting to site...");
bar.begin(LEF_BAR_CLK, LEF_BAR_DTA);
bar.setLevel(0);
bar.setLevel(10);
delay(500);
bar.setLevel(0);
myBarometer.init();
timestamp = millis();
dust_duration_sum = 0.0;
}

void loop() {
if(!c.connect(SITE_URL, 4040))
{
    Serial1.println("FAIL!");
    blink(7);
    bar.setLevel(0);
    bar.setLevel(5);
    delay(500);
    bar.setLevel(0);
    return;
}
Serial1.println("ok");
blink(4);
Serial1.println("send HTTP GET request");

int light_value = analogRead(PIN_LIGHT_SENSOR);
float temperature = 0.0;
float humidity = 0.0;
float pressure = myBarometer.bmp085GetPressure(myBarometer.bmp085ReadUP());
//float altitude = myBarometer.calcAltitude(pressure);
longuv_sum = 0;
for(int i=0; i<32; i++)
{
      uv_sum += analogRead(PIN_UV_SENSOR);
      delay(10);
}
uv_sum = (uv_sum >> 5) * 4980.0 / 1023.0;
dust_duration_sum += pulseIn(PIN_DUST_SENSOR, LOW);

c.print("GET /sensor/update?");
c.print("light=");
c.print(light_value);
if(dht.readHT(&temperature, &humidity)){
    c.print("&");
    c.print("temperature=");
    c.print(temperature);
    c.print("&");
    c.print("humidity=");
    c.print(humidity);
}
c.print("&");
c.print("pressure=");
c.print(pressure);
c.print("&");
c.print("uv=");
c.print(uv_sum);
if ((millis() - timestamp) > DUST_SENSOR_SAMPLETIME){
    c.print("&");
    c.print("dust=");
    c.print(dust_duration_sum);
    dust_duration_sum = 0;
    timestamp = millis();
}
c.println(" HTTP/1.1");
c.println("Host: " SITE_URL);
c.println("Connection: close");
c.println();
blink(4);

if (c.available()){
    char v = 0;
    while(c.available()){
      v = c.read();
      if(v < 0)
      break;
      else{
      controlled_light_level = v - '0';
      }
    }
    if (controlled_light_level) bar.setLevel(controlled_light_level - 1);
}   
delay(1000);
}
数据服务器源代码(Node.js)

index.js
/**
* Author: rapidehere@gmail.com
* Maintainer: rapidhere@gmail.com
*/

'use strict';

var server = require('./lib/server').server;

server.listen(4040, '0.0.0.0');
console.log('server start on 0.0.0.0:4040');
console.log('# PRESS CTRL-C TO STOP #');

process.on('SIGINT', function() {
server.close();
process.exit(0);
});

/lib/Server.js
/**
* Author: rapidhere@gmail.com
* Maintainer: rapidhere@gmail.com
*/
'use strict';

var http = require('http');
var url = require('url');
var util = require('util');

var sensorData = {
light: 0,
temperature: 0,
humidity: 0,
pressure: 0,
uv: 0,
dust: 0,
};

var currentLightLevel = 0;
var nextLightLevel = 0;

var _log = function(msg) {
var date = new Date();
console.log('[' + date.toISOString() + ']' + ' ' + msg);
};

var httpLogger = function(req, message) {
_log(req.socket.remoteAddress + ' ' + req.method + ' ' + req.url + ': ' + message);
};

var send200Body = function(req, res, content) {
    res.writeHead(200, 'OK', {
      'Content-Type': 'application/json',
      'Content-Length': content.length
    });
    res.end(content, function() {
      httpLogger(req, '200 OK: content: ' + content);
    });
};

var server = http.createServer(function(req, res) {
httpLogger(req, 'get request');

// only handle GET method
if(req.method !== 'GET') {
    res.writeHead(405, 'METHOD NOT ALLOWED');
    res.end(function() {
      httpLogger(req, '405 METHOD NOT ALLOWED');
    });
    return;
}

// parse url
var tmp = url.parse(req.url, true, false);

var pathname = tmp.pathname;
var query = tmp.query;

// return sensor data
if(pathname === '/sensor/get_data') {
    send200Body(req, res, JSON.stringify(sensorData));
}
else if(pathname === '/light/level') {
    nextLightLevel = parseInt(query.level || nextLightLevel);
    send200Body(req, res, JSON.stringify({
      'old_light_level': currentLightLevel
    }));
}
// echo for debug usage
else if(pathname === '/sensor/echo') {
    var msg = query.message || 'NO MESSAGE';
    send200Body(req, res, msg);
}
// sensor message update
else if(pathname === '/sensor/update') {
    sensorData.light = parseFloat(query.light || sensorData.light);
    sensorData.humidity = parseFloat(query.humidity || sensorData.humidity);
    sensorData.temperature = parseFloat(query.temperature || sensorData.temperature);
    sensorData.pressure = parseFloat(query.pressure || sensorData.pressure);
    sensorData.uv = parseFloat(query.uv || sensorData.uv);
    sensorData.dust = parseFloat(query.dust || sensorData.dust);

    httpLogger(req, 'update done: sensorData: ' + JSON.stringify(sensorData));

    var returnContent = '0';
    if(nextLightLevel !== currentLightLevel) {
      currentLightLevel = nextLightLevel;
      returnContent = String.fromCharCode(48 + currentLightLevel + 1);
      httpLogger(req, 'turn light level to ' + currentLightLevel);
    }

    send200Body(req, res, returnContent);
}
// default url, return 404
else {
    res.writeHead(404, 'NOT FOUND');
    res.end(function() {
      httpLogger(req, '404 NOT FOUND');
    });
    return ;
}
});

// server error handling
server.on('clientError', function(err, socket) {
_log('# HTTP ERROR: ' + ': ' + err + '\nsocket: ' + util.inspect(socket));
});

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
别误会 楼主学生 哈哈

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

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

路过………………{:5_175:}

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

楼主,服务端代码能不给我发一下邮箱124044389@qq.com,非常感谢!
页: [1]
查看完整版本: [Hack上海]Project Brother Octopus, 植被自动维护系统