Skip to content

Commit

Permalink
Support Led.Digits with HT16K33 controller
Browse files Browse the repository at this point in the history
- Moves print() and digits() to parent controller for reuse.
- Adds separate sendDigit() function for controller-specific concerns.
- Adds ":" support for ##:## displays.
- Column order is *not* reversed on HT16K33 controller. Add per-controller override.
- Segment order is reversed, so bits in led-chars.js are reordered for HT16K33.
- Adds tests for HT16K33 with segmented displays.
  • Loading branch information
donmccurdy committed Sep 22, 2015
1 parent 0d22d62 commit c8efd30
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 93 deletions.
2 changes: 2 additions & 0 deletions lib/led/led-chars.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ module.exports = {
"8": 0x7F,
"9": 0x7B,
" ": 0x00,
".": 0x80,
":": 0xFF,
"!": 0xB0,
"A": 0x77,
"a": 0x7D,
Expand Down
214 changes: 121 additions & 93 deletions lib/led/ledcontrol.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,19 @@ function LedControl(opts) {
// functions from Controller interface

this.clear = controller.clear;
this.digit = controller.digit;
this.led = controller.led;
this.print = controller.print;
this.row = controller.row;
this.scanLimit = controller.scanLimit;
this.send = controller.send;
this.sendDigit = controller.sendDigit;
this.initialize = controller.initialize;

// controller specific op codes
this.OP = controller.OP;

// digit indexes may be ordered left to right (1) or reversed (-1)
this.digitOrder = 1;

// extra functions for HT16K33 devices only
if (controller.writeDisplay) {
this.writeDisplay = controller.writeDisplay;
Expand Down Expand Up @@ -189,6 +191,92 @@ LedControl.prototype.setLed = function(addr, chr, val, dp) {
return this.led(addr, chr, val, dp);
};

/**
* digit Display a digit and optional decimal point.
* @param {Number} addr Device address
* @param {Number} position 0-7
* @param {String} val 0-9[.]
* @return {LedControl}
*/
LedControl.prototype.digit = function(addr, position, chr) {
var args, offset, index, character, value;
var hasDecimal = false;

if (arguments.length < 3) {
args = Array.from(arguments);
this.each(function(device) {
this.digit.apply(this, (args.unshift(device), args));
});
return this;
}

if (this.isMatrix) {
// Not sure this is the best path, will check when segment
// devices are available.
this.draw.apply(this, arguments);
return this;
}

offset = addr * this.digits;

character = String(chr);
position = Number(position);

// If controller's indexes are ordered right to left, flip
// the index around.
index = position;
if (this.digitOrder === -1) {
index = this.digits - index - 1;
}

if (character.length === 2 && character[1] === ".") {
hasDecimal = true;
character = character[0];
}

value = LedControl.DIGIT_CHARS[character];

if (!value) {
value = Math.abs(Number(character));
}

if (hasDecimal) {
value = value | LedControl.DIGIT_CHARS["."];
}

this.status[offset + index] = value;
this.sendDigit(addr, index, value);
return this;
};

/**
* print Print series of characters to the display.
* @param {String} message One or more characters to be displayed.
* @param {Object} opts (Optional) Options specifying:
* - device: {Number} Device address
* @return {LedControl}
*/
LedControl.prototype.print = function(message, opts) {
var rdigchars = /([0-9A-Z][.]|[0-9A-Z:]|[\s])/g;
var characters;

opts = opts || {
device: 0
};

if (this.isMatrix) {
// figure out what to do with Matrix displays
throw new Error("Led.Matrix does not yet support the print method");
}

characters = message.match(rdigchars);

(characters || []).forEach(function(character, position) {
this.digit(opts.device, position, character);
}, this);
return this;
};

/*
* brightness
* @param {Number} addr Address of Led device
Expand Down Expand Up @@ -413,10 +501,6 @@ Controllers = {
}
return this;
},
digit: function() {
console.log("The digit function is not implemented for HT16K33 devices");
return this;
},
/**
* led or setLed Set the status of a single Led.
*
Expand Down Expand Up @@ -501,10 +585,6 @@ Controllers = {
}
return this;
},
print: function() {
console.log("The print function is not implemented for HT16K33 devices");
return this;
},
writeDisplay: function(addr) {
var bytes = [0x00];
// always writes 8 rows (for 8x16, the values have already been rotated)
Expand Down Expand Up @@ -564,7 +644,26 @@ Controllers = {
}
this.io.i2cWrite(this.addresses[addr], [opcode | data]);
return this;
}
},

/**
* sendDigit
* @param {Number} addr Device address.
* @param {Number} index 0-7
* @param {Number} code 8-bit value 0-255
* @return {LedControl}
*/
sendDigit: function(addr, index, code) {
// Convert from hex to binary, padded to 8 bits.
code = ("00000000" + code.toString(2)).slice(-8).split("");
// Reverse bits for each display segment except the decimal,
// to match the HT16K33 controller's segment ordering.
code = code.shift() + code.reverse().join("");
// Convert to decimal and write to the display.
this.displaybuffers[addr][index] = parseInt(code, 2);
this.writeDisplay(addr);
return this;
},
},

DEFAULT: {
Expand All @@ -583,6 +682,9 @@ Controllers = {
// the basis for the `setup` constructor option
// var setup = Object.assign({}, LedControl.DEFAULTS, opts.setup || {});
// var keys = Object.keys(setup);

// digit indexes ordered right to left.
this.digitOrder = -1;

for (var device = 0; device < this.devices; device++) {
/*
Expand Down Expand Up @@ -663,67 +765,19 @@ Controllers = {
}
return this;
},

/**
* digit Display a digit
* @param {Number} addr Device address
* @param {Number} position 0-7
* @param {Number} val 0-9
* @param {Boolean} dp Show Decimal Point?
* This is a truly awful design t
* be p
*
*
* sendDigit
* @param {Number} addr Device address.
* @param {Number} index 0-7
* @param {Number} code 8-bit value 0-255
* @return {LedControl}
*/
digit: function(addr, position, chr) {
var args, offset, index, character, value;
var hasDecimal = false;

if (arguments.length < 3) {
args = Array.from(arguments);
this.each(function(device) {
this.digit.apply(this, (args.unshift(device), args));
});
} else {
if (this.isMatrix) {
// Not sure this is the best path, will check when segment
// devices are available.
//
this.draw.apply(this, arguments);
} else {

offset = addr * 8;

character = String(chr);
position = Number(position);

// Flip this around, because no one will
// ever intuitively think that positions
// start on the right and end on the left.
index = 7 - position;

if (character.length === 2 && character[1] === ".") {
hasDecimal = true;
character = character[0];
}

value = LedControl.DIGIT_CHARS[character];

if (!value) {
value = Math.abs(Number(character));
}

if (hasDecimal) {
value = value | 0x80;
}

this.status[offset + index] = value;
this.send(addr, index + 1, value);
}
}

sendDigit: function(addr, index, code) {
this.send(addr, index + 1, code);
return this;
},

/**
* led or setLed Set the status of a single Led.
*
Expand Down Expand Up @@ -759,32 +813,6 @@ Controllers = {
return this;
},

print: function(message, opts) {
var rdigchars = /([0-9A-Z][.]|[0-9A-Z]|[\s])/g;
var characters;

opts = opts || {
device: 0
};

if (this.isMatrix) {

throw new Error("Led.Matrix does not yet support the print method");
// figure out what to do with Matrix displays

// this.each(function(device) {
// this.draw(device, message[device]);
// });

} else {
characters = message.match(rdigchars);

(characters || []).forEach(function(character, position) {
this.digit(opts.device, position, character);
}, this);
}
},

/**
* row Update an entire row with an 8 bit value
* @param {Number} addr Device address
Expand Down
86 changes: 86 additions & 0 deletions test/ledcontrol.js
Original file line number Diff line number Diff line change
Expand Up @@ -1323,6 +1323,19 @@ exports["LedControl - Digits"] = {

test.done();
},
print: function(test) {
test.expect(1);

this.lc.print("1234");
test.deepEqual(this.send.args, [
[0, 8, 0x30],
[0, 7, 0x6D],
[0, 6, 0x79],
[0, 5, 0x33]
]);

test.done();
},
drawAll: function(test) {
test.expect(1);

Expand All @@ -1344,3 +1357,76 @@ exports["LedControl - Digits"] = {
test.done();
}
};

exports["LedControl - I2C Digits"] = {
setUp: function(done) {
this.board = newBoard();
this.clock = sinon.useFakeTimers();

this.i2cWrite = sinon.spy(MockFirmata.prototype, "i2cWrite");

this.lc = new LedControl({
controller: "HT16K33",
isMatrix: false,
board: this.board
});

this.each = sinon.spy(this.lc, "each");
this.row = sinon.spy(this.lc, "row");
done();
},
tearDown: function(done) {
Board.purge();
restore(this);
LedControl.reset();
done();
},
digit: function(test) {
test.expect(1);

this.i2cWrite.reset();
this.lc.digit(0, 0, 1);
test.deepEqual(this.i2cWrite.args, [
[ 0x70, [ 0, 0x06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]]
]);

test.done();
},
digitDigitWithDecimal: function(test) {
test.expect(1);

this.i2cWrite.reset();
this.lc.digit(0, 0, "1.");
test.deepEqual(this.i2cWrite.args, [
[ 0x70, [ 0, 0x86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]]
]);

test.done();
},
digitAlpha: function(test) {
test.expect(1);

this.i2cWrite.reset();
this.lc.digit(0, 0, "A");
test.deepEqual(this.i2cWrite.args, [
[ 0x70, [ 0, 0x77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]]
]);

test.done();
},
print: function(test) {
test.expect(1);

this.i2cWrite.reset();
this.lc.print("12:00");
test.deepEqual(this.i2cWrite.args, [
[ 0x70, [ 0, 0x06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]],
[ 0x70, [ 0, 0x06, 0, 0x5B, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]],
[ 0x70, [ 0, 0x06, 0, 0x5B, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]],
[ 0x70, [ 0, 0x06, 0, 0x5B, 0, 0xFF, 0, 0x3F, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]],
[ 0x70, [ 0, 0x06, 0, 0x5B, 0, 0xFF, 0, 0x3F, 0, 0x3F, 0, 0, 0, 0, 0, 0, 0 ]]
]);

test.done();
}
};

0 comments on commit c8efd30

Please sign in to comment.