Skip to content

Instantly share code, notes, and snippets.

@tmpvar
Created August 11, 2012 04:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmpvar/3320889 to your computer and use it in GitHub Desktop.
Save tmpvar/3320889 to your computer and use it in GitHub Desktop.
quick and dirty .hex -> avr programmer using nodejs and node-serialport
var
serialport = require('serialport'),
intelHex2Binary = require('./intelhex'),
fs = require('fs'),
sp = new serialport.SerialPort('/dev/tty.usbmodemfd121'),
argv = require('optimist').argv,
debug = 1,
s = function(d, fn) {
if (fn) {
cmds.push(function() {
sp.once('data', function(data) {
fn(data);
});
return d;
});
} else {
cmds.push(d);
}
if (loc === 0) {
nextCmd();
}
},
dec = function(c) {
return (c + '').charCodeAt(0);
},
cmds = [],
loc = 0,
timer,
inData = false,
nextCmd = function() {
if (cmds[loc]) {
var cmd = (typeof cmds[loc] === 'function') ?
cmds[loc]() :
cmds[loc];
debug && console.log('')
inData = true;
debug && process.stdout.write('Send: ' + cmd);
sp.write(cmd);
loc++;
}
}, hexfile;
if (!argv._[0]) {
console.log('Usage: node index.js <filename.hex>');
process.exit(1);
}
hexfile = argv._[0];
sp.on('open', function() {
console.log('connected');
sp.on('data', function(d) {
inData && debug && process.stdout.write(' -> ');
inData = false;
if (debug) {
for (var i=0; i<d.length; i++) {
var c = d.toString().substring(i, i+1);
if (c.charCodeAt(0) < 32 || c.charCodeAt(0) > 126) {
c = '.';
}
process.stdout.write(c + ' [' + d.readUInt8(i).toString(16) + '] ');
}
}
process.nextTick(nextCmd);
});
setTimeout(function() {
var flashChunkSize = 0;
s('S')
s('V')
s('v')
s('p')
s('a')
s('b', function(d) {
flashChunkSize = d.readUInt8(2);
console.log('flashChunkSize', flashChunkSize);
});
s('t')
s('TD')
s('P')
s('F')
s('F')
s('F')
s('N')
s('N')
s('N')
s('Q')
s('Q')
s('Q')
s([dec('A'), 0x03, 0xfc]);
s([dec('g'), 0x00, 0x01, dec('E')]);
s([dec('A'), 0x03, 0xff])
s([dec('g'), 0x00, 0x01, dec('E')]);
s([dec('A'), 0x03, 0xff]);
s([dec('g'), 0x00, 0x01, dec('E')]);
s([dec('A'), 0x03, 0xff]);
s([dec('g'), 0x00, 0x01, dec('E')]);
s('e') // erase
s([dec('A'), 0x00, 0x00], function() {
var contents = fs.readFileSync(hexfile).toString();
var ee = intelHex2Binary(contents)
var bytes = [], totalBytes = 0;
ee.on('data', function(data) {
totalBytes+=data.bytes.length;
// buffer the bytes so we can push them in the expected size on 'end'
Array.prototype.push.apply(bytes, data.bytes);
});
ee.on('end', function() {
var chunks = [];
for (var i=0; i<bytes.length; i+=flashChunkSize) {
var chunk = bytes.slice(i, i+flashChunkSize);
chunks.push(chunk);
s([dec('B'), 0x00, chunk.length, dec('F')].concat(chunk));
}
// compare flash on device with the chunks we sent
s([dec('A'), 0x00, 0x00], function() {
var
index = 0,
compare = function(d) {
var localChunk = chunks[index];
index++;
// Quick check to make sure the lengths match
if (localChunk.length !== d.length) {
throw new Error(
"Flashed content length differs! local:" + localChunk.length +
'vs device: ' + d.length
);
}
localChunk.forEach(function(val, idx) {
if (val !== d.readUInt8(idx)) {
throw new Error('Firmware on the device does not match local data');
}
});
if (bytes.length/flashChunkSize < index) {
console.log('\n\nLooks good');
fuseCheck();
} else {
process.nextTick(function() {
var readSize = flashChunkSize;
console.log(totalBytes - index*flashChunkSize);
if (totalBytes - index*flashChunkSize < flashChunkSize) {
readSize = totalBytes - index*flashChunkSize;
}
s([dec('g'), 0x00, readSize, dec('F')], compare);
nextCmd();
});
}
},
fuseCheck = function() {
console.log('checking fuses');
// fuse check
s('F');
s('F');
s('F');
s('N');
s('N');
s('N');
s('Q');
s('Q');
s('Q');
s('L');
s('E');
};
console.log('\n\nVerifying flash..')
s([dec('g'), 0x00, flashChunkSize, dec('F')], compare);
});
nextCmd();
});
});
}, 500);
});
var EventEmitter = require('events').EventEmitter;
var parse = module.exports = function(string, fn) {
var
ev = new EventEmitter();
parts = string.split(':');
string = string.replace(/[\r\n]/g,'');
process.nextTick(function() {
parts.forEach(function(line) {
if (!line) {
return;
}
var data = {
size : parseInt(line.substring(0,2), 16),
// TODO: test this
address : parseInt(line.substring(2, 6), 16),
type : parseInt(line.substring(6,8), 16),
bytes : []
};
switch (data.type) {
// Data record
case 0:
for (var i = 0, p = 0; i<data.size*2; i+=2, p++) {
var byte = parseInt(line.substring(i+8, i+10), 16);
data.bytes.push(byte);
}
data.checksum = parseInt(line.substring(8 + data.size*2,8 + data.size*2 + 2), 16);
ev.emit('data', data);
break;
// End of file
case 1:
ev.emit('end');
break;
// Extended Segment Address Record
case 2:
ev.emit('error', new Error('TODO: Extended segment Address record'));
break;
// Start Segment Address Record
case 3:
ev.emit('error', new Error('TODO: Start Segment Address Record'));
break;
// Extended Linear Address Record
case 4:
ev.emit('error', new Error('TODO: Extended Linear Address Record'));
break;
// Start Linear Address Record
case 5:
ev.emit('error', new Error('TODO: Start Linear Address Record'));
break;
// Invalid format
default:
ev.emit('error', new Error('Invalid Intel Hex format'));
break;
}
});
});
return ev;
};
//parse(':1000000093C00000B5C00000B3C00000B1C0000044..:10001000AFC00000ADC00000ABC00000A9C0000030').on('data', console.log);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment