Hace poco hice un proyecto similar, excepto que la actualización se realizó a través de UART en lugar de inalámbrica. Todo estaba en un microcontrolador, un Silicon Labs SiM3C166. El flash integrado se segmentó lógicamente en 3 secciones, el cargador de arranque, la aplicación activa y la aplicación pendiente (en realidad había una 4ta, pequeña sección que contenía un hash de la aplicación pendiente, para verificar que se había cargado correctamente).
El código de la aplicación incluyó el código para recibir la nueva imagen y escribirlo en la sección "pendiente". Una vez que toda la imagen se cargó y verificó contra el hash, el código hizo un reinicio suave. Esto inicia el gestor de arranque, que se encuentra en la dirección 0.
La función del gestor de arranque es simple para verificar la aplicación pendiente contra su hash y compararla con la aplicación existente. Si el hash es válido y la nueva imagen es diferente a la anterior, copia la nueva aplicación sobre la antigua y luego borra el hash.
Finalmente, independientemente de si la aplicación fue copiada o no, transfirió el control a la aplicación actual. (Esto consistió en reubicar el bloque de vector de interrupción al espacio de la aplicación y derivar a la primera palabra de la aplicación).
Cosas a tener en cuenta: la aplicación tenía que estar vinculada para ubicarse en una dirección distinta de 0, específicamente a la base de la sección "activa" de flash. Esto incluía mover los vectores de interrupción. El procesador ARM M3 tiene un registro del sistema que apunta a la tabla de vectores interrot y se usó para cambiar los vectores del cargador de arranque a los vectores de aplicación.
Una vez que el código de la aplicación se está ejecutando, el código del cargador de arranque no se volverá a usar hasta que se reinicie el procesador.
Con suerte, esto te dará una idea de lo que se puede hacer.