started working on automatically downloading cape code
diff --git a/bbb_cape/src/bbb/bbb.gyp b/bbb_cape/src/bbb/bbb.gyp
index 9e24f0c..7c14d3d 100644
--- a/bbb_cape/src/bbb/bbb.gyp
+++ b/bbb_cape/src/bbb/bbb.gyp
@@ -154,6 +154,9 @@
         'data_struct',
         '<(AOS)/common/common.gyp:time',
         'sensor_generation',
+        '<(AOS)/linux_code/linux_code.gyp:configuration',
+        'crc',
+        '<(EXTERNALS):stm32flash',
       ],
       'export_dependent_settings': [
         'uart_reader',
diff --git a/bbb_cape/src/bbb/sensor_reader.cc b/bbb_cape/src/bbb/sensor_reader.cc
index 2c36ae3..a685881 100644
--- a/bbb_cape/src/bbb/sensor_reader.cc
+++ b/bbb_cape/src/bbb/sensor_reader.cc
@@ -3,13 +3,62 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <inttypes.h>
+#include <stdint.h>
+
+#include "stm32flash/parsers/parser.h"
+#include "stm32flash/parsers/hex.h"
+
+#include "aos/linux_code/configuration.h"
 
 #include "bbb/sensor_generation.q.h"
+#include "bbb/crc.h"
 
 namespace bbb {
+namespace {
 
-SensorReader::SensorReader(const ::std::string &/*cape_code*/)
-    : reader_(750000), packet_finder_(&reader_, DATA_STRUCT_SEND_SIZE - 4) {
+__attribute__((noreturn)) void PrintParserError(parser_t *parser,
+                                                parser_err_t perr) {
+  if (perr == PARSER_ERR_SYSTEM) {
+    LOG(ERROR, "%d: %s\n", errno, strerror(errno));
+  }
+  LOG(FATAL, "%s error: %s\n", parser->name, parser_errstr(perr));
+}
+
+uint32_t ReadChecksum(const ::std::string &cape_code) {
+  parser_t *parser = &PARSER_HEX;
+  void *p_st = parser->init();
+  if (p_st == NULL) {
+    LOG(FATAL, "%s parser failed to initialize.\n", parser->name);
+  }
+  ::std::string filename =
+      ::std::string(::aos::configuration::GetRootDirectory()) + "/main_" +
+      cape_code + ".hex";
+  parser_err_t perr = parser->open(p_st, filename.c_str(), 0);
+  if (perr != PARSER_ERR_OK) {
+    PrintParserError(parser, perr);
+  }
+
+  const unsigned int allocated_size = parser->size(p_st);
+  uint8_t *buffer = new uint8_t[allocated_size];
+  unsigned int read_size = allocated_size;
+  if (parser->read(p_st, buffer, &read_size) != PARSER_ERR_OK) {
+    PrintParserError(parser, perr);
+  }
+  if (allocated_size != read_size) {
+    LOG(WARNING, "expected %u bytes but got %u\n", allocated_size, read_size);
+  }
+
+  uint32_t r = ::cape::CalculateChecksum(buffer, read_size);
+  parser->close(p_st);
+  return r;
+}
+
+}  // namespace
+
+SensorReader::SensorReader(const ::std::string &cape_code)
+    : reader_(750000),
+      packet_finder_(&reader_, DATA_STRUCT_SEND_SIZE - 4),
+      expected_checksum_(ReadChecksum(cape_code)) {
   static_assert(sizeof(SensorGeneration::reader_pid) >= sizeof(pid_t),
                 "pid_t is really big");
   ResetHappened();
diff --git a/bbb_cape/src/bbb/sensor_reader.h b/bbb_cape/src/bbb/sensor_reader.h
index 722dfe3..a1b489d 100644
--- a/bbb_cape/src/bbb/sensor_reader.h
+++ b/bbb_cape/src/bbb/sensor_reader.h
@@ -1,6 +1,8 @@
 #ifndef BBB_CAPE_SRC_BBB_SENSOR_READER_H_
 #define BBB_CAPE_SRC_BBB_SENSOR_READER_H_
 
+#include <stdint.h>
+
 #include <string>
 
 #include "aos/common/time.h"
@@ -41,6 +43,8 @@
   UartReader reader_;
   PacketFinder packet_finder_;
 
+  const uint32_t expected_checksum_;
+
   int cape_resets_ = 0;
   ::aos::time::Time last_received_time_ = ::aos::time::Time::InSeconds(0);
   uint64_t last_cape_timestamp_;
diff --git a/bbb_cape/src/cape/Makefile b/bbb_cape/src/cape/Makefile
index 2971e88..ce65f8b 100644
--- a/bbb_cape/src/cape/Makefile
+++ b/bbb_cape/src/cape/Makefile
@@ -53,6 +53,8 @@
 	uart_common \
 	uart_byte \
 	led \
+	bootloader_impl \
+	crc \
 
 OBJECTS_main_test := $(OBJECTS_main_common) \
 	robot_test \
diff --git a/bbb_cape/src/cape/bootloader.c b/bbb_cape/src/cape/bootloader.c
index 3562252..199a3d1 100644
--- a/bbb_cape/src/cape/bootloader.c
+++ b/bbb_cape/src/cape/bootloader.c
@@ -5,6 +5,10 @@
 #include "cape/bootloader_handoff.h"
 #include "cape/led.h"
 
+// Actually runs the bootloader code.
+// Implemented in bootloader_impl.c.
+void bootloader_start(void) __attribute__((noreturn));
+
 // Sets everything up and then jumps to the main code.
 static void jump_to_main(void) __attribute__((noreturn));
 static void jump_to_main(void) {
@@ -54,7 +58,8 @@
 
 void _start(void) {
   // Enable the GPIO pin clocks.
-  // We don't have anything on the 1 port D pin, so don't bother enabling it.
+  // We don't have anything attached to the 1 port D pin, so don't bother
+  // enabling it.
   RCC->AHB1ENR |=
       RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;
   led_init();
@@ -66,5 +71,9 @@
   SYSCFG->CMPCR |= SYSCFG_CMPCR_CMP_PD;  // enable IO compensation cell
   while (!(SYSCFG->CMPCR & SYSCFG_CMPCR_READY)) {}  // wait for it to be ready
 
-  jump_to_main();
+  if (GPIOC->IDR & (1 << 2)) {
+    jump_to_main();
+  } else {
+    bootloader_start();
+  }
 }
diff --git a/bbb_cape/src/cape/bootloader_handoff.h b/bbb_cape/src/cape/bootloader_handoff.h
index 1e48e90..3804d97 100644
--- a/bbb_cape/src/cape/bootloader_handoff.h
+++ b/bbb_cape/src/cape/bootloader_handoff.h
@@ -9,6 +9,10 @@
 // Where the main code's flash starts.
 #define MAIN_FLASH_START BOOTLOADER_FLASH_SIZE
 
+#define MAIN_FLASH_START_SECTOR 1
+#define MAIN_FLASH_END_SECTOR 11
+#define MAIN_FLASH_END 0x100000
+
 #define RAM_START 0x20000000
 #define RAM_SIZE 0x20000
 
diff --git a/bbb_cape/src/cape/bootloader_impl.c b/bbb_cape/src/cape/bootloader_impl.c
new file mode 100644
index 0000000..1c1fde4
--- /dev/null
+++ b/bbb_cape/src/cape/bootloader_impl.c
@@ -0,0 +1,103 @@
+#include "cape/bootloader_handoff.h"
+
+#include <string.h>
+
+#include "cape/uart_byte.h"
+#include "cape/uart_common.h"
+#include "cape/crc.h"
+#include "cape/util.h"
+
+// The protocol is pretty simple. Basically, when the bootloader is started, it
+// expects repeated "packets" of data to write. It starts at MAIN_FLASH_START,
+// erases from MAIN_FLASH_START_SECTOR to MAIN_FLASH_END_SECTOR, and keeps
+// writing until MAIN_FLASH_END (if it gets data).
+//
+// The bootloader sends READY when it is first ready to receive bytes. It then
+// expects DATA_BYTES-sized packets (+ the checksum calculated with the standard
+// CRC algorithm). When it successfully receives one and writes it out, it sends
+// ACK. If it has any errors, it waits until there's a 1-second gap (or it
+// receives all the bytes and sees a checksum error) and then sends a NACK.
+
+#define DATA_BYTES 256
+
+#define ACK 0x79
+#define NACK 0x1F
+#define READY 0x7F
+
+static void process_buffer(uint32_t *buffer) {
+  static uint32_t *out_pointer = (uint32_t *)MAIN_FLASH_START;
+  if ((out_pointer + DATA_BYTES / 4) >= (uint32_t *)MAIN_FLASH_END) return;
+
+  while (FLASH->SR & FLASH_SR_BSY) {}
+  FLASH->CR |= FLASH_CR_PG;
+  for (int i = 0; i < (DATA_BYTES / 4); ++i) {
+    *(out_pointer++) = buffer[i];
+  }
+  while (FLASH->SR & FLASH_SR_BSY) {}
+  FLASH->CR &= ~FLASH_CR_PG;
+}
+
+__attribute__((noreturn)) void bootloader_start(void) {
+  crc_init();
+
+  // Unlock the flash so we can program it.
+  FLASH->KEYR = 0x45670123;
+  FLASH->KEYR = 0xCDEF89AB;
+  while (FLASH->CR & FLASH_CR_LOCK) {}
+
+  FLASH->CR =
+      (FLASH->CR & ~(FLASH_CR_PSIZE_0 | FLASH_CR_PSIZE_1)) | FLASH_CR_PSIZE_1;
+
+  FLASH->CR |= FLASH_CR_SER;
+  for (int i = MAIN_FLASH_START_SECTOR; i <= MAIN_FLASH_END_SECTOR; ++i) {
+    while (FLASH->SR & FLASH_SR_BSY) {}
+    FLASH->CR = (FLASH->CR & ~(FLASH_CR_SNB_0 | FLASH_CR_SNB_1 |
+                               FLASH_CR_SNB_2 | FLASH_CR_SNB_3)) |
+                i << 3;
+    FLASH->CR |= FLASH_CR_STRT;
+    while (FLASH->CR & FLASH_SR_BSY) {}
+  }
+  FLASH->CR &= ~FLASH_CR_SER;
+
+  uart_common_configure(115200);
+  uart_byte_configure();
+
+  uint8_t buffer[DATA_BYTES + 4] __attribute__((aligned(4)));
+  // Whether we've already encountered an error in this block or not.
+  int error = 0;
+  int bytes_received = 0;
+
+  uart_byte_send(READY);
+
+  while (1) {
+    // Receive with a 1 second timeout.
+    int received = uart_byte_receive(60000, 1000 - 1);
+    if (received < 0) {
+      if (received == -1) {
+        uart_byte_send(NACK);
+        error = 0;
+        bytes_received = 0;
+      } else {
+        error = 1;
+      }
+    } else {  // successfully received a byte
+      if (error == 0) {
+        buffer[bytes_received++] = (uint8_t)received;
+        if (bytes_received == sizeof(buffer)) {
+          bytes_received = 0;
+          uint32_t checksum;
+          memcpy(&checksum, &buffer[DATA_BYTES], 4);
+          uint32_t *buffer32;
+          uint8_t *buffer8 = buffer;
+          memcpy(&buffer32, &buffer8, 4);
+          if (crc_calculate(buffer32, DATA_BYTES / 4) != checksum) {
+            uart_byte_send(NACK);
+          } else {
+            process_buffer(buffer32);
+            uart_byte_send(ACK);
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/bbb_cape/src/cape/uart_byte.c b/bbb_cape/src/cape/uart_byte.c
index cfcf1bf..2cd95b4 100644
--- a/bbb_cape/src/cape/uart_byte.c
+++ b/bbb_cape/src/cape/uart_byte.c
@@ -24,8 +24,15 @@
     }
   }
 
+  int r = UART->DR;  // do it now to clear interrupts etc
+
+  if (UART->SR & USART_SR_PE) r = -2;
+  if (UART->SR & USART_SR_FE) r = -3;
+  if (UART->SR & USART_SR_NE) r = -4;
+  if (UART->SR & USART_SR_ORE) r = -5;
+
   TIMEOUT_TIM->CR1 &= ~TIM_CR1_CEN;
-  return UART->DR;
+  return r;
 }
 
 void uart_byte_send(uint8_t value) {
diff --git a/bbb_cape/src/cape/uart_byte.h b/bbb_cape/src/cape/uart_byte.h
index e037e36..3423d7a 100644
--- a/bbb_cape/src/cape/uart_byte.h
+++ b/bbb_cape/src/cape/uart_byte.h
@@ -8,7 +8,13 @@
 
 // Spins until 1 byte is received or some amount of time. The timeout is
 // timeout_count*(timeout_divider+1)/60MHz.
-// The result is <0 for timeout or the received byte.
+// The result is <0 for timeout/error or the received byte.
+// errors:
+//   -1  timeout
+//   -2  parity
+//   -3  framing
+//   -4  noise detected
+//   -5  overrun
 int uart_byte_receive(uint16_t timeout_count, uint16_t timeout_divider);
 
 // Spins until 1 byte can be written out.
diff --git a/bbb_cape/src/cape/uart_common.c b/bbb_cape/src/cape/uart_common.c
index 03d4cdd..f22c597 100644
--- a/bbb_cape/src/cape/uart_common.c
+++ b/bbb_cape/src/cape/uart_common.c
@@ -13,6 +13,12 @@
   gpio_setup_alt(GPIOA, 10, 7);
   RCC->APB2ENR |= RCC_APB2ENR_UARTEN;
 
+  UART->CR1 =
+      //USART_CR1_M /* 9th bit for the parity */ |
+      //USART_CR1_PCE /* enable parity (even by default) */ |
+      //USART_CR1_OVER8 /* support going faster */ |
+      0;
+
   // baud = 60MHz / kMultiplier * (whole_part + fraction / kMultiplier))
   static const int kMultiplier = 16 /* 8 * (2 - OVER8) */;
   // The divisor of FPCLK that we want (*2).
@@ -22,10 +28,6 @@
   // The fractional part of the divisor (*2).
   int fraction = divisor % (kMultiplier * 2);
   UART->BRR = (mantissa << 4) | ((fraction + 1) / 2);
-  UART->CR1 =
-      //USART_CR1_M /* 9th bit for the parity */ |
-      //USART_CR1_PCE /* enable parity (even by default) */ |
-      //USART_CR1_OVER8 /* support going faster */ |
-      0;
+
   UART->CR1 |= USART_CR1_UE;  // enable it
 }
diff --git a/bbb_cape/src/flasher/.gitignore b/bbb_cape/src/flasher/.gitignore
deleted file mode 100644
index 0c665e0..0000000
--- a/bbb_cape/src/flasher/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-stm32flash/
diff --git a/bbb_cape/src/flasher/build.sh b/bbb_cape/src/flasher/build.sh
index ed4937f..ecf6a2b 100755
--- a/bbb_cape/src/flasher/build.sh
+++ b/bbb_cape/src/flasher/build.sh
@@ -1,10 +1,5 @@
 #!/bin/bash
 
-set -e
-
 cd $(dirname $0)
 
-[[ -d stm32flash ]] || ( git clone https://git.gitorious.org/stm32flash/stm32flash.git stm32flash &&
-	cd stm32flash && git checkout 8399fbe1baf2b7d097746786458021d92895d71b )
-
 ../../../aos/build/build.sh linux flasher.gyp no flasher "$@"
diff --git a/bbb_cape/src/flasher/flasher.gyp b/bbb_cape/src/flasher/flasher.gyp
index c9e190e..dea4e5c 100644
--- a/bbb_cape/src/flasher/flasher.gyp
+++ b/bbb_cape/src/flasher/flasher.gyp
@@ -14,24 +14,9 @@
         'stm32_flasher.cc',
       ],
       'dependencies': [
-        'stm32flash',
+        '<(EXTERNALS):stm32flash',
         '<(AOS)/build/aos.gyp:logging',
       ],
     },
-    {
-      'target_name': 'stm32flash',
-      'type': 'static_library',
-      'sources': [
-        'stm32flash/init.c',
-        'stm32flash/parsers/hex.c',
-        'stm32flash/serial_common.c',
-        'stm32flash/serial_platform.c',
-        'stm32flash/utils.c',
-        'stm32flash/stm32.c',
-      ],
-      'cflags': [
-        '-Wno-error',
-      ],
-    },
   ],
 }