3
0
Fork 0

Added project

This commit is contained in:
Willem Cazander 2022-11-13 01:46:38 +01:00
parent fe9aa14dfd
commit 2d73cc8845
186 changed files with 21174 additions and 0 deletions

51
.gitignore vendored Normal file
View file

@ -0,0 +1,51 @@
#
# Xensit Xnode Ignore list
#
# Ignore all build folders
build
# Ignore all generated source
*.generated
# Ignore local include makefile
lib-build/make/Makefile.inc.local
# Ignore nodejs deps + www logs
lib-build/debug-server/www_logs
lib-build/debug-server/node_modules
lib-build/debug-server/npm-debug.log
lib-build/debug-server/www_static/js/lib
lib-build/debug-server/www_static/css/lib
# Ignore binary files
*.o
*.class
*.old
*.bak
*.backup
*.dat
*.data
*.gif
*.pdf
*.doc
# Ignore ~ backup files.
*~
# Ignore some eclipse files
.settings
.classpath
# Ignore netbeans directory
nbproject
# Ignore mac finder files
.DS_Store
# Ignore windows files.
Thumbs.db
Desktop.ini
# Ignore kde dolphin files
.directory

11
.project Normal file
View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>xnode</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
</natures>
</projectDescription>

52
Makefile Normal file
View file

@ -0,0 +1,52 @@
#
# Call ordered depency make commands
#
# Save command line option for child
ARD_HOME_ARGU = ${ARD_HOME}
# Optional include an local override for arduino home
-include lib-build/make/Makefile.inc.local
# Safety check
ifndef ARD_HOME
$(error $$(ARD_HOME) not defined)
endif
# Only pass to child if defined from command line.
ifndef ARD_HOME_ARGU
ARD_HOME_ARGU_CHILD = ARD_HOME=${ARD_HOME}
endif
# Locate and check for avr-size
AVR_SIZE ?= $(ARD_HOME)/hardware/tools/avr/bin/avr-size
ifeq ("$(wildcard $(AVR_SIZE))","")
$(error $$(AVR_SIZE) not found)
endif
# Define all projects to build
PROJECTS = \
xnode-base \
xnode-mega-flash \
xnode-satellite \
xnode-test-blink
# Hook all to projects
all: projects
# Declare subprojects targets
.PHONY: $(PROJECTS)
projects: $(PROJECTS) projects-result
$(PROJECTS):
$(MAKE) -s -C $@ clean all ${ARD_HOME_ARGU_CHILD}
projects-result:
@echo
@echo "Full xnode build is ready to burn."
@echo
@echo "Binary program sizes;"
${AVR_SIZE} xnode*/build/*.hex
@echo
@echo "Done."
# Declare subprojects depencies
xnode-mega-flash: xnode-satellite xnode-base xnode-test-blink

38
build.txt Normal file
View file

@ -0,0 +1,38 @@
Minimal build setup:
1) Download+unzip arduino ide version 1.6.0 is tested !
Get it from http://www.arduino.cc/
2) Install make and tcl;
apt-get install tcl make
3) Build all project in one go;
make ARD_HOME=/path/to/arduino-ide-1.6.0
Optional:
To override multiple flags and parameters for every
build there is a local makefile include template.
1) cp lib-build/make/Makefile.inc.local-template lib-build/make/Makefile.inc.local
2) Edit lib-build/make/Makefile.inc.local
3) make or cd xnode-base;make/etc
Now the make command of the sub projects and this parent build
will use the local make settings.
Build commands;
Build all hex files;
cd project-root/
make
Build clean satellite only
cd project-root/xnode-satellite
make clean all
Build and upload clean base only
cd project-root/xnode-base
make clean upload
make clean upload ISP_PORT=/dev/ttyACM1

View file

@ -0,0 +1,291 @@
var XDeviceEncryption = require('./xdevice-encryption');
var Mongoose = require('mongoose');
var XSystemState = Mongoose.model( 'xsystem-state' );
var XNode = Mongoose.model( 'xnode' );
var XNodeBase = Mongoose.model( 'xnode-base' );
var XNodeBaseCommand = Mongoose.model( 'xnode-base-command' );
var XNodeData = Mongoose.model( 'xnode-data' );
var XNodeDataValue = Mongoose.model( 'xnode-data-value' );
var Async = require('async');
var Winston = require('winston');
var Log = Winston.loggers.get('main');
var createXnodeBase = function(created_ip,callbackResult) {
Async.series({
cnt_base_net_id: function(callback) {
XSystemState.incHexByName('_a_seq_base_net_id', function(err, xprop) {
callback(err,xprop.value);
});
},
cnt_base_net_key: function(callback){
XSystemState.incHexByName('_a_seq_base_net_key', function(err, xprop) {
callback(err,xprop.value);
});
},
cnt_base_net_mac: function(callback){
XSystemState.incHexByName('_a_seq_base_net_mac', function(err, xprop) {
callback(err,xprop.value);
});
},
cnt_base_rf_key: function(callback){
XSystemState.incHexByName('_a_seq_base_rf_key', function(err, xprop) {
callback(err,xprop.value);
});
}
},
function(err, result) {
if (err) {
callbackResult(err);
return;
}
var xnodeBase = new XNodeBase();
xnodeBase.created_ip = created_ip;
xnodeBase.net_id = result.cnt_base_net_id;
xnodeBase.net_key = result.cnt_base_net_key;
xnodeBase.net_mac = result.cnt_base_net_mac;
xnodeBase.rf_key = result.cnt_base_rf_key;
xnodeBase.init_index = 0;
xnodeBase.save(function(err,xnodeBase) {
Log.debug("XDeviceConnectorA.createXnodeBase _id="+xnodeBase._id+" net_id="+xnodeBase.net_id);
callbackResult(err,xnodeBase);
});
});
}
var handleInit = function(req,res,next) {
Log.debug('XDeviceConnectorA.handleInit');
var net_id = req.body.ni;
if (net_id == 0) {
createXnodeBase(req.ip,function(err,xnodeBase) {
if (err) { return next(err); }
Log.info('XDeviceConnectorA.handleInit response: Xinet_id '+xnodeBase.net_id);
res.send('Xinet_id '+xnodeBase.net_id+'\n');
});
} else {
XNodeBase.findOneByNetId(net_id,function(err,xnodeBase) {
if (err) { return next(err); }
if (xnodeBase == null) {
Log.warn('XDeviceConnectorA.handleInit illegal net_id='+req.body.ni);
res.send('X');
return;
}
if (xnodeBase.init_index > 4) {
Log.error('initXnode stateError, device was already init-ited.');
res.send('XXreboot');
return;
}
xnodeBase.init_index++;
xnodeBase.save(function(err) {
var result;
if (xnodeBase.init_index == 1) {
result = 'Xinet_key '+xnodeBase.net_key;
} else if (xnodeBase.init_index == 2) {
result = 'Xinet_mac '+xnodeBase.net_mac;
} else if (xnodeBase.init_index == 3) {
result = 'Xirf_key '+xnodeBase.rf_key;
} else if (xnodeBase.init_index == 4) {
result = 'Xireboot';
XNodeBaseCommand.insert(net_id,'help',req.ip,next); // trigger node data on next ping :)
} else {
result = 'X'; // code error
}
Log.info('XDeviceConnectorA.initXnode response net_id='+xnodeBase.net_id+' step='+xnodeBase.init_index+' reply='+result);
res.send(result);
});
});
}
};
var replyCommand = function(req,res,next) {
XNodeBaseCommand.findOpenByNetId(req.body.ni, function ( err, data ) {
if (err) { return next(err); }
if (data.length > 0) {
Log.debug('XDeviceConnectorA.replyCommand send cmd: '+data[0].command);
data[0].send_date = new Date();
data[0].save(function(err) {
if (err) { return next(err); }
res.send('XX'+data[0].command);
});
} else {
res.send('X');
}
});
}
var logData = function(xnode,data_raw,data_text,req) {
var xdata = new XNodeData();
xdata.remote_ip = req.ip;
xdata.node_id = xnode.node_id;
xdata.net_id = req.body.ni;
xdata.data_raw = data_raw;
xdata.data_text = data_text;
xdata.save(function(err,xdata) {
if (err) { return next(err); }
Log.debug("XDeviceConnectorA.logData _id="+xdata._id+" net_id="+xdata.net_id+" node_id="+xdata.node_id+" data_text.length="+xdata.data_text.length);
});
}
var logDataValue = function(xnode,data_text,req, next) {
if (data_text.indexOf('help') > 0) {
return; // done't save help cmds as values.
}
if (data_text.indexOf('#ERR') > 0) {
return; // done't save errors.
}
var data_lines = data_text.split('\n');
for(var i = 0; i < data_lines.length; i ++) {
var data_line = data_lines[i];
var data_rows = data_line.split('=');
var data_name = data_rows[0];
var data_value = data_rows[1];
if (data_name.length == 0) {
continue;
}
xval = new XNodeDataValue();
xval.net_id = xnode.net_id;
xval.node_id = xnode.node_id;
xval.name = data_name;
xval.value = data_value;
xval.save(function(err,xval) {
if (err) { return next(err); }
Log.debug("XDeviceConnectorA.logDataValue _id="+xval._id+" net_id="+xval.net_id+" node_id="+xval.node_id+" name="+xval.name+" value="+xval.value);
});
}
}
var logRxCount = function(xnode,data_text,next) {
xnode.rx_date=new Date();
xnode.rx_requests++;
xnode.rx_bytes=data_text.length + xnode.rx_bytes;
xnode.save(function(err,xnode) {
if (err) { return next(err); }
Log.debug("XDeviceConnectorA.logRxCount _id="+xnode._id+" rx_requests="+xnode.rx_requests);
});
}
var updateMetaInfo = function(xnode,req, next) {
if (xnode.info_date == null) {
xnode.info_date = new Date(new Date().getTime() - 60*60*1000 - 1000); // triggers update
}
var dateLastHour = new Date(new Date().getTime() - 60*60*1000);
if (dateLastHour.getTime() > xnode.info_date.getTime() ) {
if (xnode.node_id == 1) {
XNodeBaseCommand.insert(xnode.net_id,'sys_info',req.ip, next);
XNodeBaseCommand.insert(xnode.net_id,'rf_info',req.ip, next);
XNodeBaseCommand.insert(xnode.net_id,'net_info',req.ip, next);
XNodeBaseCommand.insert(xnode.net_id,'net_info_eth0',req.ip, next);
} else {
Log.debug("todo");
//XNodeBaseCommand.insert(xnode.net_id,'@2 sys_info',req.ip, next);
//XNodeBaseCommand.insert(xnode.net_id,'@2 rf_info',req.ip, next);
}
xnode.info_date = new Date();
xnode.save(function(err,xsat) {
if (err) { return next(err); }
Log.debug("XDeviceConnectorA.updateMetaInfo _id="+xnode._id);
});
}
}
var handleDataNode = function(xnode,req,res,next) {
var net_id = req.body.ni;
var node_id = req.body.nn;
var data_raw = req.body.nd;
Log.debug('XDeviceConnectorA.handleDataNode ni='+xnode.net_id);
// log.debug('XDeviceConnectorA.handleData TODO FIX XXTEA');
// var result = XDeviceEncryption.xxteaDecrypt(req.body.nd,xnode.hw_net_key);
// log.debug('XDeviceConnectorA.handleData xxteaDecrypt='+result+' hw_net_key='+xnode.hw_net_key);
var data_text = data_raw;
logRxCount (xnode,data_text,next);
logData (xnode,data_raw,data_text,req);
logDataValue (xnode,data_text, req, next);
updateMetaInfo (xnode,req, next);
replyCommand (req,res,next);
}
var handleData = function(req,res,next) {
var net_id = req.body.ni;
var node_id = req.body.nn;
if (net_id == '000000000000') {
Log.warn('XDeviceConnectorA.handleData zero net_id='+net_id);
res.send('X');
return;
}
if (node_id == 0) {
Log.warn('XDeviceConnectorA.handleData zero node_id='+node_id);
res.send('X');
return;
}
Log.debug('XDeviceConnectorA.handleData ni='+net_id+' nn='+node_id);
XNode.findByNetIdAndNodeId(net_id,node_id,function(err,xnode) {
if (err) { return next(err); }
if (xnode == null) {
Log.debug('XDeviceConnectorA.handleData creating new XNode.');
xnode = new XNode();
xnode.net_id = net_id;
xnode.node_id = node_id;
xnode.save(function(err) {
if (err) { return next(err); }
handleDataNode(xnode,req,res,next);
});
} else {
handleDataNode(xnode,req,res,next);
}
});
}
var handlePing = function(req,res,next) {
var net_id = req.body.ni;
if (net_id == '000000000000') {
Log.debug('XDeviceConnectorA.handlePing zero net_id='+net_id);
res.send('X');
return;
}
Log.debug('XDeviceConnectorA.handlePing net_id='+net_id);
XNodeBase.findOneByNetId(net_id,function(err,xnodeBase) {
if (err) { return next(err); }
if (xnodeBase == null) {
Log.warn('XDeviceConnectorA.handlePing illegal net_id='+net_id);
res.send('X');
return;
}
xnodeBase.ping_counter++;
xnodeBase.ping_last_date=new Date();
xnodeBase.save(function(err) {
if (err) { return next(err); }
if (req.body.rc == 0) {
res.send('X'); // no cmd on first ping.
} else {
replyCommand(req,res,next);
}
});
});
}
exports.deviceControl = function(req, res, next) {
var postType = req.body.pt;
var url = req.originalUrl || req.url;
var ip = req.ip;
Log.debug('POST '+url+' ip='+ip+' pt='+postType+' rc='+req.body.rc+' ni='+req.body.ni);
if (postType == 'i') {
handleInit(req,res,next);
return;
}
if (postType == 'p') {
handlePing(req,res,next);
return;
}
if (postType == 'd') {
handleData(req,res,next);
return;
}
Log.warn('XDeviceConnectorA.deviceControl unknown postType='+postType);
res.send('X');
}

View file

@ -0,0 +1,120 @@
var winston = require('winston');
var log = winston.loggers.get('main');
var delta = 0x9E3779B9;
var XXTEA_NUM_ROUNDS = 32;
//var xxxteaEncrypt = function(unsigned long v[2]) {
// unsigned int i;
// unsigned long v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
// for (i=0; i < XXTEA_NUM_ROUNDS; i++) {
// v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + XBHardware.config.net_key.u32[sum & 3]);
// sum += delta;
// v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + XBHardware.config.net_key.u32[(sum>>11) & 3]);
// }
// v[0]=v0; v[1]=v1;
//}
var xxxteaDecrypt = function(vArray,keyBuff) {
log.debug('XDeviceEncryption.xxxteaDecrypt vArray='+JSON.stringify(vArray)+' keyBuff='+JSON.stringify(keyBuff));
var v0=vArray.v0;
var v1=vArray.v1;
log.debug('XDeviceEncryption.xxxteaDecrypt start v0='+v0.toString(16)+' v1='+v1.toString(16));
var delta=0x9E3779B9
var sum=delta*XXTEA_NUM_ROUNDS;
for (var i=0; i < XXTEA_NUM_ROUNDS; i++) {
v1 -= (((v0 << 4) ^ (v0 >>> 5)) + v0) ^ (sum + keyBuff.readUInt32BE((sum>>>11) & 3));
var v1Org = v1;
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >>> 5)) + v1) ^ (sum + keyBuff.readUInt32BE(sum & 3));
//log.debug('XDeviceEncryption.xxxteaDecrypt v0='+v0.toString(16)+' v1='+v1.toString(16));
if (v0 < 0) { v0 = Math.pow(2,32) + v0; }
if (v0 > Math.pow(2,32)) { v0 = v0 - Math.pow(2,32); }
if (v1 < 0) { v1 = Math.pow(2,32) + v1; }
if (v1 > Math.pow(2,32)) { v1 = v1 - Math.pow(2,32); }
}
//v0 = v0 & 0xFFFFFFFF;
//v1 = v1 & 0xFFFFFFFF;
var result = {v0: v0,v1: v1};
log.debug('XDeviceEncryption.xxxteaDecrypt result='+JSON.stringify(result));
return result;
}
// public static method encrypt
exports.xxteaEncrypt = function(string, key) {
log.debug('XDeviceEncryption.xxteaEncrypt key='+key+' data='+string);
if (string.length == 0) {
return('');
}
//var v = strToLongs(Utf8.encode(string));
//var k = strToLongs(Utf8.encode(key).slice(0,16));
var v = buffToLongs(new Buffer(string, 'hex'));
var k = buffToLongs(new Buffer(key, 'hex'));
//if (v.length <= 1) v[1] = 0; // algorithm doesn't work for n<2 so fudge by adding a null
var n = v.length;
// ---- <TEA coding> ----
var z = v[n-1], y = v[0], delta = 0x9E3779B9;
var mx, e, q = Math.floor(6 + 52/n), sum = 0;
while (q-- > 0) { // 6 + 52/n operations gives between 6 & 32 mixes on each word
sum += delta;
e = sum>>>2 & 3;
for (var p = 0; p < n; p++) {
y = v[(p+1)%n];
mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z);
z = v[p] += mx;
}
}
// ---- </TEA> ----
var buff = longsToBuff(v);
return buff.toString('ascii');
}
exports.xxteaDecrypt = function(data, key) {
log.debug('XDeviceEncryption.xxteaDecrypt key='+key+' data='+data);
if (data.length == 0) {
return('');
}
var d = new Buffer(data, 'hex');
var k = new Buffer(key, 'hex');
var n = d.length;
var r = new Buffer(n);
log.debug('XDeviceEncryption.xxteaDecrypt dataBuffer='+d.toString('hex'));
// ---- <TEA decoding> ----
for (var i=0;i<n/4-1;i=i+2) {
//log.debug('XDeviceEncryption.xxteaDecrypt n='+n+' i='+i+' c='+(i*4)+' cc='+(i*4+1)+' r='+d.readUInt32LE(i*4).toString(16)+' r1='+d.readUInt32BE(i*4).toString(16));
var ed = {v0: d.readUInt32BE(i*4),v1: d.readUInt32BE((i+1)*4)};
var dd = xxxteaDecrypt(ed,k);
r.writeUInt32BE(dd.v0,i*4);
r.writeUInt32BE(dd.v1,(i+1)*4);
}
return r.toString('ascii');
//var plaintext = longsToStr(v);
//plaintext = plaintext.replace(/\0+$/,'');
//return plaintext;
//return new Buffer(plaintext,'utf8').toString('ascii');
//var plaintext = longsToStr(v);
//plaintext = plaintext.replace(/\0+$/,'');// strip trailing null chars resulting from filling 4-char blocks:
//return Utf8.decode(plaintext);
}

View file

@ -0,0 +1,123 @@
var logger = require('winston').loggers.get('main');
var clone = require('clone');
var mongoose = require('mongoose');
var validate = require('validate.io');
var tcrudField = require('./tcrud-field');
var buildConfig = require('./build-config');
function autoFieldType(fieldMeta,fieldType) {
if (!fieldMeta) {
throw new Error('no fieldMeta');
}
if (fieldType && fieldType.length != 0) {
return fieldType;
}
if (fieldMeta.type == Date) {
return 'date';
}
return 'text';
}
exports.buildFields = function(modelMeta) {
if (!modelMeta) {
throw new Error('no modelMeta');
}
var tfields = {};
var keys = Object.keys(modelMeta);
for (i = 0; i < keys.length; i++) {
var key = keys[i];
var value = modelMeta[key];
var tfield = null;
if (key && value && value.tfield) {
//console.log('tfield model cloned');
tfield = clone(value.tfield);
tfield.tid = key;
tfield.tname = tcrudField.autoFieldName(key,tfield.tname);
tfield.type = autoFieldType(value,tfield.ttype);
} else if (key && value) {
//console.log('tfield model auto created');
tfield = tcrudField.newInstance(key);
tfield.tname = tcrudField.autoFieldName(key);
tfield.type = autoFieldType(value);
}
if (tfield.tvalidate && tfield.tvalidate.io) {
//console.log('tfield validate rule: '+tfield.tvalidate.io);
}
tcrudField.fillDefaults(tfield);
tfields[tfield.tid] = tfield;
}
return tfields;
}
function ss(valueRule) {
return function (value, response) {
response(validate(valueRule,value));
};
}
exports.createModelValidators = function (modelSchema,modelFields) {
if (!modelSchema) {
throw new Error('no modelSchema');
}
if (!modelFields) {
throw new Error('no modelFields');
}
var keys = Object.keys(modelFields);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var tfield = modelFields[key];
if (!tfield.tvalidate) {
continue;
}
if (!tfield.tvalidate.io) {
continue;
}
modelSchema.path(tfield.tid).validate(ss(tfield.tvalidate.io), '{PATH} validation failed: '+tfield.tvalidate.io);
}
}
exports.buildStatics = function(modelFields,modelStatics) {
if (!modelFields) {
throw new Error('no modelFields');
}
if (!modelStatics) {
modelStatics = {};
}
modelStatics['ff_tcrud_fields'] = modelFields;
return modelStatics;
}
exports.buildTCrudModel = function(mongoose,tcrudParent,name) {
var model = mongoose.model(name);
var tcrud = buildConfig.createTCrud(tcrudParent,name);
var tfields = model['ff_tcrud_fields'];
if (tfields) {
tcrud.tmeta.tfields = tfields;
}
tcrud.tmodel = name;
//tcrud.tbackend = 'mongoose';
return tcrud;
}
exports.buildTCrudModels = function(mongoose,tcrudParent) {
var result = [];
var modelNames = mongoose.connection.modelNames();
for (var i = 0; i < modelNames.length; i++) {
result.push(exports.buildTCrudModel(mongoose,tcrudParent,modelNames[i]))
}
return result;
}
// ----- wrappers
exports.buildStaticsModel = function(modelMeta,modelStatics) {
return exports.buildStatics(exports.buildFields(modelMeta),modelStatics);
}
exports.buildStaticsModelValidated = function(modelMeta,modelSchema,modelStatics) {
var modelFields = exports.buildFields(modelMeta);
exports.createModelValidators(modelSchema,modelFields);
return exports.buildStatics(modelFields,modelStatics);
}

View file

@ -0,0 +1,16 @@
var logger = require('winston').loggers.get('main');
var clone = require('clone');
var tcrud = require('./tcrud');
var tcrudField = require('./tcrud-field');
exports.createTCrudRoot = function() {
return tcrud.newInstance('root');
}
exports.createTCrud = function(parent, id) {
var result = tcrud.newInstance(id);
parent.tchilds.push(result);
result.tparent = parent;
return result;
}

View file

@ -0,0 +1,95 @@
var logger = require('winston').loggers.get('main');
var clone = require('clone');
var tcrud = require('./tcrud');
var tcrudView = require('./tcrud-view');
exports.createBaseApiUri = function(tview, tech, action) {
var uriView = tview.tslug;
var uriTech = tview.tmeta.texport[tech].tslug;
var uriAction = '';
if (action) {
uriAction = '/' + tview[action].texport[tech].tslug;
}
//logger.info('createBaseApiUri uriTech: '+uriTech+' uriView: '+uriView+' uriAction: '+uriAction);
if (tview.tmeta.tserver.tpopfix) {
return uriTech + '/' + uriView + uriAction;
} else {
return uriView + '/' + uriTech + uriAction;
}
};
function allowSlugSlash(tcrud) {
var result = true;
if (tcrud.tparent) {
result = allowSlugSlash(tcrud.tparent);
if (!result) {
return result;
}
}
if (tcrud.tcount.tslug === '') {
return 'tcount';
}
if (tcrud.tlist.tslug === '') {
return 'tlist';
}
if (tcrud.tcreate.tslug === '') {
return 'tcreate';
}
if (tcrud.tread.tslug === '') {
return 'tread';
}
if (tcrud.tedit.tslug === '') {
return 'tedit';
}
if (tcrud.tdelete.tslug === '') {
return 'tdelete';
}
// if (tcrud.tverify.tslug === '') {
//return false;
// }
return null;
}
function createTCrudViewSlug(tcrud) {
var uriViewSlash = '/';
var slug = uriViewSlash + tcrud.tslug;
if (!tcrud.tenable || !tcrud.tparent) {
slug = '';
}
if (tcrud.tparent) {
return createTCrudViewSlug(tcrud.tparent)+slug;
}
return slug;
}
var fetchEmptyRoles = function() {
return function (callback) {
callback(null,[]);
};
}
exports.createTCrudView = function(tcrud,xrollesCallback) {
if (!tcrud) {
throw new Error('no tcrud');
}
if (!xrollesCallback) {
xrollesCallback = fetchEmptyRoles;
}
var tview = tcrudView.newInstance(tcrud);
tview.tslug = createTCrudViewSlug(tcrud).substring(1);
/*
var uriViewSlashAllow = allowSlugSlash(tcrud);
if (uriViewSlashAllow !== null) {
uriViewSlash = '_';
console.log('detected crud actions with empty tslug; deploying in list mode slash; '+uriViewSlash+' in: '+uriViewSlashAllow+' of: '+tcrud.xid);
tview.tslug = buildSlug(uriViewSlash,tcrud).substring(1);
} else {
tview.tslug = buildSlug(uriViewSlash,tcrud);
}
*/
return tview;
}

View file

@ -0,0 +1,338 @@
var logger = require('winston').loggers.get('main');
var clone = require('clone');
var fs = require('fs');
var ejs = require('ejs');
var xmlmapping = require('xml-mapping');
var rawbody = require('raw-body');
var typeis = require('type-is');
var mcrud = require('crud-mongoose');
var ncrud = require('node-crud');
var mongoose = require('mongoose');
var buildView = require('./build-view');
exports.bodyParserXml = function() {
return function(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
if (!typeis(req, 'xml')) return next();
// flag as parsed
req._body = true;
// parse
rawbody(req, {
limit: options.limit || '100kb',
length: req.headers['content-length'],
encoding: 'utf8'
}, function (err, buf) {
if (err) return next(err);
if (0 == buf.length) {
return next(error(400, 'invalid xml, empty body'));
}
try {
req.body = xmlmapping.dump(buf/*, options.reviver*/);
} catch (err){
err.body = buf;
err.status = 400;
return next(err);
}
next();
})
}
}
var renderTemplateDataRead = function(tview, exportType, contentType, model, projection) {
return function(data, query, cb) {
var res = this.response;
res.set('Content-Type', contentType);
var cursor, fields;
if (query.hasOwnProperty('fields')) {
fields = query.fields;
delete query.fields;
}
delete query.export;
cursor = model.findOne(query);
cursor.select(mcrud.tools.select(projection, fields));
cursor.lean().exec(function(err,doc) {
if (err) {
cb(err || 'error in stream');
return;
}
if (doc == null) {
cb('no data'); // TODO: error is still in json...
return;
}
//var templateReadHeader = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/read-header.ejs', 'utf8');
var templateReadRecord = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/read-record.ejs', 'utf8');
//var templateReadFooter = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/read-footer.ejs', 'utf8');
res.write(ejs.render(templateReadRecord,{
tview: tview,
record: doc,
}));
res.end();
//cb(); // FIXME
});
}
}
var renderTemplateDataList = function(tview, exportType, contentType) {
return function(res, cursor, cb) {
res.set('Content-Type', contentType);
var templateHeader = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/list-header.ejs', 'utf8');
var templateRecord = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/list-record.ejs', 'utf8');
var templateFooter = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/list-footer.ejs', 'utf8');
res.write(ejs.render(templateHeader,{
tview: tview,
}));
cursor.stream()
.on('data', function(doc) {
res.write(ejs.render(templateRecord,{
tview: tview,
record: doc,
}));
})
.on('error', function(e) {
cb(e || 'error in stream');
})
.on('close', function() {
res.write(ejs.render(templateFooter,{
tview: tview,
}));
res.end();
cb();
});
};
}
exports.injectExportFormat = function(apiBase) {
return function ( req, res, next ) {
if (req.url.indexOf(apiBase) >= 0) {
if (req.url.indexOf('xml') > 0) {
req.query['export'] = 'xml';
} else if (req.url.indexOf('csv') > 0) {
req.query['export'] = 'csv';
} else if (req.url.indexOf('rss') > 0) {
req.query['export'] = 'rss';
}
if (req.url.indexOf('json') == 0) {
console.log('inject export type: '+req.query['export']);
}
}
next();
};
}
var _ = require('underscore');
var filterQueryList = function (model) {
return mcrud.parseQuery()
.defaults({ limit: '10' })
.overrides({})
.maxes({ limit: 1000 });
//.removes()
}
var filterDataUpdate = function (model) {
return mcrud.parseData()
overrides({});
//.removes('auth')
//.overrides({ updated: Date.now })
//.defaults({ 'info.gender': 'M' }))
}
function allowCrudType(tview,tcrudType) {
return tview[tcrudType].tenable; // todo add roles
}
//var rssXNodeMap = function(link,feed,data) {
// feed.addNewItem(data.net_id, link+'/ui/XNode/read/'+data._id, data.changed_data, data.net_id+' id', {});
//}
var sendRedirect = function (location) {
if (!location) {
throw new Error('no location');
}
return function(req, res) {
res.redirect(location);
};
};
var renderCrudView = function (tview) {
return function (req, res, next) {
var result = {
tview: tview,
}
res.json({
data: result
});
};
}
var renderCrudController = function (tview, thtmlPrefix, tapiPrefix) {
return function (req, res, next) {
res.set('Content-Type', 'text/javascript');
res.render('node-ff-tcrud/angular/controller',{
tview: tview,
thtmlPrefix: thtmlPrefix,
tapiPrefix: tapiPrefix,
});
};
}
var renderCrudTemplate = function (tview,paction) {
return function (req, res, next) {
res.render('node-ff-tcrud/angular/'+paction+'/'+req.params.action,{
tview: tview,
});
};
}
var buildCrud = function (server,tcrud) {
if (!tcrud.tenable || !tcrud.tmodel) {
logger.info('disabled server: '+tcrud.tid);
return;
}
var tview = buildView.createTCrudView(tcrud /*, fetchCrudRoles()*/);
var model = mongoose.model( tcrud.tmodel );
// Export tdebug
var uriDebug = tcrud.tmeta.tserver.tslug + '/' +buildView.createBaseApiUri(tview,'json') + '/_debug/';
var tcrudClone = clone(tcrud);
tcrudClone.tparent = null; // rm loop: tparent.tchilds[] = this
server.get(uriDebug + 'tview', renderCrudView(tview));
server.get(uriDebug + 'tcrud', renderCrudView(tcrudClone));
if (_.contains(tview.tmeta.tserver.texports,'json')) {
if (allowCrudType(tview,'tlist')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tlist')).Read()
.pipe(filterQueryList(model))
.pipe(mcrud.findAll(model).stream());
}
if (allowCrudType(tview,'tcreate')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tcreate')).Create()
.pipe(mcrud.createNew(model));
}
if (allowCrudType(tview,'tread')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tread') + '/:_id').Read()
.pipe(mcrud.findOne(model));
}
if (allowCrudType(tview,'tedit')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tedit') + '/:_id').Update()
.pipe(filterDataUpdate(model))
.pipe(mcrud.updateOne (model));
}
if (allowCrudType(tview,'tdelete')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tdelete') +'/:_id').Delete()
.pipe(mcrud.removeOne(model));
}
if (allowCrudType(tview,'tcount')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tcount')).Read()
.pipe(filterQueryList(model))
.pipe(mcrud.findAll(model).stream());
}
if (allowCrudType(tview,'tverify')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tverify') + '/:_id').Update()
.pipe(mcrud.findOne(model));
}
}
if (_.contains(tview.tmeta.tserver.texports,'xml')) {
if (allowCrudType(tview,'tlist')) {
ncrud.entity(buildView.createBaseApiUri(tview,'xml','tlist')).Read()
.pipe(filterQueryList(model))
.pipe(mcrud.findAll(model).stream().exports({ xml: renderTemplateDataList(tview,'xml','text/xml') }));
}
if (allowCrudType(tview,'tcreate')) {
ncrud.entity(buildView.createBaseApiUri(tview,'xml','tcreate')).Create()
.pipe(mcrud.createNew(model));
}
if (allowCrudType(tview,'tread')) {
ncrud.entity(buildView.createBaseApiUri(tview,'xml','tread') +'/:_id').Read()
.pipe(renderTemplateDataRead(tview,'xml','text/xml',model));
}
if (allowCrudType(tview,'tedit')) {
ncrud.entity(buildView.createBaseApiUri(tview,'xml','tedit')+'/:_id').Update()
.pipe(filterDataUpdate(model))
.pipe(mcrud.updateOne (model));
}
if (allowCrudType(tview,'tdelete')) {
ncrud.entity(buildView.createBaseApiUri(tview,'xml','tdelete')+'/:_id').Delete()
.pipe(mcrud.removeOne (model));
}
}
if (_.contains(tview.tmeta.tserver.texports,'csv')) {
if (allowCrudType(tview,'tlist')) {
ncrud.entity(buildView.createBaseApiUri(tview,'csv','tlist')).Read()
.pipe(filterQueryList(model))
.pipe(mcrud.findAll(model).stream().exports({ csv: renderTemplateDataList(tview,'csv','text/csv') }));
}
if (allowCrudType(tview,'tread')) {
ncrud.entity(buildView.createBaseApiUri(tview,'csv','tread') +'/:_id').Read()
.pipe(renderTemplateDataRead(tview,'csv','text/csv',model));
}
}
if (_.contains(tview.tmeta.tserver.texports,'rss')) {
if (allowCrudType(tview,'tlist')) {
ncrud.entity(buildView.createBaseApiUri(tview,'rss','tlist')).Read()
.pipe(mcrud.parseQuery()
.defaults({ limit: '10' })
.maxes({ limit: 50 })
)
.pipe(mcrud.findAll(model)
.stream()
.exports({ rss: renderTemplateDataList(tview,'rss','text/xml') })
);
}
//.exports({ rss: exports.exportRss('todo',tview.xid,rssXNodeMap) })
}
if (_.contains(tview.tmeta.tserver.texports,'angular')) {
var uriPrefix = tcrud.tmeta.tserver.tslug + '/' + buildView.createBaseApiUri(tview,'angular');
var thtmlPrefix = uriPrefix + '/' + tview.tmeta.texport.angular.thtml;
var tapiPrefix = tcrud.tmeta.tserver.tslug + '/' +buildView.createBaseApiUri(tview,'json');
//server.get('/ui/include/crud/:name/:action', XViewInclude.renderTemplateCrud);
//server.get('/ui/include/js/controller/:name', XViewInclude.renderCrudController);
server.get(uriPrefix + '/thtml/crud/:action', renderCrudTemplate(tview,'thtml/crud/'));
server.get(uriPrefix + '/controller.js', renderCrudController(tview,thtmlPrefix,tapiPrefix));
//server.get(uriPrefix + '/service.js', renderCrudService(tview))
}
}
function walkViews(server,tcrud) {
//logger.debug('walk views: '+tcrud.tid);
if (tcrud.tparent) {
buildCrud(server,tcrud);
}
for (var i = 0; i < tcrud.tchilds.length; i++) {
walkViews(server,tcrud.tchilds[i]);
}
}
function buildCrudApi(server) { // todo add return function
troot = server.get('ff_root_tcrud_view');
walkViews(server,troot);
server.use(exports.injectExportFormat(troot.tmeta.tserver.tslug));
server.use(exports.bodyParserXml());
ncrud.launch(server,{
base: troot.tmeta.tserver.tslug,
cors: false,
timeoute: 10e3
});
}
module.exports = buildCrudApi;

View file

@ -0,0 +1,138 @@
var logger = require('winston').loggers.get('main');
var clone = require('clone');
/*
var createRouter = function(server,path) {
var router = express.Router();
router.path = path;
server.use(router.path,router);
return router;
}
*/
var walkRouteTree = function(result,stack,parent_path) {
if (parent_path == null) {
parent_path = '';
}
stack.forEach(function(middleware) {
if (middleware.route){
var path = parent_path + middleware.route.path;
for (var httpMethod in middleware.route.methods) {
var data = {
"uriPath": path,
"httpMethod": httpMethod
};
result.push(data);
}
} else if (middleware.name === 'router') {
var pp = parent_path + middleware.handle.path;
walkRouteTree(result,middleware.handle.stack,pp);
} else {
//log.info('route err: '+JSON.stringify(middleware));
}
});
return result;
}
exports.renderServerUptime = function(options) {
if (!options) {
options: {};
}
var timeBoot = new Date().getTime();
return function (req, res, next) {
var timeNow = new Date().getTime();
var result = {
time_boot: timeBoot,
time_now: timeNow,
uptime: timeNow-timeBoot
}
res.json({
data: result
});
};
};
exports.renderServerRoutes = function(server) {
if (!server) {
throw new Error('no server');
}
return function (req, res, next) {
var routeList = server.get('ff_tcrud_route_list');
if (!routeList) {
console.log('no routeList');
routeList = [];
}
var reqGroups = req.query.groups;
var groupList = [];
if (reqGroups) {
groupList = reqGroups.split(',');
}
//console.log('groups: '+reqGroups);
var result = {
all: [],
};
for (var i = 0; i < groupList.length; i++) {
var groupName = groupList[i].split('/').join('_');
result[groupName] = [];
}
groupList.sort(function(a, b) {
return a.length < b.length; // longest first as we break on first hit
});
for (i = 0; i < routeList.length; i++) {
var route = routeList[i];
var added = false;
for (var ii = 0; ii < groupList.length; ii++) {
if (route.uriPath.indexOf(groupList[ii]) > 0) {
var groupName = groupList[ii].split('/').join('_');
result[groupName].push(route);
added = true;
break;
}
}
if (!added) {
result.all.push(route);
}
}
res.json({
data: result
});
};
};
exports.buildServerRoutes = function(server) { // todo add return function
if (!server) {
throw new Error('no server');
}
var result = walkRouteTree([],server._router.stack);
result.sort(function(a, b) {
return a.uriPath.localeCompare(b.uriPath);
});
server.set('ff_tcrud_route_list',result);
};
exports.sendRedirect = function (location) {
if (!location) {
throw new Error('no location');
}
return function(req, res) {
res.redirect(location);
};
};
exports.renderTemplatePath = function (viewPath) {
if (!viewPath) {
viewPath = '';
}
return function (req, res) {
res.locals.query = req.query;
//console.log('template query keys: '+Object.keys(req.query));
var qi = req.url.indexOf('?');
if (qi === -1) {
qi = req.url.length;
}
res.render(viewPath + req.url.substring(req.route.path.length-1, qi));
};
};
exports.buildCrudApi = require('./factory-express-api');

View file

@ -0,0 +1,54 @@
'use strict';
var tcrud = {
factory: {
express: require('./factory-express'),
},
build: {
config: require('./build-config'),
view: require('./build-view'),
backend: {
mongoose: require('./build-backend-mongoose'),
},
}
};
module.exports = tcrud;
/*
//app.use(function (req, res, next) {res.locals.c_var='test';next();});
var http = require('http');
var ejs = require('ejs');
var fs = require('fs');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/xml'});
fs.readFile('placemark.ejs', 'utf8', function (err, template) {
var content = ejs.render(template,{
name:"test name",
description:"this is the description",
coordinates:"-122.0822035425683,37.42228990140251,0",
filename: __dirname + '/placemark.ejs'
});
res.write(content);
res.end()
});
}).listen(8000);
var scripts = document.getElementsByTagName("script");
var currentScriptPath = scripts[scripts.length-1].src;
console.log("currentScriptPath:"+currentScriptPath);
var baseUrl = currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1);
console.log("baseUrl:"+baseUrl);
var bu2 = document.querySelector("script[src$='routes.js']");
currentScriptPath = bu2.src;
console.log("bu2:"+bu2);
console.log("src:"+bu2.src);
baseUrl = currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1);
console.log("baseUrl:"+baseUrl);
*/

View file

@ -0,0 +1,110 @@
var clone = require('clone');
var template = {
tid: null,
tname: null,
ttype: null,
tslug: null,
tvalidate: {
io: null,
},
tlist: {
tenable: true,
ttext: null,
troles: [],
},
tread: {
tenable: true,
ttext: null,
troles: [],
},
tedit: {
tenable: true,
ttext: null,
troles: [],
},
tcreate: {
tenable: true,
ttext: null,
troles: [],
},
};
exports.autoFieldName = function (fieldKey,fieldName) {
if (fieldKey === undefined) {
throw new Error('no fieldKey');
}
if (fieldName && fieldName.length !== 0) {
return fieldName;
}
var result = '';
var names = fieldKey.split('_');
for (var i in names) {
var name = names[i];
if (name.length > 1) {
name = name.substring(0,1).toUpperCase() + name.substring(1);
}
result = result + name + ' ';
}
return result;
}
var autoFieldEnable = function(tfield,type) {
if (type === undefined) {
throw new Error('no type');
}
if (tfield[type] === undefined) {
tfield[type] = {};
}
if (tfield[type].tenable !== undefined) {
return;
}
var fieldKey = tfield.tid;
var result = true;
if ('tlist' === type) {
var name = fieldKey.toLowerCase();
if (fieldKey.indexOf('description') >= 0) {
result = false;
} else if (fieldKey.indexOf('comment') >= 0) {
result = false;
}
}
tfield[type].tenable = result;
}
exports.newInstance = function (id) {
var tfield = clone(template);
tfield.tid = id;
return tfield;
}
exports.fillDefaults = function (tfield) {
if (tfield === undefined) {
throw new Error('no tfield');
}
var tid = tfield.tid;
if (tid === undefined) {
throw new Error('no tfield.tid');
}
if (tfield.tname === undefined) {
tfield.tname = exports.autoFieldName(tid);
}
if (tfield.tname === undefined) {
tfield.tname = exports.autoFieldName(tid);
}
if (tfield.tslug === undefined) {
tfield.tslug = tid;
}
if (tfield.ttype === undefined) {
tfield.ttype = 'text';
}
if (tfield.tlist === undefined) {
tfield.tlist = {};
}
autoFieldEnable(tfield,'tlist');
autoFieldEnable(tfield,'tread');
autoFieldEnable(tfield,'tedit');
autoFieldEnable(tfield,'tcreate');
}

View file

@ -0,0 +1,204 @@
var clone = require('clone');
var tsurgeon = require('tree-surgeon');
var template = {
tid: null,
tname: null,
tplural: null,
tslug: null,
tcode: null,
tmodel: null,
tcount: {
ttext: null,
tenable: null,
texport: null,
},
tlist: {
tfields: [],
ttext: null,
tlinks: null,
tenable: null,
texport: null,
},
tread: {
tfields: [],
ttext: null,
tenable: null,
texport: null,
},
tedit: {
tfields: [],
ttext: null,
tenable: null,
texport: null,
},
tcreate: {
tfields: [],
ttext: null,
tenable: null,
texport: null,
},
tdelete: {
ttext: null,
tenable: null,
texport: null,
},
tverify: {
ttext: null,
tenable: null,
texport: null,
},
tmeta: {
tfields: null,
tvalidate: null,
tserver: null,
texport: null,
},
};
function copyByTemplate(prefix,objectDest,objectSrc,copyTemplate) {
var keys = Object.keys(copyTemplate);
var result = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = objectSrc[key];
if (!value) {
//console.log(prefix+'.'+key+' src object has no value.');
continue; // no src value
}
var templateValue = copyTemplate[key];
if (templateValue === null) {
if (objectDest[key] !== null) {
//console.log(prefix+'.'+key+' has own value: '+objectDest[key]);
continue;
} else {
//console.log(prefix+'.'+key+' copy value: '+value);
objectDest[key] = clone(value);
}
} else {
//console.log(prefix+'.'+key+' going deeper');
copyByTemplate(prefix+'.'+key,objectDest[key],value,templateValue)
}
}
}
function addFilteredFields(tview,type,tcrud) {
result = [];
var keys = Object.keys(tcrud.tmeta.tfields);
var result = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var tfield = tcrud.tmeta.tfields[key];
if (tfield[type]) {
if (!tfield[type].tenable) {
//console.log('------------- type: ' + type + ' key: ' + key + ' res: ' + tfield[type].tenable)
continue;
}
}
result.push(key); // default is true..
}
tview[type].tfields = result;
}
function forceLookupKeySimple() {
var low = 100000;
var high = 999999;
return Math.floor(Math.random() * (high - low + 1) + low).toString(16).toUpperCase();
}
function forceLookupTFields(tview) {
var keys = Object.keys(tview.tmeta.tfields);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var keyNew = 'FTL_' +forceLookupKeySimple() + '_' + key.substring(key.length/3,key.length/3*2); // no....its; Force template lookup
var tfield = tview.tmeta.tfields[key];
tview.tmeta.tfields[key] = undefined;
tview.tmeta.tfields[keyNew] = tfield;
var ckeys = Object.keys(tview);
for (var ii = 0; ii < ckeys.length; ii++) {
var ckey = ckeys[ii];
if (ckey === 'tmeta') {
continue;
}
var obj = tview[ckey];
if (obj && obj.tfields) {
var tfieldsNew = [];
for (var iii = 0; iii < obj.tfields.length; iii++) {
var tkey = obj.tfields[iii];
if (tkey === key) {
tfieldsNew.push(keyNew);
} else {
tfieldsNew.push(tkey);
}
}
obj.tfields = tfieldsNew;
}
}
}
}
function formatValuePart(value,replaceChar,splitChar,partFn) {
var result = '';
var parts = value.split(splitChar);
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
if (part.length > 1) {
result += partFn(part);
} else {
result += part;
}
if (i < parts.length - 1) {
result += replaceChar;
}
}
return result;
}
function formatValue(value,removeChars,replaceChar,partFn) {
for (var i = 0; i < removeChars.length; i++) {
value = formatValuePart(value,replaceChar,removeChars[i],partFn);
}
return value;
}
exports.newInstance = function (tcrud) {
var tview = clone(template);
var tid = tcrud.tid;
tview.tid = tid;
copyByTemplate('tview',tview,tcrud,template);
//console.info('TCrudView.tslug: '+tview.tslug);
if (!tview.tslug) {
tview.tslug = formatValue(tid,['.',',','/','=','&','?',' '],'', function (part) {
return part; // todo use fully correct uri removeal and escaping.
})
//console.info('TCrudView.tslug auto: '+tview.tslug);
}
if (!tview.tname) {
tview.tname = formatValue(tid,['_','-','.',','],' ', function (part) {
return part.substring(0,1).toUpperCase()+part.substring(1).toLowerCase();
});
}
if (!tview.tcode) {
tview.tcode = formatValue(tid,[' ','_','-','.',','],'',function (part) {
return part.toLowerCase();
});
}
if (!tview.tplural) {
tview.tplural = tview.tname + 's';
}
addFilteredFields(tview,'tlist',tcrud);
addFilteredFields(tview,'tread',tcrud);
addFilteredFields(tview,'tedit',tcrud);
addFilteredFields(tview,'tcreate',tcrud);
forceLookupTFields(tview);
return tview;
}

View file

@ -0,0 +1,230 @@
var clone = require('clone');
var template = {
tid: null,
tname: null,
tplural: null,
tslug: null,
tmodel: null,
tenable: true,
tparam: null,
troles: [],
tparent: null,
tchilds: [],
tlist: {
tenable: true,
ttext: 'Listing of ..',
troles: [],
tserver: {
tpipe: {
query: {
defaults: { limit: '10' },
maxes: { limit: 50 },
},
},
},
texport: {
json: {
tslug: 'list',
},
xml: {
tslug: 'list',
},
rss: {
tslug: 'list',
},
csv: {
tslug: 'list',
},
angular: {
tslug: 'list',
thtml: 'crud/list',
tcontroller: {
prefix: 'tcrudAuto',
postfix: 'ListCntr',
argu: '$scope, $http, $location, $routeParams',
},
tlinks: {
'DataTODO':'/ui/XNodeData/list/XNode/{{row.net_id}}/{{row.net_id}}',
},
},
},
},
tcreate: {
tenable: true,
ttext: null,
troles: [],
texport: {
json: {
tslug: 'create',
},
xml: {
tslug: 'create',
},
angular: {
tslug: 'create',
thtml: 'crud/create',
tcontroller: {
prefix: 'tcrudAuto',
postfix: 'CreateCntr',
argu: '$scope, $http, $location, $routeParams',
},
},
},
},
tedit: {
tenable: true,
ttext: null,
troles: [],
texport: {
json: {
tslug: 'edit',
},
xml: {
tslug: 'edit',
},
angular: {
tslug: 'edit',
thtml: 'crud/edit',
tcontroller: {
prefix: 'tcrudAuto',
postfix: 'EditCntr',
argu: '$scope, $http, $location, $routeParams',
},
},
},
},
tread: {
tenable: true,
ttext: null,
troles: [],
tslug: '',
texport: {
json: {
tslug: 'read',
},
xml: {
tslug: 'read',
},
csv: {
tslug: 'read',
},
angular: {
tslug: 'read',
thtml: 'crud/read',
troute: {
},
tcontroller: {
prefix: 'tcrudAuto',
postfix: 'ReadCntr',
argu: '$scope, $http, $location, $routeParams',
},
},
},
},
tdelete: {
tenable: true,
ttext: null,
troles: [],
texport: {
json: {
tslug: 'delete',
},
xml: {
tslug: 'delete',
},
angular: {
tslug: 'delete',
thtml: 'crud/delete',
tcontroller: {
prefix: 'tcrudAuto',
postfix: 'DeleteCntr',
argu: '$scope, $http, $location, $routeParams',
},
},
},
},
tcount: {
tenable: true,
ttext: 'Count records',
troles: [],
texport: {
json: {
tslug: 'list-count',
},
xml: {
tslug: 'list-count',
},
csv: {
tslug: 'list-count',
},
},
},
tverify: {
tenable: true,
ttext: 'Verify data',
troles: [],
texport: {
json: {
tslug: 'verify',
},
xml: {
tslug: 'verify',
},
csv: {
tslug: 'verify',
},
},
},
ttemplate: {
tid: '_id',
tname: '_id',
tdescription: '_id',
},
tmeta: {
troles: [],
tfields: {},
tvalidate: {
io: { 'admin_role': 'object|properties[test]' },
},
tserver: {
thost: 'http://localhost:8080/',
tslug: '/api',
tpopfix: true,
texports: ['json','xml','csv','rss','angular'],
},
texport: {
json: {
tslug: 'json',
},
xml: {
tslug: 'xml',
},
csv: {
tslug: 'csv',
},
rss: {
tslug: 'rss',
itemTitle: '$data._id',
itemLink: '$siteLink/ui/$modelUri/read/$[\'data._id\']',
itemDate: '',
itemDescription: '',
//itemFields: '',
},
angular: {
tslug: 'angular',
tbase: '/ui',
thtml: 'thtml',
},
},
},
};
exports.newInstance = function (tid) {
var tcrud = clone(template);
tcrud.tid = tid;
tcrud.tslug = tid; // view fills more defaults.
return tcrud;
}

View file

@ -0,0 +1,8 @@
$scope.<%= taction %>None = function () {
<% if (tview.tlist) { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.texport.angular.tslug %>');
<% } else { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/');
<% } %>
}

View file

@ -0,0 +1,5 @@
$routeProvider.when('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview[taction].texport.angular.tslug %><%= routeEnd %>', {
templateUrl: '<%= thtmlPrefix %>/<%= tview[taction].texport.angular.thtml %>',
controller: <%= tview[taction].texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview[taction].texport.angular.tcontroller.postfix %>
});

View file

@ -0,0 +1,99 @@
'use strict';
// Auto generated controller mapping for: <%= tview.tid %>
crudRouteInit.push(<%= tview.tcode %>Init);
function <%= tview.tcode %>Init($routeProvider, $locationProvider) {
<% if (tview.tlist) { %>
$routeProvider.when('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/', {
redirectTo: '<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.texport.angular.tslug %>'
});
<%- include('controller-route', {tview: tview,thtmlPrefix:thtmlPrefix,taction: 'tlist', routeEnd: ''}); %>
<% } %>
<% if (tview.tcreate) { %>
<%- include('controller-route', {tview: tview,thtmlPrefix:thtmlPrefix,taction: 'tcreate', routeEnd: ''}); %>
<% } %>
<% if (tview.tedit) { %>
<%- include('controller-route', {tview: tview,thtmlPrefix:thtmlPrefix,taction: 'tedit', routeEnd: '/:id'}); %>
<% } %>
<% if (tview.tread) { %>
<%- include('controller-route', {tview: tview,thtmlPrefix:thtmlPrefix,taction: 'tread', routeEnd: '/:id'}); %>
<% } %>
<% if (tview.tdelete) { %>
<%- include('controller-route', {tview: tview,thtmlPrefix:thtmlPrefix,taction: 'tdelete', routeEnd: '/:id'}); %>
<% } %>
}
<% if (tview.tlist) { %>
function <%= tview.tlist.texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview.tlist.texport.angular.tcontroller.postfix %>(<%= tview.tlist.texport.angular.tcontroller.argu %>) {
$http.get('<%= tapiPrefix %>/<%= tview.tlist.texport.json.tslug %>').success(function(data, status, headers, config) {
$scope.data = data.data;
});
}
<% } %>
<% if (tview.tcreate) { %>
function <%= tview.tcreate.texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview.tcreate.texport.angular.tcontroller.postfix %>(<%= tview.tcreate.texport.angular.tcontroller.argu %>) {
$scope.data = {};
$scope.tcreateData = function () {
$http.post('<%= tapiPrefix %>/<%= tview.tcreate.texport.json.tslug %>', $scope.data).success(function(data) {
<% if (tview.tlist) { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.texport.angular.tslug %>');
<% } else { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/');
<% } %>
});
}
<%- include('controller-action-none', {tview: tview,taction: 'tcreate'}); %>
}
<% } %>
<% if (tview.tread) { %>
function <%= tview.tread.texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview.tread.texport.angular.tcontroller.postfix %>(<%= tview.tread.texport.angular.tcontroller.argu %>) {
$scope.data = {};
$http.get('<%= tapiPrefix %>/<%= tview.tread.texport.json.tslug %>/' + $routeParams.id).success(function(data) {
$scope.data = data.data;
});
<%- include('controller-action-none', {tview: tview,taction: 'tread'}); %>
}
<% } %>
<% if (tview.tedit) { %>
function <%= tview.tedit.texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview.tedit.texport.angular.tcontroller.postfix %>(<%= tview.tedit.texport.angular.tcontroller.argu %>) {
$scope.data = {};
$http.get('<%= tapiPrefix %>/<%= tview.tread.texport.json.tslug %>/' + $routeParams.id).success(function(data) {
$scope.data = data.data;
});
$scope.teditData = function () {
$http.put('<%= tapiPrefix %>/<%= tview.tedit.texport.json.tslug %>/' + $routeParams.id, $scope.data ).success(function(data) {
<% if (tview.tread) { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tread.texport.angular.tslug %>/' + $routeParams.id);
<% } else { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/');
<% } %>
});
}
<%- include('controller-action-none', {tview: tview,taction: 'tedit'}); %>
}
<% } %>
<% if (tview.tdelete) { %>
function <%= tview.tdelete.texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview.tdelete.texport.angular.tcontroller.postfix %>(<%= tview.tdelete.texport.angular.tcontroller.argu %>) {
$scope.data = {};
$http.get('<%= tapiPrefix %>/<%= tview.tread.texport.json.tslug %>/' + $routeParams.id).success(function(data) {
$scope.data = data.data;
});
$scope.tdeleteData = function () {
$http.delete('<%= tapiPrefix %>/<%= tview.tdelete.texport.json.tslug %>/'+ $routeParams.id, $scope.data).success(function(data) {
<% if (tview.tlist) { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.texport.angular.tslug %>');
<% } else { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/');
<% } %>
});
}
<%- include('controller-action-none', {tview: tview,taction: 'tdelete'}); %>
}
<% } %>

View file

@ -0,0 +1,20 @@
<h2>Create <%= tview.tname %></h2>
<div class="row">
<div class="col-lg-8">
<form role="form">
<% tview.tedit.tfields.forEach(function (fieldKey) { %>
<div class="form-group">
<label for="<%= tview.tmeta.tfields[fieldKey].tid %>"><%= tview.tmeta.tfields[fieldKey].tname %></label>
<input ng-model="data.<%= tview.tmeta.tfields[fieldKey].tid %>" name="<%= tview.tmeta.tfields[fieldKey].tid %>" class="form-control"></input>
</div>
<% }) %>
<button type="submit" class="btn btn-default" ng-click="tcreateData()">Submit</button>
<button type="submit" class="btn btn-default" ng-click="tcreateNone()">Cancel</button>
</form>
</div>
<div class="col-lg-4"></div>
</div>
<!--
06 308 500 37
-->

View file

@ -0,0 +1,6 @@
<h2>Delete <%= tview.tname %></h2>
<div>
<p>Are you sure you want to delete this <%= tview.tname %> {{data._id}} ?</p>
<button ng-click="tdeleteData()">Yes</button> | -
<button ng-click="tdeleteNone()">No thanks</button>
<div>

View file

@ -0,0 +1,16 @@
<h2> Edit <%= tview.tname %></h2>
<div class="row">
<div class="col-lg-8">
<form role="form">
<% tview.tedit.tfields.forEach(function (fieldKey) { %>
<div class="form-group">
<label for="<%= tview.tmeta.tfields[fieldKey].tid %>"><%= tview.tmeta.tfields[fieldKey].tname %></label>
<input ng-model="data.<%= tview.tmeta.tfields[fieldKey].tid %>" name="<%= tview.tmeta.tfields[fieldKey].tid %>" class="form-control"></input>
</div>
<% }) %>
<button type="submit" class="btn btn-default" ng-click="teditData()">Submit</button>
<button type="submit" class="btn btn-default" ng-click="teditNone()">Cancel</button>
</form>
<div class="col-lg-4"></div>
</div>

View file

@ -0,0 +1,50 @@
<p>There are {{data.length}} <%= tview.tplural %></p>
<% if (tview.tcreate) { %>
<p>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tcreate.texport.angular.tslug %>">Create New</a>
</p>
<% } %>
<div class="table-responsive">
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<% if (tview.tread) { %><th>Open</th><% } %>
<% if (tview.tedit) { %><th>Edit</th><% } %>
<% if (tview.tdelete) { %><th>Delete</th><% } %>
<% tview.tlist.tfields.forEach(function (fieldKey) { %>
<th><%= tview.tmeta.tfields[fieldKey].tname %></th>
<% }) %>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in data">
<% if (tview.tread) { %>
<td>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tread.texport.angular.tslug %>/{{row._id}}">Read</a>
</td>
<% } %>
<% if (tview.tedit) { %>
<td>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tedit.texport.angular.tslug %>/{{row._id}}">Edit</a>
</td>
<% } %>
<% if (tview.tdelete) { %>
<td>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tdelete.texport.angular.tslug %>/{{row._id}}">Delete</a>
</td>
<% } %>
<% tview.tlist.tfields.forEach(function (fieldKey) { %>
<td>{{row.<%= tview.tmeta.tfields[fieldKey].tid %>}}</td>
<% }) %>
</tr>
</tbody>
</table>
</div>
<% if (tview.tcreate) { %>
<p>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tcreate.texport.angular.tslug %>">Create New</a>
</p>
<% } %>

View file

@ -0,0 +1,19 @@
<section>
<h2> {{data._id}} </h2>
<% if (tview.tedit) { %>
<p>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tedit.texport.angular.tslug %>/{{data._id}}">Edit</a>
</p>
<% } %>
<% if (tview.tdelete) { %>
<p>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tdelete.texport.angular.tslug %>/{{data._id}}">Delete</a>
</p>
<% } %>
<% tview.tread.tfields.forEach(function (fieldKey) { %>
<p><%= tview.tmeta.tfields[fieldKey].tname %>: {{data.<%= tview.tmeta.tfields[fieldKey].tid %>}}</p>
<% }) %>
<p>
<button ng-click="treadNone()">Back</button>
</p>
<sector>

View file

@ -0,0 +1 @@
# CSV: <%= tview.tid %>

View file

@ -0,0 +1 @@
<% Object.keys(tview.tlist.tfields).forEach(function (tfieldKey) {var tfield = tview.tlist.tfields[tfieldKey]; %><%= record[tfield.tid] %>,<% }) %>

View file

@ -0,0 +1 @@
<% Object.keys(tview.tlist.tfields).forEach(function (tfieldKey) {var tfield = tview.tlist.tfields[tfieldKey]; %><%= record[tfield.tid] %>,<% }) %>

View file

@ -0,0 +1 @@
</<%= tview.tid %>List>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<<%= tview.tid %>List>

View file

@ -0,0 +1,5 @@
<<%= tview.tid %>>
<% Object.keys(tview.tlist.tfields).forEach(function (tfieldKey) {var tfield = tview.tlist.tfields[tfieldKey]; %>
<<%= tfieldKey %> type="<%= tfield.type %>"><%= record[tfield.tid] %></<%= tfieldKey %>>
<% }) %>
</<%= tview.tid %>>

View file

@ -0,0 +1,2 @@
</<%= tview.tid %>List>
</data>

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<data>
<<%= tview.tid %>List>

View file

@ -0,0 +1,5 @@
<<%= tview.tid %>>
<% Object.keys(tview.tlist.tfields).forEach(function (tfieldKey) {var tfield = tview.tlist.tfields[tfieldKey]; %>
<<%= tfieldKey %> type="<%= tfield.type %>"><%= record[tfield.tid] %></<%= tfieldKey %>>
<% }) %>
</<%= tview.tid %>>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<data>
<<%= tview.tid %>>
<% Object.keys(tview.tlist.tfields).forEach(function (tfieldKey) {var tfield = tview.tlist.tfields[tfieldKey]; %>
<<%= tfieldKey %> type="<%= tfield.type %>"><%= record[tfield.tid] %></<%= tfieldKey %>>
<% }) %>
</<%= tview.tid %>>
</data>

View file

@ -0,0 +1,75 @@
var logger = require('winston').loggers.get('main');
var mongoose = require('mongoose');
var tmongoose = require('../lib/node-ff-tcrud').build.backend.mongoose;
var modelName = __filename.split('/').pop().split('.')[0];
var modelBackend = modelName.split('-').join('_');
var modelMeta = {
net_id: {
type: String,
index: { unique: false },
tfield: {
tvalidate: { io: 'string' },
},
},
command: {
type: String,
tfield: {
tvalidate: { io: 'string' },
},
},
insert_ip: {
type: String,
tfield: {
tvalidate: { io: 'string' },
},
},
insert_date: {
type: Date,
default: Date.now,
tfield: {
tlist: { tenable: false },
},
},
send_date: {
type: Date
},
done_date: {
type: Date
}
};
var modelSchema = new mongoose.Schema(modelMeta);
modelSchema.statics = tmongoose.buildStaticsModelValidated(modelMeta,modelSchema, {
findByNetIdLimit10: function (net_id, callback) {
logger.debug(modelBackend+'.findByNetId net_id='+net_id);
this.find({net_id:net_id}).sort('-insert_date').limit(10).exec(callback);
},
findOpenByNetId: function (net_id, callback) {
logger.debug(modelBackend+'.findOpenByNetId net_id='+net_id);
this.find({net_id:net_id}).where('send_date',null).sort('insert_date').limit(1).exec(callback);
},
findOpenCommandLimit5: function (callback) {
logger.debug(modelBackend+'.findOpenCommands');
var dateLastHour = new Date(new Date().getTime() - 30*60*1000); // soft limit on long open errors.
this.find({insert_date: { $gt: dateLastHour}}).where('send_date',null).sort('insert_date').limit(5).exec(callback);
},
findSendCommandLimit5: function (callback) {
logger.debug(modelBackend+'.findSendCommands');
var dateLastHour = new Date(new Date().getTime() - 60*60*1000);
this.find({send_date: { $gt: dateLastHour}}).sort('-insert_date').limit(5).exec(callback);
},
insert: function (net_id,command,insert_ip, next) {
var xcmd = new this();
xcmd.net_id = net_id;
xcmd.command = command;
xcmd.insert_ip = insert_ip;
xcmd.save(function(err,xcmd) {
if (err) { return next(err); }
logger.debug(modelBackend+'.insert _id='+xcmd._id+' net_id='+xcmd.net_id);
});
}
});
module.exports = mongoose.model(modelName, modelSchema, modelBackend);

View file

@ -0,0 +1,98 @@
var logger = require('winston').loggers.get('main');
var mongoose = require('mongoose');
var tmongoose = require('../lib/node-ff-tcrud').build.backend.mongoose;
var modelName = __filename.split('/').pop().split('.')[0];
var modelBackend = modelName.split('-').join('_');
var modelMeta = {
net_id: {
type: String,
index: { unique: true },
tfield: {
tvalidate: { io: 'string' },
},
},
net_key: {
type: String,
tfield: {
tvalidate: { io: 'string' },
tlist: { tenable: false },
},
},
net_mac: {
type: String,
tcrud: {
tname: 'Net MAC',
tlist: { tenable: false },
}
},
rf_key: {
type: String,
tfield: {
tname: 'RF Key',
tlist: { tenable: false },
}
},
init_index: {
type: Number,
tfield: {
tlist: { tenable: false },
}
},
ping_counter: {
type: Number,
default: 0,
tfield: {
tname: 'Ping #'
}
},
ping_last_date: {
type: Date,
index: { unique: true, sparse: true }
},
ping_rtt: {
type: Number,
default: 0,
tfield: {
tname: 'Ping RTT'
}
},
changed_date: {
type: Date,
default: Date.now,
tfield: {
tlist: { tenable: false },
}
},
created_date: {
type: Date,
default: Date.now,
tfield: {
tlist: { tenable: false },
}
},
created_ip: {
type: String,
tfield: {
tvalidate: { io: 'string' },
},
}
};
var modelSchema = new mongoose.Schema(modelMeta);
modelSchema.statics = tmongoose.buildStaticsModelValidated(modelMeta,modelSchema, {
findOneByNetId: function (net_id, callback) {
logger.debug(modelBackend+'.findByNetId net_id='+net_id);
this.findOne({net_id:net_id}).exec(callback);
},
findLastPingLimit5: function (callback) {
var dateLastHour = new Date(new Date().getTime() - 60*60*1000);
logger.debug(modelBackend+'.findLastPinged lastDate: '+dateLastHour);
this.find({}).where('ping_last_date').gt(dateLastHour).sort('-ping_last_date').limit(5).exec(callback);
}
});
module.exports = mongoose.model(modelName, modelSchema, modelBackend);

View file

@ -0,0 +1,59 @@
var logger = require('winston').loggers.get('main');
var mongoose = require('mongoose');
var tmongoose = require('../lib/node-ff-tcrud').build.backend.mongoose;
var modelName = __filename.split('/').pop().split('.')[0];
var modelBackend = modelName.split('-').join('_');
var modelMeta = {
/*
uuid: {
type: String,
index: { unique: true },
tfield: {
tvalidate: { io: 'string' },
},
},
*/
node_id: {
type: Number,
index: { unique: false },
tfield: {
tvalidate: { io: 'number' },
},
},
insert_date: {
type: Date,
index: { unique: false },
default: Date.now,
},
name: {
type: String,
index: { unique: false },
tfield: {
tvalidate: { io: 'string' },
},
},
value: {
type: String,
}
};
var modelSchema = new mongoose.Schema(modelMeta);
modelSchema.statics = tmongoose.buildStaticsModelValidated(modelMeta,modelSchema, {
findLastAddedLimit5: function (callback) {
logger.debug(modelBackend+'.findLastAddedLimit5');
this.find({}).sort('-insert_date').limit(5).exec(callback);
},
findByNetIdAndNodeId: function (net_id,node_id, callback) {
logger.debug(modelBackend+'.findByNetIdAndNodeId net_id='+net_id+' node_id='+node_id);
this
.aggregate({ $match: { net_id:net_id ,node_id:node_id }})
.group({ _id: "$name", value: { $min : "$value" } })
.sort("_id")
.exec(callback);
//this.find({net_id:net_id,node_id:node_id}).sort('insert_date').exec(callback);
}
});
module.exports = mongoose.model(modelName, modelSchema, modelBackend);

View file

@ -0,0 +1,53 @@
var logger = require('winston').loggers.get('main');
var mongoose = require('mongoose');
var tmongoose = require('../lib/node-ff-tcrud').build.backend.mongoose;
var modelName = __filename.split('/').pop().split('.')[0];
var modelBackend = modelName.split('-').join('_');
var modelMeta = {
//node : {type : Schema.ObjectId, ref : 'XNode'}
node_id: {
type: Number,
index: { unique: false },
tfield: {
tvalidate: { io: 'number' },
},
},
data_raw: {
type: String,
tfield: {
ttype: 'textarea',
tvalidate: { io: 'string' },
tlist: { tenable: false },
}
},
data_text: {
type: String,
tfield: {
tvalidate: { io: 'number' },
ttype: 'textarea',
}
},
remote_ip: {
type: String,
tfield: {
tname: 'Remote IP',
tvalidate: { io: 'ip_address' },
tlist: { tenable: false },
}
},
insert_date: {
type: Date, default: Date.now,
}
};
var modelSchema = new mongoose.Schema(modelMeta);
modelSchema.statics = tmongoose.buildStaticsModelValidated(modelMeta,modelSchema, {
findLastLimit5: function (callback) {
logger.debug(modelBackend+'.findLastLimit5');
this.find({}).sort('-insert_date').limit(5).exec(callback);
}
});
module.exports = mongoose.model(modelName, modelSchema, modelBackend);

View file

@ -0,0 +1,98 @@
var logger = require('winston').loggers.get('main');
var mongoose = require('mongoose');
var tmongoose = require('../lib/node-ff-tcrud').build.backend.mongoose;
var modelName = __filename.split('/').pop().split('.')[0];
var modelBackend = modelName.split('-').join('_');
var modelMeta = {
net_id: {
type: String,
index: { unique: false },
tfield: {
tvalidate: { io: 'string' },
},
},
node_id: {
type: Number,
index: { unique: false },
tfield: {
tvalidate: { io: 'number' },
},
},
insert_date: {
type: Date,
index: { unique: false },
default: Date.now,
tfield: {
tlist: { tenable: false },
}
},
info_date: {
type: Date,
tfield: {
tlist: { tenable: false },
}
},
name: {
type: String,
},
description: {
type: String,
tfield: {
tlist: { tenable: false },
}
},
gps_latitude: {
type: Number,
tfield: {
tname: 'Latitude',
tlist: { tenable: false },
}
},
gps_longitude: {
type: Number,
tfield: {
tname: 'Longitude',
tlist: { tenable: false },
}
},
rx_bytes: {
type: Number,
default: 0,
tfield: {
tname: 'RX bytes'
}
},
rx_requests: {
type: Number,
default: 0,
tfield: {
tname: 'RX req#'
}
},
rx_date: {
type: Date,
index: { unique: false },
default: Date.now,
tfield: {
tname: 'RX Date'
}
}
};
var modelSchema = new mongoose.Schema(modelMeta);
modelSchema.statics = tmongoose.buildStaticsModelValidated(modelMeta,modelSchema, {
findByNetId: function (net_id, callback) {
logger.debug(modelBackend+'.findByNetId net_id='+net_id+"");
this.find({net_id:net_id}).sort('insert_date').exec(callback);
},
findByNetIdAndNodeId: function (net_id,node_id, callback) {
logger.debug(modelBackend+'.findByNetIdAndNodeId net_id='+net_id+' node_id='+node_id);
this.findOne({net_id:net_id,node_id:node_id}).sort('insert_date').exec(callback);
}
});
module.exports = mongoose.model(modelName, modelSchema, modelBackend);

View file

@ -0,0 +1,35 @@
var logger = require('winston').loggers.get('main');
var mongoose = require('mongoose');
var tmongoose = require('../lib/node-ff-tcrud').build.backend.mongoose;
var modelName = __filename.split('/').pop().split('.')[0];
var modelBackend = modelName.split('-').join('_');
var modelMeta = {
_id: {
type: String,
tfield: {
tname: 'SID',
ttype: 'textarea'
}
},
session: {
type: String,
tfield: {
ttype: 'textarea'
}
},
expires: {
type: Date,
},
};
var modelSchema = new mongoose.Schema(modelMeta);
modelSchema.statics = tmongoose.buildStaticsModelValidated(modelMeta,modelSchema, {
findLastChangedLimit5: function (callback) {
logger.debug(modelBackend+'.findLastChangedLimit5');
this.find({}).sort('-changed_date').limit(5).exec(callback);
},
});
module.exports = mongoose.model(modelName, modelSchema, modelBackend);

View file

@ -0,0 +1,146 @@
var logger = require('winston').loggers.get('main');
var mongoose = require('mongoose');
var tmongoose = require('../lib/node-ff-tcrud').build.backend.mongoose;
var modelName = __filename.split('/').pop().split('.')[0];
var modelBackend = modelName.split('-').join('_');
var modelMeta = {
name: {
type: String,
trim: true,
index: { unique: true },
tfield: {
tvalidate: { io: 'string' },
},
},
type: {
type: String,
trim: true,
index: { unique: false },
tfield: {
tvalidate: { io: 'string' },
},
},
value: {
type: String,
trim: true,
tfield: {
xtype: 'textarea',
tvalidate: { io: 'string' },
},
},
description: {
type: String,
trim: true,
tfield: {
ttype: 'textarea',
tvalidate: { io: 'string' },
},
},
changed_date: {
type: Date,
default: Date.now,
tfield: {
tlist: { tenable: false },
},
},
created_date: {
type: Date,
default: Date.now,
tfield: {
tlist: { tenable: false },
},
}
};
var modelSchema = new mongoose.Schema(modelMeta);
modelSchema.statics = tmongoose.buildStaticsModelValidated(modelMeta,modelSchema, {
findLastChangedLimit5: function (callback) {
logger.debug(modelBackend+'.findLastChangedLimit5');
this.find({}).sort('-changed_date').limit(5).exec(callback);
},
findOneByName: function (name, callback) {
logger.debug(modelBackend+'.findByName name='+name);
this.findOne({name:name}).exec(callback);
},
ensureExcists: function (name, type, defaultValue, description, callback) {
this.findOneByName(name, function(err, xprop) {
if (err) {
callback(err);
return;
}
if (xprop == null) {
logger.debug(modelBackend+'.getByName create name='+name+' defaultValue='+defaultValue);
var model = mongoose.model(modelName);
xprop = new model();
xprop.name = name;
xprop.type = type;
xprop.value = defaultValue;
xprop.description = description;
xprop.save(function(err,xprop) {
if (callback) {
callback(err, xprop);
}
});
} else {
logger.debug(modelBackend+'.getByName fetched name='+name);
if (callback) {
callback(null, xprop);
}
}
});
},
setByName: function (name, value, callback) {
this.findOneByName(name, function(err, xprop) {
if (err) { throw err }
logger.debug(modelBackend+'.setByName name='+name+' valueNew='+value+' valueOld='+xprop.value);
xprop.value = value;
xprop.save(function(err) {
callback(err, xprop);
});
});
},
incByName: function (name, callback) {
this.findOneByName(name, function(err, xprop) {
if (err) { throw err }
xprop.value++;
logger.debug(modelBackend+'.incByName name='+name+' value='+xprop.value);
xprop.save(function(err) {
callback(err, xprop);
});
});
},
incHexByName: function (name, callback) {
this.findOneByName(name, function(err, xprop) {
// DIY inc per 8 chars as JS goes to ~14chars before zering the rest.
var v = xprop.value;
var r = '';
var inc = true;
for (var i=0;i<v.length/8;i++) {
var sv = v.substring(v.length-(i*8)-8,v.length-(i*8));
var svNum = parseInt(sv, 16);
if (inc) {
svNum++;
inc = false;
}
if (svNum == (1+0xFFFFFFFF)) {
svNum = 0;
inc = true;
}
sv = svNum.toString(16).toUpperCase();
while (i<((v.length/8)-1) && sv.length < 8) {
sv = '0' + sv;
}
r = sv + r;
}
xprop.value = r; ///(parseInt(xprop.value, 16)+1).toString(16).toUpperCase();
logger.debug(modelBackend+'.incHexByName name='+name+' value='+xprop.value);
xprop.save(function(err) {
callback(err, xprop);
});
});
}
});
module.exports = mongoose.model(modelName, modelSchema, modelBackend);

View file

@ -0,0 +1,46 @@
var logger = require('winston').loggers.get('main');
var mongoose = require('mongoose');
var tmongoose = require('../lib/node-ff-tcrud').build.backend.mongoose;
var modelName = __filename.split('/').pop().split('.')[0];
var modelBackend = modelName.split('-').join('_');
var modelMeta = {
username: {
type: String,
trim: true,
index: { unique: true },
tfield: {
tvalidate: { io: 'string' },
},
},
password: {
type: String
},
active: {
type: Boolean,
default: true
},
changed_date: {
type: Date,
default: Date.now
},
created_date: {
type: Date,
default: Date.now
}
};
var modelSchema = new mongoose.Schema(modelMeta);
modelSchema.statics = tmongoose.buildStaticsModelValidated(modelMeta,modelSchema, {
findLastChangedLimit5: function (callback) {
logger.debug(modelBackend+'.findLastChangedLimit5');
this.find({}).sort('-changed_date').limit(5).exec(callback);
},
findOneByUsername: function (username, callback) {
logger.debug(modelBackend+'.findByName username='+username);
this.findOne({username:username}).exec(callback);
}
});
module.exports = mongoose.model(modelName, modelSchema, modelBackend);

View file

@ -0,0 +1,38 @@
var favicon = require('static-favicon');
var morgan = require('morgan')
var cookieParser = require('cookie-parser')
var bodyParser = require('body-parser');
var express = require('express');
var tcrud = require('./lib/node-ff-tcrud');
var xdeviceA = require('./device/xdevice-connector-a');
var renderIndex = function(server) {
return function (req, res, next) {
res.render('index', {
pageTitle: server.get('config').application.index.pageTitle,
pageKeywords: server.get('config').application.index.pageKeywords,
pageCssFiles: server.get('ff_assets_css'),
pageJsFiles: server.get('ff_assets_js'),
});
}
}
exports.initConfig = function(server,config) {
server.use(favicon());
server.use(morgan(config.options.morgan.logLevel));
server.use(cookieParser(config.options.cookieParser.secretKey));
server.use(bodyParser.json());
server.use(bodyParser.urlencoded());
server.post('/_a', xdeviceA.deviceControl);
tcrud.factory.express.buildCrudApi(server);
server.get('/api/json/server/uptime', tcrud.factory.express.renderServerUptime());
server.get('/api/json/server/routes', tcrud.factory.express.renderServerRoutes(server));
server.get('/', tcrud.factory.express.sendRedirect('/ui'));
server.get('/ui', renderIndex(server));
server.get('/ui/thtml/*', tcrud.factory.express.renderTemplatePath('thtml/'));
server.get('/ui/*', renderIndex(server)); // must be last; for HTML5 history
}

View file

@ -0,0 +1,44 @@
{
"name": "xnode-debug-server",
"description": "XNode Debug Server",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "node server.js",
"size": "npm run-script size-json;npm run-script size-html;npm run-script size-code",
"size-code": "find . -type f -name '*.js' |egrep -v '/node_modules|/lib' | xargs wc -l",
"size-json": "find . -type f -name '*.json' |egrep -v '/node_modules|/lib' | xargs wc -l",
"size-html": "find . -type f -name '*.ejs' |egrep -v '/node_modules|/lib' | xargs wc -l"
},
"dependencies": {
"async": "~0.9.0",
"body-parser": "~1.0.1",
"bootstrap": "3.3.2",
"clone": "^1.0.0",
"connect-mongo": "^0.7.0",
"cookie-parser": "^1.0.1",
"crud-mongoose": "^1.0.10",
"ejs": "^2.3.1",
"errorhandler": "^1.1.1",
"express": "^4.11.0",
"express-session": "^1.10.3",
"fetch": "^0.3.6",
"flot": "0.8.0-alpha",
"fs-extra": "^0.16.3",
"mongoose": "~3.8.13",
"morgan": "~1.1.1",
"node-crud": "2.0.8",
"node-ff-assets": "^0.2.4",
"raw-body": "1.2.2",
"static-favicon": "~1.0.0",
"tree-surgeon": "0.0.13",
"type-is": "1.1.0",
"underscore": "^1.8.2",
"validate.io": "^1.5.0",
"winston": "~0.7.3",
"xml-mapping": "~1.6.1"
},
"devDependencies": {
"mocha-jshint": "0.0.9"
}
}

View file

@ -0,0 +1,27 @@
{
"linkMapping" : {
"/static/module/bootstrap/": "node_modules/bootstrap/dist/",
"/static/module/flot/": "node_modules/flot/",
"/static/": "www_static/"
},
"css": {
"linkTarget": "/static/css/lib/assets.css",
"linkSources": [
"/static/module/bootstrap/css/bootstrap.css"
]
},
"js": {
"linkTarget": "/static/js/lib/assets.js",
"linkSources": [
"/static/js/lib/jquery-2.1.3/jquery.js@http://code.jquery.com/jquery-2.1.3.js",
"/static/module/bootstrap/js/bootstrap.js",
"/static/module/flot/jquery.flot.js",
"/static/module/flot/jquery.flot.resize.js",
"/static/module/flot/jquery.flot.pie.js",
"/static/js/lib/angularjs-1.4.0-b4/angular.js@https://code.angularjs.org/1.4.0-beta.4/angular.js",
"/static/js/lib/angularjs-1.4.0-b4/angular-route.js@https://code.angularjs.org/1.4.0-beta.4/angular-route.js",
"/static/js/lib/angularjs-1.4.0-b4/angular-resource.js@https://code.angularjs.org/1.4.0-beta.4/angular-resource.js",
"/static/js/lib/angularjs-1.4.0-b4/angular-touch.js@https://code.angularjs.org/1.4.0-beta.4/angular-touch.js"
]
}
}

View file

@ -0,0 +1,71 @@
module.exports = {
winston: {
console: {
level: 'debug',
silent: false,
colorize: true,
timestamp: null
},
file: {
level: 'info',
silent: false,
colorize: false,
json: false,
//timestamp: null,
//maxFiles: 100,
//maxsize: 1024*1024*1024,
filename: 'www_logs/server.log'
}
},
mongo: {
url: process.env.MONGO_URL || 'mongodb://localhost:27017/xnode-debug-server',
options : {
//user: 'myUserName',
//pass: 'myPassword',
db: {
fsync: false,
journal: false,
native_parser: true,
forceServerObjectId: true
},
server: {
poolSize: 4,
socketOptions: {
connectTimeoutMS: 500,
keepAlive: 1,
auto_reconnect: true
}
}
}
},
server: {
httpPort: process.env.HTTP_PORT || 8008,
httpTrustProxy: true,
sessionSecret: 'xensitXDS',
sessionTTL: 14 * 24 * 60 * 60, // = 14 days. Default
},
application: {
name: 'Xensit Debug Server',
index: {
pageTitle: 'Xensit Debug Server',
pageKeywords: 'xnode,sensor,data,weather,wireless',
},
},
options: {
static: {
maxAge: 86400000
},
morgan: {
logLevel: process.env.LOG_MORGAN || 'dev'
},
cookieParser: {
secretKey: 'DeBugMe@11'
},
rss: {
link: 'http://localhost:8008',
author: 'Xensit',
options: {
}
}
}
};

View file

@ -0,0 +1,102 @@
'use strict';
var logger = require('winston').loggers.get('main');
var mongoose = require('mongoose');
var async = require('async');
var fs = require('fs');
var tcrud = require('./node_lib/lib/node-ff-tcrud');
var assets = require('node-ff-assets');
var xsystemState = mongoose.model( 'xsystem-state' );
function walkTree(builderConfigJs,server,crud) {
if (crud.tparent && crud.tenable && crud.tmodel) {
var localPort = server.get('config').server.httpPort;
var tview = tcrud.build.view.createTCrudView(crud/*, fetchCrudRoles()*/);
var base = troot.tmeta.tserver.tslug + '/' + tcrud.build.view.createBaseApiUri(tview,'angular');
var assetLink = '/static/js/lib/tcrud/'+tview.tslug+'/controller.js@@http://localhost:'+localPort+base+'/controller.js';
//console.log('asssetLinkDyna; '+assetLink);
builderConfigJs.linkSources.push(assetLink);
}
for (var i = 0; i < crud.tchilds.length; i++) {
walkTree(builderConfigJs,server,crud.tchilds[i]);
}
}
function pushDynamicLinkSourcesJs(builderConfigJs,server) {
return function(callback) {
// Extend js list with crud entries
troot = server.get('ff_root_tcrud_view');
walkTree(builderConfigJs,server,troot);
callback();
};
}
function buildAssets(server,callbackDone) {
var singleResult = 'false' !== process.env.DEV_ASSETS_SINGLE_RESULT;
var assetsConfig = require('./server-assets.json');
assets.build({
assets: {
js: {
configCreate: assets.factory.builder.configCreate.fromJSON(assetsConfig,'js'),
configFill: function (config, callback) {
async.series([
assets.factory.lib.async.pushLinkSources(config, '/static/js/', 'www_static/js/'),
assets.factory.lib.async.pushLinkSources(config, '/static/js/controller/', 'www_static/js/controller/'),
pushDynamicLinkSourcesJs(config,server),
],callback);
},
},
css: {
configCreate: assets.factory.builder.configCreate.fromJSON(assetsConfig,'css'),
configFill: function (config, callback) {
async.series([
assets.factory.lib.async.pushLinkSources(config, '/static/css/', 'www_static/css/'),
],callback);
},
//assemblerCreate: function (assemblerConfig, callback) {
// callback(null,new assets.AssetAssembler(assemblerConfig,assets.factory.assembler.constructor.readFileMinify()));
//},
},
},
// assemblerCreate: assets.factory.builder.assemblerCreate.readFileRegex(),
assemblerFill: function (assembler, callback) {
var serverResultKey = 'ff_assets_'+assembler.config.assetType;
assembler.on ('log', assets.factory.assembler.event.log.winston(logger,assembler.config.assetType));
assembler.on ('result', assets.factory.assembler.event.result.objectSet(server,serverResultKey));
assembler.config.linkTargetSingleResult = singleResult;
callback();
},
},callbackDone);
}
var initServerStateValue = function(server,type,name,defaultValue,description) {
xsystemState.ensureExcists(name,type,defaultValue,description,function(err,data) {
if (err) { throw err }
logger.info('state checked: '+data.name);
});
}
exports.build = function(server) {
buildAssets(server,function(err) {
if (err) {
logger.info('Server init failed: '+err);
return;
}
logger.info('Server init done.');
// init other stuff...
initServerStateValue(server,'_a','_a_seq_base_net_id', '1F2E3D4C5B6A','XNode Base net_id sequence');
initServerStateValue(server,'_a','_a_seq_base_net_key' ,'9A2EE3A293486E9FE73D77EFC8087D2F','XNode Base net_key sequence')
initServerStateValue(server,'_a','_a_seq_base_net_mac', '7469694D33B1','XNode Base net_mac sequence')
initServerStateValue(server,'_a','_a_seq_base_rf_key', '1A2B93586E9F170D72EFC8FE72D53A9F','XNode Base rf_key sequence');
initServerStateValue(server,'xds','xds_boot_date',new Date().getTime(),'Server Last boot date');
initServerStateValue(server,'xds','xds_boot_count','0','Server Boot Counter');
setTimeout(function() {
xsystemState.incByName('xds_boot_count',function(err,data) {});
xsystemState.setByName('xds_boot_date',new Date().getTime(),function(err,data) {});
}, 500);
});
}

View file

@ -0,0 +1,115 @@
// server.js
var config = require('./server-config');
var winston = require('winston');
winston.loggers.add('main',config.winston);
var log = winston.loggers.get('main');
var express = require('express');
var path = require('path');
var fs = require('fs');
var mongoose = require('mongoose');
var expressSession = require('express-session');
var mongoStore = require('connect-mongo')(expressSession);
var tcrud = require('./node_lib/lib/node-ff-tcrud');
function startMongoose(callback) {
log.info('Starting '+config.application.name);
log.info('Connecting to: ' + config.mongo.url);
mongoose.connection.on('error', callback);
mongoose.connection.on('connected', callback);
mongoose.connect(config.mongo.url,config.mongo.options);
}
function startMongooseModels(server,callback) {
var modelsCount = 0;
var modelsPath = __dirname + '/node_lib/model';
fs.readdirSync(modelsPath).forEach(function (file) {
if (~file.indexOf('.js')) {
log.debug('Loading mongoose model: '+file);
require(modelsPath + '/' + file);
modelsCount++;
}
});
log.info('Loaded mongoose models: '+modelsCount);
var crudRoot = tcrud.build.config.createTCrudRoot();
var crudAdmin = tcrud.build.config.createTCrud(crudRoot,'admin');
crudAdmin.troles.push('admin');
//crudAdmin.tenable = false;
var crudAdminModels = tcrud.build.backend.mongoose.buildTCrudModels(mongoose,crudAdmin);
log.info('crud admin models created: '+crudAdminModels.length);
//var crudAdminNodeBaseCmd = crudAdmin
server.set('ff_root_tcrud_view',crudRoot);
callback();
}
// Build Server
function createServerSync() {
var server = express();
server.disable('x-powered-by');
server.set('config', config);
server.set('trust proxy', config.server.httpTrustProxy);
server.set('views', [path.join(__dirname, 'www_views'),path.join(__dirname, 'node_lib/lib/www_views')]);
server.set('view engine', 'ejs');
server.use('/static',express.static(path.join(__dirname,'www_static'),config.options.static));
server.use('/static/module/bootstrap',express.static(path.join(__dirname,'node_modules/bootstrap/dist'),config.options.static));
server.use('/static/module/flot',express.static(path.join(__dirname,'node_modules/flot'),config.options.static));
// Add Server session support
// server.use(expressSession({
// secret: config.server.sessionSecret,
// store: new mongoStore({
// mongooseConnection: mongoose.connection,
// collection: 'xsystem_session',
// ttl: server.sessionTTL,
// })
// }));
return server;
}
function configServer(server,callback) {
// Add Server config and routing
var router = require('./node_lib/server-build');
router.initConfig(server,config);
// Store router list for server/routes page
tcrud.factory.express.buildServerRoutes(server);
// Auto build site assets
var ServerInit = require('./server-init');
ServerInit.build(server);
// Test
//var test = require('./node_lib/device/xdevice-encryption');
//log.info('testA='+test.xxteaDecrypt('D57408F9D7F10917AFE9AA92C8051450','9A2EE3A293486E9FE73D77EFC8087D31'));
//log.info('testA='+test.xxteaDecrypt('9A2EE3A293486E9F','D57408F9D7F10917AFE9AA92C8051450'));
//log.info('testB=rf_fail=1');
//log.info('testC='+test.xxteaDecrypt('944DF85BAEE43DC0D7914910D2B69547','9A2EE3A293486E9F'));
callback();
}
function startServer(server) {
server.listen(config.server.httpPort);
log.info('Started '+config.application.name+' on port: ' + config.server.httpPort);
}
startMongoose(function(err) {
if (err) { throw err };
server = createServerSync();
startMongooseModels(server,function(err) {
if (err) { throw err };
configServer(server,function(err) {
if (err) { throw err };
startServer(server)
});
});
});
module.exports = {
version: 'development'
};

View file

@ -0,0 +1,149 @@
body {
margin-top: 100px;
background-color: #222;
}
@media(min-width:768px) {
body {
margin-top: 50px;
}
}
#wrapper {
padding-left: 0;
}
#page-wrapper {
width: 100%;
padding: 0;
background-color: #fff;
}
.huge {
font-size: 50px;
line-height: normal;
}
@media(min-width:768px) {
#wrapper {
padding-left: 225px;
}
#page-wrapper {
padding: 10px;
}
}
/* Top Navigation */
.top-nav {
padding: 0 15px;
}
.top-nav>li {
display: inline-block;
float: left;
}
.top-nav>li>a {
padding-top: 15px;
padding-bottom: 15px;
line-height: 20px;
color: #999;
}
.top-nav>li>a:hover,
.top-nav>li>a:focus,
.top-nav>.open>a,
.top-nav>.open>a:hover,
.top-nav>.open>a:focus {
color: #fff;
background-color: #000;
}
.top-nav>.open>.dropdown-menu {
float: left;
position: absolute;
margin-top: 0;
border: 1px solid rgba(0,0,0,.15);
border-top-left-radius: 0;
border-top-right-radius: 0;
background-color: #fff;
-webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175);
box-shadow: 0 6px 12px rgba(0,0,0,.175);
}
.top-nav>.open>.dropdown-menu>li>a {
white-space: normal;
}
ul.message-dropdown {
padding: 0;
max-height: 250px;
overflow-x: hidden;
overflow-y: auto;
}
li.message-preview {
width: 275px;
border-bottom: 1px solid rgba(0,0,0,.15);
}
li.message-preview>a {
padding-top: 15px;
padding-bottom: 15px;
}
li.message-footer {
margin: 5px 0;
}
ul.alert-dropdown {
width: 200px;
}
/* Side Navigation */
@media(min-width:768px) {
.side-nav {
position: fixed;
top: 51px;
left: 225px;
width: 225px;
margin-left: -225px;
border: none;
border-radius: 0;
overflow-y: auto;
background-color: #222;
}
.side-nav>li>a {
width: 225px;
}
.side-nav li a:hover,
.side-nav li a:focus {
outline: none;
background-color: #000 !important;
}
}
.side-nav>li>ul {
padding: 0;
}
.side-nav>li>ul>li>a {
display: block;
padding: 10px 15px 10px 38px;
text-decoration: none;
color: #999;
}
.side-nav>li>ul>li>a:hover {
color: #fff;
}
.huge {
font-size: 40px;
}

View file

@ -0,0 +1,11 @@
.flot-chart {
display: block;
height: 400px;
}
.flot-chart-content {
width: 100%;
height: 100%;
}

View file

@ -0,0 +1,56 @@
.panel-green {
border-color: #5cb85c;
}
.panel-green .panel-heading {
border-color: #5cb85c;
color: #fff;
background-color: #5cb85c;
}
.panel-green a {
color: #5cb85c;
}
.panel-green a:hover {
color: #3d8b3d;
}
.panel-red {
border-color: #d9534f;
}
.panel-red .panel-heading {
border-color: #d9534f;
color: #fff;
background-color: #d9534f;
}
.panel-red a {
color: #d9534f;
}
.panel-red a:hover {
color: #b52b27;
}
.panel-yellow {
border-color: #f0ad4e;
}
.panel-yellow .panel-heading {
border-color: #f0ad4e;
color: #fff;
background-color: #f0ad4e;
}
.panel-yellow a {
color: #f0ad4e;
}
.panel-yellow a:hover {
color: #df8a13;
}

View file

@ -0,0 +1,54 @@
#wrapper {
padding-right: 0px;
padding-left: 0px;
}
.side-nav {
right: 0px;
left: 0px;
}
.btn {
margin-right: 10px;
}
.navbar-footer {
margin-left: 45%;
}
.navbar-toggle {
display: none;
}
.navbar-brand {
background-color: #2CABE1;
}
.navbar-inverse .navbar-brand {
color: #FFFFFF;
}
.navbar-inverse .navbar-brand:focus {
outline: none;
}
.navbar-inverse .navbar-brand:hover {
background-color: #FF9A28;
outline: none;
}
.navbar-inverse {
background-color: #2CABE1;
}
#menuLogo {
position: absolute;
top: 47px;
right: 0px;
background: url("../img/xensit-sm.png") 50% 0px no-repeat transparent !important;
height: 77px;
width: 212px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View file

@ -0,0 +1,13 @@
pageRouteInit.push(function ($routeProvider, $locationProvider) {
$routeProvider.when('/ui/server/about', {
templateUrl: '/ui/thtml/server/about',
controller: XPageServerAbout
});
});
function XPageServerAbout($scope, $http) {
//$http.get('/api/posts').success(function(data, status, headers, config) {
// $scope.posts = data.posts;
//});
}

View file

@ -0,0 +1,14 @@
pageRouteInit.push(function ($routeProvider, $locationProvider) {
$routeProvider.when('server/dash', {
templateUrl: '/ui/thtml/server/dash',
controller: XPageServerDash
});
});
function XPageServerDash($scope, $http) {
//$http.get('/api/posts').success(function(data, status, headers, config) {
// $scope.posts = data.posts;
//});
}

View file

@ -0,0 +1,13 @@
pageRouteInit.push(function ($routeProvider, $locationProvider) {
$routeProvider.when('/ui', {
templateUrl: '/ui/thtml/server/index',
controller: XPageServerIndex
});
});
function XPageServerIndex($scope, $http) {
//$http.get('/api/posts').success(function(data, status, headers, config) {
// $scope.posts = data.posts;
//});
}

View file

@ -0,0 +1,26 @@
pageRouteInit.push(function ($routeProvider, $locationProvider) {
$routeProvider.when('/ui/server/routes', {
redirectTo: '/ui/server/routes/tech',
});
$routeProvider.when('/ui/server/routes/tech', {
templateUrl: '/ui/thtml/server/routes?group1=all,rss,json&group2=csv,xml,ui,angular',
controller: XPageServerTechRoutes,
});
$routeProvider.when('/ui/server/routes/model', {
templateUrl: '/ui/thtml/server/routes?group1=xnode,xnode_base,xnode-base-command&group2=xnode-data,xnode-data-value,xsystem-state,all',
controller: XPageServerModelRoutes,
});
});
function XPageServerTechRoutes($scope, $http) {
$http.get('/api/json/server/routes?groups=json,xml,rss,csv,ui,angular').success(function(data, status, headers, config) {
$scope.serverRoutes = data.data;
});
}
function XPageServerModelRoutes($scope, $http) {
$http.get('/api/json/server/routes?groups=xnode,xnode-base,xnode-base-command,xnode-data,xnode-data-value,system-state').success(function(data, status, headers, config) {
$scope.serverRoutes = data.data;
});
}

View file

@ -0,0 +1,9 @@
'use strict';
angular.module('xdsUI.directives', []).
directive('appVersion', ['version', function(version) {
return function(scope, elm, attrs) {
elm.text(version);
};
}]);

View file

@ -0,0 +1,9 @@
'use strict';
angular.module('xdsUI.filters', []).
filter('interpolate', ['version', function(version) {
return function(text) {
return String(text).replace(/\%VERSION\%/mg, version);
}
}]);

View file

@ -0,0 +1,5 @@
'use strict';
angular.module('xdsUI.services', []).
value('version', '0.1');

View file

@ -0,0 +1,27 @@
'use strict';
var crudRouteInit = [];
var pageRouteInit = [];
angular.module('xdsUI', ['ngRoute','xdsUI.filters', 'xdsUI.services', 'xdsUI.directives']).
config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
// init page controllers
for (var i = 0; i < pageRouteInit.length; i++) {
pageRouteInit[i]($routeProvider, $locationProvider);
}
// init crud controllers
for (var i = 0; i < crudRouteInit.length; i++) {
crudRouteInit[i]($routeProvider, $locationProvider);
}
//$routeProvider.otherwise("/404", {
// templateUrl: "partials/404.html",
// controller: "PageCtrl"
//});
$routeProvider.otherwise({ redirectTo: '/ui' });
// todo: add ie9 warning, base kills svg url property
$locationProvider.html5Mode({enabled: true, requireBase: false});
}]);

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en" ng-app="xdsUI">
<head>
<title><%= pageTitle %></title>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="keywords" content="<%= pageKeywords %>">
<% if (pageCssFiles.length) { %><% pageCssFiles.forEach(function (cssFile) { %><link rel="stylesheet" href="<%= cssFile %>"><% }) %><% } %>
</head>
<body>
<div id="wrapper">
<div ng-include="'/ui/thtml/layout/header'"></div>
<div id="page-wrapper">
<div id="container-fluid">
<div ng-view></div>
</div>
</div>
</div>
<div ng-include="'/ui/thtml/layout/footer'"></div>
<% if (pageJsFiles.length) { %><% pageJsFiles.forEach(function (jsFile) { %><script src="<%= jsFile %>"></script><% }) %><% } %>
</body>
</html>

View file

@ -0,0 +1,7 @@
<div>
<h1>400</h1>
<h2>Page Not Found</h2>
<pre>
Please check you url.
</pre>
</div>

View file

@ -0,0 +1,7 @@
<div>
<h1>500</h1>
<h2>Server Error</h2>
<pre>
Sorry server at fault.
</pre>
</div>

View file

@ -0,0 +1,7 @@
<nav class="navbar">
<div class="navbar-footer">
<a href="/ui/">Home</a> | -
<a href="/ui/server/about">About</a> | -
<a href="/ui/server/routes">Routes<a/>
</div>
</nav>

View file

@ -0,0 +1,16 @@
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/ui/">Home</a>
<a class="navbar-brand" href="/ui/admin/xnode/list">XNodes</a>
<a class="navbar-brand" href="/ui/admin/xnode-base/list">XNodeBase</a>
<a class="navbar-brand" href="/ui/admin/xsystem-state/list">XSysState</a>
<a class="navbar-brand" href="/ui/admin/xsystem-session/list">XSysSession</a>
</div>
<div id="menuLogo"></div>
</nav>

View file

@ -0,0 +1,5 @@
<div>
<h2>About</h2>
<p>Testing debug server for xensit xnodes.</p>
<p>Internal use only.</p>
</div>

View file

@ -0,0 +1,96 @@
extends layout
block content
h1= title
div
h2 Last five node data.
table
thead
tr
th Net id
th Node Id
th Value
tbody
each row in lastData
tr
td #{row.net_id}
td #{row.node_id}
td
p #{row.insert_date}
p #{row.data_text}
div(id='split')
div(id='splitLeft')
div
h2 Last five node values.
table
thead
tr
th Net id
th Name
th Value
th insert_date
tbody
each row in lastValues
tr
td #{row.net_id}
td #{row.name}
td #{row.value}
td #{row.insert_date}
div
h2 Last five changed settings
table
thead
tr
th Name
th Value
tbody
each row in lastSettings
tr
td #{row.name}
td #{row.value}
div(id='splitRight')
div
h2 Last five pings
table
thead
tr
th Net id
th Ping Count
th Ping Last
tbody
each xnode in lastPinged
tr
td #{xnode.hw_net_id}
td #{xnode.ping_counter}
td #{xnode.ping_last_date}
div
h2 Last five open Cmds
table
thead
tr
th Net id
th Command
th Insert date
tbody
each xnode in openCommands
tr
td #{xnode.net_id}
td #{xnode.command}
td #{xnode.insert_date}
div
h2 Last five send Cmds
table
thead
tr
th Net id
th Command
th Send date
tbody
each xnode in sendCommands
tr
td #{xnode.net_id}
td #{xnode.command}
td #{xnode.send_date}

View file

@ -0,0 +1,5 @@
<div>
<h2>XNode Debug Server Home</h2>
<p>Welcome</p>
<p>Make your self at home.</p>
</div>

View file

@ -0,0 +1,17 @@
<div class="table-responsive">
<h3><%= routeGroup.toUpperCase() %></h3>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Path</th>
<th>Method</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="route in serverRoutes['<%= routeGroup %>']">
<td>{{route.uriPath}}</td>
<td>{{route.httpMethod}}</td>
</tr>
</tbody>
</table>
</div>

View file

@ -0,0 +1,28 @@
<div>
<h2>Server Routes</h2>
<p>This page show all the nodejs express routes.</p>
<div class="row">
<% if (query.group1 && query.group2) { %>
<div class="col-lg-6">
<% query.group1.split(',').forEach(function (routeGroup) { %>
<%- include('routes-group', {routeGroup: routeGroup}); %>
<% }) %>
</div>
<div class="col-lg-6">
<% query.group2.split(',').forEach(function (routeGroup) { %>
<%- include('routes-group', {routeGroup: routeGroup}); %>
<% }) %>
</div>
<% } else if (query.group1) { %>
<div class="col-lg-12">
<% query.group1.split(',').forEach(function (routeGroup) { %>
<%- include('routes-group', {routeGroup: routeGroup}); %>
<% }) %>
</div>
<% } else { %>
<div class="col-lg-12">
<%- include('routes-group', {routeGroup: 'all'}); %>
</div>
<% } %>
</div>
</div>

View file

@ -0,0 +1,40 @@
#
# Managed debug logging
#
# Supported arguments are: NONE,ALL,NETWORK,RADIO,SYSTEM,SERIAL,SENSOR
# Replace keywords with debug flags.
ifneq (,$(findstring NETWORK,$(DEBUG_LOGGING)))
DEBUG_NETWORK ?= -DDEBUG_NETWORK
endif
ifneq (,$(findstring RADIO,$(DEBUG_LOGGING)))
DEBUG_RADIO ?= -DDEBUG_RADIO
endif
ifneq (,$(findstring SYSTEM,$(DEBUG_LOGGING)))
DEBUG_SYSTEM ?= -DDEBUG_SYSTEM
endif
ifneq (,$(findstring SERIAL,$(DEBUG_LOGGING)))
DEBUG_SERIAL ?= -DDEBUG_SERIAL
endif
ifneq (,$(findstring SENSOR,$(DEBUG_LOGGING)))
DEBUG_SENSOR ?= -DDEBUG_SENSOR
endif
ifneq (,$(findstring ALL,$(DEBUG_LOGGING)))
DEBUG_NETWORK ?= -DDEBUG_NETWORK
DEBUG_RADIO ?= -DDEBUG_RADIO
DEBUG_SYSTEM ?= -DDEBUG_SYSTEM
DEBUG_SERIAL ?= -DDEBUG_SERIAL
DEBUG_SENSOR ?= -DDEBUG_SENSOR
endif
# Let NONE keyword override to disable.
ifeq (,$(findstring NONE,$(DEBUG_LOGGING)))
# Argragate debug flags to build.
DEBUG_FLAGS ?= \
$(DEBUG_NETWORK) \
$(DEBUG_RADIO) \
$(DEBUG_SYSTEM) \
$(DEBUG_SERIAL) \
$(DEBUG_SENSOR)
endif

View file

@ -0,0 +1,36 @@
#
# Managed libraries versions
#
# Replace libraries with versioned libraries paths.
ifneq (,$(findstring spi,$(MASTER_LIBS)))
II_SPI = $(ARD_HOME)/hardware/arduino/avr/libraries/SPI
endif
ifneq (,$(findstring rfm,$(MASTER_LIBS)))
II_RFM = ../lib-ext/rfm-69.git
endif
ifneq (,$(findstring dht,$(MASTER_LIBS)))
II_DHT = ../lib-ext/dht.git
endif
ifneq (,$(findstring ethercard,$(MASTER_LIBS)))
II_ETHERCARD = ../lib-ext/ethercard.git
endif
ifneq (,$(findstring isp-repair,$(MASTER_LIBS)))
II_ISP_REPAIR = ../lib/isp-repair
endif
ifneq (,$(findstring xnode-shared,$(MASTER_LIBS)))
II_XNODE_SHARED = ../lib/xnode-shared
endif
ifneq (,$(findstring xnode-shared-satellite,$(MASTER_LIBS)))
II_XNODE_SHARED_SATELLITE = ../lib/xnode-shared-satellite
endif
# Argragate Internal Includes to ROOT_LIBS
ROOT_LIBS = \
$(II_SPI) \
$(II_RFM) \
$(II_DHT) \
$(II_ETHERCARD) \
$(II_ISP_REPAIR) \
$(II_XNODE_SHARED) \
$(II_XNODE_SHARED_SATELLITE)

View file

@ -0,0 +1,20 @@
# Local env included makefile
# This file should be ignored in version control
# note: the ?= is so you can override those again in cmdline.
#
# Change to local arduino installation.
ARD_HOME ?= /home/willemc/devv/avr/ide/arduino-1.6.0
# Default port to isp
# PORT ?= /dev/ttyACM0
# Uncomment to have serial debug printing
# Supported arguments are: NONE,ALL,NETWORK,RADIO,SYSTEM,SERIAL,SENSOR
# DEBUG_LOGGING ?= ALL
# Uncomment to disable lookup and this this address.
# DEBUG_NET_HISIP ?= {10,11,12,177}
# Only needed in some extreme cases where there is
# transparant http proxy filtering in the network...
# DEBUG_NET_GATE ?= {10,11,12,177}

View file

@ -0,0 +1,284 @@
#
# Copyright 2011 Alan Burlison, alan@bleaklow.com. All rights reserved.
# Subsequently modified by Matthieu Weber, matthieu.weber@jyu.fi.
# Use is subject to license terms.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY ALAN BURLISON "AS IS" AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL ALAN BURLISON OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#
# Changed some code for working without pde/ino files.
# Added extra build flag and libary support.
# wc, 2014-01-13
#
# Minor adjustments for Mac OS X and for educational purposes by Maik Schmidt,
# contact@maik-schmidt.de.
#
# Makefile for building Arduino projects outside of the Arduino environment
#
# This makefile should be included into a per-project Makefile of the following
# form:
#
# ----------
# BOARD = mega
# PORT = /dev/term/0
# INC_DIRS = ../common
# LIB_DIRS = ../libraries/Task ../../libraries/VirtualWire
# include ../../Makefile.master
# ----------
#
# Where:
# BOARD : Arduino board type, from $(ARD_HOME)/hardware/boards.txt
# PORT : USB port
# INC_DIRS : List pf directories containing header files
# LIB_DIRS : List of directories containing library source
#
# Before using this Makefile you can adjust the following macros to suit
# your environment, either by editing this file directly or by defining them in
# the Makefile that includes this one, in which case they will override the
# definitions below:
# ARD_REV : arduino software revision, e.g. 0017, 0018
# ARD_HOME : installation directory of the Arduino software.
# ARD_BIN : location of compiler binaries
# AVRDUDE : location of avrdude executable
# AVRDUDE_CONF : location of avrdude configuration file
# PROGRAMMER : avrdude programmer type
# MON_CMD : serial monitor command
# MON_SPEED : serial monitor speed
#
# Global configuration.
ARD_REV ?= 0022
ARD_HOME ?= /Applications/Arduino.app/Contents/Resources/Java
ARD_BIN ?= /usr/local/CrossPack-AVR/bin
AVRDUDE ?= $(ARD_HOME)/hardware/tools/avr/bin/avrdude
AVRDUDE_CONF ?= $(ARD_HOME)/hardware/tools/avr/etc/avrdude.conf
PROGRAMMER ?= stk500v1
MON_SPEED ?= 57600
MON_CMD ?= picocom
PORT ?= /dev/tty.usbserial-A60061a3
BOARD ?= atmega328
### Nothing below here should require editing. ###
# Check for the required definitions.
ifndef BOARD
$(error $$(BOARD) not defined)
endif
ifndef PORT
$(error $$(PORT) not defined)
endif
# Version-specific settings
ARD_BOARDS = $(ARD_HOME)/hardware/arduino/avr/boards.txt
ARD_SRC_DIR = $(ARD_HOME)/hardware/arduino/avr/cores/arduino
ARD_MAIN = $(ARD_SRC_DIR)/main.cpp
# Standard macros.
SKETCH = $(notdir $(CURDIR))
BUILD_DIR = build
VPATH = $(LIB_DIRS)
# Macros derived from boards.txt
MCU := $(shell sed -n 's/$(BOARD)\.build\.mcu=\(.*\)/\1/p' < $(ARD_BOARDS))
F_CPU := $(shell sed -n 's/$(BOARD)\.build\.f_cpu=\(.*\)/\1/p' < $(ARD_BOARDS))
UPLOAD_SPEED := \
$(shell sed -n 's/$(BOARD)\.upload\.speed=\(.*\)/\1/p' < $(ARD_BOARDS))
# Build tools.
CC = $(ARD_BIN)/avr-gcc
CXX = $(ARD_BIN)/avr-g++
CXXFILT = $(ARD_BIN)/avr-c++filt
OBJCOPY = $(ARD_BIN)/avr-objcopy
OBJDUMP = $(ARD_BIN)/avr-objdump
AR = $(ARD_BIN)/avr-ar
SIZE = $(ARD_BIN)/avr-size
NM = $(ARD_BIN)/avr-nm
MKDIR = mkdir -p
RM = rm -rf
MV = mv -f
LN = ln -f
# Compiler flags.
INC_FLAGS = \
$(addprefix -I,$(INC_DIRS)) $(addprefix -I,$(LIB_DIRS)) -I$(ARD_SRC_DIR)
ARD_FLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -DARDUINO=$(ARD_REV) $(SKETCH_FLAGS)
C_CXX_FLAGS = \
-Wall -Wextra -Wundef -Wno-unused-parameter \
-fdiagnostics-show-option -g -Wa,-adhlns=$(BUILD_DIR)/$*.lst
C_FLAGS = \
$(C_CXX_FLAGS) -std=gnu99 -Wstrict-prototypes -Wno-old-style-declaration
CXX_FLAGS = $(C_CXX_FLAGS)
# Optimiser flags.
# optimise for size, unsigned by default, pack data.
# separate sections, drop unused ones, shorten branches, jumps.
# don't inline, vectorise loops. no exceptions.
# no os preamble, use function calls in prologues.
# http://gcc.gnu.org/onlinedocs/gcc-4.3.5/gcc/
# http://www.tty1.net/blog/2008-04-29-avr-gcc-optimisations_en.html
OPT_FLAGS = \
-Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \
-ffunction-sections -fdata-sections -Wl,--gc-sections,--relax \
-fno-inline-small-functions -fno-tree-scev-cprop -fno-exceptions \
-ffreestanding -mcall-prologues
# Build parameters.
IMAGE = $(BUILD_DIR)/$(SKETCH)
ARD_C_SRC = $(wildcard $(ARD_SRC_DIR)/*.c)
ARD_CXX_SRC = $(wildcard $(ARD_SRC_DIR)/*.cpp)
ARD_C_OBJ = $(patsubst %.c,%.o,$(notdir $(ARD_C_SRC)))
ARD_CXX_OBJ = $(patsubst %.cpp,%.o,$(notdir $(ARD_CXX_SRC)))
ARD_LIB = arduino
ARD_AR = $(BUILD_DIR)/lib$(ARD_LIB).a
ARD_AR_OBJ = $(ARD_AR)($(ARD_C_OBJ) $(ARD_CXX_OBJ))
ARD_LD_FLAG = -l$(ARD_LIB)
# Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
$(ARD_AR)(Tone.o) : CXX_FLAGS += -w
# Sketch libraries.
LIB_C_SRC = $(foreach ld,$(LIB_DIRS),$(wildcard $(ld)/*.c))
LIB_CXX_SRC = $(foreach ld,$(LIB_DIRS),$(wildcard $(ld)/*.cpp))
LIB_SRC = $(LIB_C_SRC) $(LIB_CXX_SRC)
ifneq "$(strip $(LIB4_C_SRC) $(LIB_CXX_SRC))" ""
LIB_C_OBJ = $(patsubst %.c,%.o,$(notdir $(LIB_C_SRC)))
LIB_CXX_OBJ = $(patsubst %.cpp,%.o,$(notdir $(LIB_CXX_SRC)))
LIB_LIB = library
LIB_AR = $(BUILD_DIR)/lib$(LIB_LIB).a
LIB_AR_OBJ = $(LIB_AR)($(LIB_C_OBJ)$(LIB_CXX_OBJ))
LIB_LD_FLAG = -l$(LIB_LIB)
endif
# Sketch PDE source.
# SKT_PDE_SRC = $(wildcard *.pde)
#ifneq "$(strip $(SKT_PDE_SRC))" ""
# SKT_PDE_OBJ = $(BUILD_DIR)/$(SKETCH)_pde.o
#endif
SKT_PDE_OBJ = $(patsubst %.cpp,%.o,$(notdir $(SKT_PDE_SRC)))
# C and C++ source.
SKT_C_SRC = $(wildcard *.c)
SKT_CXX_SRC = $(wildcard *.cpp)
ifneq "$(strip $(SKT_C_SRC) $(SKT_CXX_SRC))" ""
SKT_C_OBJ = $(patsubst %.c,%.o,$(SKT_C_SRC))
SKT_CXX_OBJ = $(patsubst %.cpp,%.o,$(SKT_CXX_SRC))
SKT_LIB = sketch
SKT_AR = $(BUILD_DIR)/lib$(SKT_LIB).a
SKT_AR_OBJ = $(SKT_AR)($(SKT_C_OBJ) $(SKT_CXX_OBJ))
#SKT_AR_OBJ = $(SKT_AR)/($(SKT_C_OBJ) $(SKT_CXX_OBJ)) // mmm: '/' rmed after make 4.0 to work
SKT_LD_FLAG = -l$(SKT_LIB)
endif
# Definitions.
define run-cc
@ $(CC) $(ARD_FLAGS) $(INC_FLAGS) -M -MT '$@($%)' -MF $@_$*.dep $<
$(CC) -c $(C_FLAGS) $(OPT_FLAGS) $(ARD_FLAGS) $(INC_FLAGS) \
$< -o $(BUILD_DIR)/$%
@ $(AR) rc $@ $(BUILD_DIR)/$%
@ $(RM) $(BUILD_DIR)/$%
@ $(CXXFILT) < $(BUILD_DIR)/$*.lst > $(BUILD_DIR)/$*.lst.tmp
@ $(MV) $(BUILD_DIR)/$*.lst.tmp $(BUILD_DIR)/$*.lst
endef
define run-cxx
@ $(CXX) $(ARD_FLAGS) $(INC_FLAGS) -M -MT '$@($%)' -MF $@_$*.dep $<
$(CXX) -c $(CXX_FLAGS) $(OPT_FLAGS) $(ARD_FLAGS) $(INC_FLAGS) \
$< -o $(BUILD_DIR)/$%
@ $(AR) rc $@ $(BUILD_DIR)/$%
@ $(RM) $(BUILD_DIR)/$%
@ $(CXXFILT) < $(BUILD_DIR)/$*.lst > $(BUILD_DIR)/$*.lst.tmp
@ $(MV) $(BUILD_DIR)/$*.lst.tmp $(BUILD_DIR)/$*.lst
endef
# Rules.
.PHONY : all clean upload monitor upload_monitor
all : $(BUILD_DIR) $(IMAGE).hex
clean :
$(RM) $(BUILD_DIR)
$(BUILD_DIR) :
$(MKDIR) $@
$(SKT_PDE_OBJ) : $(SKT_PDE_SRC)
$(CXX) -c $(CXX_FLAGS) $(OPT_FLAGS) $(ARD_FLAGS) $(INC_FLAGS) \
$< -o $(BUILD_DIR)/$(SKT_PDE_OBJ)
# echo '#include <Arduino.h>' > $(BUILD_DIR)/$(SKETCH)_pde.cpp
# echo '#include "$(SKT_PDE_SRC)"' >> $(BUILD_DIR)/$(SKETCH)_pde.cpp
# $(LN) $(SKT_PDE_SRC) $(BUILD_DIR)/$(SKT_PDE_SRC)
# cd $(BUILD_DIR) && ../$(CXX) -c $(subst build/,,$(CXX_FLAGS)) \
# $(OPT_FLAGS) $(ARD_FLAGS) -I.. \
# $(patsubst -I..%,-I../..%,$(INC_FLAGS)) \
# $(SKETCH)_pde.cpp -o $(@F)
(%.o) : $(ARD_SRC_DIR)/%.c
$(run-cc)
(%.o) : $(ARD_SRC_DIR)/%.cpp
$(run-cxx)
(%.o) : %.c
$(run-cc)
(%.o) : %.cpp
$(run-cxx)
# not used
#$(BUILD_DIR)/%.d %.c
# $(run-cc-d)
#
#$(BUILD_DIR)/%.d %.cpp
# $(run-cxx-d)
#build/libsketch.a build/libarduino.a
$(IMAGE).hex : $(ARD_AR_OBJ) $(LIB_AR_OBJ) $(SKT_AR_OBJ) $(SKT_PDE_OBJ)
$(CC) $(CXX_FLAGS) $(OPT_FLAGS) $(ARD_FLAGS) -L$(BUILD_DIR) \
$(BUILD_DIR)/$(SKT_PDE_OBJ) $(SKT_LD_FLAG) $(LIB_LD_FLAG) $(ARD_LD_FLAG) -lm \
-o $(IMAGE).elf
$(OBJCOPY) -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load \
--no-change-warnings --change-section-lma .eeprom=0 $(IMAGE).elf \
$(IMAGE).eep
$(OBJCOPY) -O ihex -R .eeprom $(IMAGE).elf $(IMAGE).hex
$(OBJDUMP) -h -S $(IMAGE).elf | $(CXXFILT) -t > $(IMAGE).lst
$(SIZE) $(IMAGE).hex
# START:makemods
upload : all
- pkill -f '$(MON_CMD).*$(PORT)'
- sleep 1
- stty -f $(PORT) hupcl
- $(AVRDUDE) -V -C$(AVRDUDE_CONF) -p$(MCU) -c$(PROGRAMMER) \
-P$(PORT) -b$(UPLOAD_SPEED) -D -Uflash:w:$(IMAGE).hex:i
monitor :
$(MON_CMD) $(PORT) $(MON_SPEED)
# END:makemods
upload_monitor : upload monitor
-include $(wildcard $(BUILD_DIR)/*.dep))
# vim:ft=make

View file

@ -0,0 +1,50 @@
# Include this makefile per project
#
# Optional parameters;
# MASTER_LIBS = Simple library name list.
# MASTER_INC_DIRS = hook to include extra header files.
# MASTER_LIB_DIRS = hook to include extra libraries.
# MASTER_FLAGS = hook for extra build flags
#
ifndef SKT_PDE_SRC
$(error $$(SKT_PDE_SRC) not defined)
endif
THIS_PATH = ../lib-build/make/
INC_LOCAL = .
# Optional include an local override
-include $(THIS_PATH)/Makefile.inc.local
# Copy debug ips
ifdef DEBUG_NET_HISIP
DEBUG_NET_FLAG_HISIP = -DDEBUG_NET_HISIP=$(DEBUG_NET_HISIP)
endif
ifdef DEBUG_NET_GATE
DEBUG_NET_FLAG_GATE = -DDEBUG_NET_GATE=$(DEBUG_NET_GATE)
endif
# Add some normal defaults for root makefile
ARD_REV ?= 105
ARD_BIN ?= $(ARD_HOME)/hardware/tools/avr/bin
AVRDUDE ?= $(ARD_HOME)/hardware/tools/avr/bin/avrdude
AVRDUDE_CONF ?= $(ARD_HOME)/hardware/tools/avr/etc/avrdude.conf
BOARD ?= uno
PORT ?= /dev/ttyACM0
PROGRAMMER ?= arduino
MON_SPEED ?= 115200
MON_CMD ?= picocom
ROOT_INCS = $(ARD_HOME)/hardware/arduino/avr/variants/standard/
# Include managed build parameters
include $(THIS_PATH)/Makefile.inc.debug
include $(THIS_PATH)/Makefile.inc.libs
# Create build dirs for root makefile
INC_DIRS = $(MASTER_INC_DIRS) $(ROOT_INCS) $(INC_LOCAL)
LIB_DIRS = $(MASTER_LIB_DIRS) $(ROOT_LIBS)
SKETCH_FLAGS = $(MASTER_FLAGS) $(DEBUG_NET_FLAG_HISIP) $(DEBUG_NET_FLAG_GATE) $(DEBUG_FLAGS)
# Include the root parent makefile
include $(THIS_PATH)/Makefile.inc.root

View file

@ -0,0 +1,45 @@
# Master makefile to flash tool projects
#
# Parameter;
# MASTER_FLASH_PROG = The hex file to flash
#
# Option Parameter;
# MASTER_FLASH_BOOT = The bootloader to flash
# MASTER_FLASH_PAIR_EXTRA = extra pair list of hex files to flash,
# note where the keyword optiboot get replaced by the optiboot hex file.
#
ifndef MASTER_FLASH_PROG
$(error $$(MASTER_FLASH_PROG) not defined)
endif
# Define libary includes
MASTER_LIBS = isp-repair
# Include our master
include ../lib-build/make/Makefile.master
HEX_FILE_BOOT_OPTI = $(ARD_HOME)/hardware/arduino/avr/bootloaders/optiboot/optiboot_atmega328.hex
HEX_FILE_BLINK_TEST = ../xnode-test-blink/build/xnode-test-blink.hex
# Define local variables
HEX2C = tclsh ../lib-build/tools/mega-hex2c.tcl
HEX_DATA = xnode-flashdata.generated
HEX_PAIR_BLINK = $(HEX_FILE_BOOT_OPTI) $(HEX_FILE_BLINK_TEST)
MASTER_FLASH_BOOT ?= $(HEX_FILE_BOOT_OPTI)
HEX_PAIR_MASTER = $(MASTER_FLASH_BOOT) $(MASTER_FLASH_PROG)
HEX_PAIR_EXTRA = $(subst optiboot,$(HEX_FILE_BOOT_OPTI),$(MASTER_FLASH_PAIR_EXTRA))
HEX_PAIR_LIST = $(HEX_PAIR_BLINK) $(HEX_PAIR_MASTER) $(HEX_PAIR_EXTRA)
# Hook in our local extra build targets
clean : clean-hex
$(BUILD_DIR) : $(HEX_DATA)
# Generate flashdata before compiling
$(HEX_DATA) :
$(HEX2C) $(HEX_PAIR_LIST) > $(HEX_DATA)
clean-hex :
$(RM) $(HEX_DATA)

View file

@ -0,0 +1,86 @@
#!/usr/bin/env tclsh
# Changed to byte array per file with index struct for mega version
# wc, 2014-01-13
# Generate a C include file from one or more Intel HEX files (avr-gcc output)
# jcw, 2010-04-18
# Examples:
# ./hex2c.tcl Blink.cpp.hex ATmegaBOOT_168_atmega328.hex >data_blink.h
# ./hex2c.tcl RF12demo.cpp.hex ATmegaBOOT_168_atmega328.hex >data_rf12demo.h
# ./hex2c.tcl Blink.cpp.hex optiboot_atmega328.hex >opti_blink.h
# ./hex2c.tcl RF12demo.cpp.hex optiboot_atmega328.hex >opti_rf12demo.h
if {$argv eq ""} {
puts stderr "Usage: [info script] infile.hex ?...? >outfile.h"
exit 1
}
set bytes {}
set psi 0
foreach a $argv {
set start [llength $bytes]
set mtime [clock format [file mtime $a] -format "%Y-%m-%d"]
puts stderr $a:
set fd [open $a]
while {[gets $fd line] > 0} {
if {[scan $line {:%2x%4x%2x%s} count addr type data]} {
if {$type == 0} {
if {![info exists next]} {
set first [format 0x%04X $addr]
set next $addr
}
while {$next < $addr} {
lappend bytes 255 ;# pad with unset bytes if there is a gap
incr next
}
if {$next != $addr} {
puts stderr "non-contiguous data ($next vs $addr)"
}
incr next $count
foreach {x y} [split [string range $data 0 end-2] ""] {
lappend bytes [scan $x$y %x]
}
}
}
}
incr next -$first
set f [format %-30s [file tail $a]]
lappend sections "\"$f\",\"$mtime\",progdata_$psi,$first,$start,$next"
#lappend sections "\"$mtime $f ${next}b @ $first\",$first,$start,$next"
unset next
close $fd
lappend byte_sections $bytes
set bytes {}
incr psi
}
puts "// This file was generated by hex2c.tcl on [clock format [clock seconds]]"
puts ""
puts ""
set pgi 0
foreach bs $byte_sections {
puts "const unsigned char progdata_$pgi\[] PROGMEM = {"
set out " "
foreach x $bs {
if {[string length $out$x,] > 80} {
puts $out
set out " "
}
append out $x ,
}
puts $out
puts "};"
incr pgi
}
puts ""
puts "mega_flash_data_struct sections\[] = {"
foreach x $sections {
puts " {$x},"
}
puts "};"
puts ""

171
lib-build/tools/xxtea.c Normal file
View file

@ -0,0 +1,171 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(uint32_t *v, int n, uint32_t const key[4]) {
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) { /* Coding Part */
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do {
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++) {
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
} while (--rounds);
} else if (n < -1) { /* Decoding Part */
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--) {
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
}
void convToQU32(uint32_t *v/*, int n*/,int *data,int dataOffset) {
for (int i=0;i<4;i++) {
int idx = dataOffset + i*4;
//uint32_t value = (data[idx]) + (data[idx+1] << 8) + (data[idx+2] << 16) + (data[idx+3] << 24);
uint32_t value = (data[idx+3]) + (data[idx+2] << 8) + (data[idx+1] << 16) + (data[idx+0] << 24);
printf("convToQU32 %08x \n", value);
v[i] = value;
}
}
#define XXTEA_NUM_ROUNDS 32
void xxteaDecrypt(unsigned long v[2],unsigned long net_key[4])
{
unsigned int i;
uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*XXTEA_NUM_ROUNDS;
for (i=0; i < XXTEA_NUM_ROUNDS; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + net_key[(sum>>11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + net_key[sum & 3]);
}
v[0]=v0; v[1]=v1;
}
int main (int argc, char *argv[] ) {
// Arguments parsing
if ( argc != 4 ) {
printf( "usage: %s encrypt|decrypt key message\n", argv[0] ); // prog name
printf( "note: parameters are pure hex and key should be 32 chars. \n");
return 1;
}
int operation = 0;
if (strcmp("encrypt", argv[1]) == 0) {
operation = 1;
}
if (strcmp("decrypt", argv[1]) == 0) {
operation = -1;
}
if (operation == 0) {
printf( "Could not parse first argument.\n");
return 1;
}
printf( "operation: %d.\n",operation);
char* keyBuffer = argv[2];
int keyBufferLength = strlen(keyBuffer);
if (keyBufferLength != 32) {
printf( "key should by 32 chars.\n");
return 1;
}
char* messageBuffer = argv[3];
int messageBufferLength = strlen(messageBuffer);
if (messageBufferLength < 16) {
printf( "message should be minimal 16 chars.\n");
return 1;
}
// Done arguments
// Convert data
int keyData[16];
int keyDataIndex = 0;
for (int i=0;i<keyBufferLength/2;i++) {
int value;
sscanf(keyBuffer+2*i,"%02x",&value);
keyData[keyDataIndex] = value;
keyDataIndex++;
}
printf( "Key data size %d\n", keyDataIndex );
for (int i=0;i<keyDataIndex;i++) {
printf( "%02x", keyData[i] );
}
printf("\n");
int messageData[512];
int messageDataIndex = 0;
for (int i=0;i<512;i++) {
messageData[i] = 0;
}
for (int i=0;i<messageBufferLength/2;i++) {
int value;
sscanf(messageBuffer+2*i,"%02x",&value);
messageData[messageDataIndex] = value;
messageDataIndex++;
}
printf( "Message data size %d\n", messageDataIndex );
for (int i=0;i<messageDataIndex;i++) {
printf( "%02x", messageData[i] );
}
printf("\n");
uint32_t key[4];
uint32_t data[4];
convToQU32(key,keyData,0);
convToQU32(data,messageData,0);
printf("OUT-ppre: ");
for (int i=0;i<4;i++) {
printf("%08x", data[i]);
}
printf("\n");
operation = operation* (messageDataIndex/4);
printf( "word length: %d\n",operation);
//btea(data,operation,key);
xxteaDecrypt(data,key);
printf("OUT-post: ");
for (int i=0;i<4;i++) {
printf("%08x", data[i]);
}
printf("\n");
printf("OUT-AS: ");
for (int i=0;i<4;i++) {
//printf("%02x ", data[i] & 255);
//printf("%02x ", data[i] >> 8 & 255);
//printf("%02x ", data[i] >> 16 & 255);
//printf("%02x ", data[i] >> 24 & 255);
printf("%c ", data[i] & 255);
printf("%c ", data[i] >> 8 & 255);
printf("%c ", data[i] >> 16 & 255);
printf("%c ", data[i] >> 24 & 255);
}
printf("\n");
return 0;
}

179
lib-ext/dht.git/DHT.cpp Normal file
View file

@ -0,0 +1,179 @@
/* DHT library
MIT license
written by Adafruit Industries
*/
#include "DHT.h"
DHT::DHT(uint8_t pin, uint8_t type, uint8_t count) {
_pin = pin;
_type = type;
_count = count;
firstreading = true;
}
void DHT::begin(void) {
// set up the pins!
pinMode(_pin, INPUT);
digitalWrite(_pin, HIGH);
_lastreadtime = 0;
}
//boolean S == Scale. True == Farenheit; False == Celcius
float DHT::readTemperature(bool S) {
float f;
if (read()) {
switch (_type) {
case DHT11:
f = data[2];
if(S)
f = convertCtoF(f);
return f;
case DHT22:
case DHT21:
f = data[2] & 0x7F;
f *= 256;
f += data[3];
f /= 10;
if (data[2] & 0x80)
f *= -1;
if(S)
f = convertCtoF(f);
return f;
}
}
return NAN;
}
float DHT::convertCtoF(float c) {
return c * 9 / 5 + 32;
}
float DHT::convertFtoC(float f) {
return (f - 32) * 5 / 9;
}
float DHT::readHumidity(void) {
float f;
if (read()) {
switch (_type) {
case DHT11:
f = data[0];
return f;
case DHT22:
case DHT21:
f = data[0];
f *= 256;
f += data[1];
f /= 10;
return f;
}
}
return NAN;
}
float DHT::computeHeatIndex(float tempFahrenheit, float percentHumidity) {
// Adapted from equation at: https://github.com/adafruit/DHT-sensor-library/issues/9 and
// Wikipedia: http://en.wikipedia.org/wiki/Heat_index
return -42.379 +
2.04901523 * tempFahrenheit +
10.14333127 * percentHumidity +
-0.22475541 * tempFahrenheit*percentHumidity +
-0.00683783 * pow(tempFahrenheit, 2) +
-0.05481717 * pow(percentHumidity, 2) +
0.00122874 * pow(tempFahrenheit, 2) * percentHumidity +
0.00085282 * tempFahrenheit*pow(percentHumidity, 2) +
-0.00000199 * pow(tempFahrenheit, 2) * pow(percentHumidity, 2);
}
boolean DHT::read(void) {
uint8_t laststate = HIGH;
uint8_t counter = 0;
uint8_t j = 0, i;
unsigned long currenttime;
// Check if sensor was read less than two seconds ago and return early
// to use last reading.
currenttime = millis();
if (currenttime < _lastreadtime) {
// ie there was a rollover
_lastreadtime = 0;
}
if (!firstreading && ((currenttime - _lastreadtime) < 2000)) {
return true; // return last correct measurement
//delay(2000 - (currenttime - _lastreadtime));
}
firstreading = false;
/*
Serial.print("Currtime: "); Serial.print(currenttime);
Serial.print(" Lasttime: "); Serial.print(_lastreadtime);
*/
_lastreadtime = millis();
data[0] = data[1] = data[2] = data[3] = data[4] = 0;
// pull the pin high and wait 250 milliseconds
digitalWrite(_pin, HIGH);
delay(250);
// now pull it low for ~20 milliseconds
pinMode(_pin, OUTPUT);
digitalWrite(_pin, LOW);
delay(20);
noInterrupts();
digitalWrite(_pin, HIGH);
delayMicroseconds(40);
pinMode(_pin, INPUT);
// read in timings
for ( i=0; i< MAXTIMINGS; i++) {
counter = 0;
while (digitalRead(_pin) == laststate) {
counter++;
delayMicroseconds(1);
if (counter == 255) {
break;
}
}
laststate = digitalRead(_pin);
if (counter == 255) break;
// ignore first 3 transitions
if ((i >= 4) && (i%2 == 0)) {
// shove each bit into the storage bytes
data[j/8] <<= 1;
if (counter > _count)
data[j/8] |= 1;
j++;
}
}
interrupts();
/*
Serial.println(j, DEC);
Serial.print(data[0], HEX); Serial.print(", ");
Serial.print(data[1], HEX); Serial.print(", ");
Serial.print(data[2], HEX); Serial.print(", ");
Serial.print(data[3], HEX); Serial.print(", ");
Serial.print(data[4], HEX); Serial.print(" =? ");
Serial.println(data[0] + data[1] + data[2] + data[3], HEX);
*/
// check we read 40 bits and that the checksum matches
if ((j >= 40) &&
(data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) ) {
return true;
}
return false;
}

41
lib-ext/dht.git/DHT.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef DHT_H
#define DHT_H
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
/* DHT library
MIT license
written by Adafruit Industries
*/
// how many timing transitions we need to keep track of. 2 * number bits + extra
#define MAXTIMINGS 85
#define DHT11 11
#define DHT22 22
#define DHT21 21
#define AM2301 21
class DHT {
private:
uint8_t data[6];
uint8_t _pin, _type, _count;
unsigned long _lastreadtime;
boolean firstreading;
public:
DHT(uint8_t pin, uint8_t type, uint8_t count=6);
void begin(void);
float readTemperature(bool S=false);
float convertCtoF(float);
float convertFtoC(float);
float computeHeatIndex(float tempFahrenheit, float percentHumidity);
float readHumidity(void);
boolean read(void);
};
#endif

View file

@ -0,0 +1,3 @@
This is an Arduino library for the DHT series of low cost temperature/humidity sensors.
To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder DHT. Check that the DHT folder contains DHT.cpp and DHT.h. Place the DHT library folder your <arduinosketchfolder>/libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE.

View file

@ -0,0 +1,71 @@
// Example testing sketch for various DHT humidity/temperature sensors
// Written by ladyada, public domain
#include "DHT.h"
#define DHTPIN 2 // what pin we're connected to
// Uncomment whatever type you're using!
//#define DHTTYPE DHT11 // DHT 11
#define DHTTYPE DHT22 // DHT 22 (AM2302)
//#define DHTTYPE DHT21 // DHT 21 (AM2301)
// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor
// Initialize DHT sensor for normal 16mhz Arduino
DHT dht(DHTPIN, DHTTYPE);
// NOTE: For working with a faster chip, like an Arduino Due or Teensy, you
// might need to increase the threshold for cycle counts considered a 1 or 0.
// You can do this by passing a 3rd parameter for this threshold. It's a bit
// of fiddling to find the right value, but in general the faster the CPU the
// higher the value. The default for a 16mhz AVR is a value of 6. For an
// Arduino Due that runs at 84mhz a value of 30 works.
// Example to initialize DHT sensor for Arduino Due:
//DHT dht(DHTPIN, DHTTYPE, 30);
void setup() {
Serial.begin(9600);
Serial.println("DHTxx test!");
dht.begin();
}
void loop() {
// Wait a few seconds between measurements.
delay(2000);
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = dht.readHumidity();
// Read temperature as Celsius
float t = dht.readTemperature();
// Read temperature as Fahrenheit
float f = dht.readTemperature(true);
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(t) || isnan(f)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
// Compute heat index
// Must send in temp in Fahrenheit!
float hi = dht.computeHeatIndex(f, h);
Serial.print("Humidity: ");
Serial.print(h);
Serial.print(" %\t");
Serial.print("Temperature: ");
Serial.print(t);
Serial.print(" *C ");
Serial.print(f);
Serial.print(" *F\t");
Serial.print("Heat index: ");
Serial.print(hi);
Serial.println(" *F");
}

3
lib-ext/ethercard.git/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.DS_Store
*.esproj
/html

View file

@ -0,0 +1,22 @@
Contributing to EtherCard
=========================
Contributions to the EtherCard codebase are welcome using the usual Github pull request workflow.
Please remember that memory, particularly RAM, is often limited on Arduino, so try and be efficient when adding new features.
Code Style
----------
When making contributions, please use the following code style to keep the codebase consistent:
* Indent using *4 spaces* not tabs
* When placing an opening brace on the same line, there should be one space before it
* Closing braces should be broken from the preceding line
If in doubt, this is the same as the default [astyle] settings, so use the astyle tool to check your code.
[astyle]: http://astyle.sourceforge.net/

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View file

@ -0,0 +1,73 @@
/* Get rid of all those overriden font families */
body, table, div, p, dl,
#projectname,
#projectbrief,
#nav-tree .label {
font: 15px/21px Georgia,serif
}
#projectname {
color: #990000;
font-weight: bold;
font-size: 24px;
}
#titlearea table {
padding: 20px;
}
dl.reflist dt, div.memproto {
border: 1px solid #A8B8D9;
}
dl.reflist dd, div.memdoc {
border-width: 0;
}
div.contents {
margin-left: 20px; margin-right: 16px;
width: 700 px;
}
/* Get rid of the gradient backgrounds and background colors */
div.header,
#nav-tree,
.navpath ul,
.memproto, dl.reflist dt {
background: none;
}
#nav-tree .selected {
background: none;
background-color: #990000;
text-shadow: none;
}
a, a:link, a:visited {
color: #2A5685;
text-decoration: underline;
}
a:active, a:hover {
color: #CC0000;
}
.directory tr.even,
pre.fragment,
.mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams,
.memproto, dl.reflist dt {
background-color: #EEE;
box-shadow: none;
border-radius: 0;
}
.memdoc, dl.reflist dd {
background: none;
box-shadow: none;
border-radius: 0;
}
pre.fragment {
background-color: #FAFAFA;
border: 1px solid #DADADA;
margin: 1em 1em 1em 1.6em;
overflow-x: auto;
overflow-y: hidden;
width: auto;
}

View file

@ -0,0 +1,416 @@
// This code slightly follows the conventions of, but is not derived from:
// EHTERSHIELD_H library for Arduino etherShield
// Copyright (c) 2008 Xing Yu. All right reserved. (this is LGPL v2.1)
// It is however derived from the enc28j60 and ip code (which is GPL v2)
// Author: Pascal Stang
// Modified by: Guido Socher
// DHCP code: Andrew Lindsay
// Hence: GPL V2
//
// 2010-05-19 <jc@wippler.nl>
#include <EtherCard.h>
#include <stdarg.h>
#include <avr/eeprom.h>
//#define FLOATEMIT // uncomment line to enable $T in emit_P for float emitting
byte Stash::map[256/8];
Stash::Block Stash::bufs[2];
uint8_t Stash::allocBlock () {
for (uint8_t i = 0; i < sizeof map; ++i)
if (map[i] != 0)
for (uint8_t j = 0; j < 8; ++j)
if (bitRead(map[i], j)) {
bitClear(map[i], j);
return (i << 3) + j;
}
return 0;
}
void Stash::freeBlock (uint8_t block) {
bitSet(map[block>>3], block & 7);
}
uint8_t Stash::fetchByte (uint8_t blk, uint8_t off) {
return blk == bufs[0].bnum ? bufs[0].bytes[off] :
blk == bufs[1].bnum ? bufs[1].bytes[off] :
ether.peekin(blk, off);
}
void Stash::initMap (uint8_t last) {
while (--last > 0)
freeBlock(last);
}
void Stash::load (uint8_t idx, uint8_t blk) {
if (blk != bufs[idx].bnum) {
if (idx == 0) {
ether.copyout(bufs[idx].bnum, bufs[idx].bytes);
if (blk == bufs[1].bnum)
bufs[1].bnum = 255; // forget read page if same
} else if (blk == bufs[0].bnum) {
// special case: read page is same as write buffer
memcpy(&bufs[1], &bufs[0], sizeof bufs[0]);
return;
}
bufs[idx].bnum = blk;
ether.copyin(bufs[idx].bnum, bufs[idx].bytes);
}
}
uint8_t Stash::freeCount () {
uint8_t count = 0;
for (uint8_t i = 0; i < 256/8; ++i)
for (uint8_t m = 0x80; m != 0; m >>= 1)
if (map[i] & m)
++count;
return count;
}
uint8_t Stash::create () {
uint8_t blk = allocBlock();
load(0, blk);
bufs[0].head.count = 0;
bufs[0].head.first = bufs[0].head.last = blk;
bufs[0].tail = sizeof (StashHeader);
bufs[0].next = 0;
return open(blk);
}
uint8_t Stash::open (uint8_t blk) {
curr = blk;
offs = sizeof (StashHeader);
load(1, curr);
memcpy((StashHeader*) this, bufs[1].bytes, sizeof (StashHeader));
return curr;
}
void Stash::save () {
load(0, first);
memcpy(bufs[0].bytes, (StashHeader*) this, sizeof (StashHeader));
if (bufs[1].bnum == first)
load(1, 0); // invalidates original in case it was the same block
}
void Stash::release () {
while (first > 0) {
freeBlock(first);
first = ether.peekin(first, 63);
}
}
void Stash::put (char c) {
load(0, last);
uint8_t t = bufs[0].tail;
bufs[0].bytes[t++] = c;
if (t <= 62)
bufs[0].tail = t;
else {
bufs[0].next = allocBlock();
last = bufs[0].next;
load(0, last);
bufs[0].tail = bufs[0].next = 0;
++count;
}
}
char Stash::get () {
load(1, curr);
if (curr == last && offs >= bufs[1].tail)
return 0;
uint8_t b = bufs[1].bytes[offs];
if (++offs >= 63 && curr != last) {
curr = bufs[1].next;
offs = 0;
}
return b;
}
uint16_t Stash::size () {
return 63 * count + fetchByte(last, 62) - sizeof (StashHeader);
}
static char* wtoa (uint16_t value, char* ptr) {
if (value > 9)
ptr = wtoa(value / 10, ptr);
*ptr = '0' + value % 10;
*++ptr = 0;
return ptr;
}
void Stash::prepare (PGM_P fmt, ...) {
Stash::load(0, 0);
uint16_t* segs = Stash::bufs[0].words;
*segs++ = strlen_P(fmt);
#ifdef __AVR__
*segs++ = (uint16_t) fmt;
#else
*segs++ = (uint32_t) fmt;
*segs++ = (uint32_t) fmt >> 16;
#endif
va_list ap;
va_start(ap, fmt);
for (;;) {
char c = pgm_read_byte(fmt++);
if (c == 0)
break;
if (c == '$') {
#ifdef __AVR__
uint16_t argval = va_arg(ap, uint16_t), arglen = 0;
#else
uint32_t argval = va_arg(ap, int), arglen = 0;
#endif
switch (pgm_read_byte(fmt++)) {
case 'D': {
char buf[7];
wtoa(argval, buf);
arglen = strlen(buf);
break;
}
case 'S':
arglen = strlen((const char*) argval);
break;
case 'F':
arglen = strlen_P((PGM_P) argval);
break;
case 'E': {
byte* s = (byte*) argval;
char d;
while ((d = eeprom_read_byte(s++)) != 0)
++arglen;
break;
}
case 'H': {
Stash stash (argval);
arglen = stash.size();
break;
}
}
#ifdef __AVR__
*segs++ = argval;
#else
*segs++ = argval;
*segs++ = argval >> 16;
#endif
Stash::bufs[0].words[0] += arglen - 2;
}
}
va_end(ap);
}
uint16_t Stash::length () {
Stash::load(0, 0);
return Stash::bufs[0].words[0];
}
void Stash::extract (uint16_t offset, uint16_t count, void* buf) {
Stash::load(0, 0);
uint16_t* segs = Stash::bufs[0].words;
#ifdef __AVR__
PGM_P fmt = (PGM_P) *++segs;
#else
PGM_P fmt = (PGM_P)((segs[2] << 16) | segs[1]);
segs += 2;
#endif
Stash stash;
char mode = '@', tmp[7], *ptr = NULL, *out = (char*) buf;
for (uint16_t i = 0; i < offset + count; ) {
char c = 0;
switch (mode) {
case '@': {
c = pgm_read_byte(fmt++);
if (c == 0)
return;
if (c != '$')
break;
#ifdef __AVR__
uint16_t arg = *++segs;
#else
uint32_t arg = *++segs;
arg |= *++segs << 16;
#endif
mode = pgm_read_byte(fmt++);
switch (mode) {
case 'D':
wtoa(arg, tmp);
ptr = tmp;
break;
case 'S':
case 'F':
case 'E':
ptr = (char*) arg;
break;
case 'H':
stash.open(arg);
ptr = (char*) &stash;
break;
}
continue;
}
case 'D':
case 'S':
c = *ptr++;
break;
case 'F':
c = pgm_read_byte(ptr++);
break;
case 'E':
c = eeprom_read_byte((byte*) ptr++);
break;
case 'H':
c = ((Stash*) ptr)->get();
break;
}
if (c == 0) {
mode = '@';
continue;
}
if (i >= offset)
*out++ = c;
++i;
}
}
void Stash::cleanup () {
Stash::load(0, 0);
uint16_t* segs = Stash::bufs[0].words;
#ifdef __AVR__
PGM_P fmt = (PGM_P) *++segs;
#else
PGM_P fmt = (PGM_P)((segs[2] << 16) | segs[1]);
segs += 2;
#endif
for (;;) {
char c = pgm_read_byte(fmt++);
if (c == 0)
break;
if (c == '$') {
#ifdef __AVR__
uint16_t arg = *++segs;
#else
uint32_t arg = *++segs;
arg |= *++segs << 16;
#endif
if (pgm_read_byte(fmt++) == 'H') {
Stash stash (arg);
stash.release();
}
}
}
}
void BufferFiller::emit_p(PGM_P fmt, ...) {
va_list ap;
va_start(ap, fmt);
for (;;) {
char c = pgm_read_byte(fmt++);
if (c == 0)
break;
if (c != '$') {
*ptr++ = c;
continue;
}
c = pgm_read_byte(fmt++);
switch (c) {
case 'D':
#ifdef __AVR__
wtoa(va_arg(ap, uint16_t), (char*) ptr);
#else
wtoa(va_arg(ap, int), (char*) ptr);
#endif
break;
#ifdef FLOATEMIT
case 'T':
dtostrf ( va_arg(ap, double), 10, 3, (char*)ptr );
break;
#endif
case 'H': {
#ifdef __AVR__
char p1 = va_arg(ap, uint16_t);
#else
char p1 = va_arg(ap, int);
#endif
char p2;
p2 = (p1 >> 4) & 0x0F;
p1 = p1 & 0x0F;
if (p1 > 9) p1 += 0x07; // adjust 0x0a-0x0f to come out 'a'-'f'
p1 += 0x30; // and complete
if (p2 > 9) p2 += 0x07; // adjust 0x0a-0x0f to come out 'a'-'f'
p2 += 0x30; // and complete
*ptr++ = p2;
*ptr++ = p1;
continue;
}
case 'L':
ltoa(va_arg(ap, long), (char*) ptr, 10);
break;
case 'S':
strcpy((char*) ptr, va_arg(ap, const char*));
break;
case 'F': {
PGM_P s = va_arg(ap, PGM_P);
char d;
while ((d = pgm_read_byte(s++)) != 0)
*ptr++ = d;
continue;
}
case 'E': {
byte* s = va_arg(ap, byte*);
char d;
while ((d = eeprom_read_byte(s++)) != 0)
*ptr++ = d;
continue;
}
default:
*ptr++ = c;
continue;
}
ptr += strlen((char*) ptr);
}
va_end(ap);
}
EtherCard ether;
uint8_t EtherCard::mymac[6]; // my MAC address
uint8_t EtherCard::myip[4]; // my ip address
uint8_t EtherCard::netmask[4]; // subnet mask
uint8_t EtherCard::broadcastip[4]; // broadcast address
uint8_t EtherCard::gwip[4]; // gateway
uint8_t EtherCard::dhcpip[4]; // dhcp server
uint8_t EtherCard::dnsip[4]; // dns server
uint8_t EtherCard::hisip[4]; // ip address of remote host
uint16_t EtherCard::hisport = 80; // tcp port to browse to
bool EtherCard::using_dhcp = false;
bool EtherCard::persist_tcp_connection = false;
int16_t EtherCard::delaycnt = 0; //request gateway ARP lookup
uint8_t EtherCard::begin (const uint16_t size,
const uint8_t* macaddr,
uint8_t csPin) {
using_dhcp = false;
Stash::initMap(56);
copyMac(mymac, macaddr);
return initialize(size, mymac, csPin);
}
bool EtherCard::staticSetup (const uint8_t* my_ip,
const uint8_t* gw_ip,
const uint8_t* dns_ip,
const uint8_t* mask) {
using_dhcp = false;
if (my_ip != 0)
copyIp(myip, my_ip);
if (gw_ip != 0)
setGwIp(gw_ip);
if (dns_ip != 0)
copyIp(dnsip, dns_ip);
if(mask != 0)
copyIp(netmask, mask);
updateBroadcastAddress();
delaycnt = 0; //request gateway ARP lookup
return true;
}

View file

@ -0,0 +1,587 @@
// This code slightly follows the conventions of, but is not derived from:
// EHTERSHIELD_H library for Arduino etherShield
// Copyright (c) 2008 Xing Yu. All right reserved. (this is LGPL v2.1)
// It is however derived from the enc28j60 and ip code (which is GPL v2)
// Author: Pascal Stang
// Modified by: Guido Socher
// DHCP code: Andrew Lindsay
// Hence: GPL V2
//
// 2010-05-19 <jc@wippler.nl>
//
//
// PIN Connections (Using Arduino UNO):
// VCC - 3.3V
// GND - GND
// SCK - Pin 13
// SO - Pin 12
// SI - Pin 11
// CS - Pin 8
//
#define __PROG_TYPES_COMPAT__
#ifndef EtherCard_h
#define EtherCard_h
#if ARDUINO >= 100
#include <Arduino.h> // Arduino 1.0
#define WRITE_RESULT size_t
#define WRITE_RETURN return 1;
#else
#include <WProgram.h> // Arduino 0022
#define WRITE_RESULT void
#define WRITE_RETURN
#endif
#include <avr/pgmspace.h>
#include "enc28j60.h"
#include "net.h"
/** This type definition defines the structure of a UDP server event handler callback funtion */
typedef void (*UdpServerCallback)(
uint16_t dest_port, ///< Port the packet was sent to
uint8_t src_ip[4], ///< IP address of the sender
const char *data, ///< UDP payload data
uint16_t len); ///< Length of the payload data
/** This type definition defines the structure of a DHCP Option callback funtion */
typedef void (*DhcpOptionCallback)(
uint8_t option, ///< The option number
const byte* data, ///< DHCP option data
uint8_t len); ///< Length of the DHCP option data
/** This structure describes the structure of memory used within the ENC28J60 network interface. */
typedef struct {
uint8_t count; ///< Number of allocated pages
uint8_t first; ///< First allocated page
uint8_t last; ///< Last allocated page
} StashHeader;
/** This class provides access to the memory within the ENC28J60 network interface. */
class Stash : public /*Stream*/ Print, private StashHeader {
uint8_t curr; //!< Current page
uint8_t offs; //!< Current offset in page
typedef struct {
union {
uint8_t bytes[64];
uint16_t words[32];
struct {
StashHeader head;
uint8_t filler[59];
uint8_t tail;
uint8_t next;
};
};
uint8_t bnum;
} Block;
static uint8_t allocBlock ();
static void freeBlock (uint8_t block);
static uint8_t fetchByte (uint8_t blk, uint8_t off);
static Block bufs[2];
static uint8_t map[256/8];
public:
static void initMap (uint8_t last);
static void load (uint8_t idx, uint8_t blk);
static uint8_t freeCount ();
Stash () : curr (0) { first = 0; }
Stash (uint8_t fd) { open(fd); }
uint8_t create ();
uint8_t open (uint8_t blk);
void save ();
void release ();
void put (char c);
char get ();
uint16_t size ();
virtual WRITE_RESULT write(uint8_t b) { put(b); WRITE_RETURN }
// virtual int available() {
// if (curr != last)
// return 1;
// load(1, last);
// return offs < bufs[1].tail;
// }
// virtual int read() {
// return available() ? get() : -1;
// }
// virtual int peek() {
// return available() ? bufs[1].bytes[offs] : -1;
// }
// virtual void flush() {
// curr = last;
// offs = 63;
// }
static void prepare (PGM_P fmt, ...);
static uint16_t length ();
static void extract (uint16_t offset, uint16_t count, void* buf);
static void cleanup ();
friend void dumpBlock (const char* msg, uint8_t idx); // optional
friend void dumpStash (const char* msg, void* ptr); // optional
};
/** This class populates network send and receive buffers.
*
* This class provides formatted printing into memory. Users can use it to write into send buffers.
*
* Nota: PGM_P: is a pointer to a string in program space (defined in the source code)
*
* # Format string
*
* | Format | Parameter | Output
* |--------|-------------|----------
* | $D | uint16_t | Decimal representation
* | $T ¤ | double | Decimal representation with 3 digits after decimal sign ([-]d.ddd)
* | $H | uint16_t | Hexadecimal value of lsb (from 00 to ff)
* | $L | long | Decimal representation
* | $S | const char* | Copy null terminated string from main memory
* | $F | PGM_P | Copy null terminated string from program space
* | $E | byte* | Copy null terminated string from EEPROM space
* | $$ | _none_ | '$'
*
* ¤ _Available only if FLOATEMIT is defined_
*
* # Examples
* ~~~~~~~~~~~~~{.c}
* uint16_t ddd = 123;
* double ttt = 1.23;
* uint16_t hhh = 0xa4;
* long lll = 123456789;
* char * sss;
* char fff[] PROGMEM = "MyMemory";
*
* sss[0] = 'G';
* sss[1] = 'P';
* sss[2] = 'L';
* sss[3] = 0;
* buf.emit_p( PSTR("ddd=$D\n"), ddd ); // "ddd=123\n"
* buf.emit_p( PSTR("ttt=$T\n"), ttt ); // "ttt=1.23\n" **TO CHECK**
* buf.emit_p( PSTR("hhh=$H\n"), hhh ); // "hhh=a4\n"
* buf.emit_p( PSTR("lll=$L\n"), lll ); // "lll=123456789\n"
* buf.emit_p( PSTR("sss=$S\n"), sss ); // "sss=GPL\n"
* buf.emit_p( PSTR("fff=$F\n"), fff ); // "fff=MyMemory\n"
* ~~~~~~~~~~~~~
*
*/
class BufferFiller : public Print {
uint8_t *start; //!< Pointer to start of buffer
uint8_t *ptr; //!< Pointer to cursor position
public:
/** @brief Empty constructor
*/
BufferFiller () {}
/** @brief Constructor
* @param buf Pointer to the ethernet data buffer
*/
BufferFiller (uint8_t* buf) : start (buf), ptr (buf) {}
/** @brief Add formatted text to buffer
* @param fmt Format string (see Class description)
* @param ... parameters for format string
*/
void emit_p (PGM_P fmt, ...);
/** @brief Add data to buffer from main memory
* @param s Pointer to data
* @param n Number of characters to copy
*/
void emit_raw (const char* s, uint16_t n) { memcpy(ptr, s, n); ptr += n; }
/** @brief Add data to buffer from program space string
* @param p Program space string pointer
* @param n Number of characters to copy
*/
void emit_raw_p (PGM_P p, uint16_t n) { memcpy_P(ptr, p, n); ptr += n; }
/** @brief Get pointer to start of buffer
* @return <i>uint8_t*</i> Pointer to start of buffer
*/
uint8_t* buffer () const { return start; }
/** @brief Get cursor position
* @return <i>uint16_t</i> Cursor postion
*/
uint16_t position () const { return ptr - start; }
/** @brief Write one byte to buffer
* @param v Byte to add to buffer
*/
virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN }
};
/** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use.
* @note All TCP/IP client (outgoing) connections are made from source port in range 2816-3071. Do not use these source ports for other purposes.
*/
class EtherCard : public Ethernet {
public:
static uint8_t mymac[6]; ///< MAC address
static uint8_t myip[4]; ///< IP address
static uint8_t netmask[4]; ///< Netmask
static uint8_t broadcastip[4]; ///< Subnet broadcast address
static uint8_t gwip[4]; ///< Gateway
static uint8_t dhcpip[4]; ///< DHCP server IP address
static uint8_t dnsip[4]; ///< DNS server IP address
static uint8_t hisip[4]; ///< DNS lookup result
static uint16_t hisport; ///< TCP port to connect to (default 80)
static bool using_dhcp; ///< True if using DHCP
static bool persist_tcp_connection; ///< False to break connections on first packet received
static int16_t delaycnt; ///< Counts number of cycles of packetLoop when no packet received - used to trigger periodic gateway ARP request
// EtherCard.cpp
/** @brief Initialise the network interface
* @param size Size of data buffer
* @param macaddr Hardware address to assign to the network interface (6 bytes)
* @param csPin Arduino pin number connected to chip select. Default = 8
* @return <i>uint8_t</i> Firmware version or zero on failure.
*/
static uint8_t begin (const uint16_t size, const uint8_t* macaddr,
uint8_t csPin =8);
/** @brief Configure network interface with static IP
* @param my_ip IP address (4 bytes). 0 for no change.
* @param gw_ip Gateway address (4 bytes). 0 for no change. Default = 0
* @param dns_ip DNS address (4 bytes). 0 for no change. Default = 0
* @param mask Subnet mask (4 bytes). 0 for no change. Default = 0
* @return <i>bool</i> Returns true on success - actually always true
*/
static bool staticSetup (const uint8_t* my_ip,
const uint8_t* gw_ip = 0,
const uint8_t* dns_ip = 0,
const uint8_t* mask = 0);
// tcpip.cpp
/** @brief Sends a UDP packet to the IP address of last processed received packet
* @param data Pointer to data payload
* @param len Size of data payload (max 220)
* @param port Source IP port
*/
static void makeUdpReply (const char *data, uint8_t len, uint16_t port);
/** @brief Parse received data
* @param plen Size of data to parse (e.g. return value of packetReceive()).
* @return <i>uint16_t</i> Offset of TCP payload data in data buffer or zero if packet processed
* @note Data buffer is shared by receive and transmit functions
* @note Only handles ARP and IP
*/
static uint16_t packetLoop (uint16_t plen);
/** @brief Accept a TCP/IP connection
* @param port IP port to accept on - do nothing if wrong port
* @param plen Number of bytes in packet
* @return <i>uint16_t</i> Offset within packet of TCP payload. Zero for no data.
*/
static uint16_t accept (uint16_t port, uint16_t plen);
/** @brief Send a response to a HTTP request
* @param dlen Size of the HTTP (TCP) payload
*/
static void httpServerReply (uint16_t dlen);
/** @brief Send a response to a HTTP request
* @param dlen Size of the HTTP (TCP) payload
* @param flags TCP flags
*/
static void httpServerReply_with_flags (uint16_t dlen , uint8_t flags);
/** @brief Acknowledge TCP message
* @todo Is this / should this be private?
*/
static void httpServerReplyAck ();
/** @brief Set the gateway address
* @param gwipaddr Gateway address (4 bytes)
*/
static void setGwIp (const uint8_t *gwipaddr);
/** @brief Updates the broadcast address based on current IP address and subnet mask
*/
static void updateBroadcastAddress();
/** @brief Check if got gateway hardware address (ARP lookup)
* @return <i>unit8_t</i> True if gateway found
*/
static uint8_t clientWaitingGw ();
/** @brief Check if got gateway DNS address (ARP lookup)
* @return <i>unit8_t</i> True if DNS found
*/
static uint8_t clientWaitingDns ();
/** @brief Prepare a TCP request
* @param result_cb Pointer to callback function that handles TCP result
* @param datafill_cb Pointer to callback function that handles TCP data payload
* @param port Remote TCP/IP port to connect to
* @return <i>unit8_t</i> ID of TCP/IP session (0-7)
* @note Return value provides id of the request to allow up to 7 concurrent requests
*/
static uint8_t clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t),
uint16_t (*datafill_cb)(uint8_t),uint16_t port);
/** @brief Prepare HTTP request
* @param urlbuf Pointer to c-string URL folder
* @param urlbuf_varpart Pointer to c-string URL file
* @param hoststr Pointer to c-string hostname
* @param additionalheaderline Pointer to c-string with additional HTTP header info
* @param callback Pointer to callback function to handle response
* @note Request sent in main packetloop
*/
static void browseUrl (const char *urlbuf, const char *urlbuf_varpart,
const char *hoststr, const char *additionalheaderline,
void (*callback)(uint8_t,uint16_t,uint16_t));
/** @brief Prepare HTTP request
* @param urlbuf Pointer to c-string URL folder
* @param urlbuf_varpart Pointer to c-string URL file
* @param hoststr Pointer to c-string hostname
* @param callback Pointer to callback function to handle response
* @note Request sent in main packetloop
*/
static void browseUrl (const char *urlbuf, const char *urlbuf_varpart,
const char *hoststr,
void (*callback)(uint8_t,uint16_t,uint16_t));
/** @brief Prepare HTTP post message
* @param urlbuf Pointer to c-string URL folder
* @param hoststr Pointer to c-string hostname
* @param additionalheaderline Pointer to c-string with additional HTTP header info
* @param postval Pointer to c-string HTML Post value
* @param callback Pointer to callback function to handle response
* @note Request sent in main packetloop
*/
static void httpPost (const char *urlbuf, const char *hoststr,
const char *additionalheaderline, const char *postval,
void (*callback)(uint8_t,uint16_t,uint16_t));
/** @brief Send NTP request
* @param ntpip IP address of NTP server
* @param srcport IP port to send from
*/
static void ntpRequest (uint8_t *ntpip,uint8_t srcport);
/** @brief Process network time protocol response
* @param time Pointer to integer to hold result
* @param dstport_l Destination port to expect response. Set to zero to accept on any port
* @return <i>uint8_t</i> True (1) on success
*/
static uint8_t ntpProcessAnswer (uint32_t *time, uint8_t dstport_l);
/** @brief Prepare a UDP message for transmission
* @param sport Source port
* @param dip Pointer to 4 byte destination IP address
* @param dport Destination port
*/
static void udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport);
/** @brief Transmit UDP packet
* @param len Size of payload
*/
static void udpTransmit (uint16_t len);
/** @brief Sends a UDP packet
* @param data Pointer to data
* @param len Size of payload (maximum 220 octets / bytes)
* @param sport Source port
* @param dip Pointer to 4 byte destination IP address
* @param dport Destination port
*/
static void sendUdp (const char *data, uint8_t len, uint16_t sport,
const uint8_t *dip, uint16_t dport);
/** @brief Resister the function to handle ping events
* @param cb Pointer to function
*/
static void registerPingCallback (void (*cb)(uint8_t*));
/** @brief Send ping
* @param destip Ponter to 4 byte destination IP address
*/
static void clientIcmpRequest (const uint8_t *destip);
/** @brief Check for ping response
* @param ip_monitoredhost Pointer to 4 byte IP address of host to check
* @return <i>uint8_t</i> True (1) if ping response from specified host
*/
static uint8_t packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost);
/** @brief Send a wake on lan message
* @param wolmac Pointer to 6 byte hardware (MAC) address of host to send message to
*/
static void sendWol (uint8_t *wolmac);
// new stash-based API
/** @brief Send TCP request
*/
static uint8_t tcpSend ();
/** @brief Get TCP reply
* @return <i>char*</i> Pointer to TCP reply payload. NULL if no data.
*/
static const char* tcpReply (uint8_t fd);
/** @brief Configure TCP connections to be persistent or not
* @param persist True to maintain TCP connection. False to finish TCP connection after first packet.
*/
static void persistTcpConnection(bool persist);
//udpserver.cpp
/** @brief Register function to handle incomint UDP events
* @param callback Function to handle event
* @param port Port to listen on
*/
static void udpServerListenOnPort(UdpServerCallback callback, uint16_t port);
/** @brief Pause listing on UDP port
* @brief port Port to pause
*/
static void udpServerPauseListenOnPort(uint16_t port);
/** @brief Resume listing on UDP port
* @brief port Port to pause
*/
static void udpServerResumeListenOnPort(uint16_t port);
/** @brief Check if UDP server is listening on any ports
* @return <i>bool</i> True if listening on any ports
*/
static bool udpServerListening(); //called by tcpip, in packetLoop
/** @brief Passes packet to UDP Server
* @param len Not used
* @return <i>bool</i> True if packet processed
*/
static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop
// dhcp.cpp
/** @brief Update DHCP state
* @param len Length of received data packet
*/
static void DhcpStateMachine(uint16_t len);
/** @brief Not implemented
* @todo Implement dhcpStartTime or remove declaration
*/
static uint32_t dhcpStartTime ();
/** @brief Not implemented
* @todo Implement dhcpLeaseTime or remove declaration
*/
static uint32_t dhcpLeaseTime ();
/** @brief Not implemented
* @todo Implement dhcpLease or remove declaration
*/
static bool dhcpLease ();
/** @brief Configure network interface with DHCP
* @return <i>bool</i> True if DHCP successful
* @note Blocks until DHCP complete or timeout after 60 seconds
*/
static bool dhcpSetup (const char *hname = NULL, bool fromRam =false);
/** @brief Register a callback for a specific DHCP option number
* @param option The option number to request from the DHCP server
* @param callback The function to be call when the option is received
*/
static void dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback);
// dns.cpp
/** @brief Perform DNS lookup
* @param name Host name to lookup
* @param fromRam Set true to look up cached name. Default = false
* @return <i>bool</i> True on success.
* @note Result is stored in <i>hisip</i> member
*/
static bool dnsLookup (const char* name, bool fromRam =false);
// webutil.cpp
/** @brief Copies an IP address
* @param dst Pointer to the 4 byte destination
* @param src Pointer to the 4 byte source
* @note There is no check of source or destination size. Ensure both are 4 bytes
*/
static void copyIp (uint8_t *dst, const uint8_t *src);
/** @brief Copies a hardware address
* @param dst Pointer to the 6 byte destination
* @param src Pointer to the 6 byte destination
* @note There is no check of source or destination size. Ensure both are 6 bytes
*/
static void copyMac (uint8_t *dst, const uint8_t *src);
/** @brief Output to serial port in dotted decimal IP format
* @param buf Pointer to 4 byte IP address
* @note There is no check of source or destination size. Ensure both are 4 bytes
*/
static void printIp (const uint8_t *buf);
/** @brief Output message and IP address to serial port in dotted decimal IP format
* @param msg Pointer to null terminated string
* @param buf Pointer to 4 byte IP address
* @note There is no check of source or destination size. Ensure both are 4 bytes
*/
static void printIp (const char* msg, const uint8_t *buf);
/** @brief Output Flash String Helper and IP address to serial port in dotted decimal IP format
* @param ifsh Pointer to Flash String Helper
* @param buf Pointer to 4 byte IP address
* @note There is no check of source or destination size. Ensure both are 4 bytes
* @todo What is a FlashStringHelper?
*/
static void printIp (const __FlashStringHelper *ifsh, const uint8_t *buf);
/** @brief Search for a string of the form key=value in a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\\r\\n
* @param str Pointer to the null terminated string to search
* @param strbuf Pointer to buffer to hold null terminated result string
* @param maxlen Maximum length of result
* @param key Pointer to null terminated string holding the key to search for
* @return <i>unit_t</i> Length of the value. 0 if not found
* @note Ensure strbuf has memory allocated of at least maxlen + 1 (to accomodate result plus terminating null)
*/
static uint8_t findKeyVal(const char *str,char *strbuf,
uint8_t maxlen, const char *key);
/** @brief Decode a URL string e.g "hello%20joe" or "hello+joe" becomes "hello joe"
* @param urlbuf Pointer to the null terminated URL
* @note urlbuf is modified
*/
static void urlDecode(char *urlbuf);
/** @brief Encode a URL, replacing illegal charaters like ' '
* @param str Pointer to the null terminated string to encode
* @param urlbuf Pointer to a buffer to contain the null terminated encoded URL
* @note There must be enough space in urlbuf. In the worst case that is 3 times the length of str
*/
static void urlEncode(char *str,char *urlbuf);
/** @brief Convert an IP address from dotted decimal formated string to 4 bytes
* @param bytestr Pointer to the 4 byte array to store IP address
* @param str Pointer to string to parse
* @return <i>uint8_t</i> 0 on success
*/
static uint8_t parseIp(uint8_t *bytestr,char *str);
/** @brief Convert a byte array to a human readable display string
* @param resultstr Pointer to a buffer to hold the resulting null terminated string
* @param bytestr Pointer to the byte array containing the address to convert
* @param len Length of the array (4 for IP address, 6 for hardware (MAC) address)
* @param separator Delimiter character (typically '.' for IP address and ':' for hardware (MAC) address)
* @param base Base for numerical representation (typically 10 for IP address and 16 for hardware (MAC) address
*/
static void makeNetStr(char *resultstr,uint8_t *bytestr,uint8_t len,
char separator,uint8_t base);
};
extern EtherCard ether; //!< Global presentation of EtherCard class
#endif

View file

@ -0,0 +1,46 @@
# EtherCard
**EtherCard** is a driver for the ENC28J60 chip, compatible with Arduino IDE.
Adapted and extended from code written by Guido Socher and Pascal Stang.
License: GPL2
The documentation for this library is at http://jeelabs.net/pub/docs/ethercard/.
## Library Installation
1. Download the ZIP file from https://github.com/jcw/ethercard/archive/master.zip
2. From the Arduino IDE: Sketch -> Import Library... -> Add Library...
3. Restart the Arduino IDE to see the new "EtherCard" library with examples
See the comments in the example sketches for details about how to try them out.
## Physical Installation
### PIN Connections (Using Arduino UNO):
VCC - 3.3V
GND - GND
SCK - Pin 13
SO - Pin 12
SI - Pin 11
CS - Pin 8 # Selectable with the ether.begin() function
### PIN Connections using an Arduino Mega
VCC - 3.3V
GND - GND
SCK - Pin 52
SO - Pin 50
SI - Pin 51
CS - Pin 53 # Selectable with the ether.begin() function
# The default CS pin defaults to 8, so you have to set it on a mega:
ether.begin(sizeof Ethernet::buffer, mymac, 53)
## Support
For questions and help, see the [forums][F] (at JeeLabs.net).
The issue tracker has been moved back to [Github][I] again.
[F]: http://jeenet.net/projects/cafe/boards
[I]: https://github.com/jcw/ethercard/issues

View file

@ -0,0 +1,432 @@
// DHCP look-up functions based on the udp client
// http://www.ietf.org/rfc/rfc2131.txt
//
// Author: Andrew Lindsay
// Rewritten and optimized by Jean-Claude Wippler, http://jeelabs.org/
//
// Rewritten dhcpStateMachine by Chris van den Hooven
// as to implement dhcp-renew when lease expires (jun 2012)
//
// Various modifications and bug fixes contributed by Victor Aprea (oct 2012)
//
// Copyright: GPL V2
// See http://www.gnu.org/licenses/gpl.html
//#define DHCPDEBUG
#include "EtherCard.h"
#include "net.h"
#define gPB ether.buffer
#define DHCP_BOOTREQUEST 1
#define DHCP_BOOTRESPONSE 2
// DHCP Message Type (option 53) (ref RFC 2132)
#define DHCP_DISCOVER 1
#define DHCP_OFFER 2
#define DHCP_REQUEST 3
#define DHCP_DECLINE 4
#define DHCP_ACK 5
#define DHCP_NAK 6
#define DHCP_RELEASE 7
// DHCP States for access in applications (ref RFC 2131)
enum {
DHCP_STATE_INIT,
DHCP_STATE_SELECTING,
DHCP_STATE_REQUESTING,
DHCP_STATE_BOUND,
DHCP_STATE_RENEWING,
};
/*
op 1 Message op code / message type.
1 = BOOTREQUEST, 2 = BOOTREPLY
htype 1 Hardware address type, see ARP section in "Assigned
Numbers" RFC; e.g., '1' = 10mb ethernet.
hlen 1 Hardware address length (e.g. '6' for 10mb
ethernet).
hops 1 Client sets to zero, optionally used by relay agents
when booting via a relay agent.
xid 4 Transaction ID, a random number chosen by the
client, used by the client and server to associate
messages and responses between a client and a
server.
secs 2 Filled in by client, seconds elapsed since client
began address acquisition or renewal process.
flags 2 Flags (see figure 2).
ciaddr 4 Client IP address; only filled in if client is in
BOUND, RENEW or REBINDING state and can respond
to ARP requests.
yiaddr 4 'your' (client) IP address.
siaddr 4 IP address of next server to use in bootstrap;
returned in DHCPOFFER, DHCPACK by server.
giaddr 4 Relay agent IP address, used in booting via a
relay agent.
chaddr 16 Client hardware address.
sname 64 Optional server host name, null terminated string.
file 128 Boot file name, null terminated string; "generic"
name or null in DHCPDISCOVER, fully qualified
directory-path name in DHCPOFFER.
options var Optional parameters field. See the options
documents for a list of defined options.
*/
// size 236
typedef struct {
byte op, htype, hlen, hops;
uint32_t xid;
uint16_t secs, flags;
byte ciaddr[4], yiaddr[4], siaddr[4], giaddr[4];
byte chaddr[16], sname[64], file[128];
// options
} DHCPdata;
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
// timeouts im ms
#define DHCP_REQUEST_TIMEOUT 10000
#define DHCP_HOSTNAME_MAX_LEN 32
// RFC 2132 Section 3.3:
// The time value of 0xffffffff is reserved to represent "infinity".
#define DHCP_INFINITE_LEASE 0xffffffff
static byte dhcpState = DHCP_STATE_INIT;
static char hostname[DHCP_HOSTNAME_MAX_LEN] = "Arduino-00";
static uint32_t currentXid;
static uint32_t stateTimer;
static uint32_t leaseStart;
static uint32_t leaseTime;
static byte* bufPtr;
static uint8_t dhcpCustomOptionNum = 0;
static DhcpOptionCallback dhcpCustomOptionCallback = NULL;
// static uint8_t allOnes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static void addToBuf (byte b) {
*bufPtr++ = b;
}
static void addBytes (byte len, const byte* data) {
while (len-- > 0)
addToBuf(*data++);
}
// Main DHCP sending function
// implemented
// state / msgtype
// INIT / DHCPDISCOVER
// SELECTING / DHCPREQUEST
// BOUND (RENEWING) / DHCPREQUEST
// ----------------------------------------------------------
// | |SELECTING |RENEWING |INIT |
// ----------------------------------------------------------
// |broad/unicast |broadcast |unicast |broadcast |
// |server-ip |MUST |MUST NOT |MUST NOT | option 54
// |requested-ip |MUST |MUST NOT |MUST NOT | option 50
// |ciaddr |zero |IP address |zero |
// ----------------------------------------------------------
// options used (both send/receive)
// 12 Host Name Option
// 50 Requested IP Address
// 51 IP Address Lease Time
// 53 DHCP message type
// 54 Server-identifier
// 55 Parameter request list
// 58 Renewal (T1) Time Value
// 61 Client-identifier
// 255 End
static void send_dhcp_message(uint8_t *requestip) {
uint8_t allOnes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
memset(gPB, 0, UDP_DATA_P + sizeof( DHCPdata ));
EtherCard::udpPrepare(DHCP_CLIENT_PORT,
(dhcpState == DHCP_STATE_BOUND ? EtherCard::dhcpip : allOnes),
DHCP_SERVER_PORT);
// If we ever don't do this, the DHCP renewal gets sent to whatever random
// destmacaddr was used by other code. Rather than cache the MAC address of
// the DHCP server, just force a broadcast here in all cases.
EtherCard::copyMac(gPB + ETH_DST_MAC, allOnes); //force broadcast mac
// Build DHCP Packet from buf[UDP_DATA_P]
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
dhcpPtr->op = DHCP_BOOTREQUEST;
dhcpPtr->htype = 1;
dhcpPtr->hlen = 6;
dhcpPtr->xid = currentXid;
if (dhcpState == DHCP_STATE_BOUND) {
EtherCard::copyIp(dhcpPtr->ciaddr, EtherCard::myip);
}
EtherCard::copyMac(dhcpPtr->chaddr, EtherCard::mymac);
// options defined as option, length, value
bufPtr = gPB + UDP_DATA_P + sizeof( DHCPdata );
// DHCP magic cookie, followed by message type
static byte cookie[] = { 99, 130, 83, 99, 53, 1 };
addBytes(sizeof cookie, cookie);
// addToBuf(53); // DHCP_STATE_SELECTING, DHCP_STATE_REQUESTING
// addToBuf(1); // Length
addToBuf(dhcpState == DHCP_STATE_INIT ? DHCP_DISCOVER : DHCP_REQUEST);
// Client Identifier Option, this is the client mac address
addToBuf(61); // Client identifier
addToBuf(7); // Length
addToBuf(0x01); // Ethernet
addBytes(6, EtherCard::mymac);
addToBuf(12); // Host name Option
addToBuf(10);
addBytes(10, (byte*) hostname);
if (requestip != NULL) {
addToBuf(50); // Request IP address
addToBuf(4);
addBytes(4, requestip);
addToBuf(54); // DHCP Server IP address
addToBuf(4);
addBytes(4, EtherCard::dhcpip);
}
// Additional info in parameter list - minimal list for what we need
byte len = 3;
if (dhcpCustomOptionNum)
len++;
addToBuf(55); // Parameter request list
addToBuf(len); // Length
addToBuf(1); // Subnet mask
addToBuf(3); // Route/Gateway
addToBuf(6); // DNS Server
if (dhcpCustomOptionNum)
addToBuf(dhcpCustomOptionNum); // Custom option
addToBuf(255); // end option
// packet size will be under 300 bytes
EtherCard::udpTransmit((bufPtr - gPB) - UDP_DATA_P);
}
static void process_dhcp_offer(uint16_t len, uint8_t *offeredip) {
// Map struct onto payload
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
// Offered IP address is in yiaddr
EtherCard::copyIp(offeredip, dhcpPtr->yiaddr);
// Search for the
byte *ptr = (byte*) (dhcpPtr + 1) + 4;
do {
byte option = *ptr++;
byte optionLen = *ptr++;
if (option == 54) {
EtherCard::copyIp(EtherCard::dhcpip, ptr);
break;
}
ptr += optionLen;
} while (ptr < gPB + len);
}
static void process_dhcp_ack(uint16_t len) {
// Map struct onto payload
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
// Allocated IP address is in yiaddr
EtherCard::copyIp(EtherCard::myip, dhcpPtr->yiaddr);
// Scan through variable length option list identifying options we want
byte *ptr = (byte*) (dhcpPtr + 1) + 4;
bool done = false;
do {
byte option = *ptr++;
byte optionLen = *ptr++;
switch (option) {
case 1:
EtherCard::copyIp(EtherCard::netmask, ptr);
break;
case 3:
EtherCard::copyIp(EtherCard::gwip, ptr);
break;
case 6:
EtherCard::copyIp(EtherCard::dnsip, ptr);
break;
case 51:
case 58:
leaseTime = 0; // option 58 = Renewal Time, 51 = Lease Time
for (byte i = 0; i<4; i++)
leaseTime = (leaseTime << 8) + ptr[i];
if (leaseTime != DHCP_INFINITE_LEASE) {
leaseTime *= 1000; // milliseconds
}
break;
case 255:
done = true;
break;
default: {
// Is is a custom configured option?
if (dhcpCustomOptionCallback && option == dhcpCustomOptionNum) {
dhcpCustomOptionCallback(option, ptr, optionLen);
}
}
}
ptr += optionLen;
} while (!done && ptr < gPB + len);
}
static bool dhcp_received_message_type (uint16_t len, byte msgType) {
// Map struct onto payload
DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P);
if (len >= 70 && gPB[UDP_SRC_PORT_L_P] == DHCP_SERVER_PORT &&
dhcpPtr->xid == currentXid ) {
byte *ptr = (byte*) (dhcpPtr + 1) + 4;
do {
byte option = *ptr++;
byte optionLen = *ptr++;
if(option == 53 && *ptr == msgType ) {
// DHCP Message type match found
return true;
}
ptr += optionLen;
} while (ptr < gPB + len);
}
return false;
}
static char toAsciiHex(byte b) {
char c = b & 0x0f;
c += (c <= 9) ? '0' : 'A'-10;
return c;
}
bool EtherCard::dhcpSetup (const char *hname, bool fromRam) {
// Use during setup, as this discards all incoming requests until it returns.
// That shouldn't be a problem, because we don't have an IP-address yet.
// Will try 60 secs to obtain DHCP-lease.
using_dhcp = true;
if(hname != NULL) {
if(fromRam) {
strncpy(hostname, hname, DHCP_HOSTNAME_MAX_LEN);
}
else {
strncpy_P(hostname, hname, DHCP_HOSTNAME_MAX_LEN);
}
}
else {
// Set a unique hostname, use Arduino-?? with last octet of mac address
hostname[8] = toAsciiHex(mymac[5] >> 4);
hostname[9] = toAsciiHex(mymac[5]);
}
dhcpState = DHCP_STATE_INIT;
uint16_t start = millis();
while (dhcpState != DHCP_STATE_BOUND && (uint16_t) (millis() - start) < 60000) {
if (isLinkUp()) DhcpStateMachine(packetReceive());
}
updateBroadcastAddress();
delaycnt = 0;
return dhcpState == DHCP_STATE_BOUND ;
}
void EtherCard::dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback)
{
dhcpCustomOptionNum = option;
dhcpCustomOptionCallback = callback;
}
void EtherCard::DhcpStateMachine (uint16_t len)
{
#ifdef DHCPDEBUG
if (dhcpState != DHCP_STATE_BOUND) {
Serial.print(millis());
Serial.print(" State: ");
}
switch (dhcpState) {
case DHCP_STATE_INIT:
Serial.println("Init");
break;
case DHCP_STATE_SELECTING:
Serial.println("Selecting");
break;
case DHCP_STATE_REQUESTING:
Serial.println("Requesting");
break;
case DHCP_STATE_RENEWING:
Serial.println("Renew");
break;
}
#endif
switch (dhcpState) {
case DHCP_STATE_BOUND:
//!@todo Due to millis() 49 day wrap-around, DHCP renewal may not work as expected
if (leaseTime != DHCP_INFINITE_LEASE && millis() >= leaseStart + leaseTime) {
send_dhcp_message(myip);
dhcpState = DHCP_STATE_RENEWING;
stateTimer = millis();
}
break;
case DHCP_STATE_INIT:
currentXid = millis();
memset(myip,0,4); // force ip 0.0.0.0
send_dhcp_message(NULL);
enableBroadcast(true); //Temporarily enable broadcasts
dhcpState = DHCP_STATE_SELECTING;
stateTimer = millis();
break;
case DHCP_STATE_SELECTING:
if (dhcp_received_message_type(len, DHCP_OFFER)) {
uint8_t offeredip[4];
process_dhcp_offer(len, offeredip);
send_dhcp_message(offeredip);
dhcpState = DHCP_STATE_REQUESTING;
stateTimer = millis();
} else {
if (millis() > stateTimer + DHCP_REQUEST_TIMEOUT) {
dhcpState = DHCP_STATE_INIT;
}
}
break;
case DHCP_STATE_REQUESTING:
case DHCP_STATE_RENEWING:
if (dhcp_received_message_type(len, DHCP_ACK)) {
disableBroadcast(true); //Disable broadcast after temporary enable
process_dhcp_ack(len);
leaseStart = millis();
if (gwip[0] != 0) setGwIp(gwip); // why is this? because it initiates an arp request
dhcpState = DHCP_STATE_BOUND;
} else {
if (millis() > stateTimer + DHCP_REQUEST_TIMEOUT) {
dhcpState = DHCP_STATE_INIT;
}
}
break;
}
}

View file

@ -0,0 +1,117 @@
// DNS look-up functions based on the udp client
// Author: Guido Socher
// Copyright: GPL V2
//
// 2010-05-20 <jc@wippler.nl>
#include "EtherCard.h"
#include "net.h"
#define gPB ether.buffer
static byte dnstid_l; // a counter for transaction ID
#define DNSCLIENT_SRC_PORT_H 0xE0
static void dnsRequest (const char *hostname, bool fromRam) {
++dnstid_l; // increment for next request, finally wrap
if (ether.dnsip[0] == 0)
memset(ether.dnsip, 8, 4); // use 8.8.8.8 Google DNS as default
ether.udpPrepare((DNSCLIENT_SRC_PORT_H << 8) | dnstid_l, ether.dnsip, 53);
memset(gPB + UDP_DATA_P, 0, 12);
byte *p = gPB + UDP_DATA_P + 12;
char c;
do {
byte n = 0;
for(;;) {
c = fromRam ? *hostname : pgm_read_byte(hostname);
++hostname;
if (c == '.' || c == 0)
break;
p[++n] = c;
}
*p++ = n;
p += n;
} while (c != 0);
*p++ = 0; // terminate with zero, means root domain.
*p++ = 0;
*p++ = 1; // type A
*p++ = 0;
*p++ = 1; // class IN
byte i = p - gPB - UDP_DATA_P;
gPB[UDP_DATA_P] = i;
gPB[UDP_DATA_P+1] = dnstid_l;
gPB[UDP_DATA_P+2] = 1; // flags, standard recursive query
gPB[UDP_DATA_P+5] = 1; // 1 question
ether.udpTransmit(i);
}
/** @brief Check if packet is DNS response.
@param plen Size of packet
@return <i>bool</i> True if DNS response has error. False if not DNS response or DNS response OK.
@note hisip contains IP address of requested host or 0.0.0.0 on failure
*/
static bool checkForDnsAnswer (uint16_t plen) {
byte *p = gPB + UDP_DATA_P; //start of UDP payload
if (plen < 70 || gPB[UDP_SRC_PORT_L_P] != 53 || //from DNS source port
gPB[UDP_DST_PORT_H_P] != DNSCLIENT_SRC_PORT_H || //response to same port as we sent from (MSB)
gPB[UDP_DST_PORT_L_P] != dnstid_l || //response to same port as we sent from (LSB)
p[1] != dnstid_l) //message id same as we sent
return false; //not our DNS response
if((p[3] & 0x0F) != 0)
return true; //DNS response recieved with error
p += *p; // we encoded the query len into tid
for (;;) {
if (*p & 0xC0)
p += 2;
else
while (++p < gPB + plen) {
if (*p == 0) {
++p;
break;
}
}
if (p + 14 > gPB + plen)
break;
if (p[1] == 1 && p[9] == 4) { // type "A" and IPv4
ether.copyIp(ether.hisip, p + 10);
break;
}
p += p[9] + 10;
}
return false; //No error
}
// use during setup, as this discards all incoming requests until it returns
bool EtherCard::dnsLookup (const char* name, bool fromRam) {
word start = millis();
while(!isLinkUp())
{
if ((word) (millis() - start) >= 30000)
return false; //timeout waiting for link
}
while(clientWaitingDns())
{
packetLoop(packetReceive());
if ((word) (millis() - start) >= 30000)
return false; //timeout waiting for gateway ARP
}
memset(hisip, 0, 4);
dnsRequest(name, fromRam);
start = millis();
while (hisip[0] == 0) {
if ((word) (millis() - start) >= 30000)
return false; //timout waiting for dns response
word len = packetReceive();
if (len > 0 && packetLoop(len) == 0) //packet not handled by tcp/ip packet loop
if(checkForDnsAnswer(len))
return false; //DNS response recieved with error
}
return true;
}

View file

@ -0,0 +1,96 @@
/**
@mainpage EtherCard is a driver for the ENC28J60 chip, compatible with Arduino IDE.<br/>
Adapted and extended from code written by Guido Socher and Pascal Stang.<br/>
The home page for this library is at http://jeelabs.net/projects/ethercard/wiki.<br/>
License: <a href="http://www.gnu.org/licenses/gpl-2.0.html">GPL2</a><br/><br/>
<b>PIN Connections (Using Arduino UNO):</b>
<table border="1">
<tr><th>EtherCard</th><th>Arduino UNO</th></tr>
<tr><td>VCC</td><td>3.3V</td></tr>
<tr><td>GND</td><td>GND</td></tr>
<tr><td>SCK</td><td>Pin 13</td></tr>
<tr><td>SO</td><td>Pin 12</td></tr>
<tr><td>SI</td><td>Pin 11</td></tr>
<tr><td>CS</td><td>Pin 8</td></tr>
</table>
@section intro_sec Introduction
EtherCard is a library that performs low-level interfacing with network interfaces based on the MicroChip (C) ENC28J60.
High-level routines are provided to allow a variety of purposes including simple data transfer through to HTTP handling.
EtherCard is a driver for the ENC28J60 chip, compatible with Arduino IDE.
Adapted and extended from code written by Guido Socher and Pascal Stang.
The documentation for this library is at http://jeelabs.net/pub/docs/ethercard/
The code is available on GitHub, at https://github.com/jcw/ethercard - to install:
Download the ZIP file from https://github.com/jcw/ethercard/archive/master.zip
From the Arduino IDE: Sketch -> Import Library... -> Add Library...
Restart the Arduino IDE to see the new "EtherCard" library with examples
See the comments in the example sketches for details about how to try them out.
For questions and help, see the forums. For bugs and feature requests, see the issue tracker.
@section eg_sec Examples
Several @link examples example scripts @endlink are provided with the library which demonstrate various features. Below are descriptions on how to use the library.
<p>Note: <b>ether</b> is defined globally and may be used to access the library thus: ether.<i>member</i>.
@subsection Initiate To initiate the library call EtherCard::begin
<pre>
uint8_t Ethernet::buffer[700]; // configure buffer size to 700 octets
static uint8_t mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; // define (unique on LAN) hardware (MAC) address
uint8_type nFirmwareVersion ether.begin(sizeof Ethernet::buffer, mymac);
if(0 == nFirmwareVersion)
{
// handle failure to initiate network interface
}
</pre>
@subsection DHCP To configure IP address via DHCP use EtherCard::dhcpSetup
<pre>
if(!ether.dhcpSetup())
{
// handle failure to obtain IP address via DHCP
}
ether.printIp("IP: ", ether.myip); // output IP address to Serial
ether.printIp("GW: ", ether.gwip); // output gateway address to Serial
ether.printIp("Mask: ", ether.mymask); // output netmask to Serial
ether.printIp("DHCP server: ", ether.dhcpip); // output IP address of the DHCP server
</pre>
@subsection StaticIP To configure a static IP address use EtherCard::staticSetup
<pre>
const static uint8_t ip[] = {192,168,0,100};
const static uint8_t gw[] = {192,168,0,254};
const static uint8_t dns[] = {192,168,0,1};
if(!ether.staticSetup(ip, gw, dns);
{
// handle failure to configure static IP address (current implementation always returns true!)
}
</pre>
@subsection SendUDP Send UDP packet
To send a UDP packet use EtherCard::sendUdp
<pre>
char payload[] = "My UDP message";
uint8_t nSourcePort = 1234;
uint8_t nDestinationPort = 5678;
uint8_t ipDestinationAddress[4];
ether.parseIp(ipDestinationAddress, "192.168.0.200");
ether.sendUdp(payload, sizeof(payload), nSourcePort, ipDestinationAddress, nDestinationPort);
</pre>
@subsection DNSLookup DNS Lookup
To perform a DNS lookup use EtherCard::dnsLookup
<pre>
if(!ether.dnsLookup("google.com"))
{
// handle failure of DNS lookup
}
ether.printIp("Server: ", ether.hispip); // Result of DNS lookup is placed in the hisip member of EtherCard.
</pre>
*/

View file

@ -0,0 +1,611 @@
// Microchip ENC28J60 Ethernet Interface Driver
// Author: Guido Socher
// Copyright: GPL V2
//
// Based on the enc28j60.c file from the AVRlib library by Pascal Stang.
// For AVRlib See http://www.procyonengineering.com/
// Used with explicit permission of Pascal Stang.
//
// 2010-05-20 <jc@wippler.nl>
#if ARDUINO >= 100
#include <Arduino.h> // Arduino 1.0
#else
#include <Wprogram.h> // Arduino 0022
#endif
#include "enc28j60.h"
uint16_t ENC28J60::bufferSize;
bool ENC28J60::broadcast_enabled = false;
// ENC28J60 Control Registers
// Control register definitions are a combination of address,
// bank number, and Ethernet/MAC/PHY indicator bits.
// - Register address (bits 0-4)
// - Bank number (bits 5-6)
// - MAC/PHY indicator (bit 7)
#define ADDR_MASK 0x1F
#define BANK_MASK 0x60
#define SPRD_MASK 0x80
// All-bank registers
#define EIE 0x1B
#define EIR 0x1C
#define ESTAT 0x1D
#define ECON2 0x1E
#define ECON1 0x1F
// Bank 0 registers
#define ERDPT (0x00|0x00)
#define EWRPT (0x02|0x00)
#define ETXST (0x04|0x00)
#define ETXND (0x06|0x00)
#define ERXST (0x08|0x00)
#define ERXND (0x0A|0x00)
#define ERXRDPT (0x0C|0x00)
// #define ERXWRPT (0x0E|0x00)
#define EDMAST (0x10|0x00)
#define EDMAND (0x12|0x00)
// #define EDMADST (0x14|0x00)
#define EDMACS (0x16|0x00)
// Bank 1 registers
#define EHT0 (0x00|0x20)
#define EHT1 (0x01|0x20)
#define EHT2 (0x02|0x20)
#define EHT3 (0x03|0x20)
#define EHT4 (0x04|0x20)
#define EHT5 (0x05|0x20)
#define EHT6 (0x06|0x20)
#define EHT7 (0x07|0x20)
#define EPMM0 (0x08|0x20)
#define EPMM1 (0x09|0x20)
#define EPMM2 (0x0A|0x20)
#define EPMM3 (0x0B|0x20)
#define EPMM4 (0x0C|0x20)
#define EPMM5 (0x0D|0x20)
#define EPMM6 (0x0E|0x20)
#define EPMM7 (0x0F|0x20)
#define EPMCS (0x10|0x20)
// #define EPMO (0x14|0x20)
#define EWOLIE (0x16|0x20)
#define EWOLIR (0x17|0x20)
#define ERXFCON (0x18|0x20)
#define EPKTCNT (0x19|0x20)
// Bank 2 registers
#define MACON1 (0x00|0x40|0x80)
#define MACON2 (0x01|0x40|0x80)
#define MACON3 (0x02|0x40|0x80)
#define MACON4 (0x03|0x40|0x80)
#define MABBIPG (0x04|0x40|0x80)
#define MAIPG (0x06|0x40|0x80)
#define MACLCON1 (0x08|0x40|0x80)
#define MACLCON2 (0x09|0x40|0x80)
#define MAMXFL (0x0A|0x40|0x80)
#define MAPHSUP (0x0D|0x40|0x80)
#define MICON (0x11|0x40|0x80)
#define MICMD (0x12|0x40|0x80)
#define MIREGADR (0x14|0x40|0x80)
#define MIWR (0x16|0x40|0x80)
#define MIRD (0x18|0x40|0x80)
// Bank 3 registers
#define MAADR1 (0x00|0x60|0x80)
#define MAADR0 (0x01|0x60|0x80)
#define MAADR3 (0x02|0x60|0x80)
#define MAADR2 (0x03|0x60|0x80)
#define MAADR5 (0x04|0x60|0x80)
#define MAADR4 (0x05|0x60|0x80)
#define EBSTSD (0x06|0x60)
#define EBSTCON (0x07|0x60)
#define EBSTCS (0x08|0x60)
#define MISTAT (0x0A|0x60|0x80)
#define EREVID (0x12|0x60)
#define ECOCON (0x15|0x60)
#define EFLOCON (0x17|0x60)
#define EPAUS (0x18|0x60)
// ENC28J60 ERXFCON Register Bit Definitions
#define ERXFCON_UCEN 0x80
#define ERXFCON_ANDOR 0x40
#define ERXFCON_CRCEN 0x20
#define ERXFCON_PMEN 0x10
#define ERXFCON_MPEN 0x08
#define ERXFCON_HTEN 0x04
#define ERXFCON_MCEN 0x02
#define ERXFCON_BCEN 0x01
// ENC28J60 EIE Register Bit Definitions
#define EIE_INTIE 0x80
#define EIE_PKTIE 0x40
#define EIE_DMAIE 0x20
#define EIE_LINKIE 0x10
#define EIE_TXIE 0x08
#define EIE_WOLIE 0x04
#define EIE_TXERIE 0x02
#define EIE_RXERIE 0x01
// ENC28J60 EIR Register Bit Definitions
#define EIR_PKTIF 0x40
#define EIR_DMAIF 0x20
#define EIR_LINKIF 0x10
#define EIR_TXIF 0x08
#define EIR_WOLIF 0x04
#define EIR_TXERIF 0x02
#define EIR_RXERIF 0x01
// ENC28J60 ESTAT Register Bit Definitions
#define ESTAT_INT 0x80
#define ESTAT_LATECOL 0x10
#define ESTAT_RXBUSY 0x04
#define ESTAT_TXABRT 0x02
#define ESTAT_CLKRDY 0x01
// ENC28J60 ECON2 Register Bit Definitions
#define ECON2_AUTOINC 0x80
#define ECON2_PKTDEC 0x40
#define ECON2_PWRSV 0x20
#define ECON2_VRPS 0x08
// ENC28J60 ECON1 Register Bit Definitions
#define ECON1_TXRST 0x80
#define ECON1_RXRST 0x40
#define ECON1_DMAST 0x20
#define ECON1_CSUMEN 0x10
#define ECON1_TXRTS 0x08
#define ECON1_RXEN 0x04
#define ECON1_BSEL1 0x02
#define ECON1_BSEL0 0x01
// ENC28J60 MACON1 Register Bit Definitions
#define MACON1_LOOPBK 0x10
#define MACON1_TXPAUS 0x08
#define MACON1_RXPAUS 0x04
#define MACON1_PASSALL 0x02
#define MACON1_MARXEN 0x01
// ENC28J60 MACON2 Register Bit Definitions
#define MACON2_MARST 0x80
#define MACON2_RNDRST 0x40
#define MACON2_MARXRST 0x08
#define MACON2_RFUNRST 0x04
#define MACON2_MATXRST 0x02
#define MACON2_TFUNRST 0x01
// ENC28J60 MACON3 Register Bit Definitions
#define MACON3_PADCFG2 0x80
#define MACON3_PADCFG1 0x40
#define MACON3_PADCFG0 0x20
#define MACON3_TXCRCEN 0x10
#define MACON3_PHDRLEN 0x08
#define MACON3_HFRMLEN 0x04
#define MACON3_FRMLNEN 0x02
#define MACON3_FULDPX 0x01
// ENC28J60 MICMD Register Bit Definitions
#define MICMD_MIISCAN 0x02
#define MICMD_MIIRD 0x01
// ENC28J60 MISTAT Register Bit Definitions
#define MISTAT_NVALID 0x04
#define MISTAT_SCAN 0x02
#define MISTAT_BUSY 0x01
// ENC28J60 EBSTCON Register Bit Definitions
#define EBSTCON_PSV2 0x80
#define EBSTCON_PSV1 0x40
#define EBSTCON_PSV0 0x20
#define EBSTCON_PSEL 0x10
#define EBSTCON_TMSEL1 0x08
#define EBSTCON_TMSEL0 0x04
#define EBSTCON_TME 0x02
#define EBSTCON_BISTST 0x01
// PHY registers
#define PHCON1 0x00
#define PHSTAT1 0x01
#define PHHID1 0x02
#define PHHID2 0x03
#define PHCON2 0x10
#define PHSTAT2 0x11
#define PHIE 0x12
#define PHIR 0x13
#define PHLCON 0x14
// ENC28J60 PHY PHCON1 Register Bit Definitions
#define PHCON1_PRST 0x8000
#define PHCON1_PLOOPBK 0x4000
#define PHCON1_PPWRSV 0x0800
#define PHCON1_PDPXMD 0x0100
// ENC28J60 PHY PHSTAT1 Register Bit Definitions
#define PHSTAT1_PFDPX 0x1000
#define PHSTAT1_PHDPX 0x0800
#define PHSTAT1_LLSTAT 0x0004
#define PHSTAT1_JBSTAT 0x0002
// ENC28J60 PHY PHCON2 Register Bit Definitions
#define PHCON2_FRCLINK 0x4000
#define PHCON2_TXDIS 0x2000
#define PHCON2_JABBER 0x0400
#define PHCON2_HDLDIS 0x0100
// ENC28J60 Packet Control Byte Bit Definitions
#define PKTCTRL_PHUGEEN 0x08
#define PKTCTRL_PPADEN 0x04
#define PKTCTRL_PCRCEN 0x02
#define PKTCTRL_POVERRIDE 0x01
// SPI operation codes
#define ENC28J60_READ_CTRL_REG 0x00
#define ENC28J60_READ_BUF_MEM 0x3A
#define ENC28J60_WRITE_CTRL_REG 0x40
#define ENC28J60_WRITE_BUF_MEM 0x7A
#define ENC28J60_BIT_FIELD_SET 0x80
#define ENC28J60_BIT_FIELD_CLR 0xA0
#define ENC28J60_SOFT_RESET 0xFF
// The RXSTART_INIT must be zero. See Rev. B4 Silicon Errata point 5.
// Buffer boundaries applied to internal 8K ram
// the entire available packet buffer space is allocated
#define RXSTART_INIT 0x0000 // start of RX buffer, room for 2 packets
#define RXSTOP_INIT 0x0BFF // end of RX buffer
#define TXSTART_INIT 0x0C00 // start of TX buffer, room for 1 packet
#define TXSTOP_INIT 0x11FF // end of TX buffer
#define SCRATCH_START 0x1200 // start of scratch area
#define SCRATCH_LIMIT 0x2000 // past end of area, i.e. 3.5 Kb
#define SCRATCH_PAGE_SHIFT 6 // addressing is in pages of 64 bytes
#define SCRATCH_PAGE_SIZE (1 << SCRATCH_PAGE_SHIFT)
// max frame length which the conroller will accept:
// (note: maximum ethernet frame length would be 1518)
#define MAX_FRAMELEN 1500
#define FULL_SPEED 1 // switch to full-speed SPI for bulk transfers
static byte Enc28j60Bank;
static int gNextPacketPtr;
static byte selectPin;
void ENC28J60::initSPI () {
pinMode(SS, OUTPUT);
digitalWrite(SS, HIGH);
pinMode(MOSI, OUTPUT);
pinMode(SCK, OUTPUT);
pinMode(MISO, INPUT);
digitalWrite(MOSI, HIGH);
digitalWrite(MOSI, LOW);
digitalWrite(SCK, LOW);
SPCR = bit(SPE) | bit(MSTR); // 8 MHz @ 16
bitSet(SPSR, SPI2X);
}
static void enableChip () {
cli();
digitalWrite(selectPin, LOW);
}
static void disableChip () {
digitalWrite(selectPin, HIGH);
sei();
}
static void xferSPI (byte data) {
SPDR = data;
while (!(SPSR&(1<<SPIF)))
;
}
static byte readOp (byte op, byte address) {
enableChip();
xferSPI(op | (address & ADDR_MASK));
xferSPI(0x00);
if (address & 0x80)
xferSPI(0x00);
byte result = SPDR;
disableChip();
return result;
}
static void writeOp (byte op, byte address, byte data) {
enableChip();
xferSPI(op | (address & ADDR_MASK));
xferSPI(data);
disableChip();
}
static void readBuf(uint16_t len, byte* data) {
enableChip();
xferSPI(ENC28J60_READ_BUF_MEM);
while (len--) {
xferSPI(0x00);
*data++ = SPDR;
}
disableChip();
}
static void writeBuf(uint16_t len, const byte* data) {
enableChip();
xferSPI(ENC28J60_WRITE_BUF_MEM);
while (len--)
xferSPI(*data++);
disableChip();
}
static void SetBank (byte address) {
if ((address & BANK_MASK) != Enc28j60Bank) {
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_BSEL1|ECON1_BSEL0);
Enc28j60Bank = address & BANK_MASK;
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, Enc28j60Bank>>5);
}
}
static byte readRegByte (byte address) {
SetBank(address);
return readOp(ENC28J60_READ_CTRL_REG, address);
}
static uint16_t readReg(byte address) {
return readRegByte(address) + (readRegByte(address+1) << 8);
}
static void writeRegByte (byte address, byte data) {
SetBank(address);
writeOp(ENC28J60_WRITE_CTRL_REG, address, data);
}
static void writeReg(byte address, uint16_t data) {
writeRegByte(address, data);
writeRegByte(address + 1, data >> 8);
}
static uint16_t readPhyByte (byte address) {
writeRegByte(MIREGADR, address);
writeRegByte(MICMD, MICMD_MIIRD);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
writeRegByte(MICMD, 0x00);
return readRegByte(MIRD+1);
}
static void writePhy (byte address, uint16_t data) {
writeRegByte(MIREGADR, address);
writeReg(MIWR, data);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
}
byte ENC28J60::initialize (uint16_t size, const byte* macaddr, byte csPin) {
bufferSize = size;
if (bitRead(SPCR, SPE) == 0)
initSPI();
selectPin = csPin;
pinMode(selectPin, OUTPUT);
disableChip();
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
delay(2); // errata B7/2
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY)
;
gNextPacketPtr = RXSTART_INIT;
writeReg(ERXST, RXSTART_INIT);
writeReg(ERXRDPT, RXSTART_INIT);
writeReg(ERXND, RXSTOP_INIT);
writeReg(ETXST, TXSTART_INIT);
writeReg(ETXND, TXSTOP_INIT);
enableBroadcast(); // change to add ERXFCON_BCEN recommended by epam
writeReg(EPMM0, 0x303f);
writeReg(EPMCS, 0xf7f9);
writeRegByte(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
writeRegByte(MACON2, 0x00);
writeOp(ENC28J60_BIT_FIELD_SET, MACON3,
MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
writeReg(MAIPG, 0x0C12);
writeRegByte(MABBIPG, 0x12);
writeReg(MAMXFL, MAX_FRAMELEN);
writeRegByte(MAADR5, macaddr[0]);
writeRegByte(MAADR4, macaddr[1]);
writeRegByte(MAADR3, macaddr[2]);
writeRegByte(MAADR2, macaddr[3]);
writeRegByte(MAADR1, macaddr[4]);
writeRegByte(MAADR0, macaddr[5]);
writePhy(PHCON2, PHCON2_HDLDIS);
SetBank(ECON1);
writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
byte rev = readRegByte(EREVID);
// microchip forgot to step the number on the silcon when they
// released the revision B7. 6 is now rev B7. We still have
// to see what they do when they release B8. At the moment
// there is no B8 out yet
if (rev > 5) ++rev;
return rev;
}
bool ENC28J60::isLinkUp() {
return (readPhyByte(PHSTAT2) >> 2) & 1;
}
void ENC28J60::packetSend(uint16_t len) {
// see http://forum.mysensors.org/topic/536/
// while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS)
if (readRegByte(EIR) & EIR_TXERIF) {
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF);
}
writeReg(EWRPT, TXSTART_INIT);
writeReg(ETXND, TXSTART_INIT+len);
writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
writeBuf(len, buffer);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
}
uint16_t ENC28J60::packetReceive() {
uint16_t len = 0;
if (readRegByte(EPKTCNT) > 0) {
writeReg(ERDPT, gNextPacketPtr);
struct {
uint16_t nextPacket;
uint16_t byteCount;
uint16_t status;
} header;
readBuf(sizeof header, (byte*) &header);
gNextPacketPtr = header.nextPacket;
len = header.byteCount - 4; //remove the CRC count
if (len>bufferSize-1)
len=bufferSize-1;
if ((header.status & 0x80)==0)
len = 0;
else
readBuf(len, buffer);
buffer[len] = 0;
if (gNextPacketPtr - 1 > RXSTOP_INIT)
writeReg(ERXRDPT, RXSTOP_INIT);
else
writeReg(ERXRDPT, gNextPacketPtr - 1);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
}
return len;
}
void ENC28J60::copyout (byte page, const byte* data) {
uint16_t destPos = SCRATCH_START + (page << SCRATCH_PAGE_SHIFT);
if (destPos < SCRATCH_START || destPos > SCRATCH_LIMIT - SCRATCH_PAGE_SIZE)
return;
writeReg(EWRPT, destPos);
writeBuf(SCRATCH_PAGE_SIZE, data);
}
void ENC28J60::copyin (byte page, byte* data) {
uint16_t destPos = SCRATCH_START + (page << SCRATCH_PAGE_SHIFT);
if (destPos < SCRATCH_START || destPos > SCRATCH_LIMIT - SCRATCH_PAGE_SIZE)
return;
writeReg(ERDPT, destPos);
readBuf(SCRATCH_PAGE_SIZE, data);
}
byte ENC28J60::peekin (byte page, byte off) {
byte result = 0;
uint16_t destPos = SCRATCH_START + (page << SCRATCH_PAGE_SHIFT) + off;
if (SCRATCH_START <= destPos && destPos < SCRATCH_LIMIT) {
writeReg(ERDPT, destPos);
readBuf(1, &result);
}
return result;
}
// Contributed by Alex M. Based on code from: http://blog.derouineau.fr
// /2011/07/putting-enc28j60-ethernet-controler-in-sleep-mode/
void ENC28J60::powerDown() {
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
while(readRegByte(ESTAT) & ESTAT_RXBUSY);
while(readRegByte(ECON1) & ECON1_TXRTS);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV);
}
void ENC28J60::powerUp() {
writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV);
while(!readRegByte(ESTAT) & ESTAT_CLKRDY);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
}
void ENC28J60::enableBroadcast (bool temporary) {
writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_BCEN);
if(!temporary)
broadcast_enabled = true;
}
void ENC28J60::disableBroadcast (bool temporary) {
if(!temporary)
broadcast_enabled = false;
if(!broadcast_enabled)
writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_BCEN);
}
void ENC28J60::enableMulticast () {
writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_MCEN);
}
void ENC28J60::disableMulticast () {
writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_MCEN);
}
uint8_t ENC28J60::doBIST ( byte csPin) {
#define RANDOM_FILL 0b0000
#define ADDRESS_FILL 0b0100
#define PATTERN_SHIFT 0b1000
#define RANDOM_RACE 0b1100
// init
if (bitRead(SPCR, SPE) == 0)
initSPI();
selectPin = csPin;
pinMode(selectPin, OUTPUT);
disableChip();
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
delay(2); // errata B7/2
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) ;
// now we can start the memory test
uint16_t macResult;
uint16_t bitsResult;
// clear some of the registers registers
writeRegByte(ECON1, 0);
writeReg(EDMAST, 0);
// Set up necessary pointers for the DMA to calculate over the entire memory
writeReg(EDMAND, 0x1FFFu);
writeReg(ERXND, 0x1FFFu);
// Enable Test Mode and do an Address Fill
SetBank(EBSTCON);
writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_BISTST | ADDRESS_FILL);
// wait for BISTST to be reset, only after that are we actually ready to
// start the test
// this was undocumented :(
while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST);
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
// now start the actual reading an calculating the checksum until the end is
// reached
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN);
SetBank(EDMACS);
while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
macResult = readReg(EDMACS);
bitsResult = readReg(EBSTCS);
// Compare the results
// 0xF807 should always be generated in Address fill mode
if ((macResult != bitsResult) || (bitsResult != 0xF807)) {
return 0;
}
// reset test flag
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
// Now start the BIST with random data test, and also keep on swapping the
// DMA/BIST memory ports.
writeRegByte(EBSTSD, 0b10101010 | millis());
writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_PSEL | EBSTCON_BISTST | RANDOM_FILL);
// wait for BISTST to be reset, only after that are we actually ready to
// start the test
// this was undocumented :(
while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST);
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
// now start the actual reading an calculating the checksum until the end is
// reached
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN);
SetBank(EDMACS);
while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
macResult = readReg(EDMACS);
bitsResult = readReg(EBSTCS);
// The checksum should be equal
return macResult == bitsResult;
}

View file

@ -0,0 +1,114 @@
// Microchip ENC28J60 Ethernet Interface Driver
// Author: Pascal Stang
// Modified by: Guido Socher
// Copyright: GPL V2
//
// This driver provides initialization and transmit/receive
// functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY.
// This chip is novel in that it is a full MAC+PHY interface all in a 28-pin
// chip, using an SPI interface to the host processor.
//
// 2010-05-20 <jc@wippler.nl>
#ifndef ENC28J60_H
#define ENC28J60_H
/** This class provide low-level interfacing with the ENC28J60 network interface. This is used by the EtherCard class and not intended for use by (normal) end users. */
class ENC28J60 {
public:
static uint8_t buffer[]; //!< Data buffer (shared by recieve and transmit)
static uint16_t bufferSize; //!< Size of data buffer
static bool broadcast_enabled; //!< True if broadcasts enabled (used to allow temporary disable of broadcast for DHCP or other internal functions)
static uint8_t* tcpOffset () { return buffer + 0x36; } //!< Pointer to the start of TCP payload
/** @brief Initialise SPI interface
* @note Configures Arduino pins as input / output, etc.
*/
static void initSPI ();
/** @brief Initialise network interface
* @param size Size of data buffer
* @param macaddr Pointer to 6 byte hardware (MAC) address
* @param csPin Arduino pin used for chip select (enable network interface SPI bus). Default = 8
* @return <i>uint8_t</i> ENC28J60 firmware version or zero on failure.
*/
static uint8_t initialize (const uint16_t size, const uint8_t* macaddr,
uint8_t csPin = 8);
/** @brief Check if network link is connected
* @return <i>bool</i> True if link is up
*/
static bool isLinkUp ();
/** @brief Sends data to network interface
* @param len Size of data to send
* @note Data buffer is shared by recieve and transmit functions
*/
static void packetSend (uint16_t len);
/** @brief Copy recieved packets to data buffer
* @return <i>uint16_t</i> Size of recieved data
* @note Data buffer is shared by recieve and transmit functions
*/
static uint16_t packetReceive ();
/** @brief Copy data from ENC28J60 memory
* @param page Data page of memory
* @param data Pointer to buffer to copy data to
*/
static void copyout (uint8_t page, const uint8_t* data);
/** @brief Copy data to ENC28J60 memory
* @param page Data page of memory
* @param data Pointer to buffer to copy data from
*/
static void copyin (uint8_t page, uint8_t* data);
/** @brief Get single byte of data from ENC28J60 memory
* @param page Data page of memory
* @param off Offset of data within page
* @return Data value
*/
static uint8_t peekin (uint8_t page, uint8_t off);
/** @brief Put ENC28J60 in sleep mode
*/
static void powerDown(); // contrib by Alex M.
/** @brief Wake ENC28J60 from sleep mode
*/
static void powerUp(); // contrib by Alex M.
/** @brief Enable reception of broadcast messages
* @param temporary Set true to temporarily enable broadcast
* @note This will increase load on recieved data handling
*/
static void enableBroadcast(bool temporary = false);
/** @brief Disable reception of broadcast messages
* @param temporary Set true to only disable if temporarily enabled
* @note This will reduce load on recieved data handling
*/
static void disableBroadcast(bool temporary = false);
/** @brief Enables reception of mulitcast messages
* @note This will increase load on recieved data handling
*/
static void enableMulticast ();
/** @brief Disable reception of mulitcast messages
* @note This will reduce load on recieved data handling
*/
static void disableMulticast();
/** @brief Reset and fully initialise ENC28J60
* @param csPin Arduino pin used for chip select (enable SPI bus)
* @return <i>uint8_t</i> 0 on failure
*/
static uint8_t doBIST(uint8_t csPin = 8);
};
typedef ENC28J60 Ethernet; //!< Define alias Ethernet for ENC28J60
#endif

View file

@ -0,0 +1,320 @@
// Collect RF12 packets and send them on as UDP collectd packets on Ethernet.
// 2010-05-20 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
// This sketch is derived from RF12eth.pde (and etherNode.ino):
// May 2010, Andras Tucsni, http://opensource.org/licenses/mit-license.php
#include <EtherCard.h>
#include <JeeLib.h>
#include <avr/eeprom.h>
#define DEBUG 1 // set to 1 to display free RAM on web page
#define SERIAL 1 // set to 1 to show incoming requests on serial port
#define CONFIG_EEPROM_ADDR ((byte*) 0x10)
// configuration, as stored in EEPROM
struct Config {
byte band;
byte group;
byte collect;
word port;
byte valid; // keep this as last byte
} config;
// ethernet interface mac address - must be unique on your network
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
// buffer for an outgoing data packet
static byte outBuf[RF12_MAXDATA], outDest;
static char outCount = -1;
// this buffer will be used to construct a collectd UDP packet
static byte collBuf [200], collPos;
#define NUM_MESSAGES 3 // Number of messages saved in history
#define MESSAGE_TRUNC 15 // Truncate message payload to reduce memory use
static BufferFiller bfill; // used as cursor while filling the buffer
static byte history_rcvd[NUM_MESSAGES][MESSAGE_TRUNC+1]; //history record
static byte history_len[NUM_MESSAGES]; // # of RF12 messages+header in history
static byte next_msg; // pointer to next rf12rcvd line
static word msgs_rcvd; // total number of lines received modulo 10,000
byte Ethernet::buffer[700]; // tcp/ip send and receive buffer
static void loadConfig () {
for (byte i = 0; i < sizeof config; ++i)
((byte*) &config)[i] = eeprom_read_byte(CONFIG_EEPROM_ADDR + i);
if (config.valid != 253) {
config.valid = 253;
config.band = 8;
config.group = 1;
config.collect = 1;
config.port = 25827;
}
byte freq = config.band == 4 ? RF12_433MHZ :
config.band == 8 ? RF12_868MHZ :
RF12_915MHZ;
rf12_initialize(31, freq, config.group);
}
static void saveConfig () {
for (byte i = 0; i < sizeof config; ++i)
eeprom_write_byte(CONFIG_EEPROM_ADDR + i, ((byte*) &config)[i]);
}
#if DEBUG
static int freeRam () {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
#endif
void setup (){
Serial.begin(57600);
Serial.println("\n[JeeUdp]");
loadConfig();
if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
Serial.println( "Failed to access Ethernet controller");
if (!ether.dhcpSetup())
Serial.println("DHCP failed");
ether.printIp("IP: ", ether.myip);
}
const char okHeader[] PROGMEM =
"HTTP/1.0 200 OK\r\n"
"Content-Type: text/html\r\n"
"Pragma: no-cache\r\n"
;
static void homePage (BufferFiller& buf) {
word mhz = config.band == 4 ? 433 : config.band == 8 ? 868 : 915;
buf.emit_p(PSTR("$F\r\n"
"<title>RF12 JeeUdp</title>"
"<h2>RF12 JeeUdp @ $D - RF12 @ $D.$D</h2>"
"<a href='c'>Configure</a> - <a href='s'>Send Packet</a>"
"<h3>Last $D messages:</h3>"
"<pre>"), okHeader, config.port, mhz, config.group, NUM_MESSAGES);
for (byte i = 0; i < NUM_MESSAGES; ++i) {
byte j = (next_msg + i) % NUM_MESSAGES;
if (history_len[j] > 0) {
word n = msgs_rcvd - NUM_MESSAGES + i;
buf.emit_p(PSTR("\n$D$D$D$D: OK"), // hack, to show leading zero's
n/1000, (n/100) % 10, (n/10) % 10, n % 10);
for (byte k = 0; k < history_len[j]; ++k)
buf.emit_p(PSTR(" $D"), history_rcvd[j][k]);
}
}
long t = millis() / 1000;
word h = t / 3600;
byte m = (t / 60) % 60;
byte s = t % 60;
buf.emit_p(PSTR(
"</pre>"
"Uptime is $D$D:$D$D:$D$D"), h/10, h%10, m/10, m%10, s/10, s%10);
#if DEBUG
buf.emit_p(PSTR(" ($D bytes free)"), freeRam());
#endif
}
static int getIntArg(const char* data, const char* key, int value =-1) {
char temp[10];
if (ether.findKeyVal(data + 7, temp, sizeof temp, key) > 0)
value = atoi(temp);
return value;
}
static void configPage (const char* data, BufferFiller& buf) {
// pick up submitted data, if present
if (data[6] == '?') {
byte b = getIntArg(data, "b", 8);
byte g = getIntArg(data, "g", 1);
byte c = getIntArg(data, "c", 0);
word p = getIntArg(data, "p", 25827);
if (1 <= g && g <= 250 && 1024 <= p && p <= 30000) {
// store values as new settings
config.band = b;
config.group = g;
config.collect = c;
config.port = p;
saveConfig();
// re-init RF12 driver
loadConfig();
// clear history
memset(history_len, 0, sizeof history_len);
// redirect to the home page
buf.emit_p(PSTR(
"HTTP/1.0 302 found\r\n"
"Location: /\r\n"
"\r\n"));
return;
}
}
// else show a configuration form
buf.emit_p(PSTR("$F\r\n"
"<h3>Server node configuration</h3>"
"<form>"
"<p>"
"Freq band <input type=text name=b value='$D' size=1> (4, 8, or 9)<br>"
"Net group <input type=text name=g value='$D' size=3> (1..250)<br>"
"Collect mode: <input type=checkbox name=c value='1' $S> "
"(don't send ACKs)<br><br>"
"UDP Port <input type=text name=p value='$D' size=5> (1024..30000)"
"</p>"
"<input type=submit value=Set>"
"</form>"), okHeader, config.band, config.group,
config.collect ? "CHECKED" : "",
config.port);
}
static void sendPage (const char* data, BufferFiller& buf) {
// pick up submitted data, if present
const char* p = strstr(data, "b=");
byte d = getIntArg(data, "d");
if (data[6] == '?' && p != 0 && 0 <= d && d <= 31) {
// prepare to send data as soon as possible in loop()
outDest = d & RF12_HDR_MASK ? RF12_HDR_DST | d : 0;
outCount = 0;
// convert the input string to a number of decimal data bytes in outBuf
++p;
while (*p != 0 && *p != '&') {
outBuf[outCount] = 0;
while ('0' <= *++p && *p <= '9')
outBuf[outCount] = 10 * outBuf[outCount] + (*p - '0');
++outCount;
}
#if SERIAL
Serial.print("Send to ");
Serial.print(outDest, DEC);
Serial.print(':');
for (byte i = 0; i < outCount; ++i) {
Serial.print(' ');
Serial.print(outBuf[i], DEC);
}
Serial.println();
#endif
// redirect to home page
buf.emit_p(PSTR(
"HTTP/1.0 302 found\r\n"
"Location: /\r\n"
"\r\n"));
return;
}
// else show a send form
buf.emit_p(PSTR("$F\r\n"
"<h3>Send a wireless data packet</h3>"
"<form>"
"<p>"
"Data bytes <input type=text name=b size=50> (decimal)<br>"
"Destination node <input type=text name=d size=3> "
"(1..31, or 0 to broadcast)<br>"
"</p>"
"<input type=submit value=Send>"
"</form>"), okHeader);
}
static void collectTypeLen (word type, word len) {
len += 4;
collBuf[collPos++] = type >> 8;
collBuf[collPos++] = (byte) type;
collBuf[collPos++] = len >> 8;
collBuf[collPos++] = (byte) len;
}
static void collectStr (word type, const char* data) {
word len = strlen(data) + 1;
collectTypeLen(type, len);
strcpy((char*) collBuf + collPos, data);
collPos += len;
}
static void collectPayload (word type) {
// Copy the received RF12 data into a as many values as needed.
byte num = rf12_len / 8 + 1; // this many values will be needed
collectTypeLen(type, 2 + 9 * num);
collBuf[collPos++] = 0;
collBuf[collPos++] = num;
for (byte i = 0; i < num; ++i)
collBuf[collPos++] = 0; // counter
for (char i = 0; i < 8 * num; ++i) // include -1, i.e. the length byte
collBuf[collPos++] = i <= rf12_len ? rf12_data[i-1] : 0;
}
static void forwardToUDP () {
static byte destIp[] = { 239,192,74,66 }; // UDP multicast address
char buf[10];
collPos = 0;
collectStr(0x0000, "JeeUdp");
collectStr(0x0002, "RF12");
word mhz = config.band == 4 ? 433 : config.band == 8 ? 868 : 915;
sprintf(buf, "%d.%d", mhz, config.group);
collectStr(0x0003, buf);
collectStr(0x0004, "OK");
sprintf(buf, "%d", rf12_hdr);
collectStr(0x0005, buf);
collectPayload(0x0006);
ether.sendUdp ((char*) collBuf, collPos, 23456, destIp, config.port);
#if SERIAL
Serial.println("UDP sent");
#endif
}
void loop (){
word len = ether.packetReceive();
word pos = ether.packetLoop(len);
// check if valid tcp data is received
if (pos) {
bfill = ether.tcpOffset();
char* data = (char *) Ethernet::buffer + pos;
#if SERIAL
Serial.println(data);
#endif
// receive buf hasn't been clobbered by reply yet
if (strncmp("GET / ", data, 6) == 0)
homePage(bfill);
else if (strncmp("GET /c", data, 6) == 0)
configPage(data, bfill);
else if (strncmp("GET /s", data, 6) == 0)
sendPage(data, bfill);
else
bfill.emit_p(PSTR(
"HTTP/1.0 401 Unauthorized\r\n"
"Content-Type: text/html\r\n"
"\r\n"
"<h1>401 Unauthorized</h1>"));
ether.httpServerReply(bfill.position()); // send web page data
}
// RFM12 loop runner, don't report acks
if (rf12_recvDone() && rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0) {
history_rcvd[next_msg][0] = rf12_hdr;
for (byte i = 0; i < rf12_len; ++i)
if (i < MESSAGE_TRUNC)
history_rcvd[next_msg][i+1] = rf12_data[i];
history_len[next_msg] = rf12_len < MESSAGE_TRUNC ? rf12_len+1
: MESSAGE_TRUNC+1;
next_msg = (next_msg + 1) % NUM_MESSAGES;
msgs_rcvd = (msgs_rcvd + 1) % 10000;
if (RF12_WANTS_ACK && !config.collect) {
#if SERIAL
Serial.println(" -> ack");
#endif
rf12_sendStart(RF12_ACK_REPLY, 0, 0);
}
forwardToUDP();
}
// send a data packet out if requested
if (outCount >= 0 && rf12_canSend()) {
rf12_sendStart(outDest, outBuf, outCount, 1);
outCount = -1;
}
}

View file

@ -0,0 +1,144 @@
#include <EtherCard.h>// |Mac adress|
const char SSDP_RESPONSE[] PROGMEM = "HTTP/1.1 200 OK\r\nCACHE-CONTROL: max-age=1200\r\nEXT:\r\nSERVER:Arduino\r\nST: upnp:rootdevice\r\nUSN: uuid:abcdefgh-7dec-11d0-a765-7499692d3040\r\nLOCATION: http://"; //dont forget our mac adress USN: uuid:abcdefgh-7dec-11d0-a765-Mac addr
const char SSDP_RESPONSE_XML[] PROGMEM = "/??\r\n\r\n"; // here is the adress of xml file /?? in this exemple but you could use another /upnp.xml\r\n\r\n
const char XML_DESCRIP[] PROGMEM = "HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\n\r\n<?xml version='1.0'?>\r<root xmlns='urn:schemas-upnp-org:device-1-0'><device><deviceType>urn:schemas-upnp-org:device:BinaryLight:1</deviceType><presentationURL>/</presentationURL><friendlyName>Arduino</friendlyName><manufacturer>Fredycpu</manufacturer><manufacturerURL>http://fredycpu.pro</manufacturerURL><serialNumber>1</serialNumber><UDN>uuid:abcdefgh-7dec-11d0-a765-7499692d3040</UDN></device></root> ";
const char SSDP_NOTIFY[] PROGMEM = "NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1200\r\nNT: upnp:rootdevice\r\nUSN: uuid:abcdefga-7dec-11d0-a765-7499692d3040::upnp:rootdevice\r\nNTS: ssdp:alive\r\nSERVER: Arduino UPnP/1.0\r\nLOCATION: http://"; //dont forget our mac adress USN: uuid:abcdefgh-7dec-11d0-a765-Mac addr
// in XML_DESCRIP // <deviceType>urn:schemas-upnp-org:device:BinaryLight:1</deviceType> // declare as home automation
// in XML_DESCRIP // <friendlyName>Arduino</friendlyName> // declare the name of the service here Arduino
// in XML_DESCRIP // <presentationURL>/</presentationURL> // adress of the page who would opened on service double click ,you could use http://ip but if you use dhcp it's better so and dont wase memory
// this is the entire protocol, but you can try to use SSDP_NOTIFY as SSDP_RESPONSE with most systems will work and you can free a bit of flash mem.
static byte myip[] = {
192,168,0,67 };
static byte gwip[] = {
192,168,0,250 };
static byte ssdp[] = {
239,255,255,250 };
static byte mymac[] = {
0x74,0x99,0x69,0x2D,0x30,0x40 }; // if you change it you must update SSDP_RESPONSE and XML_DESCRIP
byte Ethernet::buffer[750]; // tcp ip send and receive buffer
unsigned long timer=9999;
const char pageA[] PROGMEM =
"HTTP/1.0 200 OK\r\n"
"Content-Type: text/html\r\n"
"\r\n"
"<html>"
"<head><title>"
"multipackets Test"
"</title></head>"
"<body>"
"<a href='/'>Start here</a><br>"
"<a href='/test.jpg'>Image test</a><br>"
"<h3>packet 1</h3>"
"<p><em>"
"the first packet send "
"</em></p>"
;
const char pageB[] PROGMEM =
"<h3>packet 2</h3>"
"<p><em>"
"if you read this it mean it works"
"</em></p>"
;
const char pageC[] PROGMEM =
"<h3>packet 3</h3>"
"<p><em>"
"if you read this it mean it works"
"</em></p>"
;
const char pageD[] PROGMEM =
"<h3>packet 4</h3>"
"<p><em>"
"if you read this it mean it works"
"</em></p>"
;
void setup(){
ether.begin(sizeof Ethernet::buffer, mymac , 10);// 53 for the mega ethernet shield and 10 for normal ethernet shield
ether.staticSetup(myip, gwip);
ENC28J60::disableMulticast(); //disable multicast filter means enable multicast reception
Serial.begin(115200);
}
void loop(){
wait:
word pos = ether.packetLoop(ether.packetReceive());
// check if valid tcp data is received
if (pos) {
char* data = (char *) Ethernet::buffer + pos;
if (strncmp("GET / ", data, 6) == 0) {
ether.httpServerReplyAck(); // send ack to the request
memcpy_P(ether.tcpOffset(), pageA, sizeof pageA); // send first packet and not send the terminate flag
ether.httpServerReply_with_flags(sizeof pageA - 1,TCP_FLAGS_ACK_V);
memcpy_P(ether.tcpOffset(), pageB, sizeof pageB); // send second packet and not send the terminate flag
ether.httpServerReply_with_flags(sizeof pageB - 1,TCP_FLAGS_ACK_V);
memcpy_P(ether.tcpOffset(), pageC, sizeof pageC); // send thirdt packet and not send the terminate flag
ether.httpServerReply_with_flags(sizeof pageC - 1,TCP_FLAGS_ACK_V);
memcpy_P(ether.tcpOffset(), pageD, sizeof pageD); // send fourth packet and send the terminate flag
ether.httpServerReply_with_flags(sizeof pageD - 1,TCP_FLAGS_ACK_V|TCP_FLAGS_FIN_V);
goto wait;
}
if (strncmp("GET /??", data, 7) == 0) { // description of services (normaly an xml file but here .....)
ether.httpServerReplyAck();
memcpy_P(Ethernet::buffer + TCP_OPTIONS_P,XML_DESCRIP, sizeof XML_DESCRIP);
ether.httpServerReply_with_flags(sizeof XML_DESCRIP - 1 ,TCP_FLAGS_ACK_V|TCP_FLAGS_FIN_V);
goto wait;
}
if (strncmp("M-SEARCH", data, 8) == 0) { // service discovery request comes here (udp protocol)
ssdpresp();
goto wait;
}
}
if (((millis()-timer)>50000)||(timer>millis())) {
timer=millis();
ssdpnotify();
}
}
void ssdpresp() { //response to m-search
byte ip_dst[4];
unsigned int port_dst=Ethernet::buffer[34]*256+Ethernet::buffer[35];//extract source port of request
for( int i=0; i<4;i++) { //extract source IP of request
ip_dst[i]=Ethernet::buffer[i+26];
}
int udppos = UDP_DATA_P;
EtherCard::udpPrepare(1900,ip_dst,port_dst);
memcpy_P(Ethernet::buffer + udppos, SSDP_RESPONSE, sizeof SSDP_RESPONSE);
udppos = udppos + sizeof SSDP_RESPONSE-1;
addip(udppos);
}
void ssdpnotify() { //Notification
int udppos = UDP_DATA_P;
EtherCard::udpPrepare(1900,ssdp,1900);
memcpy_P(Ethernet::buffer + udppos, SSDP_NOTIFY, sizeof SSDP_NOTIFY);
udppos = udppos + sizeof SSDP_NOTIFY-1;
addip(udppos);
}
void addip(int udppos) { // add current ip to the request and send it
int adr;
for(int i=0;i<4;i++) { // extract the current ip of arduino
adr = ether.myip[i]/100;
if (adr) {
Ethernet::buffer[udppos]=adr+48;
udppos++;
}
adr=(ether.myip[i]%100)/10;
if (adr) {
Ethernet::buffer[udppos]=adr+48;
udppos++;
}
adr=ether.myip[i]%10;
Ethernet::buffer[udppos]=adr+48;
udppos++;
Ethernet::buffer[udppos]=46;
udppos++; //"."
}
udppos--;//erase the last point
memcpy_P(Ethernet::buffer + udppos,SSDP_RESPONSE_XML,sizeof SSDP_RESPONSE_XML);
udppos = udppos + sizeof SSDP_RESPONSE_XML;
udppos--;
EtherCard::udpTransmit(udppos-UDP_DATA_P); // send all to the computer who make the request on her ip and port who make the request
}

View file

@ -0,0 +1,63 @@
// Present a "Will be back soon web page", as stand-in webserver.
// 2011-01-30 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
#include <EtherCard.h>
#define STATIC 0 // set to 1 to disable DHCP (adjust myip/gwip values below)
#if STATIC
// ethernet interface ip address
static byte myip[] = { 192,168,1,200 };
// gateway ip address
static byte gwip[] = { 192,168,1,1 };
#endif
// ethernet mac address - must be unique on your network
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
byte Ethernet::buffer[500]; // tcp/ip send and receive buffer
const char page[] PROGMEM =
"HTTP/1.0 503 Service Unavailable\r\n"
"Content-Type: text/html\r\n"
"Retry-After: 600\r\n"
"\r\n"
"<html>"
"<head><title>"
"Service Temporarily Unavailable"
"</title></head>"
"<body>"
"<h3>This service is currently unavailable</h3>"
"<p><em>"
"The main server is currently off-line.<br />"
"Please try again later."
"</em></p>"
"</body>"
"</html>"
;
void setup(){
Serial.begin(57600);
Serial.println("\n[backSoon]");
if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
Serial.println( "Failed to access Ethernet controller");
#if STATIC
ether.staticSetup(myip, gwip);
#else
if (!ether.dhcpSetup())
Serial.println("DHCP failed");
#endif
ether.printIp("IP: ", ether.myip);
ether.printIp("GW: ", ether.gwip);
ether.printIp("DNS: ", ether.dnsip);
}
void loop(){
// wait for an incoming TCP packet, but ignore its contents
if (ether.packetLoop(ether.packetReceive())) {
memcpy_P(ether.tcpOffset(), page, sizeof page);
ether.httpServerReply(sizeof page - 1);
}
}

View file

@ -0,0 +1,276 @@
// Arduino demo sketch for testing RFM12B + ethernet
// 2010-05-20 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
// Listens for RF12 messages and displays valid messages on a webpage
// Memory usage exceeds 1K, so use Atmega328 or decrease history/buffers
//
// This sketch is derived from RF12eth.pde:
// May 2010, Andras Tucsni, http://opensource.org/licenses/mit-license.php
#include <EtherCard.h>
#include <JeeLib.h>
#include <avr/eeprom.h>
#define DEBUG 1 // set to 1 to display free RAM on web page
#define SERIAL 0 // set to 1 to show incoming requests on serial port
#define CONFIG_EEPROM_ADDR ((byte*) 0x10)
// configuration, as stored in EEPROM
struct Config {
byte band;
byte group;
byte collect;
word refresh;
byte valid; // keep this as last byte
} config;
// ethernet interface mac address - must be unique on your network
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
// buffer for an outgoing data packet
static byte outBuf[RF12_MAXDATA], outDest;
static char outCount = -1;
#define NUM_MESSAGES 10 // Number of messages saved in history
#define MESSAGE_TRUNC 15 // Truncate message payload to reduce memory use
static BufferFiller bfill; // used as cursor while filling the buffer
static byte history_rcvd[NUM_MESSAGES][MESSAGE_TRUNC+1]; //history record
static byte history_len[NUM_MESSAGES]; // # of RF12 messages+header in history
static byte next_msg; // pointer to next rf12rcvd line
static word msgs_rcvd; // total number of lines received modulo 10,000
byte Ethernet::buffer[1000]; // tcp/ip send and receive buffer
static void loadConfig() {
for (byte i = 0; i < sizeof config; ++i)
((byte*) &config)[i] = eeprom_read_byte(CONFIG_EEPROM_ADDR + i);
if (config.valid != 253) {
config.valid = 253;
config.band = 8;
config.group = 1;
config.collect = 1;
config.refresh = 5;
}
byte freq = config.band == 4 ? RF12_433MHZ :
config.band == 8 ? RF12_868MHZ :
RF12_915MHZ;
rf12_initialize(31, freq, config.group);
}
static void saveConfig() {
for (byte i = 0; i < sizeof config; ++i)
eeprom_write_byte(CONFIG_EEPROM_ADDR + i, ((byte*) &config)[i]);
}
#if DEBUG
static int freeRam () {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
#endif
void setup(){
#if SERIAL
Serial.begin(57600);
Serial.println("\n[etherNode]");
#endif
loadConfig();
if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
Serial.println( "Failed to access Ethernet controller");
if (!ether.dhcpSetup())
Serial.println("DHCP failed");
#if SERIAL
ether.printIp("IP: ", ether.myip);
#endif
}
const char okHeader[] PROGMEM =
"HTTP/1.0 200 OK\r\n"
"Content-Type: text/html\r\n"
"Pragma: no-cache\r\n"
;
static void homePage(BufferFiller& buf) {
word mhz = config.band == 4 ? 433 : config.band == 8 ? 868 : 915;
buf.emit_p(PSTR("$F\r\n"
"<meta http-equiv='refresh' content='$D'/>"
"<title>RF12 etherNode - $D MHz, group $D</title>"
"RF12 etherNode - $D MHz, group $D "
"- <a href='c'>configure</a> - <a href='s'>send packet</a>"
"<h3>Last $D messages:</h3>"
"<pre>"), okHeader, config.refresh, mhz, config.group,
mhz, config.group, NUM_MESSAGES);
for (byte i = 0; i < NUM_MESSAGES; ++i) {
byte j = (next_msg + i) % NUM_MESSAGES;
if (history_len[j] > 0) {
word n = msgs_rcvd - NUM_MESSAGES + i;
buf.emit_p(PSTR("\n$D$D$D$D: OK"), // hack, to show leading zero's
n/1000, (n/100) % 10, (n/10) % 10, n % 10);
for (byte k = 0; k < history_len[j]; ++k)
buf.emit_p(PSTR(" $D"), history_rcvd[j][k]);
}
}
long t = millis() / 1000;
word h = t / 3600;
byte m = (t / 60) % 60;
byte s = t % 60;
buf.emit_p(PSTR(
"</pre>"
"Uptime is $D$D:$D$D:$D$D"), h/10, h%10, m/10, m%10, s/10, s%10);
#if DEBUG
buf.emit_p(PSTR(" ($D bytes free)"), freeRam());
#endif
}
static int getIntArg(const char* data, const char* key, int value =-1) {
char temp[10];
if (ether.findKeyVal(data + 7, temp, sizeof temp, key) > 0)
value = atoi(temp);
return value;
}
static void configPage(const char* data, BufferFiller& buf) {
// pick up submitted data, if present
if (data[6] == '?') {
byte b = getIntArg(data, "b");
byte g = getIntArg(data, "g");
byte c = getIntArg(data, "c", 0);
word r = getIntArg(data, "r");
if (1 <= g && g <= 250 && 1 <= r && r <= 3600) {
// store values as new settings
config.band = b;
config.group = g;
config.collect = c;
config.refresh = r;
saveConfig();
// re-init RF12 driver
loadConfig();
// clear history
memset(history_len, 0, sizeof history_len);
// redirect to the home page
buf.emit_p(PSTR(
"HTTP/1.0 302 found\r\n"
"Location: /\r\n"
"\r\n"));
return;
}
}
// else show a configuration form
buf.emit_p(PSTR("$F\r\n"
"<h3>Server node configuration</h3>"
"<form>"
"<p>"
"Freq band <input type=text name=b value='$D' size=1> (4, 8, or 9)<br>"
"Net group <input type=text name=g value='$D' size=3> (1..250)<br>"
"Collect mode: <input type=checkbox name=c value='1' $S> "
"Don't send ACKs<br><br>"
"Refresh rate <input type=text name=r value='$D' size=4> (1..3600 seconds)"
"</p>"
"<input type=submit value=Set>"
"</form>"), okHeader, config.band, config.group,
config.collect ? "CHECKED" : "",
config.refresh);
}
static void sendPage(const char* data, BufferFiller& buf) {
// pick up submitted data, if present
const char* p = strstr(data, "b=");
byte d = getIntArg(data, "d");
if (data[6] == '?' && p != 0 && 0 <= d && d <= 31) {
// prepare to send data as soon as possible in loop()
outDest = d & RF12_HDR_MASK ? RF12_HDR_DST | d : 0;
outCount = 0;
// convert the input string to a number of decimal data bytes in outBuf
++p;
while (*p != 0 && *p != '&') {
outBuf[outCount] = 0;
while ('0' <= *++p && *p <= '9')
outBuf[outCount] = 10 * outBuf[outCount] + (*p - '0');
++outCount;
}
#if SERIAL
Serial.print("Send to ");
Serial.print(outDest, DEC);
Serial.print(':');
for (byte i = 0; i < outCount; ++i) {
Serial.print(' ');
Serial.print(outBuf[i], DEC);
}
Serial.println();
#endif
// redirect to home page
buf.emit_p(PSTR(
"HTTP/1.0 302 found\r\n"
"Location: /\r\n"
"\r\n"));
return;
}
// else show a send form
buf.emit_p(PSTR("$F\r\n"
"<h3>Send a wireless data packet</h3>"
"<form>"
"<p>"
"Data bytes <input type=text name=b size=50> (decimal)<br>"
"Destination node <input type=text name=d size=3> "
"(1..31, or 0 to broadcast)<br>"
"</p>"
"<input type=submit value=Send>"
"</form>"), okHeader);
}
void loop(){
word len = ether.packetReceive();
word pos = ether.packetLoop(len);
// check if valid tcp data is received
if (pos) {
bfill = ether.tcpOffset();
char* data = (char *) Ethernet::buffer + pos;
#if SERIAL
Serial.println(data);
#endif
// receive buf hasn't been clobbered by reply yet
if (strncmp("GET / ", data, 6) == 0)
homePage(bfill);
else if (strncmp("GET /c", data, 6) == 0)
configPage(data, bfill);
else if (strncmp("GET /s", data, 6) == 0)
sendPage(data, bfill);
else
bfill.emit_p(PSTR(
"HTTP/1.0 401 Unauthorized\r\n"
"Content-Type: text/html\r\n"
"\r\n"
"<h1>401 Unauthorized</h1>"));
ether.httpServerReply(bfill.position()); // send web page data
}
// RFM12 loop runner, don't report acks
if (rf12_recvDone() && rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0) {
history_rcvd[next_msg][0] = rf12_hdr;
for (byte i = 0; i < rf12_len; ++i)
if (i < MESSAGE_TRUNC)
history_rcvd[next_msg][i+1] = rf12_data[i];
history_len[next_msg] = rf12_len < MESSAGE_TRUNC ? rf12_len+1
: MESSAGE_TRUNC+1;
next_msg = (next_msg + 1) % NUM_MESSAGES;
msgs_rcvd = (msgs_rcvd + 1) % 10000;
if (RF12_WANTS_ACK && !config.collect) {
#if SERIAL
Serial.println(" -> ack");
#endif
rf12_sendStart(RF12_ACK_REPLY, 0, 0);
}
}
// send a data packet out if requested
if (outCount >= 0 && rf12_canSend()) {
rf12_sendStart(outDest, outBuf, outCount, 1);
outCount = -1;
}
}

Some files were not shown because too many files have changed in this diff Show more