diff --git a/.gitmodules b/.gitmodules
index 52708f8..7a3370e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,3 +7,7 @@
 [submodule "extras/jsmn/jsmn"]
 	path = extras/jsmn/jsmn
 	url = https://github.com/zserge/jsmn.git
+[submodule "bootloader/rboot"]
+	path = bootloader/rboot
+	url = https://github.com/raburton/rboot.git
+
diff --git a/bootloader/Makefile b/bootloader/Makefile
new file mode 100644
index 0000000..64a0ebc
--- /dev/null
+++ b/bootloader/Makefile
@@ -0,0 +1,71 @@
+# This is a wrapper around the rboot makefile, which gives us the parameters
+# we need to use rboot with esp-open-rtos
+#
+# The wrapper means we don't require esptool2 in the build process, so we can just use
+# esptool.py (still need xxd, grep, sed to generate the header - see below.)
+
+BUILD_DIR ?= build
+FIRMWARE_DIR ?= firmware
+
+# RBOOT configuration parameters.
+# RBOOT_BIG_FLASH is required for esp-open-rtos.
+export RBOOT_BIG_FLASH    = 1
+
+export RBOOT_BUILD_BASE=$(abspath $(BUILD_DIR))
+
+# Default ESPTOOL params, all the same as when using normal esp-open-rtos makefiles
+ESPTOOL ?= esptool.py
+ESPPORT ?= /dev/ttyUSB0
+ESPBAUD ?= 115200
+
+FLASH_SIZE ?= 16
+FLASH_MODE ?= qio
+FLASH_SPEED ?= 40
+
+ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
+
+ifeq ("$(V)","1")
+Q :=
+else
+Q := @
+endif
+
+all: $(FIRMWARE_DIR)/rboot.bin
+
+rboot/Makefile:
+	$(error rboot git submodule is not checkedo out. Try running 'git submodule update --init --recursive')
+
+$(FIRMWARE_DIR)/rboot.bin: $(BUILD_DIR)/rboot.elf $(FIRMWARE_DIR)
+	@echo "FW rboot.bin"
+	$(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(BUILD_DIR)/
+	$(Q) mv $(BUILD_DIR)/0x00000.bin $@
+
+# rboot generates this header using the 'esptool2 -header' option. To try and avoid
+# esptool2 as a dependency, we try it here using grep, sed, xxd (all fairly common Unix tools)
+$(BUILD_DIR)/rboot-hex2a.h: $(BUILD_DIR)/rboot-stage2a.elf $(BUILD_DIR)
+	@echo "Extracting stub image header..."
+	$(Q) xtensa-lx106-elf-objcopy $< --only-section .text -Obinary $(BUILD_DIR)/rboot-hex2a.bin
+	$(Q) xxd -i $(BUILD_DIR)/rboot-hex2a.bin > $@.in
+	$(Q) sed -i "s/unsigned char .\+\[\]/const uint8 _text_data[]/" $@.in
+	$(Q) sed -i "s/unsigned int .\+_len/const uint32 _text_len/" $@.in
+	$(Q) echo "const uint32 entry_addr = $$(xtensa-lx106-elf-objdump -f $< | grep 'start address' | grep -o '0x.\+');" >> $@.in
+	$(Q) echo "const uint32 _text_addr = 0x$$(xtensa-lx106-elf-objdump -h -j .text $< | grep ".text" | grep -o '401.....' | head -n1);" >> $@.in
+	$(Q) mv $@.in $@
+
+$(BUILD_DIR)/rboot-stage2a.elf: $(BUILD_DIR)
+	$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot-stage2a.elf
+
+$(BUILD_DIR)/rboot.elf: $(BUILD_DIR)/rboot-hex2a.h
+	$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot.elf
+
+$(BUILD_DIR) $(FIRMWARE_DIR):
+	$(Q) mkdir -p "$@"
+
+flash: $(FIRMWARE_DIR)/rboot.bin
+	$(Q) $(ESPTOOL) -p $(ESPPORT) -b $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $<
+
+clean:
+	$(Q) rm -rf $(BUILD_DIR)
+	$(Q) rm -rf $(FIRMWARE_DIR)
+
+.PHONY: all clean flash erase_config
diff --git a/bootloader/README.md b/bootloader/README.md
new file mode 100644
index 0000000..6527dba
--- /dev/null
+++ b/bootloader/README.md
@@ -0,0 +1,11 @@
+OTA Bootloader (rboot) source module and support files.
+
+Can be used to build an esp-open-rtos compatible rboot bootloader, for use when OTA=1.
+
+It is also possible to use the upstream rboot verbatim, but *ensure that the `RBOOT_BIG_FLASH` option is enabled or images in slots other than 0 won't work correctly.
+
+rboot is an open source bootloader by Richard Burton:
+https://github.com/raburton/rboot
+
+See the contents of the 'rboot' directory for more information.
+
diff --git a/bootloader/rboot b/bootloader/rboot
new file mode 160000
index 0000000..4cf6132
--- /dev/null
+++ b/bootloader/rboot
@@ -0,0 +1 @@
+Subproject commit 4cf6132f6ee624318c04b2a8d9e37da64b7c283c
diff --git a/common.mk b/common.mk
index 0141a3a..73a998d 100644
--- a/common.mk
+++ b/common.mk
@@ -165,7 +165,7 @@ LINKER_SCRIPTS += $(ROOT)ld/common.ld $(ROOT)ld/rom.ld
 ####
 
 ifndef PROGRAM
-	$(error "Set the PROGRAM environment variable in your Makefile before including common.mk"
+$(error "Set the PROGRAM environment variable in your Makefile before including common.mk")
 endif
 
 # hacky way to get a single space value
@@ -223,7 +223,7 @@ Q := @
 vecho := @echo
 endif
 
-.PHONY: all clean debug_print
+.PHONY: all clean debug_print flash flash_bootloader erase_flash
 
 all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE)
 
@@ -361,6 +361,7 @@ $(FW_FILE_1) $(FW_FILE_2): $(PROGRAM_OUT) $(FIRMWARE_DIR)
 	$(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(FIRMWARE_DIR)
 
 $(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR)
+	$(vecho) "FW $@"
 	$(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE)
 
 ifeq ($(OTA),0)
@@ -372,6 +373,12 @@ flash: $(FW_FILE)
 	$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x2000 $(FW_FILE)
 endif
 
+flash_bootloader:
+	$(MAKE) -C $(ROOT)/bootloader flash
+
+erase_flash:
+	$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash
+
 size: $(PROGRAM_OUT)
 	$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)