Self burn by i2c EEPROM (bootloader)

hi guys,

first sorry my english.

I'm trying make bootloader self write sketch by one eeprom memory on i2c bus,
the concep is:

  1. the first time:
    I burn new bootloader then burn fist version of sketch.

  2. I send hex content to i2c memory using any comunication interface, using one routine on fist sketch to do it, check the information twice ,write flag on device eprom memory and reboot device.

  3. bootloader find new version of software(flag) and selfburn new sketch using i2c eeprom and start app.

  4. the new version of sketch is runing.

I can use this routine by serial, ethernet, wifi or any comunication device, only need have apropriate function on fist sketch (and updates of this) to receive new firmware and write on i2c memory.

my problems, I'm a newbie on atmegas, I have expertise with firmwares but no with atmegas and still having problens with my "hack bootloader"

I start with(maybe) the original bootloader on /hardware/arduino/avr/bootloaders/atmega
and make this changes.

on includes of ATmegaBOOT_168.c

/* some includes */
#include <util/twi.h> //TWI Library to use i2c serial eeprom

and this 2 funcions to use EEPROM
after the functions prototypes on bootloader

void EEOpen()
{
	//Set up TWI Module
	TWBR = 5;
	TWSR &= (~((1<<TWPS1)|(1<<TWPS0)));

}

uint8_t EEReadByte(uint16_t address)
{
	uint8_t data;

	//Initiate a Dummy Write Sequence to start Random Read
	do
	{
		//Put Start Condition on TWI Bus
		TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

		//Poll Till Done
		while(!(TWCR & (1<<TWINT)));

		//Check status
		if((TWSR & 0xF8) != 0x08)
			return FALSE;

		//Now write SLA+W
		//EEPROM @ 00h
		TWDR=0b10100000;	

		//Initiate Transfer
		TWCR=(1<<TWINT)|(1<<TWEN);

		//Poll Till Done
		while(!(TWCR & (1<<TWINT)));
	
	}while((TWSR & 0xF8) != 0x18);
		

	//Now write ADDRH
	TWDR=(address>>8);

	//Initiate Transfer
	TWCR=(1<<TWINT)|(1<<TWEN);

	//Poll Till Done
	while(!(TWCR & (1<<TWINT)));

	//Check status
	if((TWSR & 0xF8) != 0x28)
		return FALSE;

	//Now write ADDRL
	TWDR=(address);

	//Initiate Transfer
	TWCR=(1<<TWINT)|(1<<TWEN);

	//Poll Till Done
	while(!(TWCR & (1<<TWINT)));

	//Check status
	if((TWSR & 0xF8) != 0x28)
		return FALSE;

	//*************************DUMMY WRITE SEQUENCE END **********************


	
	//Put Start Condition on TWI Bus
	TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

	//Poll Till Done
	while(!(TWCR & (1<<TWINT)));

	//Check status
	if((TWSR & 0xF8) != 0x10)
		return FALSE;

	//Now write SLA+R
	//EEPROM @ 00h
	TWDR=0b10100001;	

	//Initiate Transfer
	TWCR=(1<<TWINT)|(1<<TWEN);

	//Poll Till Done
	while(!(TWCR & (1<<TWINT)));

	//Check status
	if((TWSR & 0xF8) != 0x40)
		return FALSE;

	//Now enable Reception of data by clearing TWINT
	TWCR=(1<<TWINT)|(1<<TWEN);

	//Wait till done
	while(!(TWCR & (1<<TWINT)));

	//Check status
	if((TWSR & 0xF8) != 0x58)
		return FALSE;

	//Read the data
	data=TWDR;

	//Put Stop Condition on bus
	TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
	
	//Wait for STOP to finish
	while(TWCR & (1<<TWSTO));

	//Return TRUE
	return data;
}

then I make a changes on main code

add at_ep variable and start at_ep and ch variables;

uint8_t ch,ch2,at_ep;
	at_ep=0;
	ch = 0x00;

before forever loop I will test the device address to see if have version on i2c eeprom
in this case I just use the 328 to test

#if defined(__AVR_ATmega168__)  || defined(__AVR_ATmega328P__)
	address.byte[0] = 0x03; //address of the last byte on device eeprom
	address.byte[1] = 0xfe;
	address.word = address.word << 1;
	while(EECR & (1<<EEPE));
	EEAR = (uint16_t)(void *)address.word;
	EECR |= (1<<EERE);
	if(EEDR == 0xFE){
		flash_led(10); //just for I know if read the 0xFE on last byte.
		at_ep = 1;	
		EEOpen();  //start i2c
	}
	length.byte[1] = 0x00;
	length.byte[0] = 0x40; //fixe the length of page
#endif

next step is the first char identification by avrdude '0'
I only will wait this if not have a i2c flags

	/* get character from UART */
#if defined(__AVR_ATmega168__)  || defined(__AVR_ATmega328P__)
	if(at_ep != 1)
		ch = getch();
#else
	ch = getch();
#endif

on the write memory funcion 'd' I need make tests
first if is one i2c update I read memory by i2c and put informations on buff like by serial but...

/* Write memory, length is big endian and is in bytes  */
	else if((ch=='d')) || (at_ep==1)) {
	 #if defined(__AVR_ATmega168__)  || defined(__AVR_ATmega328P__)
		if(at_ep!=1){
			length.byte[1] = getch();
			length.byte[0] = getch();
			flags.eeprom = 0;
			if (getch() == 'E') flags.eeprom = 1;
			for (w=0;w<length.word;w++) {
				buff[w] = getch();	                        // Store data in buffer, can't keep up with serial data stream whilst programming pages
			}
			ch = getch();
		}else{
			for (w=0;w<length.word;w++) {
				buff[w]=EEReadByte(((uint16_t)(void *)address.word)+w);
			}
			ch = ' ';
		}
	#else
		length.byte[1] = getch();
		length.byte[0] = getch();
		flags.eeprom = 0;
		if (getch() == 'E') flags.eeprom = 1;
		for (w=0;w<length.word;w++) {
			buff[w] = getch();	                        // Store data in buffer, can't keep up with serial data stream whilst programming pages
		}
		ch = getch();
	#endif
... //after this the processes is same, get buff bytes and burn on program memory by  the asm codes.
//in the end I update to nexts adress
putch(0x14);
putch(0x10);
#if defined(__AVR_ATmega168__)  || defined(__AVR_ATmega328P__)
			if(at_ep ==1){
//check if is my last memory block (remember I only test on 328)
//I clear flag of update on device eeprom for don't come on loop on next reboot

				if((address.byte[1]==0x75)&&(address.byte[0]=0xc0)) 
				{
					address.byte[1]=0x03;
					address.byte[0]=0xff;
					while(EECR & (1<<EEPE));
					EEAR = (uint16_t)(void *)address.word;
					EEDR = 0xFF;
					EECR |= (1<<EEMPE);
					EECR |= (1<<EEPE);			
					app_start();
				}

				if(address.byte[0] == 0xC0) //check if is the last block of first byte
				{
					address.byte[1]++; // plus second byte
                                        address.byte[0] += 0x00;
				}else{
					address.byte[0] += 0x40; //plus the first byte address.
				}
			}
#endif

I assume this changes is I need to work with and without memory i2c eeprom, in this case 24lc256 (32Kb)

why I don´t use the 24lc256 to store the flag information of new firmware, because I need on write memory code to clear that, if have implementation of versions maybe no use write code.

I compile it, and burn on arduino uno using another uno, the bootloader stop to work, it need work with or without eeprom and work normal on arduino IDE but no :confused:

I make this change on makefile

atmega328: TARGET = atmega328
atmega328: MCU_TARGET = atmega328p
atmega328: CFLAGS += '-DMAX_TIME_COUNT=F_CPU>>4' '-DNUM_LED_FLASHES=1' -DBAUD_RATE=9600
atmega328: AVR_FREQ = 16000000L 
atmega328: LDSECTION  = --section-start=.text=0x7400
atmega328: $(PROGRAM)_atmega328.hex

--section-start=.text=0x7400

because the bootloader is bigger, but I don´t know if it is all I need to make this work well

I need help to make it work, and think is a good way to remote update fimwares with low cost, only need one memory i2c and 2 resistors to work.

thank you the help
and sorry my english.

You are limited to rather small sketches, just 1 Kbyte of EEPROM exists.
Bootloader code to read from EEPROM and write to Flash memory needs to be written.
Bootloader now to read from serial port and write to memory is 512 bytes.

Maybe you could have a compromise - standard bootloader, and a sketch that reads data from PC and makes some updates to EEPROM. Next reset, sketch runs with variables/settings updated from EEPROM, and if no serial data comes in to change those settings, they keep getting used.

CrossRoads,

No, I don´t want use the device(atmega) eeprom to store new sketch, the device eeprom is just to store flag of have new firmware the sketch I want store on i2c eeprom serial memory like 24lc256 (256Kb/8BitsperByte = 32Kb same size of flash of atmega328).

the bootloader need read the i2c serial memory with new sketch and write this on device program memory.

in this case the sketch can have the full memory disponible, arround 30k if bootloader have 2k.

sorry if i don´t make me understand on first time, or if I don´t understand your reply.

Hi reinaldoaf,

did you have any progress in your i2c hex flash method?
I had the same idea and was searching if someone was already working on it.

I'm on the same page, I just started to program AVRs but I'm after a solution where I could "remotely" update an AtTiny/AtMega AVR.

One approach would be to store the new AVR's Image in the EEPROM and make a bootloader capable of self programming it reinaldoaf is proposing.

Another option is to use a secondary AVR chip on the same board. This approach would add $3-$5 in components but might be safer... if something happen in the middle of firmware upgrade the process could simply be restarted and the "programmer" AVR would re-try.

At the moment this project was the closest I found to use an AVR to program another AVR: GitHub - adafruit/Standalone-Arduino-AVR-ISP-programmer: A standalone programmer for mass-programming AVR chips

Should be OK to replace the internal EEPROM with an I2C EEPROM and use a smaller AVR (AtTiny?!)

Hi all,
I have completely same idea. Do you know if there is something new on this topic? I did not find much info about it on google. Would you mind to work together on this to create such bootloader? It would have really practical impact on many remote project I would say. I would prefere the way with another avr (best would be attiny85) which would have I2C EEPROM connected to it and flash on demand another avr from the EEPROM. Is somebody interested to help with it?

(best would be attiny85)

Probably not enough pins. (2 for I2C to the EEPROM, 4 for SPI to the target.)

Optiloader and the enhancements by Adafruit and Nick Gammon will already do this sort of programming from the internal Flash of a m328; It would probably be pretty easy to modify one of those to have an external EEPROM, if that's even needed. And an eBay Arduino-pro-mini clone is probably cheaper than any special-built hardware.

Hi westfw,

thanks for your reply. You are right with the attiny85 that it is probably not the best idea and can be replaced by the pro-mini. So I will try to go this direction. About the external EEPROM and Optiloader I am not sure if the modification will be so easy to use it with the EEPROM, but will try. With the internal EEPROM there is just 512 bytes which is not enough for most of the sketches I would say.

What is the problem with using my hex uploader sketch? Gammon Forum : Electronics : Microprocessors : Atmega chip stand-alone programmer to upload .hex files

You store your new code on an SD card to do that.

CrossRoads has made stand-alone boards to do that:

http://www.crossroadsfencing.com/BobuinoRev17/Programmer.html

Hi. The following are just ideas. I've been thinking too about how to implement this. I have a data logger to be installed on remote locations, using GPRS to send and receive some data. My idea is to make it able to upgrade itself if needed (I don't like the idea, the posibility of bricking the device won't let me sleep, but my client insist). I have three options, two already mentioned:

  • Modify the MCU bootloader (ATMega1284p with Optiboot) to look for an upgrade in I2C or SPI storage (EEPROM, SD card or something similar), then upload the binary into itself, or
  • make another MCU look for an upgrade in I2C or SPI storage and then upload the binary using the SPI, or
  • ask my client if they really, really, really need this

In the first two cases the procedure would be to download the binary, store it (along with the code in use, to have the current and the new binaries available), check its checksum, upload it to the MCU, check its checksum, revert if necessary, boot. (My device has 4 Mbit of I2C EEPROM and the ATMega1284p has only 128 kbytes of flash space, so there is enough space to store the current and the new code).

For me it would be far easier just to add a second MCU and write the sync code (so that both MCUs share the I2C EEPROM, as only the first one has access to the GPRS module). And yes, I immediately thought of Nick Gammon uploader to get some ideas about how to do the uploading with another MCU.

But first I'll try to modify the Optiboot code because my hardware is already designed and I don't want another round of hardware design/prototyping/testing. Also, adding a second MCU adds cost and uses PCB space. However, I'm just beginning to understand the optiboot code (hey, it's pretty small, yet I still trying to figure out some parts of it). If I get somewhere with it I'll post it on github.

Regarding the comment from boylucky about using a second MCU being safer, I don't feel using just the bootloader is unsafe compared to using a second MCU, as you can write the bootloader to check the uploaded code and revert it to a previous version if something goes wrong.

BTW, I think there is already a bootloader to upload the code from an SD card on the SPI bus:

I have not reviewed that yet but it may be useful to somebody else (no, my hardware does not have SD card).

Still, I may try to implement the third option, because if I make a mistake somebody will have to restore the MCU in a device at the middle of the desert. (Of course, maybe implementing this will help me fix some mistake without having to go to those remote locations).

As I said, just ideas.

Regards,

MValdez.

Hello Nick and Mvaldez,

thanks for your posts. I will look to your hex updater sketch and will try to modify it to be able to use it with EEPROM instead of SD card. I would prefere EEPROM for the beginning as for me it looks like a better and cheaper solution. I had some troubles with SD card (probably cused by the SD card itselve) sketches in the past so want to avoid it.
@mvaldez - you are right with the "I don't feel using just the bootloader is unsafe compared to using a second MCU" but as I am not so experienced with bootloaders, I am not able to do such modification of the bootloader to take data from the GPRS modul, store it in EEPROM and then burn the sketch to MCU itselve. I already have a sketch for getting data from the sensors and posting them via GPRS to SQL database but currently I am not able to make a bootloader to get it worked with GPRS. So that was the reason why I go with the 2 MCU's option for now. I was also thinking to use the second MCU as watchdog. Currently I have the watchdog implemented in the first MCU but also wanted to try it with 2 MCU's (but it was just an idea). If I am able to modify the bootloader in the way to use GPRS for upload the sketch it would be perfect solution.

Hi. Regarding the bootloader, I didn't mean to say that the bootloader should take care of the GPRS, only the EEPROM reading (the plan would be that the MCU use the GPRS to download the new sketch to the EEPROM, then the bootloader would only worry about the I2C EEPROM and the burning).

But you are right, if you feel more confortable using a second MCU, that's the way to go (so that you don't introduce bugs or unexpected behaviors).

About using a second MCU as watchdog, I'm doing the same. I'm using and ATTiny45 to watch over the ATMega1284p. My reason to do it was that I would need to modify all libraries which have very long waits and sprinkle them with strobes to the internal watchdog . But even if I had used the internal watchdog, I still would want an external one as a safety net. (I tried a Dallas DS1232 but its longest wait time is 1.2 seconds).

I programmed the ATTiny to wait up to 3 or 5 minutes, while sleeping (to save power), them use a pin change interrupt to wake it up if a strobe is received from the main MCU, then reset its counter. Five minutes is a very long time but for this application that would not cause problems (I'm working on another device to control some water pumps, in that other case five minutes may be harmful for the crops).

I wonder if the ATTiny can be used easily to upload the sketch to the ATMega1284p...

Regards,

MValdez.

Our setup looks like this: one master Arduino mega commanding 7 slave Arduino mini's using an I2C bus. The sketch on the slaves is identical, apart from some parameters saved in the eeprom.

Updating the sketch of the master is taken care of by using an ESP8266, but we are
still looking for a way to update the sketch of the slaves. Our idea would be to add an i2c eeprom to hold the new updated sketch.

I wonder if there was any outcome of this thread. Has such a 'I2C bootloader' been developed ? Or what would be the preferred way ?

Successfuly connected a custom-made Arduino board (Atmega328p) to a 32 kByte I2C EEPROM. See miniboot for futher info.

That's neat. I've got it bookmarked for future use.