Flash over SPI using a secondary bootloader?

Has somebody made a secondary bootloader within an Arduino's application?

The Project Architecture
I'm planning a home-automation project (of sorts) with an array of Arduino Nanos connected by a Controller Area Network (CAN) bus.
For the project to be feasible, I would need to be able to flash a new application in via the CAN bus, so essentially via SPI after configuring a MCP2515 CAN Controller.
(I don't want to go climbing inside a roof, in walls, and up trees just to plug in USB to each board when I have an update)

Secondary Bootloader?
For standard projects, the Arduino bootloader occupies the first section of program memory, and contains all the code necessary to re-write application code, shown as "Standard" in the image below...

What I'd like to do is to build a secondary bootloader; a portion of the application that's capable of re-flashing the "nested" application (as shown).
Then, if the secondary bootloader code is faulty, the boards can still be flashed with the primary bootloader (the standard Arduino one) via serial.

Has this been done?
Instead of starting from scratch, I'd love to see if somebody something similar already.
I know this has been done in industry, it's done all the time, but I haven't seen an Arduino project with this yet.

I'd love to hear from you if you know of this being done.
Even if it's not with CAN, or even via SPI.

Other Challenges
I understand that there will be challenges, such as:

  • building the application with a set memory range for the secondary bootloader
  • identifying the memory range of the nested application and offsetting that from the application as a whole
  • triggering a re-flash, then sending the nested application (+ perhaps a checksum)
  • uniquely identifying each board on the network

But first things first; getting a baseline example of an existing secondary bootloader is what I'm looking for.

I was thinking about it but it is simpler to change the bootloader.

you can use this modified Optiboot . I use it [here](https://github.com/jandrassy/ArduinoOTA/blob/master
/src/InternalStorageAVR.cpp)

or read here http://forum.arduino.cc/index.php?topic=586707.0

the bootloader is at the end of the flash. the secondary bootloader would be below it. it could be flashed over serial, because the addresses in hex determine the location of the code.

only the bootloader can write to flash memory. the bootloader section is determined by the BOOTSZ, not by the RWW boundary. so you need the do_spm function of the Optiboot and it makes Optiboot over 512 bytes so it must have 1kB so has the space for copy_flash_pages function too. The secondary bootloader would take one or two more flash pages.

Thanks @Juraj, that's an awesome project!

Juraj:
I was thinking about it but it is simpler to change the bootloader.

I was wondering that too, and I actually still have an stk500 lying around (it's about 15 years old, I wonder if it still works).
I also have to learn about how resources & functions are shared between the bootloader and the application. The CAN stack would have to be in the secondary bootloader (Tx/Rx frames), and I would need to access it from the application.

I've been writing python so long, I may be forgetting my C code :o

Juraj:
only the bootloader can write to flash memory

Can the bootloader write to flash memory in it's own address range...
I understand that it can't re-write itself, that'd be like a surgeon doing brain surgery on themselves, but that's often the purpose of a secondary bootloader, to re-write the first, then the first is used to update the 2nd + application (or a combination thereof).

with the copy_flash_pages function the CAN part can be in the main sketch. but you must have space on flash for the second copy of the application.
with the second bootloader the CAN part would be twice in flash. in application and in second bootloader. you have only 32kB of flash on Nano