Recipe Overview and Assumptions
List of materials
Item
|
Part Description
|
Quantity
|
Link
|
1
|
MediaTek 7688 Duo
|
1
|
TBD
|
2
|
RAVPower 7800mAh Portable Charger Power Bank External Battery Pack (or similar)
|
1
|
|
3
|
Male USB A to Male USB OTG Cable
|
1
|
|
4
|
SEEED Light Sensor
|
1
|
|
5
|
SEEED DHT11 Temp and Humidity Sensor
|
1
|
|
6
|
Small Breadboard
|
1
|
Step 1: Program the MediaTek 7688 Duo MT7688
Install the necessary software
# Generate your identity key on openwrt
dropbearkey -t rsa -f ~/.ssh/id_rsa
# Convert public key from dropbear binary to openssh text
# Copy and paste output from below to bitbucket account ssh keys
dropbearkey -y -f ~/.ssh/id_rsa | head -n 2 | tail -1
# Change git ssh command
echo "#!/bin/sh" > ~/.gitssh.sh
echo "dbclient -y -i ~/.ssh/id_rsa $*" >> ~/.gitssh.sh
chmod +x ~/.gitssh.sh
echo "export GIT_SSH=$HOME/.gitssh.sh" >> /etc/profile
# Now login again to openwrt
# Now use git (finally)
git clone git@bitbucket.org:vivaplanet/seeedplanthealthsource.git
Option A: Clone the source from git
npm start
Option B: Upload the necessary code
express seeedrecipe
cd seeedrecipe
npm install
npm start
var DocumentDBClient = require('documentdb').DocumentClient;
var DocDBUtils = {
getOrCreateDatabase: function (client, databaseId, callback) {
var querySpec = {
query: 'SELECT * FROM root r WHERE r.id=@id',
parameters: [{
name: '@id',
value: databaseId
}]
};
client.queryDatabases(querySpec).toArray(function (err, results) {
if (err) {
callback(err);
} else {
if (results.length === 0) {
var databaseSpec = {
id: databaseId
};
client.createDatabase(databaseSpec, function (err, created) {
callback(null, created);
});
} else {
callback(null, results[0]);
}
}
});
},
getOrCreateCollection: function (client, databaseLink, collectionId, callback) {
var querySpec = {
query: 'SELECT * FROM root r WHERE r.id=@id',
parameters: [{
name: '@id',
value: collectionId
}]
};
client.queryCollections(databaseLink, querySpec).toArray(function (err, results) {
if (err) {
callback(err);
} else {
if (results.length === 0) {
var collectionSpec = {
id: collectionId
};
var requestOptions = {
offerType: 'S1'
};
client.createCollection(databaseLink, collectionSpec, requestOptions, function (err, created) {
callback(null, created);
});
} else {
callback(null, results[0]);
}
}
});
}
};
module.exports = DocDBUtils;
var DocumentDBClient = require('documentdb').DocumentClient;
var docdbUtils = require('./docdbUtils');
function TaskDao(documentDBClient, databaseId, collectionId) {
this.client = documentDBClient;
this.databaseId = databaseId;
this.collectionId = collectionId;
this.database = null;
this.collection = null;
}
module.exports = TaskDao;
TaskDao.prototype = {
init: function (callback) {
var self = this;
docdbUtils.getOrCreateDatabase(self.client, self.databaseId, function (err, db) {
if (err) {
callback(err);
} else {
self.database = db;
docdbUtils.getOrCreateCollection(self.client, self.database._self, self.collectionId, function (err, coll) {
if (err) {
callback(err);
} else {
self.collection = coll;
}
});
}
});
},
find: function (querySpec, callback) {
var self = this;
self.client.queryDocuments(self.collection._self, querySpec).toArray(function (err, results) {
if (err) {
callback(err);
} else {
callback(null, results);
}
});
},
addItem: function (item, callback) {
var self = this;
fs.readFile(path.join(__dirname, '/screenlog.0'), 'utf8', function (err, content) {
if (err) {
return callback(err);
}
else
{
console.log('cat returned some content: ' + content);
var tmp1 = content.split(" ")
console.log('tmp1 content: ' + tmp1);
var item = {
Address: "$00000",
DeviceID: "17564321",
Time: Date(),
LightValue: tmp1[tmp1.length - 5],
TempValue: tmp1[tmp1.length - 3],
HumidValue: tmp1[tmp1.length-1]
};
self.client.createDocument(self.collection._self, item, function (err, doc) {
if (err) {
callback(err);
}
else
{
console.log(new Date(), 'Uploaded: ' + item.Address + ' ' + item.Time + ' ' + item.LightValue + ' ' + item.TempValue + ' ' + item.HumidValue);
}
});
}
});
},
updateItem: function (itemId, callback) {
var self = this;
self.getItem(itemId, function (err, doc) {
if (err) {
callback(err);
} else {
doc.completed = true;
self.client.replaceDocument(doc._self, doc, function (err, replaced) {
if (err) {
callback(err);
} else {
callback(null, replaced);
}
});
}
});
},
getItem: function (itemId, callback) {
var self = this;
var querySpec = {
query: 'SELECT * FROM root r WHERE r.id=@id',
parameters: [{
name: '@id',
value: itemId
}]
};
self.client.queryDocuments(self.collection._self, querySpec).toArray(function (err, results) {
if (err) {
callback(err);
} else {
callback(null, results[0]);
}
});
}
};
Create the controller
var DocumentDBClient = require('documentdb').DocumentClient;
var async = require('async');
function TaskList(taskDao) {
this.taskDao = taskDao;
}
module.exports = TaskList;
TaskList.prototype = {
showTasks: function (req, res) {
var self = this;
var querySpec =
{
query: 'SELECT d.Address, d.Time, d.LightValue, d.TempValue, d.HumidValue FROM OpenDevices d WHERE d.Address=@SensorType', //d.DeviceSensors[1].SensorType=@SensorType',
parameters: [
{name: '@SensorType', value: '$00000'}
]
/*query: 'SELECT * FROM OpenDevices r'*/
};
self.taskDao.find(querySpec, function (err, items)
{
if (err)
{
callback(err);
}
res.render('index', {
title: 'My Environment Information',
tasks: items,
JSONTasks: JSON.stringify(items)
});
});
},
addTask: function (req, res) {
var self = this;
var item;
var rule = new cron.RecurrenceRule();
rule.minute = new cron.Range(0, 59, 3);//should update every 3 mins
// rule.second = 30;
cron.scheduleJob(rule, function()
{
self.taskDao.addItem(item, function (err) {
if (err) {
throw (err);
}
res.redirect('/');
});
});
},
completeTask: function (req, res) {
var self = this;
var completedTasks = Object.keys(req.body);
async.forEach(completedTasks, function taskIterator(completedTask, callback) {
self.taskDao.updateItem(completedTask, function (err) {
if (err) {
callback(err);
} else {
callback(null);
}
});
}, function goHome(err) {
if (err) {
throw err;
} else {
res.redirect('/');
}
});
}
};
Add config.js
var config = {}
var config = {}
config.host = process.env.HOST || "https://vivaplanetdbdev.documents.azure.com:443/";
config.authKey = process.env.AUTH_KEY || "0NdwA+touBPzjWHApBEvEyLGB/WDNEyMRl3t0CtOXS+Qw84EO5jTMGxoLSPdccr2Lf5iC8PedJ165B/+1ZG4vA==";
config.databaseId = "OpenSeeedRecipie-0";
config.collectionId = "OpenDevices";
module.exports = config;
Modify app.js
var DocumentDBClient = require('documentdb').DocumentClient;
var config = require('./config');
var TaskList = require('./routes/tasklist');
var TaskDao = require('./models/taskDao');
app.use('/', routes);
app.use('/users', users);
var docDbClient = new DocumentDBClient(config.host, {
masterKey: config.authKey
});
var taskDao = new TaskDao(docDbClient, config.databaseId, config.collectionId);
var taskList = new TaskList(taskDao);
taskDao.init();
app.get('/', taskList.showTasks.bind(taskList));
app.post('/addtask', taskList.addTask.bind(taskList));
app.post('/completetask', taskList.completeTask.bind(taskList));
Build a user interface
doctype html
html
head
title= title
link(rel='stylesheet', href='//ajax.aspnetcdn.com/ajax/bootstrap/3.3.2/css/bootstrap.min.css')
link(rel='stylesheet', href='/stylesheets/style.css')
body
nav.navbar.navbar-inverse.navbar-fixed-top
div.navbar-header
a.navbar-brand(href='#') My Tasks
block content
script(src='//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.2.min.js')
script(src='//ajax.aspnetcdn.com/ajax/bootstrap/3.3.2/bootstrap.min.js')
extends layout
block content
h1 #{title}
br
form(action="/", method="post")
table.table.table-striped.table-bordered
tr
td Address
td Time
td Light Intensity
td Humidity
td Temperature
if (typeof tasks === "undefined")
tr
td
else
each task in tasks
tr
td #{task.Time}
td #{task.Address}
td #{task.LightValue}
td #{task.TempValue}
td #{task.HumidValue}
hr
form.well(action="/addtask", method="post")
br
button.btn(type="submit") Start Uploading Sensor Data
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}
.well label {
display: block;
}
.well input {
margin-bottom: 5px;
}
.btn {
margin-top: 5px;
border: outset 1px #C8C8C8;
}
Step 2: Connect the sensors
Step 3: Program the Arduino Enabled ATMega323 and test
#include <dht11.h>
//
// FILE: dht11_test1.pde
// PURPOSE: DHT11 library test sketch for Arduino
//
//Celsius to Fahrenheit conversion
double Fahrenheit(double celsius)
{
return 1.8 * celsius + 32;
}
int i = 0;
// fast integer version with rounding
//int Celcius2Fahrenheit(int celcius)
//{
// return (celsius * 18 + 5)/10 + 32;
//}
//Celsius to Kelvin conversion
double Kelvin(double celsius)
{
return celsius + 273.15;
}
// dewPoint function NOAA
// reference (1) : http://wahiduddin.net/calc/density_algorithms.htm
// reference (2) : http://www.colorado.edu/geography/weather_station/Geog_site/about.htm
//
double dewPoint(double celsius, double humidity)
{
// (1) Saturation Vapor Pressure = ESGG(T)
double RATIO = 373.15 / (273.15 + celsius);
double RHS = -7.90298 * (RATIO - 1);
RHS += 5.02808 * log10(RATIO);
RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1/RATIO ))) - 1) ;
RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ;
RHS += log10(1013.246);
// factor -3 is to adjust units - Vapor Pressure SVP * humidity
double VP = pow(10, RHS - 3) * humidity;
// (2) DEWPOINT = F(Vapor Pressure)
double T = log(VP/0.61078); // temp var
return (241.88 * T) / (17.558 - T);
}
// delta max = 0.6544 wrt dewPoint()
// 6.9 x faster than dewPoint()
// reference: http://en.wikipedia.org/wiki/Dew_point
double dewPointFast(double celsius, double humidity)
{
double a = 17.271;
double b = 237.7;
double temp = (a * celsius) / (b + celsius) + log(humidity*0.01);
double Td = (b * temp) / (a - temp);
return Td;
}
#include <dht11.h>
dht11 DHT11;
#define DHT11PIN 4
#define LIGHTPIN 3
int tempPIN = 10;
int humidPIN = 12;
void setup()
{
Serial.begin(115200);
Serial.println("DHT11 TEST PROGRAM ");
Serial.print("LIBRARY VERSION: ");
Serial.println(DHT11LIB_VERSION);
Serial.println();
Serial1.begin(57600); // open internal serial connection to MT7688
// make our digital pin an output
// pinMode(tempPIN, OUTPUT);
// pinMode(humidPIN, OUTPUT);
}
float lightVal = 0;
float maxLightVal = 0;
void loop()
{
char buffer [50];
Serial1.println("n");
int chk = DHT11.read(DHT11PIN);
// Serial.print("Read sensor: ");
switch (chk)
{
case DHTLIB_OK:
Serial1.println("OK");
break;
case DHTLIB_ERROR_CHECKSUM:
Serial1.println("Checksum error");
break;
case DHTLIB_ERROR_TIMEOUT:
Serial1.println("Time out error");
break;
default:
Serial1.println("Unknown error");
break;
}
float humidVal = (float)DHT11.humidity;
float tempVal = (float)DHT11.temperature;
int lightValIn = analogRead(0);
if(lightVal > maxLightVal)//self calibrating
{
maxLightVal = lightVal;
}
lightVal = map(lightValIn, 0, 1023, 0 ,255);
float lightPct = ((float)lightVal/maxLightVal)*100;
Serial1.print("Light (%): "); Serial1.print(lightPct);
Serial1.print("Humidity (%): "); Serial1.print((float)DHT11.humidity, 2);
Serial1.print("Temperature (F): "); Serial1.print(Fahrenheit(DHT11.temperature), 2);
delay(120000); //every two mins.
}
//
// END OF FILE
Step 4: Put it all together
screen /dev/ttyUSB1 57600
npm start
Step 5: Monitor your plant’s health