Created
August 11, 2012 04:26
-
-
Save tmpvar/3320889 to your computer and use it in GitHub Desktop.
quick and dirty .hex -> avr programmer using nodejs and node-serialport
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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