How to get access to firmware flashing functions?

I’m writing some code to allow my Photons to update firmware over TFTP from a local server, but when I build, I get errors like:

../../../build/target/user/platform-6-m/applications/kjolerom//libuser.a(tftp.o): In function `tftp_update(IPAddress, char*)':
…/photon/firmware/user/applications/foo/tftp.cpp:81: undefined reference to `Spark_Prepare_For_Firmware_Update'
…/photon/firmware/user/applications/foo/tftp.cpp:98: undefined reference to `Spark_Save_Firmware_Chunk'
…/photon/firmware/user/applications/foo/tftp.cpp:110: undefined reference to `Spark_Finish_Firmware_Update'

I couldn’t find anything in the forum on how to get my application to link against those.

Build command is make SPARK_CLOUD=n PLATFORM=photon APP=foo clean all program-dfu (SPARK_CLOUD=n or not does not seem to make a difference).

Ideas, suggestions, etc?

does make clean all PLATFORM=photon APP=foo program-dfu work?

As in, reordering the arguments? Makes no difference to make, no.

You are building APP=foo but there is an error about applications/kjolerom//.
Have you added any additional references to the make file besides the ones added via the APP= parameter?

Sorry, the app is actually called kjolerom (“cooling room”, which is where this will be used), so s/foo/kjolerom/ in what I wrote, if you like.

Make runs (I run it from firmware/main, but it doesn’t seem to make a difference if I build the system firmware too):

$ make PLATFORM=photon APP=kjolerom clean all
…
Building target: ../../../build/target/user-part/platform-6-m/kjolerom.elf
Invoking: ARM GCC C++ Linker
mkdir -p ../../../build/target/user-part/platform-6-m/
arm-none-eabi-g++ -DSTM32_DEVICE -DSTM32F2XX -DPLATFORM_THREADING=1 -DPLATFORM_ID=6 -DPLATFORM_NAME=photon -DUSBD_VID_SPARK=0x2B04 -DUSBD_PID_DFU=0xD006 -DUSBD_PID_CDC=0xC006 -g3 -gdwarf-2 -Os -mcpu=cortex-m3 -mthumb -DINCLUDE_PLATFORM=1 -fno-builtin -DUSE_STDPERIPH_DRIVER -DDFU_BUILD_ENABLE -DSYSTEM_VERSION_STRING=0.4.8 -DRELEASE_BUILD -Werror -I./inc -I../../../user/inc -I../../../dynalib/inc -I../../../services/inc -I../../../hal/inc -I../../../hal/shared -I../../../hal/src/photon -I../../../hal/src/stm32f2xx -I../../../hal/src/stm32 -I../../../hal/src/photon/api -I../../../system/inc -I../../../rt-dynalib/inc -I../../../wiring/inc -I../../../modules/photon/system-part1/inc -I../../../modules/shared/stm32f2xx/inc -I../../../platform/shared/inc -I../../../platform/MCU/STM32F2xx/STM32_USB_Host_Driver/inc -I../../../platform/MCU/STM32F2xx/STM32_StdPeriph_Driver/inc -I../../../platform/MCU/STM32F2xx/STM32_USB_OTG_Driver/inc -I../../../platform/MCU/STM32F2xx/STM32_USB_Device_Driver/inc -I../../../platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc -I../../../platform/MCU/shared/STM32/inc -I../../../platform/MCU/STM32F2xx/CMSIS/Include -I../../../platform/MCU/STM32F2xx/CMSIS/Device/ST/Include -I. -MD -MP -MF ../../../build/target/user-part/platform-6-m/kjolerom.elf.d -ffunction-sections -fdata-sections -Wall -Wno-switch -Wno-error=deprecated-declarations -fmessage-length=0 -fno-strict-aliasing -DSPARK=1 -DPARTICLE=1 -DSTART_DFU_FLASHER_SERIAL_SPEED=14400 -DSTART_YMODEM_FLASHER_SERIAL_SPEED=28800 -fno-builtin-malloc -fno-builtin-free -fno-builtin-realloc -DUSER_FIRMWARE_IMAGE_SIZE=0x20000 -DUSER_FIRMWARE_IMAGE_LOCATION=0x80A0000 -DMODULAR_FIRMWARE=1 -DMODULE_VERSION=3 -DMODULE_FUNCTION=5 -DMODULE_INDEX=1 -DMODULE_DEPENDENCY=4,2,9 ../../../build/target/user-part/platform-6-m/src/user_module.o ../../../build/target/user-part/platform-6-m/src/module_info.o ../../../build/target/user-part/platform-6-m/src/user_export.o ../../../build/target/user-part/platform-6-m/src/newlib_stubs.o  --output ../../../build/target/user-part/platform-6-m/kjolerom.elf -Wl,--whole-archive ../../../hal/src/photon/lib/STM32F2xx_Peripheral_Libraries.a -Wl,--no-whole-archive -nostartfiles -Xlinker --gc-sections -L../../../build/arm/linker/stm32f2xx  -L../../../build/target/user/platform-6-m/applications/kjolerom/ -L../../../build/target/services-dynalib/arm/ -L../../../build/target/hal-dynalib/platform-6-m/ -L../../../build/target/system-dynalib/platform-6-m/ -L../../../build/target/rt-dynalib/platform-6-m/ -L../../../build/target/wiring/platform-6-m/ -L../../../build/target/communication-dynalib/platform-6-m/ -L../../../build/target/platform/platform-6-m/ -L../../../build/target/wiring_globals/platform-6-m/ -L../../../hal/src/photon/lib/ -L../../../build/arm/linker -Wl,--whole-archive -luser -lhal-dynalib -lservices-dynalib -lsystem-dynalib -lrt-dynalib -lwiring -lcommunication-dynalib -lplatform -lwiring_globals -Wl,--no-whole-archive -lnosys -L../../../modules/photon/system-part2 -L../../../modules/photon/system-part1 -L. -T./linker.ld -Wl,--defsym,USER_FIRMWARE_IMAGE_SIZE=0x20000 -Wl,--defsym,USER_FIRMWARE_IMAGE_LOCATION=0x80A0000 -Wl,-Map,../../../build/target/user-part/platform-6-m/kjolerom.map -lstdc++_nano -lm -Wl,--start-group -lgcc -lg_nano -lc_nano -Wl,--end-group -Wl,--start-group -lgcc -lc_nano -Wl,--end-group
/usr/lib/gcc/arm-none-eabi/4.9.3/../../../arm-none-eabi/bin/ld: warning: /usr/lib/gcc/arm-none-eabi/4.9.3/../../../arm-none-eabi/lib/armv7-m/libstdc++_nano.a(atexit_arm.o) uses 2-byte wchar_t yet the output is to use 4-byte wchar_t; use of wchar_t values across objects may fail
[lots of those warnings for various functions]
../../../build/target/user/platform-6-m/applications/kjolerom//libuser.a(tftp.o): In function `tftp_update(IPAddress, char*)':
/home/tfheen/src/photon/firmware/user/applications/kjolerom/tftp.cpp:81: undefined reference to `Spark_Prepare_For_Firmware_Update'
/home/tfheen/src/photon/firmware/user/applications/kjolerom/tftp.cpp:98: undefined reference to `Spark_Save_Firmware_Chunk'
/home/tfheen/src/photon/firmware/user/applications/kjolerom/tftp.cpp:110: undefined reference to `Spark_Finish_Firmware_Update'
collect2: error: ld returned 1 exit status
../../../build/module.mk:183: recipe for target '../../../build/target/user-part/platform-6-m/kjolerom.elf' failed
make[1]: *** [../../../build/target/user-part/platform-6-m/kjolerom.elf] Error 1
make[1]: Leaving directory '/home/tfheen/src/photon/firmware/modules/photon/user-part'
../build/recurse.mk:11: recipe for target 'modules/photon/user-part' failed
make: *** [modules/photon/user-part] Error 2

My guess is I need to drop something in user/applications/kjolerom/include.mk or similar to get it to link with, I think, libsystem.a where it seems those functions are defined.

@tfheen, do you have #include "system_update.h" in tftp.h? Not sure it will fix the problem but worth trying! :wink:

Yes, else it’d have stopped a lot earlier. This is a linking problem, not a compilation problem.

Tha’s a great question and one that’s not come up before. These functions aren’t currently exported from the system dynamic library. I can add them on the 0.4.9 release which will be released early next week.

If you’re in a hurry and you wanted to do this yourself, you can compile the system modules, and add those functions to the system dynamic interface in system/inc/system_dynalib.h.

EDIT: I just pushed this change to the develop branch, so you just have to compile and flash system firmware from the develop branch and recompile your application.

1 Like

Much appreciated! That got it compiling. Of course, I ended up with a red flashing LED, so now to find the bug in my code… :smile:

Ok, got a fair bit farther. I can now flash without crashing, but it seems like Spark_Prepare_For_Firmware_Update puts the firmware in a different location than it goes when I flash using dfu-util. (0x80c0000 is the address HAL_OTA_FlashAddress seems to return, dfu-util flashes to 0x80a0000). I tried the dirty trick of setting file.chunk_address to a negative value, but that didn’t work (red SOS flash).

Is the bootloader supposed to copy from 0x80c0000 to 0x80a0000 after flashing? Do I need to set a flag to make that happen? Can I jump to 0x80c0000 and execute from there? I don’t see any special handling of this is spark_protocol.cpp, but it might be I’m looking at the wrong place.

EDIT:
A bit more investigation points to this HAL_FLASH_End being unhappy with the file. I’ve tried both the built .bin and the .dfu file, and neither seems to work. Should those work or do I need to massage the files in order for them to work?

The files themselves include a module_info struct that describes where in memory the file should be stored, so set the address to 0 and the target as FIRMWARE.

Double check that you have crc32 in your path it’s used when building the file - the crc32 value is appended to the end of the file and used to validate file integrity.

First 16 octets are:

0000 0a08 0815 0a08 0000 0300 0600 0501

My understanding is this translates to something like

struct module_info_t {
    .module_start_address = 0x080a0000;
    .module_end_address = 0x080a1508;
    .reserved = 0;
    .reserved2 = 0;
    .module_version = 0x0003;
    .platform_id = 0x0006; /* Photon */
    .module_function = 0x05; /* MODULE_FUNCTION_USER_PART */
    .module_index = 0x01;
…
};

Which looks pretty ok. (Only reference to FIRMWARE would be if I built a mono firmware. I assume you don’t mean I should be trying that?

crc32 is in PATH, and it seems to be used fine in the build process. I’m using the tinker app, just to have something to test with:

Invoking: ARM GNU Create Flash Image
arm-none-eabi-objcopy -O binary ../../../build/target/user-part/platform-6-m/tinker.elf ../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc
if [ -s ../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc ]; then \
head -c $((`stat -c %s ../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc` - 38)) ../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc > ../../../build/target/user-part/platform-6-m/tinker.bin.no_crc && \
tail -c 38 ../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc > ../../../build/target/user-part/platform-6-m/tinker.bin.crc_block && \
test "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20280078563412" = `xxd -p -c 500 ../../../build/target/user-part/platform-6-m/tinker.bin.crc_block` && \
shasum -a 256 ../../../build/target/user-part/platform-6-m/tinker.bin.no_crc | cut -c 1-65 | xxd -r -p | dd bs=1 of=../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc seek=$((`stat -c %s ../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc` - 38)) conv=notrunc  && \
head -c $((`stat -c %s ../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc` - 4)) ../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc > ../../../build/target/user-part/platform-6-m/tinker.bin.no_crc && \
 crc32 ../../../build/target/user-part/platform-6-m/tinker.bin.no_crc | cut -c 1-10 | xxd -r -p | dd bs=1 of=../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc seek=$((`stat -c %s ../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc` - 4)) conv=notrunc ;\
fi
32+0 records in
32+0 records out
32 bytes (32 B) copied, 0.0121366 s, 2.6 kB/s
4+0 records in
4+0 records out
4 bytes (4 B) copied, 0.0411037 s, 0.1 kB/s
[ ! -f ../../../build/target/user-part/platform-6-m/tinker.bin ] || rm ../../../build/target/user-part/platform-6-m/tinker.bin
mv ../../../build/target/user-part/platform-6-m/tinker.bin.pre_crc ../../../build/target/user-part/platform-6-m/tinker.bin

arm-none-eabi-objcopy -O ihex ../../../build/target/user-part/platform-6-m/tinker.elf ../../../build/target/user-part/platform-6-m/tinker.hex
arm-none-eabi-size --format=berkeley ../../../build/target/user-part/platform-6-m/tinker.elf
   text	   data	    bss	    dec	    hex	filename
   5276	    112	    364	   5752	   1678	../../../build/target/user-part/platform-6-m/tinker.elf
arm-none-eabi-objdump -h -S ../../../build/target/user-part/platform-6-m/tinker.elf > ../../../build/target/user-part/platform-6-m/tinker.lst

I’m also trying to flash the firmware using the same functions but I also have the issue of HAL_UPDATE_ERROR.

Can you elaborate on the crc32?
I’m using the output bin file generated by the particle cli with command: particle compile photon c:\myapp

Thanks

The CRC32 is automatically appended by the web and CLI build tools.

Here’s an example of using the flashing API. The hex data is the factory Tinker image for the Photon, unmodified. All it does it have the Photon flash itself with the Tinker firmware, which isn’t really useful but it handy for figuring out how to use the self-flashing API.

#include "Particle.h"

#include "system_update.h"

// photon_tinker.bin
const uint8_t tinker[] = {
0x00, 0x00, 0x0a, 0x08, 0x6c, 0x0f, 0x0a, 0x08, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x05, 0x01, 0x04, 0x02, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0xdc, 0x0c, 0x0a, 0x08, 0x70, 0xb5, 0x07, 0x4b, 0x07, 0x4d, 0x00, 0x24, 0xed, 0x1a, 0xad, 0x08, 0x1e, 0x46,
0xac, 0x42, 0x04, 0xd0, 0x56, 0xf8, 0x24, 0x20, 0x90, 0x47, 0x01, 0x34, 0xf8, 0xe7, 0x70, 0xbd, 0x00, 0xbf, 0xc4, 0x0e, 0x0a,
0x08, 0xdc, 0x0e, 0x0a, 0x08, 0x09, 0x48, 0x0a, 0x49, 0x08, 0xb5, 0x88, 0x42, 0x05, 0xd0, 0x09, 0x4a, 0x82, 0x42, 0x02, 0xd0,
0x12, 0x1a, 0x00, 0xf0, 0x6f, 0xfd, 0x07, 0x48, 0x07, 0x4a, 0x00, 0x21, 0x12, 0x1a, 0x00, 0xf0, 0x72, 0xfd, 0x06, 0x48, 0x08,
0xbd, 0x00, 0xbf, 0x00, 0x02, 0x00, 0x20, 0xe0, 0x0e, 0x0a, 0x08, 0x68, 0x02, 0x00, 0x20, 0x68, 0x02, 0x00, 0x20, 0x34, 0x03,
0x00, 0x20, 0x34, 0x03, 0x00, 0x20, 0x00, 0xf0, 0x44, 0xb9, 0x00, 0xf0, 0x6a, 0xb9, 0x00, 0xf0, 0x9c, 0xb9, 0x08, 0xb5, 0x0c,
0x20, 0x00, 0x21, 0x00, 0xf0, 0x7f, 0xf9, 0xfe, 0xe7, 0x01, 0x4b, 0x03, 0x60, 0x70, 0x47, 0x00, 0xbf, 0x60, 0x0d, 0x0a, 0x08,
0x10, 0xb5, 0x03, 0x4b, 0x04, 0x46, 0x03, 0x60, 0xff, 0xf7, 0xec, 0xff, 0x20, 0x46, 0x10, 0xbd, 0x60, 0x0d, 0x0a, 0x08, 0xf0,
0xb5, 0x01, 0x21, 0x85, 0xb0, 0x05, 0x46, 0x00, 0xf0, 0xe8, 0xfb, 0xa0, 0xf1, 0x30, 0x06, 0x07, 0x2e, 0x04, 0x46, 0x25, 0xd8,
0x16, 0x49, 0x68, 0x46, 0x00, 0xf0, 0x7f, 0xfb, 0x28, 0x46, 0x69, 0x46, 0x00, 0xf0, 0xc9, 0xfb, 0x07, 0x46, 0x68, 0x46, 0x00,
0xf0, 0x35, 0xfb, 0x0f, 0xb1, 0xb4, 0xb2, 0x0e, 0xe0, 0x0f, 0x49, 0x68, 0x46, 0x00, 0xf0, 0x70, 0xfb, 0x28, 0x46, 0x69, 0x46,
0x00, 0xf0, 0xba, 0xfb, 0x05, 0x46, 0x68, 0x46, 0x00, 0xf0, 0x26, 0xfb, 0x65, 0xb1, 0x26, 0x3c, 0xa4, 0xb2, 0x20, 0x46, 0x03,
0x21, 0x00, 0xf0, 0x87, 0xf9, 0x20, 0x46, 0x00, 0xf0, 0xaf, 0xf9, 0x04, 0xe0, 0x4f, 0xf0, 0xff, 0x30, 0x01, 0xe0, 0x6f, 0xf0,
0x01, 0x00, 0x05, 0xb0, 0xf0, 0xbd, 0xf0, 0x0c, 0x0a, 0x08, 0xf2, 0x0c, 0x0a, 0x08, 0xf0, 0xb5, 0x01, 0x21, 0x85, 0xb0, 0x04,
0x46, 0x00, 0xf0, 0xac, 0xfb, 0xa0, 0xf1, 0x30, 0x06, 0x07, 0x2e, 0x45, 0xd8, 0x68, 0x46, 0x21, 0x46, 0x03, 0x22, 0x07, 0x23,
0x00, 0xf0, 0xa4, 0xfb, 0x68, 0x46, 0x24, 0x49, 0x00, 0xf0, 0x62, 0xfb, 0x05, 0x46, 0x68, 0x46, 0x00, 0xf0, 0xf8, 0xfa, 0x7d,
0xb9, 0x68, 0x46, 0x21, 0x46, 0x03, 0x22, 0x06, 0x23, 0x00, 0xf0, 0x95, 0xfb, 0x68, 0x46, 0x1d, 0x49, 0x00, 0xf0, 0x53, 0xfb,
0x07, 0x46, 0x68, 0x46, 0x00, 0xf0, 0xe9, 0xfa, 0x0f, 0xb9, 0x29, 0xe0, 0x01, 0x25, 0x19, 0x49, 0x68, 0x46, 0x00, 0xf0, 0x24,
0xfb, 0x20, 0x46, 0x69, 0x46, 0x00, 0xf0, 0x6e, 0xfb, 0x07, 0x46, 0x68, 0x46, 0x00, 0xf0, 0xda, 0xfa, 0x6f, 0xb9, 0x14, 0x49,
0x68, 0x46, 0x00, 0xf0, 0x17, 0xfb, 0x20, 0x46, 0x69, 0x46, 0x00, 0xf0, 0x61, 0xfb, 0x04, 0x46, 0x68, 0x46, 0x00, 0xf0, 0xcd,
0xfa, 0x8c, 0xb1, 0x0a, 0x36, 0xb6, 0xb2, 0x30, 0x46, 0x01, 0x21, 0x00, 0xf0, 0x2e, 0xf9, 0x30, 0x46, 0x29, 0x46, 0x00, 0xf0,
0x3b, 0xf9, 0x01, 0x20, 0x07, 0xe0, 0x4f, 0xf0, 0xff, 0x30, 0x04, 0xe0, 0x6f, 0xf0, 0x01, 0x00, 0x01, 0xe0, 0x6f, 0xf0, 0x02,
0x00, 0x05, 0xb0, 0xf0, 0xbd, 0xf4, 0x0c, 0x0a, 0x08, 0xf9, 0x0c, 0x0a, 0x08, 0xf0, 0x0c, 0x0a, 0x08, 0xf2, 0x0c, 0x0a, 0x08,
0x7f, 0xb5, 0x01, 0x21, 0x05, 0x46, 0x00, 0xf0, 0x4b, 0xfb, 0xa0, 0xf1, 0x30, 0x03, 0x07, 0x2b, 0x04, 0x46, 0x1f, 0xd8, 0x14,
0x49, 0x68, 0x46, 0x00, 0xf0, 0xe2, 0xfa, 0x28, 0x46, 0x69, 0x46, 0x00, 0xf0, 0x2c, 0xfb, 0x06, 0x46, 0x68, 0x46, 0x00, 0xf0,
0x98, 0xfa, 0xae, 0xb9, 0x0f, 0x49, 0x68, 0x46, 0x00, 0xf0, 0xd5, 0xfa, 0x28, 0x46, 0x69, 0x46, 0x00, 0xf0, 0x1f, 0xfb, 0x05,
0x46, 0x68, 0x46, 0x00, 0xf0, 0x8b, 0xfa, 0x5d, 0xb1, 0xa4, 0xf1, 0x26, 0x00, 0x80, 0xb2, 0x00, 0xf0, 0x2b, 0xf9, 0x07, 0xe0,
0x4f, 0xf0, 0xff, 0x30, 0x04, 0xe0, 0x6f, 0xf0, 0x02, 0x00, 0x01, 0xe0, 0x6f, 0xf0, 0x01, 0x00, 0x04, 0xb0, 0x70, 0xbd, 0xf0,
0x0c, 0x0a, 0x08, 0xf2, 0x0c, 0x0a, 0x08, 0xf0, 0xb5, 0x01, 0x21, 0x89, 0xb0, 0x04, 0x46, 0x00, 0xf0, 0x12, 0xfb, 0xa0, 0xf1,
0x30, 0x06, 0x07, 0x2e, 0x05, 0x46, 0x3e, 0xd8, 0x03, 0x22, 0x68, 0x46, 0x21, 0x46, 0x00, 0xf0, 0x30, 0xfb, 0x1f, 0x49, 0x04,
0xa8, 0x00, 0xf0, 0xa4, 0xfa, 0x20, 0x46, 0x04, 0xa9, 0x00, 0xf0, 0xee, 0xfa, 0x07, 0x46, 0x04, 0xa8, 0x00, 0xf0, 0x5a, 0xfa,
0x57, 0xb1, 0xb6, 0xb2, 0x01, 0x21, 0x30, 0x46, 0x00, 0xf0, 0xbc, 0xf8, 0x68, 0x46, 0x00, 0xf0, 0x22, 0xfb, 0x81, 0xb2, 0x30,
0x46, 0x17, 0xe0, 0x14, 0x49, 0x04, 0xa8, 0x00, 0xf0, 0x8c, 0xfa, 0x20, 0x46, 0x04, 0xa9, 0x00, 0xf0, 0xd6, 0xfa, 0x04, 0x46,
0x04, 0xa8, 0x00, 0xf0, 0x42, 0xfa, 0x74, 0xb1, 0x26, 0x3d, 0xad, 0xb2, 0x01, 0x21, 0x28, 0x46, 0x00, 0xf0, 0xa3, 0xf8, 0x68,
0x46, 0x00, 0xf0, 0x09, 0xfb, 0x81, 0xb2, 0x28, 0x46, 0x00, 0xf0, 0xf2, 0xf8, 0x01, 0x24, 0x01, 0xe0, 0x6f, 0xf0, 0x01, 0x04,
0x68, 0x46, 0x00, 0xf0, 0x2d, 0xfa, 0x01, 0xe0, 0x4f, 0xf0, 0xff, 0x34, 0x20, 0x46, 0x09, 0xb0, 0xf0, 0xbd, 0x00, 0xbf, 0xf0,
0x0c, 0x0a, 0x08, 0xf2, 0x0c, 0x0a, 0x08, 0x08, 0xb5, 0x0b, 0x48, 0x0b, 0x49, 0x00, 0x22, 0x00, 0xf0, 0x4e, 0xf8, 0x0a, 0x48,
0x0b, 0x49, 0x00, 0x22, 0x00, 0xf0, 0x49, 0xf8, 0x0a, 0x48, 0x0a, 0x49, 0x00, 0x22, 0x00, 0xf0, 0x44, 0xf8, 0xbd, 0xe8, 0x08,
0x40, 0x08, 0x48, 0x09, 0x49, 0x00, 0x22, 0x00, 0xf0, 0x3d, 0xb8, 0x00, 0xbf, 0xfd, 0x0c, 0x0a, 0x08, 0xbd, 0x00, 0x0a, 0x08,
0x09, 0x0d, 0x0a, 0x08, 0x35, 0x01, 0x0a, 0x08, 0x16, 0x0d, 0x0a, 0x08, 0xf9, 0x01, 0x0a, 0x08, 0x21, 0x0d, 0x0a, 0x08, 0x69,
0x02, 0x0a, 0x08, 0x70, 0x47, 0x00, 0x00, 0x13, 0xb5, 0x09, 0x4c, 0x00, 0x21, 0x0b, 0x46, 0x00, 0x91, 0x0a, 0x46, 0x20, 0x46,
0x00, 0xf0, 0x53, 0xf9, 0x20, 0x46, 0x05, 0x49, 0x06, 0x4a, 0x00, 0xf0, 0xc2, 0xfb, 0x01, 0x20, 0x02, 0xb0, 0xbd, 0xe8, 0x10,
0x40, 0x00, 0xf0, 0x0e, 0xb8, 0x6c, 0x02, 0x00, 0x20, 0x9d, 0x00, 0x0a, 0x08, 0x68, 0x02, 0x00, 0x20, 0x08, 0xb5, 0x02, 0x4b,
0x1b, 0x68, 0x5b, 0x6b, 0x01, 0x93, 0x08, 0xbd, 0xa8, 0x01, 0x02, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x5b, 0x68, 0x01,
0x93, 0x08, 0xbd, 0xa4, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x5b, 0x68, 0x01, 0x93, 0x08, 0xbd, 0xcc, 0x01,
0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x5b, 0x68, 0x01, 0x93, 0x08, 0xbd, 0xa0, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02,
0x4b, 0x1b, 0x68, 0x9b, 0x68, 0x01, 0x93, 0x08, 0xbd, 0xa0, 0x01, 0x06, 0x08, 0x10, 0xb5, 0x04, 0x46, 0x00, 0xf0, 0x98, 0xf9,
0x28, 0xb1, 0xa4, 0xf1, 0x0d, 0x03, 0x02, 0x2b, 0x01, 0xd8, 0x00, 0x20, 0x10, 0xbd, 0x00, 0xf0, 0xdb, 0xf8, 0x08, 0xb1, 0x01,
0x2c, 0xf8, 0xd9, 0x05, 0x48, 0x00, 0xf0, 0xad, 0xfa, 0x28, 0xb1, 0x12, 0x3c, 0x01, 0x2c, 0x94, 0xbf, 0x00, 0x20, 0x01, 0x20,
0x10, 0xbd, 0x01, 0x20, 0x10, 0xbd, 0x9c, 0x02, 0x00, 0x20, 0x14, 0x28, 0x38, 0xb5, 0x04, 0x46, 0x0d, 0x46, 0x0a, 0xd8, 0xff,
0x29, 0x08, 0xd0, 0xff, 0xf7, 0xd9, 0xff, 0x28, 0xb1, 0x20, 0x46, 0x29, 0x46, 0xbd, 0xe8, 0x38, 0x40, 0x00, 0xf0, 0xb8, 0xba,
0x38, 0xbd, 0x38, 0xb5, 0x04, 0x46, 0x0d, 0x46, 0x00, 0xf0, 0xba, 0xfa, 0xff, 0x28, 0x11, 0xd0, 0x80, 0xb1, 0x82, 0x1e, 0xd2,
0xb2, 0x01, 0x2a, 0x0c, 0xd9, 0x06, 0x28, 0x00, 0xd1, 0x38, 0xbd, 0x20, 0x46, 0xff, 0xf7, 0xbf, 0xff, 0x28, 0xb1, 0x20, 0x46,
0x29, 0x46, 0xbd, 0xe8, 0x38, 0x40, 0x00, 0xf0, 0xae, 0xba, 0x38, 0xbd, 0x10, 0xb5, 0x04, 0x46, 0x00, 0xf0, 0xa1, 0xfa, 0x04,
0x38, 0xc0, 0xb2, 0x01, 0x28, 0x08, 0xd9, 0x20, 0x46, 0xff, 0xf7, 0xac, 0xff, 0x20, 0xb1, 0x20, 0x46, 0xbd, 0xe8, 0x10, 0x40,
0x00, 0xf0, 0xa4, 0xba, 0x00, 0x20, 0x10, 0xbd, 0x09, 0x28, 0x10, 0xb5, 0x04, 0x46, 0x9c, 0xbf, 0x00, 0xf1, 0x0a, 0x04, 0xa4,
0xb2, 0x20, 0x46, 0xff, 0xf7, 0x9a, 0xff, 0x50, 0xb1, 0x20, 0x46, 0x03, 0x21, 0x00, 0xf0, 0x73, 0xfa, 0x03, 0x28, 0x04, 0xd1,
0x20, 0x46, 0xbd, 0xe8, 0x10, 0x40, 0x00, 0xf0, 0x9c, 0xba, 0x00, 0x20, 0x10, 0xbd, 0x38, 0xb5, 0x04, 0x46, 0x0d, 0x46, 0xff,
0xf7, 0x87, 0xff, 0xe8, 0xb1, 0x20, 0x46, 0x04, 0x21, 0x00, 0xf0, 0x60, 0xfa, 0x04, 0x28, 0x20, 0x46, 0x04, 0xd1, 0x29, 0x46,
0xbd, 0xe8, 0x38, 0x40, 0x00, 0xf0, 0x80, 0xba, 0x02, 0x21, 0x00, 0xf0, 0x55, 0xfa, 0x02, 0x28, 0x0c, 0xd1, 0x20, 0x46, 0x00,
0xf0, 0x60, 0xfa, 0x01, 0x28, 0x01, 0xd0, 0x04, 0x28, 0x05, 0xd1, 0x20, 0x46, 0xe9, 0xb2, 0xbd, 0xe8, 0x38, 0x40, 0x00, 0xf0,
0x7e, 0xba, 0x38, 0xbd, 0x00, 0x00, 0x13, 0xb5, 0x08, 0x4c, 0x00, 0x21, 0x00, 0x91, 0x0a, 0x46, 0x0b, 0x46, 0x20, 0x46, 0x00,
0xf0, 0x77, 0xf8, 0x05, 0x49, 0x05, 0x4a, 0x20, 0x46, 0x02, 0xb0, 0xbd, 0xe8, 0x10, 0x40, 0x00, 0xf0, 0xe3, 0xba, 0x00, 0xbf,
0x74, 0x02, 0x00, 0x20, 0x9d, 0x00, 0x0a, 0x08, 0x68, 0x02, 0x00, 0x20, 0xf8, 0xb5, 0x05, 0x46, 0x16, 0x46, 0x0c, 0x46, 0x8f,
0x18, 0xbc, 0x42, 0x06, 0xd0, 0x2b, 0x68, 0x28, 0x46, 0x9b, 0x68, 0x14, 0xf8, 0x01, 0x1b, 0x98, 0x47, 0xf6, 0xe7, 0x30, 0x46,
0xf8, 0xbd, 0x01, 0x4b, 0x03, 0x60, 0x70, 0x47, 0x00, 0xbf, 0x78, 0x0d, 0x0a, 0x08, 0x10, 0xb5, 0x03, 0x4b, 0x04, 0x46, 0x03,
0x60, 0xff, 0xf7, 0x84, 0xfd, 0x20, 0x46, 0x10, 0xbd, 0x78, 0x0d, 0x0a, 0x08, 0x08, 0x46, 0x00, 0xf0, 0x49, 0xba, 0x00, 0xf0,
0x4f, 0xba, 0x00, 0xf0, 0x55, 0xba, 0x00, 0xf0, 0x5b, 0xba, 0x00, 0xf0, 0x61, 0xba, 0x00, 0x00, 0x00, 0x22, 0x42, 0x60, 0x4f,
0xf4, 0x7a, 0x72, 0x82, 0x60, 0x01, 0x4a, 0x02, 0x60, 0x70, 0x47, 0x38, 0x0d, 0x0a, 0x08, 0x00, 0xf0, 0x5c, 0xba, 0x10, 0xb5,
0x05, 0x4c, 0x20, 0x46, 0xff, 0xf7, 0xef, 0xff, 0x20, 0x46, 0x03, 0x49, 0x04, 0x4a, 0xbd, 0xe8, 0x10, 0x40, 0x00, 0xf0, 0x98,
0xba, 0x7c, 0x02, 0x00, 0x20, 0x6d, 0x05, 0x0a, 0x08, 0x68, 0x02, 0x00, 0x20, 0x2d, 0xe9, 0xf0, 0x41, 0x00, 0x24, 0x07, 0x46,
0x0e, 0x46, 0x25, 0x46, 0x3b, 0x19, 0x19, 0x79, 0x0a, 0x22, 0x30, 0x46, 0x00, 0xf0, 0x92, 0xf8, 0x2e, 0x21, 0x80, 0x46, 0x30,
0x46, 0x00, 0xf0, 0x5e, 0xf8, 0x01, 0x34, 0x40, 0x44, 0x03, 0x2c, 0x05, 0x44, 0xef, 0xd1, 0x30, 0x46, 0xf9, 0x79, 0x0a, 0x22,
0x00, 0xf0, 0x83, 0xf8, 0x28, 0x44, 0xbd, 0xe8, 0xf0, 0x81, 0x30, 0xb5, 0x83, 0x71, 0x03, 0x4d, 0x9d, 0xf8, 0x0c, 0x30, 0x05,
0x60, 0x01, 0x71, 0x42, 0x71, 0xc3, 0x71, 0x30, 0xbd, 0x60, 0x0d, 0x0a, 0x08, 0x05, 0x48, 0x06, 0x4b, 0x06, 0x49, 0x03, 0x60,
0x06, 0x4a, 0x00, 0x23, 0x03, 0x71, 0x43, 0x71, 0x83, 0x71, 0xc3, 0x71, 0x00, 0xf0, 0x5c, 0xba, 0x8c, 0x02, 0x00, 0x20, 0x60,
0x0d, 0x0a, 0x08, 0x9d, 0x00, 0x0a, 0x08, 0x68, 0x02, 0x00, 0x20, 0x01, 0x4b, 0x03, 0x60, 0x70, 0x47, 0x00, 0xbf, 0x78, 0x0d,
0x0a, 0x08, 0xf8, 0xb5, 0x06, 0x46, 0x0c, 0x46, 0x8f, 0x18, 0x00, 0x25, 0xbc, 0x42, 0x07, 0xd0, 0x33, 0x68, 0x30, 0x46, 0x9b,
0x68, 0x14, 0xf8, 0x01, 0x1b, 0x98, 0x47, 0x05, 0x44, 0xf5, 0xe7, 0x28, 0x46, 0xf8, 0xbd, 0x00, 0x00, 0x10, 0xb5, 0x03, 0x4b,
0x04, 0x46, 0x03, 0x60, 0xff, 0xf7, 0xfa, 0xfc, 0x20, 0x46, 0x10, 0xbd, 0x78, 0x0d, 0x0a, 0x08, 0x38, 0xb5, 0x05, 0x46, 0x0c,
0x46, 0x08, 0x46, 0x41, 0xb1, 0x00, 0xf0, 0x68, 0xfa, 0x2b, 0x68, 0x02, 0x46, 0xdb, 0x68, 0x28, 0x46, 0x21, 0x46, 0x98, 0x47,
0x38, 0xbd, 0x38, 0xbd, 0x08, 0xb5, 0x03, 0x68, 0x9b, 0x68, 0x98, 0x47, 0x08, 0xbd, 0x30, 0xb5, 0x00, 0x23, 0x8b, 0xb0, 0x8d,
0xf8, 0x24, 0x30, 0x01, 0x2a, 0x98, 0xbf, 0x0a, 0x22, 0x0d, 0xf1, 0x23, 0x04, 0xb1, 0xfb, 0xf2, 0xf3, 0x03, 0xfb, 0x12, 0x11,
0xcd, 0xb2, 0x09, 0x2d, 0x94, 0xbf, 0x30, 0x35, 0x37, 0x35, 0xed, 0xb2, 0x21, 0x46, 0x04, 0xf8, 0x01, 0x59, 0x0b, 0xb1, 0x19,
0x46, 0xef, 0xe7, 0xff, 0xf7, 0xd0, 0xff, 0x0b, 0xb0, 0x30, 0xbd, 0x10, 0xb5, 0x22, 0xb9, 0x02, 0x68, 0xc9, 0xb2, 0x92, 0x68,
0x90, 0x47, 0x10, 0xbd, 0xd2, 0xb2, 0xbd, 0xe8, 0x10, 0x40, 0xff, 0xf7, 0xd6, 0xbf, 0xff, 0xf7, 0xf2, 0xbf, 0x00, 0xf0, 0xb0,
0xb9, 0x13, 0xb5, 0x08, 0x4c, 0x00, 0x21, 0x00, 0x91, 0x0a, 0x46, 0x0b, 0x46, 0x20, 0x46, 0xff, 0xf7, 0x73, 0xff, 0x05, 0x49,
0x05, 0x4a, 0x20, 0x46, 0x02, 0xb0, 0xbd, 0xe8, 0x10, 0x40, 0x00, 0xf0, 0xdf, 0xb9, 0x00, 0xbf, 0x94, 0x02, 0x00, 0x20, 0x9d,
0x00, 0x0a, 0x08, 0x68, 0x02, 0x00, 0x20, 0x10, 0xb5, 0x04, 0x46, 0x00, 0x68, 0xff, 0xf7, 0x35, 0xfe, 0x20, 0x46, 0x10, 0xbd,
0x10, 0xb5, 0x04, 0x46, 0x00, 0x68, 0x08, 0xb1, 0xff, 0xf7, 0x2d, 0xfe, 0x00, 0x23, 0x23, 0x60, 0xa3, 0x60, 0x63, 0x60, 0x10,
0xbd, 0x38, 0xb5, 0x04, 0x46, 0x0d, 0x46, 0x00, 0x68, 0x01, 0x31, 0xff, 0xf7, 0x29, 0xfe, 0x10, 0xb1, 0x84, 0xe8, 0x21, 0x00,
0x01, 0x20, 0x38, 0xbd, 0x10, 0xb5, 0x03, 0x68, 0x04, 0x46, 0x13, 0xb1, 0x43, 0x68, 0x8b, 0x42, 0x07, 0xd2, 0x20, 0x46, 0xff,
0xf7, 0xea, 0xff, 0x20, 0xb1, 0xa3, 0x68, 0x0b, 0xb9, 0x22, 0x68, 0x13, 0x70, 0x01, 0x20, 0x10, 0xbd, 0x70, 0xb5, 0x0e, 0x46,
0x11, 0x46, 0x04, 0x46, 0x15, 0x46, 0xff, 0xf7, 0xe8, 0xff, 0x18, 0xb9, 0x20, 0x46, 0xff, 0xf7, 0xcd, 0xff, 0x04, 0xe0, 0xa5,
0x60, 0x20, 0x68, 0x31, 0x46, 0x00, 0xf0, 0xcd, 0xf9, 0x20, 0x46, 0x70, 0xbd, 0x38, 0xb5, 0x00, 0x23, 0x04, 0x46, 0x03, 0x60,
0x43, 0x60, 0x83, 0x60, 0x03, 0x73, 0x0d, 0x46, 0x39, 0xb1, 0x08, 0x46, 0x00, 0xf0, 0xc7, 0xf9, 0x29, 0x46, 0x02, 0x46, 0x20,
0x46, 0xff, 0xf7, 0xdc, 0xff, 0x20, 0x46, 0x38, 0xbd, 0x38, 0xb5, 0x05, 0x46, 0x0c, 0x46, 0x41, 0xb1, 0x08, 0x46, 0x00, 0xf0,
0xb9, 0xf9, 0x21, 0x46, 0x02, 0x46, 0x28, 0x46, 0xff, 0xf7, 0xce, 0xff, 0x01, 0xe0, 0xff, 0xf7, 0xa3, 0xff, 0x28, 0x46, 0x38,
0xbd, 0x08, 0xb5, 0x82, 0x68, 0x0b, 0x46, 0x12, 0xb9, 0x19, 0xb9, 0x01, 0x20, 0x08, 0xbd, 0x11, 0xb9, 0x03, 0x68, 0x18, 0x78,
0x02, 0xe0, 0x00, 0x68, 0x00, 0xf0, 0x8f, 0xf9, 0xd0, 0xf1, 0x01, 0x00, 0x38, 0xbf, 0x00, 0x20, 0x08, 0xbd, 0x10, 0xb5, 0x8b,
0x68, 0x84, 0x68, 0xe4, 0x1a, 0xa2, 0x42, 0x0c, 0xd8, 0x00, 0x68, 0x68, 0xb1, 0x09, 0x68, 0x51, 0xb1, 0x10, 0x44, 0x1a, 0x46,
0x00, 0xf0, 0x96, 0xf9, 0xd0, 0xf1, 0x01, 0x00, 0x38, 0xbf, 0x00, 0x20, 0x10, 0xbd, 0x00, 0x20, 0x10, 0xbd, 0x08, 0x46, 0x10,
0xbd, 0x82, 0x68, 0x8b, 0x68, 0x9a, 0x42, 0x02, 0xd3, 0x00, 0x22, 0xff, 0xf7, 0xe2, 0xbf, 0x00, 0x20, 0x70, 0x47, 0x83, 0x68,
0x99, 0x42, 0x03, 0xd2, 0x00, 0x68, 0x10, 0xb1, 0x40, 0x5c, 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0xff, 0xf7, 0xf5, 0xbf, 0x9a,
0x42, 0x2d, 0xe9, 0xf0, 0x41, 0x07, 0x46, 0x0d, 0x46, 0x14, 0x46, 0x1e, 0x46, 0x01, 0xd9, 0x34, 0x46, 0x16, 0x46, 0x38, 0x46,
0x0c, 0x49, 0xff, 0xf7, 0x90, 0xff, 0xab, 0x68, 0x9c, 0x42, 0x0f, 0xd8, 0x9e, 0x42, 0x28, 0xbf, 0x1e, 0x46, 0x2b, 0x68, 0x00,
0x22, 0x13, 0xf8, 0x06, 0x80, 0x9a, 0x55, 0x29, 0x68, 0x38, 0x46, 0x21, 0x44, 0xff, 0xf7, 0x93, 0xff, 0x2b, 0x68, 0x03, 0xf8,
0x06, 0x80, 0x38, 0x46, 0xbd, 0xe8, 0xf0, 0x81, 0xf3, 0x0c, 0x0a, 0x08, 0x10, 0xb5, 0x04, 0x46, 0x8b, 0x68, 0xff, 0xf7, 0xd5,
0xff, 0x20, 0x46, 0x10, 0xbd, 0x00, 0x68, 0x08, 0xb1, 0x00, 0xf0, 0x08, 0xb9, 0x70, 0x47, 0x01, 0x4b, 0x03, 0x60, 0x70, 0x47,
0x00, 0xbf, 0x78, 0x0d, 0x0a, 0x08, 0x10, 0xb5, 0x03, 0x4b, 0x04, 0x46, 0x03, 0x60, 0xff, 0xf7, 0xba, 0xfb, 0x20, 0x46, 0x10,
0xbd, 0x78, 0x0d, 0x0a, 0x08, 0x00, 0x7c, 0x00, 0xf0, 0xc7, 0xb8, 0x00, 0x7c, 0x00, 0xf0, 0xd4, 0xb8, 0x00, 0x7c, 0x00, 0xf0,
0xc9, 0xb8, 0x00, 0x7c, 0x00, 0xf0, 0xd6, 0xb8, 0x00, 0x7c, 0x00, 0xf0, 0xb3, 0xb8, 0x00, 0x00, 0x10, 0xb5, 0x04, 0x46, 0x00,
0x20, 0x60, 0x60, 0x4f, 0xf4, 0x7a, 0x70, 0xa0, 0x60, 0x05, 0x48, 0x21, 0x74, 0x20, 0x60, 0x08, 0x46, 0x11, 0x46, 0x1a, 0x46,
0x00, 0xf0, 0x9b, 0xf8, 0x20, 0x46, 0x10, 0xbd, 0x00, 0xbf, 0xa0, 0x0d, 0x0a, 0x08, 0x00, 0x7c, 0x00, 0xf0, 0xc3, 0xb8, 0x00,
0x00, 0x10, 0xb5, 0x07, 0x4c, 0x00, 0x21, 0x20, 0x46, 0x06, 0x4a, 0x07, 0x4b, 0xff, 0xf7, 0xe0, 0xff, 0x20, 0x46, 0x06, 0x49,
0x06, 0x4a, 0xbd, 0xe8, 0x10, 0x40, 0x00, 0xf0, 0xbb, 0xb8, 0x00, 0xbf, 0x9c, 0x02, 0x00, 0x20, 0xf2, 0x02, 0x00, 0x20, 0xb0,
0x02, 0x00, 0x20, 0x01, 0x09, 0x0a, 0x08, 0x68, 0x02, 0x00, 0x20, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x5b, 0x68, 0x01, 0x93,
0x08, 0xbd, 0xb0, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x9b, 0x68, 0x01, 0x93, 0x08, 0xbd, 0xb0, 0x01, 0x06,
0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0xdb, 0x68, 0x01, 0x93, 0x08, 0xbd, 0xb0, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b,
0x1b, 0x68, 0x1b, 0x69, 0x01, 0x93, 0x08, 0xbd, 0xb0, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x5b, 0x69, 0x01,
0x93, 0x08, 0xbd, 0xb0, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x9b, 0x6a, 0x01, 0x93, 0x08, 0xbd, 0xb0, 0x01,
0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x1b, 0x6b, 0x01, 0x93, 0x08, 0xbd, 0xb0, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02,
0x4b, 0x1b, 0x68, 0x5b, 0x6b, 0x01, 0x93, 0x08, 0xbd, 0xb0, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x1b, 0x6a,
0x01, 0x93, 0x08, 0xbd, 0xac, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x5b, 0x6a, 0x01, 0x93, 0x08, 0xbd, 0xac,
0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x9b, 0x6a, 0x01, 0x93, 0x08, 0xbd, 0xac, 0x01, 0x06, 0x08, 0x08, 0xb5,
0x02, 0x4b, 0x1b, 0x68, 0xdb, 0x6a, 0x01, 0x93, 0x08, 0xbd, 0xac, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x1b,
0x6b, 0x01, 0x93, 0x08, 0xbd, 0xac, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x5b, 0x6b, 0x01, 0x93, 0x08, 0xbd,
0xac, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x9b, 0x69, 0x01, 0x93, 0x08, 0xbd, 0xb4, 0x01, 0x06, 0x08, 0x08,
0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x9b, 0x69, 0x01, 0x93, 0x08, 0xbd, 0xc4, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68,
0x5b, 0x6a, 0x01, 0x93, 0x08, 0xbd, 0xc4, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x9b, 0x6a, 0x01, 0x93, 0x08,
0xbd, 0xc4, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0xdb, 0x6a, 0x01, 0x93, 0x08, 0xbd, 0xc4, 0x01, 0x06, 0x08,
0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x1b, 0x6b, 0x01, 0x93, 0x08, 0xbd, 0xc4, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b,
0x68, 0x5b, 0x6b, 0x01, 0x93, 0x08, 0xbd, 0xc4, 0x01, 0x06, 0x08, 0x08, 0xb5, 0x02, 0x4b, 0x1b, 0x68, 0x9b, 0x6b, 0x01, 0x93,
0x08, 0xbd, 0xc4, 0x01, 0x06, 0x08, 0x03, 0x46, 0x08, 0x46, 0x19, 0x46, 0x00, 0xf0, 0x05, 0xb8, 0x00, 0x21, 0x0a, 0x22, 0x00,
0xf0, 0xd5, 0xb8, 0x00, 0x00, 0x70, 0xb5, 0x06, 0x4c, 0x06, 0x46, 0x0d, 0x46, 0x13, 0x46, 0x2c, 0xb1, 0x31, 0x46, 0x2a, 0x46,
0x02, 0x20, 0xaf, 0xf3, 0x00, 0x80, 0x70, 0xbd, 0x20, 0x46, 0x70, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x10, 0xb5, 0x00, 0x23, 0x93,
0x42, 0x03, 0xd0, 0xcc, 0x5c, 0xc4, 0x54, 0x01, 0x33, 0xf9, 0xe7, 0x10, 0xbd, 0x02, 0x44, 0x03, 0x46, 0x93, 0x42, 0x02, 0xd0,
0x03, 0xf8, 0x01, 0x1b, 0xfa, 0xe7, 0x70, 0x47, 0x10, 0xf8, 0x01, 0x2b, 0x11, 0xf8, 0x01, 0x3b, 0x01, 0x2a, 0x28, 0xbf, 0x9a,
0x42, 0xf7, 0xd0, 0xd0, 0x1a, 0x70, 0x47, 0x03, 0x46, 0x11, 0xf8, 0x01, 0x2b, 0x03, 0xf8, 0x01, 0x2b, 0x00, 0x2a, 0xf9, 0xd1,
0x70, 0x47, 0x03, 0x46, 0x13, 0xf8, 0x01, 0x2b, 0x00, 0x2a, 0xfb, 0xd1, 0x18, 0x1a, 0x01, 0x38, 0x70, 0x47, 0x70, 0xb5, 0x8a,
0xb1, 0x0b, 0x46, 0x05, 0x46, 0x1c, 0x78, 0x10, 0xf8, 0x01, 0x6b, 0x01, 0x31, 0xb4, 0x42, 0x01, 0xd1, 0x01, 0x2a, 0x03, 0xd1,
0x28, 0x78, 0x1b, 0x78, 0xc0, 0x1a, 0x70, 0xbd, 0x01, 0x3a, 0x00, 0x2c, 0xee, 0xd1, 0xf7, 0xe7, 0x10, 0x46, 0x70, 0xbd, 0x00,
0x00, 0x84, 0x46, 0x3f, 0x48, 0x2d, 0xe9, 0xf0, 0x4f, 0xd0, 0xf8, 0x00, 0x80, 0x0e, 0x46, 0x34, 0x46, 0x14, 0xf8, 0x01, 0x5b,
0x08, 0xeb, 0x05, 0x00, 0x40, 0x78, 0x00, 0xf0, 0x08, 0x00, 0x00, 0xf0, 0xff, 0x07, 0x08, 0xb1, 0x26, 0x46, 0xf2, 0xe7, 0x2d,
0x2d, 0x03, 0xd1, 0xb4, 0x1c, 0x75, 0x78, 0x01, 0x27, 0x03, 0xe0, 0x2b, 0x2d, 0x04, 0xbf, 0x75, 0x78, 0xb4, 0x1c, 0x33, 0xf0,
0x10, 0x00, 0x0d, 0xd1, 0x30, 0x2d, 0x08, 0xd1, 0x20, 0x78, 0x00, 0xf0, 0xdf, 0x00, 0x58, 0x28, 0x51, 0xd1, 0x65, 0x78, 0x10,
0x23, 0x02, 0x34, 0x02, 0xe0, 0x00, 0x2b, 0x08, 0xbf, 0x0a, 0x23, 0x00, 0x2f, 0x0c, 0xbf, 0x6f, 0xf0, 0x00, 0x4a, 0x4f, 0xf0,
0x00, 0x4a, 0xba, 0xfb, 0xf3, 0xf9, 0x03, 0xfb, 0x19, 0xaa, 0x00, 0x26, 0x30, 0x46, 0x08, 0xeb, 0x05, 0x0b, 0x9b, 0xf8, 0x01,
0xb0, 0x1b, 0xf0, 0x04, 0x0f, 0x01, 0xd0, 0x30, 0x3d, 0x0b, 0xe0, 0x1b, 0xf0, 0x03, 0x0b, 0x1b, 0xd0, 0xbb, 0xf1, 0x01, 0x0f,
0x14, 0xbf, 0x4f, 0xf0, 0x57, 0x0b, 0x4f, 0xf0, 0x37, 0x0b, 0xcb, 0xeb, 0x05, 0x05, 0x9d, 0x42, 0x10, 0xda, 0xb6, 0xf1, 0xff,
0x3f, 0x0a, 0xd0, 0x48, 0x45, 0x06, 0xd8, 0x01, 0xd1, 0x55, 0x45, 0x03, 0xdc, 0x03, 0xfb, 0x00, 0x50, 0x01, 0x26, 0x01, 0xe0,
0x4f, 0xf0, 0xff, 0x36, 0x14, 0xf8, 0x01, 0x5b, 0xd7, 0xe7, 0x73, 0x1c, 0x0c, 0xd1, 0x00, 0x2f, 0x4f, 0xf0, 0x22, 0x03, 0x0c,
0xbf, 0x6f, 0xf0, 0x00, 0x40, 0x4f, 0xf0, 0x00, 0x40, 0xcc, 0xf8, 0x00, 0x30, 0x2a, 0xb9, 0xbd, 0xe8, 0xf0, 0x8f, 0x07, 0xb1,
0x40, 0x42, 0x42, 0xb1, 0x06, 0xb1, 0x61, 0x1e, 0x11, 0x60, 0xbd, 0xe8, 0xf0, 0x8f, 0x00, 0x2b, 0x08, 0xbf, 0x08, 0x23, 0xb0,
0xe7, 0xbd, 0xe8, 0xf0, 0x8f, 0x00, 0x02, 0x00, 0x20, 0x30, 0xb5, 0x13, 0x46, 0x04, 0x4a, 0x05, 0x46, 0x0c, 0x46, 0x10, 0x68,
0x29, 0x46, 0x22, 0x46, 0xbd, 0xe8, 0x30, 0x40, 0xff, 0xf7, 0x72, 0xbf, 0x64, 0x02, 0x00, 0x20, 0x45, 0x00, 0x0a, 0x08, 0x1d,
0x00, 0x0a, 0x08, 0x85, 0x00, 0x0a, 0x08, 0x89, 0x00, 0x0a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x41, 0x00, 0x48, 0x49,
0x47, 0x48, 0x00, 0x4c, 0x4f, 0x57, 0x00, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x00, 0x64, 0x69,
0x67, 0x69, 0x74, 0x61, 0x6c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x00, 0x61, 0x6e, 0x61, 0x6c, 0x6f, 0x67, 0x72, 0x65, 0x61, 0x64,
0x00, 0x61, 0x6e, 0x61, 0x6c, 0x6f, 0x67, 0x77, 0x72, 0x69, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x6d, 0x05, 0x0a, 0x08, 0x79, 0x05, 0x0a, 0x08, 0x8d, 0x05, 0x0a, 0x08, 0x4d, 0x05, 0x0a, 0x08, 0x93, 0x05,
0x0a, 0x08, 0x97, 0x05, 0x0a, 0x08, 0x9b, 0x05, 0x0a, 0x08, 0x9f, 0x05, 0x0a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xe1, 0x05, 0x0a, 0x08, 0x9d, 0x00, 0x0a, 0x08, 0xa9, 0x00, 0x0a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x5d, 0x06, 0x0a, 0x08, 0x8d, 0x06, 0x0a, 0x08, 0x91, 0x00, 0x0a, 0x08, 0x69, 0x06, 0x0a, 0x08, 0x25,
0x25, 0x25, 0x64, 0x2e, 0x25, 0x64, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x09, 0x0a, 0x08, 0x0d, 0x09, 0x0a, 0x08, 0x39, 0x09, 0x0a, 0x08, 0x69, 0x06, 0x0a, 0x08, 0x21, 0x09, 0x0a,
0x08, 0x2d, 0x09, 0x0a, 0x08, 0x27, 0x09, 0x0a, 0x08, 0x33, 0x09, 0x0a, 0x08, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x28, 0x28, 0x28, 0x28, 0x28, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x88, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x10, 0x10, 0x10, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00,
0x65, 0x03, 0x0a, 0x08, 0x1d, 0x05, 0x0a, 0x08, 0xbd, 0x05, 0x0a, 0x08, 0x35, 0x06, 0x0a, 0x08, 0x25, 0x07, 0x0a, 0x08, 0x71,
0x09, 0x0a, 0x08, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0d, 0x0a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xc1, 0x0e, 0x0a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x02, 0x00, 0x20, 0x00, 0x00, 0x2b, 0xa4, 0xe7, 0x1e, 0x84, 0x0f, 0x59, 0x6b, 0x81, 0x20, 0x03, 0x88, 0x2a,
0xae, 0x7c, 0xa6, 0x49, 0x6f, 0x15, 0x90, 0xca, 0x4a, 0x04, 0x93, 0x10, 0xaf, 0x76, 0xea, 0xf1, 0x1c, 0x94, 0x3a, 0x24, 0x00,
0xba, 0x4f, 0x59, 0xab};

SerialDebugOutput debugOutput(9600, ALL_LEVEL);

void flashTinker(); // forward declaration
bool uploaded = false;


void setup() {
	Serial.begin(9600);
}

void loop() {

	if (!uploaded) {
		uploaded = true;

		delay(5000);
		flashTinker();
	}

}

void flashTinker() {
	FileTransfer::Descriptor file;

	Serial.printlnf("starting flash size=%d", sizeof(tinker));

	file.file_length = sizeof(tinker);
	file.file_address = 0; // Automatically set to HAL_OTA_FlashAddress if store is FIRMWARE
	file.chunk_address = 0;
	file.chunk_size = 0; // use default
	file.store = FileTransfer::Store::FIRMWARE;

	int result = Spark_Prepare_For_Firmware_Update(file, 0, NULL);
	if (result != 0) {
		Serial.printlnf("prepare failed %d", result);
		return;
	}

	Serial.printlnf("chunk_size=%d file_address=0x%x", file.chunk_size, file.file_address);
	if (file.chunk_size == 0) {
		file.chunk_size = 512;
	}

	// Note that Spark_Prepare_For_Firmware_Update sets file.file_address so it's not really zero here
	// even though it's what we initialize it to above!
	file.chunk_address = file.file_address;

	size_t offset = 0;

	while(offset < file.file_length) {
		if (file.chunk_size > (file.file_length - offset)) {
			file.chunk_size = (file.file_length - offset);
		}
		Serial.printlnf("chunk_address=0x%x chunk_size=%d", file.chunk_address, file.chunk_size);
		result = Spark_Save_Firmware_Chunk(file, &tinker[offset], NULL);
		if (result != 0) {
			Serial.printlnf("save chunk failed %d", result);
			return;
		}
		file.chunk_address += file.chunk_size;
		offset += file.chunk_size;
	}

	result = Spark_Finish_Firmware_Update(file, 0x1, NULL);
	if (result != 0) {
		Serial.printlnf("finish failed %d", result);
		return;
	}

	Serial.printlnf("update complete");

}


5 Likes

Thanks for this code. It was very useful!

I was trying to do that in the SoftAP page handler but it didn’t work. I had to save the data and do the upgrade in the main loop and it worked!!!

I documented this here so I’ll be able to refer to it later and it can help others: https://github.com/Suprazz/ParticleUpgradeExample

2 Likes

Am I right to assume that there is no way to do this using Particle Dev? I have to be compiling locally ?
I think it would be a nice feature to set up a FTP server on the Photon, so we can send our .bin files up to it, then flash itself bypassing the whole flash from the cloud feature.

Double posting is not appreciated:
I've answered your parallel question here

Sorry, it was not a double post, I was asking a question and then sharing my intent of what I was going to try to do.

1 Like

@rickkas7

You are everywhere!

You’re awesome contribution at https://github.com/rickkas7/sdfirmwareflash has been great, but I’m trying to update firmware reading and writing directly from OpenLog. So, I’m trying to understand what you are actually sending in Spark_Save_Firmware_Chunk.

From the above code snippet, it looks like a chunk of the tinker binary, but in the following example, you are passing the buf object as a second argument to Spark_Save_Firmware_Chunk. It doesn’t look like buf is ever given the file contents that were read. What is the trick here?

I can't quite follow.
Relative to the position of your quote the above code snippet refers AFAICT to the tinker[] binary Spark_Save_Firmware_Chunk is not set to buf but to &tinker[offset].
On the other hand the snippet below your quote does refer to buf but that actually is filled with contents by the (readily highlighted code line) firmwareFile.read(buf, file.chunk_size) call - that reads the SD file content into the destination buffer.
The file parameter of the Spark_Save_Firmware_Chunk() call is the destination (internal flash) where the contents of the buffer provided as source parameter (second) comes from.

1 Like