I've been investigating options for getting the standard bootloader below 1k (currently it is 2k), but it is the sort of thing that is hard to get folks excited about.
Currently all arduinos and clones are using 2k of flash that could theoretically be done with one k of flash, freeing up 1k for all users who use it. This requires getting the chip sellers and arduino
IDE folks to come to the table and cooperate, as well as all the work testing and making extra sure the solution works.
I have a working model bootloader that is under 1k (and could be ported to the 328) and associated java based loader utility, so it isn't a theoretical thing, it is very doable, but currently requires the custom java loader as the small bootloader only understands a subset of the stk500 commands.
So I'm trying to decide if it is worth it to keep harping on this to not let 1k of flash on most every single arduino and clone ever made go unchallenged, or just let it die. You tell me (and anyone else who might be listening).
Here's the test subjects, loader runs standalone but pretty simple to integrate into the ide:
/* java based uploader for arduino and any other %100 open source projects,
might also work with stk500 by chance */
//uses rxtx http://users.frii.com/jarvi/rxtx/download.html
//courtesy dcb AT opengauge.org
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
public class ArduinoLoader {
public static final long rxtimeoutms = 5000;
public static final byte[] hello = { 0x30, 0x20 };
public static final byte[] okrsp = { 0x14, 0x10 };
public static int startaddr = 0x00;
static InputStream input2;
static OutputStream output2;
public static int waitForChar(InputStream i) throws Exception {
long t = System.currentTimeMillis();
while (i.available() == 0) {
try {
Thread.sleep(50);
} catch (Exception e) {
}
if (System.currentTimeMillis() - t > rxtimeoutms) {
throw new Exception("Timed out waiting for response");
}
}
return i.read();
}
public static void chkok(InputStream i) throws Exception {
if ((waitForChar(i) != okrsp[0]) || (waitForChar(i) != okrsp[1]))
throw new Exception("Invalid response");
}
static int[] image = new int[200000];
static int imagesize = 0;
// pass hex file, com port ID, baud, page size
public static void main(String[] args) throws Exception {
long g = System.currentTimeMillis();
// args = new String[] { "/Blink.hex", "COM4", "19200", "128" };
if (args.length != 4) {
System.out
.println("Arguments: full_path_to_hex_file com_port_id baud page_size");
System.exit(1);
}
upload(args[0], args[1], Integer.parseInt(args[2]), Integer
.parseInt(args[3]), System.out);
g=System.currentTimeMillis()-g;
System.out.println("Completed, " + imagesize + " bytes uploaded " + g);
}
static void println(String s, OutputStream o) throws Exception {
o.write((s + "\n").getBytes());
}
public static void upload(String filename, String comport, int baud,
int pageSize, OutputStream out) throws Exception {
println("Serial Proxy Starting, port:" + comport + ", baud:" + baud
+ ", pagesize:" + pageSize + ", file:" + filename, out);
// load the hex file into memory
parsehex(filename);
// out=new FileOutputStream("/arduinoloader.log");
out = System.out;
CommPortIdentifier portId2 = CommPortIdentifier
.getPortIdentifier(comport);
SerialPort port2 = (SerialPort) portId2.open("serial madness2", 4001);
input2 = port2.getInputStream();
output2 = port2.getOutputStream();
port2.setSerialPortParams(baud, SerialPort.DATABITS_8,
SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
port2.setDTR(false);
output2.write(hello);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
while (input2.available() > 0) {
input2.read();
}
;
for (int x = 0; x < 3; x++) {
try {
output2.write(hello);
chkok(input2);
} catch (Exception e) {
}
try {
Thread.sleep(500);
} catch (Exception e) {
}
}
output2.write(hello);
chkok(input2);
// write the hex file
int addr = 0;
while (addr < imagesize) {
output2.write('U');
output2.write((addr / 2) % 256);
output2.write((addr / 2) / 256);
output2.write(' ');
chkok(input2);
int ps = Math.min(pageSize, imagesize - addr);
output2.write('d');
output2.write(ps / 256);
output2.write(ps % 256);
output2.write('F');
for (int x = 0; x < ps; x++) {
output2.write(image[addr + x]);
}
output2.write(' ');
chkok(input2);
addr += ps;
}
// validate the image
addr = 0;
output2.write('U');
output2.write((addr / 2) % 256);
output2.write((addr / 2) / 256);
output2.write(' ');
chkok(input2);
output2.write('t');
output2.write(imagesize / 256);
output2.write(imagesize % 256);
output2.write('F');
output2.write(' ');
if ((waitForChar(input2) != okrsp[0]))
throw new Exception("Invalid response");
while (addr < imagesize) {
int c = waitForChar(input2);
if (c != image[addr])
throw new Exception("Validation error at offset " + addr
+ ". Expected " + image[addr] + " received " + c);
addr++;
// System.out.print(hexval(c));
if (addr % 16 == 0)
System.out.println("");
}
if ((waitForChar(input2) != okrsp[1]))
throw new Exception("Invalid response");
output2.write('Q');
output2.write(' ');
chkok(input2);
port2.setDTR(true);
port2.close();
}
public static void waitfor(int w) throws Exception {
int c;
do {
while (input2.available() == 0)
;
c = input2.read();
System.out.println((char) c + " " + (int) c);
} while (c != w);
}
static String hexvals[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8",
"9", "A", "B", "C", "D", "E", "F" };
static String hexval(int i) {
return hexvals[i / 16] + hexvals[i % 16];
}
static void parsehex(String fname) throws Exception {
BufferedReader in = new BufferedReader(new FileReader(fname));
String t = in.readLine();
int line = 0;
imagesize = 0;
while (t != null) {
line++;
if (!":".equals(t.substring(0, 1)))
throw new Exception("line#" + line
+ " Invalid format in hex file " + fname);
int len = Integer.parseInt(t.substring(1, 3), 16);
imagesize += len;
int addr = Integer.parseInt(t.substring(3, 7), 16);
int type = Integer.parseInt(t.substring(7, 9), 16);
String datav = t.substring(9, 9 + (len * 2));
int[] data = new int[datav.length() / 2];
for (int x = 0; x < data.length; x++) {
data[x] = Integer.parseInt(datav.substring(x * 2, x * 2 + 2),
16);
}
int cksum = Integer.parseInt(t.substring(9 + (len * 2),
11 + (len * 2)), 16);
// compute checksum of line just read
int cks = (256 - len) + (256 - (addr / 256)) + (256 - (addr % 256))
+ (256 - type);
for (int x = 0; x < data.length; x++) {
cks += (256 - data[x]);
}
cks %= 256;
if (cks != cksum)
throw new Exception("line#" + line
+ " Invalid checksum in hex file " + fname);
// copy to the image so we can work with the page size easier
for (int x = 0; x < data.length; x++) {
image[addr + x] = data[x];
}
t = in.readLine();
}
}
}