[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
棒棒哒~~早说蹭妹子,赞助你一打啊
:funk: :funk: :funk: 好厉害的样子 楼主,你的ID简直拉仇恨啊~555 luna 发表于 2015-11-11 17:58
楼主,你的ID简直拉仇恨啊~555
别误会 楼主学生 哈哈 给力 放了假的人 发表于 2015-11-11 23:25
别误会 楼主学生 哈哈
{:5_137:} 还是不回复比较好~(刚才只是站着哭,现在已经跪着哭了……) 路过………………{:5_175:} 楼主,服务端代码能不给我发一下邮箱124044389@qq.com,非常感谢!
页:
[1]