Give this code a try... I used emulation code to emulate the i2c / sensor and embedded it all into an html. You can run it right off the link of the uploaded/download file. If opened in Chrome and inspected, you should get about the the same view as attached when clicking the buttons init(), logStatus(), and readTemp().
The AtlasSensor code is based on your initial code that extracts the commands into command specifications. I used longer variable names than usual for documentation purposes and added also some comments.
Methods starting with underscore (_) hint privacy and are not to be called 'from outside'.
var p; // prototype var
var STATE =
{ SUCCESS: 1
, FAILURE: 2
, INVALID_CMD: 201 // other than what already taken
, PENDING: 254 // command in execution / pending request
, NO_DATA: 255 // no command sent / no pending request
};
var tempDescr =
// temp sensor descriptor, most used first
// pack everything in an anonymous function
// to allow reusable functions without
// polluting global name
(function() {
// reusable functions to be used in command descriptor / cmds
// - none yet so far
// setup commandSpecifications for tempDescriptor
// one by one to make one by one executable
var cmdSpecs = [];
// [0] in cmdSpec is command string
// [1] properties the cmd result is setting
// [2] function to interpret/handle read raw data
// [3] time after cmd / before result (rawData) can be read
cmdSpecs[0] =
[ "R" , ["temp", "tempTs"]
, function(rawData) {
this.temp = rawData * 1;
this.tempTs = getTime();
}, 300
]
;
cmdSpecs[1] =
[ "STATUS", ["restartCode", "volts", "statusTs"]
, function(rawData, cmdDescr) {
var dates = rawData.split(",");
this.restartCode = dates[1];
this.volts = dates[2] * 1;
this.statusTs = getTime();
}, 300
]
;
// return descriptor
return (
{ id: "AtlasTempSensor"
, cmdSpecs: cmdSpecs
}
); // /return expression
})();
var AtlasSensor = function(id, i2c, addr, descr) {
this.cmdEvtQueue = [];
currCmdEvt = null;
lastCmdStr = "";
this.id = id;
this.i2c = i2c;
this.addr = addr;
this.descr = descr;
lastCmdTs = getTime();
lastCmdState = -1;
lastRawData = "";
descr.cmdSpecs.forEach(function(cmdSpec){ // for each command...
cmdSpec[1].forEach(function(prop){ // ...setup obj with...
this.prop = null; // ...sensor and cmd specific...
}, this); // ...properties
}, this);
};
AtlasSensor.prototype.cmd = function(cmd, argOrArgsOrCallback, callback) {
this.cmdEvtQueue.push([cmd, argOrArgsOrCallback, callback]);
if (!this.currCmdEvt) {
this.currCmdEvt = this.cmdEvtQueue.splice(0,1)[0];
this._cmd.apply(this, this.currCmdEvt);
}
};
AtlasSensor.prototype.getLastReading = function(cmd) {
var cmdSpec = this._getCmdSpec(cmd);
return ((cmdSpec)
? this._getData(cmdSpec, STATE.SUCCESS)
: { state: STATE.INVALID_CMD }
);
};
AtlasSensor.prototype._cmd = function(cmd, argOrArgsOrCallback, callback) {
var cmdSpec = this._getCmdSpec(cmd), arg, cb, args = [], cmdStr;
if ("function" === typeof (arg = argOrArgsOrCallback)) {
cb = arg;
} else if ("array" === typeof arg) {
args = arg;
} else if ((arg != undefined) && (arg !== null)) {
args = [arg];
};
cb = (cb) ? cb : callback;
if (cmdSpec) {
cmdStr = cmdSpec[0];
args.forEach(function(arg){ cmdStr += "," + arg; });
this.i2c.writeTo(this.addr, this.lastCmdStr = cmdStr);
setTimeout(
this._read.bind(this) // deferred function
, cmdSpec[3] // defer time
, cmdSpec, cb // params
);
} else { // invalid command
this.lastCmdTs = getTime();
this.lastCmdState = STATE.INVALID_CMD;
this.lastRawData = null;
if (cb) {
setTimeout(
cb
, 1
, { state: this.lastCmdState }
);
}
this._nextCmd();
}
};
AtlasSensor.prototype._read = function(cmdSpec, callback) {
var reading = this._readRaw();
if (reading.state === STATE.SUCCESS) {
cmdSpec[2].bind(this)(reading.data);
reading = this._getData(cmdSpec, STATE.SUCCESS);
}
this._nextCmd();
if (callback) { callback(reading); }
};
AtlasSensor.prototype._nullProps = function(cmdSpec) {
cmdSpec[1].forEach(function(prop){ this[prop] = null; }, this);
};
AtlasSensor.prototype._getData = function(cmdSpec, state) {
var data = { state: state };
if (cmdSpec) {
cmdSpec[1].forEach(function(prop){
data[prop] = this[prop];
}, this);
}
return data;
};
AtlasSensor.prototype._getCmdSpec = function(cmd) { // get cmdSpec by cmd
var cmdSpec, cmdSpecs = this.descr.cmdSpecs,
idx = 0, idxMax = cmdSpecs.length - 1;
while (idx <= idxMax) {
if ((cmdSpec = cmdSpecs[idx])[0] === cmd) { return cmdSpec; }
idx++;
}
return null;
};
AtlasSensor.prototype._nextCmd = function() {
if (this.cmdEvtQueue.length > 0) {
this.currCmdEvt = this.cmdEvtQueue.splice(0,1)[0];
this._cmd.apply(this, this.currCmdEvt);
} else {
this.currCmdEvt = null;
}
}
AtlasSensor.prototype._readRaw = function() {
var state = -1; // init read status
var data = null; // init read data
var byte; // read buffer
byte = this.i2c.readFrom(this.addr,1)[0]; // read first byte (state)
if ((state = byte) === STATE.SUCCESS) { // success of command
data = "";
byte = this.i2c.readFrom(this.addr,1)[0];
while (byte !== 0) { // data, not null / ending yet
data += String.fromCharCode(byte);
byte = this.i2c.readFrom(this.addr,1)[0];
}
} else { // anything else then success
this.i2c.readFrom(this.addr,1); // consume ending null
// just let the state pass through
}
this.lastCmdState = state;
this.lastRawData = data;
return { state: state, data: data };
};
// *** Test Code
var tempSensor;
function init() {
tempSensor = new AtlasSensor("temp1", tempI2CEmulator, 1, tempDescr);
}
function logStatus() {
tempSensor.cmd("STATUS",function(reading){
console.log(reading);
});
}
function readTemp() {
tempSensor.cmd("R",function(reading){
console.log(reading);
});
}
Executed as part of the test code in Espruino html emulation, the delay of 0.300 seconds in the response is clearly noticeable.
The emulation code (embedded into html to run in browser):
Now it is your turn to add the calibration command implementation...
You may have noticed that all requests go into a queue to make sure the writes and reads are nicely paired. This queuing allows to place multiple requests in a row without having to worry about timings.
For cascading and combination with other sensors that need values from other sensors, callback implementation gets cumbersome; a promise implementation would look much cleaner.
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
Give this code a try... I used emulation code to emulate the i2c / sensor and embedded it all into an html. You can run it right off the link of the uploaded/download file. If opened in Chrome and inspected, you should get about the the same view as attached when clicking the buttons init(), logStatus(), and readTemp().
The AtlasSensor code is based on your initial code that extracts the commands into command specifications. I used longer variable names than usual for documentation purposes and added also some comments.
Methods starting with underscore (_) hint privacy and are not to be called 'from outside'.
Executed as part of the test code in Espruino html emulation, the delay of 0.300 seconds in the response is clearly noticeable.
The emulation code (embedded into html to run in browser):
Now it is your turn to add the calibration command implementation...
You may have noticed that all requests go into a queue to make sure the writes and reads are nicely paired. This queuing allows to place multiple requests in a row without having to worry about timings.
For cascading and combination with other sensors that need values from other sensors, callback implementation gets cumbersome; a promise implementation would look much cleaner.
2 Attachments