added support for downloading to our custom bootloader
diff --git a/bbb_cape/src/bbb/bbb.gyp b/bbb_cape/src/bbb/bbb.gyp
index d6c7d80..e46c99f 100644
--- a/bbb_cape/src/bbb/bbb.gyp
+++ b/bbb_cape/src/bbb/bbb.gyp
@@ -18,13 +18,15 @@
       ],
       'dependencies': [
         '<(AOS)/common/common.gyp:once',
+        '<(AOS)/build/aos.gyp:logging',
+        'byte_io',
       ],
     },
     {
-      'target_name': 'byte_reader',
+      'target_name': 'byte_io',
       'type': 'static_library',
       'sources': [
-        # 'byte_reader.h',
+        # 'byte_io.h',
       ],
       'dependencies': [
         '<(AOS)/common/common.gyp:time',
@@ -43,11 +45,11 @@
       'dependencies': [
         '<(AOS)/build/aos.gyp:logging',
         '<(AOS)/common/common.gyp:time',
-        'byte_reader',
+        'byte_io',
       ],
       'export_dependent_settings': [
         '<(AOS)/common/common.gyp:time',
-        'byte_reader',
+        'byte_io',
       ],
     },
     {
@@ -72,6 +74,7 @@
         'packet_finder',
         '<(AOS)/common/common.gyp:queue_testutils',
         '<(AOS)/common/common.gyp:time',
+        'byte_io',
       ],
     },
     {
@@ -92,16 +95,15 @@
         'packet_finder.cc',
       ],
       'dependencies': [
-        '<(DEPTH)/bbb_cape/src/cape/cape.gyp:cows',
         '<(AOS)/build/aos.gyp:logging',
-        'crc',
         '<(AOS)/common/common.gyp:time',
-        'byte_reader',
+        '<(DEPTH)/bbb_cape/src/cape/cape.gyp:cows',
+        'crc',
+        'byte_io',
       ],
       'export_dependent_settings': [
         '<(AOS)/build/aos.gyp:logging',
         '<(AOS)/common/common.gyp:time',
-        'byte_reader',
       ],
     },
     {
@@ -130,7 +132,6 @@
         'packet_finder',
         'data_struct',
         '<(AOS)/common/common.gyp:time',
-        'gpios',
       ],
     },
     {
@@ -152,21 +153,67 @@
         'sensor_reader.cc',
       ],
       'dependencies': [
-        'uart_reader',
         'packet_finder',
         'data_struct',
+        'cape_manager',
         '<(AOS)/common/common.gyp:time',
+        'hex_byte_reader',
+        'crc',
         'sensor_generation',
         '<(AOS)/linux_code/linux_code.gyp:configuration',
-        'crc',
-        '<(EXTERNALS):stm32flash',
       ],
       'export_dependent_settings': [
-        'uart_reader',
         'packet_finder',
         'data_struct',
+        'cape_manager',
         '<(AOS)/common/common.gyp:time',
       ],
     },
+    {
+      'target_name': 'cape_flasher',
+      'type': 'static_library',
+      'sources': [
+        'cape_flasher.cc',
+      ],
+      'dependencies': [
+        'byte_io',
+        'crc',
+      ],
+    },
+    {
+      'target_name': 'hex_byte_reader',
+      'type': 'static_library',
+      'sources': [
+        'hex_byte_reader.cc',
+      ],
+      'dependencies': [
+        'byte_io',
+        '<(AOS)/common/common.gyp:time',
+        '<(EXTERNALS):stm32flash',
+        '<(AOS)/build/aos.gyp:logging',
+      ],
+      'export_dependent_settings': [
+        'byte_io',
+        '<(AOS)/common/common.gyp:time',
+      ],
+    },
+    {
+      'target_name': 'cape_manager',
+      'type': 'static_library',
+      'sources': [
+        'cape_manager.cc',
+      ],
+      'dependencies': [
+        'gpios',
+        'uart_reader',
+        'cape_flasher',
+        '<(AOS)/common/common.gyp:time',
+        'hex_byte_reader',
+      ],
+      'export_dependent_settings': [
+        'gpios',
+        'uart_reader',
+      ],
+    },
   ],
 }
diff --git a/bbb_cape/src/bbb/byte_io.h b/bbb_cape/src/bbb/byte_io.h
new file mode 100644
index 0000000..41708e9
--- /dev/null
+++ b/bbb_cape/src/bbb/byte_io.h
@@ -0,0 +1,51 @@
+#ifndef BBB_BYTE_READER_H_
+#define BBB_BYTE_READER_H_
+
+#include <sys/types.h>
+
+#include "aos/common/time.h"
+
+namespace bbb {
+
+class ByteReaderInterface {
+ public:
+  virtual ~ByteReaderInterface() {}
+
+  // Implemented by subclasses to provide a data source
+  // for these algorithms.
+  // Returns the number of bytes read, -1 if there is an error in errno, or -2
+  // if reading takes longer than timeout.
+  virtual ssize_t ReadBytes(
+      uint8_t *dest, size_t max_bytes,
+      const ::aos::time::Time &timeout = ::aos::time::Time(0, 0)) = 0;
+};
+
+class ByteWriterInterface {
+ public:
+  virtual ~ByteWriterInterface() {}
+
+  // Implemented by subclasses to actually write the data somewhere.
+  // Returns true if it succeeds or false if it fails and there is an error in
+  // errno.
+  virtual bool WriteBytes(uint8_t *bytes, size_t number_bytes) = 0;
+};
+
+class ByteReaderWriterInterface : public ByteReaderInterface,
+                                  public ByteWriterInterface {};
+
+class ByteReaderAndWriter : public ByteReaderWriterInterface {
+ public:
+  ByteReaderAndWriter(ByteReaderInterface *reader, ByteWriterInterface *writer)
+      : reader_(reader), writer_(writer) {}
+
+  ByteReaderInterface *reader() { return reader_; }
+  ByteWriterInterface *writer() { return writer_; }
+
+ private:
+  ByteReaderInterface *const reader_;
+  ByteWriterInterface *const writer_;
+};
+
+}  // namespace bbb
+
+#endif  // BBB_BYTE_READER_H_
diff --git a/bbb_cape/src/bbb/byte_reader.h b/bbb_cape/src/bbb/byte_reader.h
deleted file mode 100644
index 80bb208..0000000
--- a/bbb_cape/src/bbb/byte_reader.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef BBB_BYTE_READER_H_
-#define BBB_BYTE_READER_H_
-
-#include <sys/types.h>
-
-#include "aos/common/time.h"
-
-namespace bbb {
-
-class ByteReader {
-public:
-  // We have 64-bit ints in some of our data.
-  typedef char __attribute__((aligned(8))) AlignedChar;
-
-  // Implemented by subclasses to provide a data source
-  // for these algorithms.
-  // Returns the number of bytes read, -1 if there is an error in errno, or -2
-  // if reading takes longer than timeout.
-  virtual ssize_t ReadBytes(AlignedChar *dest, size_t max_bytes,
-                            const ::aos::time::Time &timeout_time) = 0;
-};
-
-}  // namespace bbb
-
-#endif  // BBB_BYTE_READER_H_
diff --git a/bbb_cape/src/bbb/cape_flasher.cc b/bbb_cape/src/bbb/cape_flasher.cc
new file mode 100644
index 0000000..744c045
--- /dev/null
+++ b/bbb_cape/src/bbb/cape_flasher.cc
@@ -0,0 +1,97 @@
+#include "bbb/cape_flasher.h"
+
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "aos/common/logging/logging.h"
+
+#include "bbb/crc.h"
+#include "bbb/byte_io.h"
+
+namespace bbb {
+namespace {
+namespace command {
+
+const uint8_t kAck = 0x79;
+const uint8_t kNack = 0x1F;
+
+}  // namespace command
+}  // namespace
+
+CapeFlasher::CapeFlasher(ByteReaderWriterInterface *uart) : uart_(uart) {}
+
+void CapeFlasher::Download(ByteReaderInterface *file) {
+  uint8_t bootloader_receive = 0;
+  uint8_t buffer[256];
+  ssize_t bootloader_read = 0;
+
+  while (bootloader_read <= 0 || bootloader_receive != command::kNack) {
+    bootloader_read = uart_->ReadBytes(&bootloader_receive, 1,
+                                       ::aos::time::Time::InSeconds(2));
+    if (bootloader_read == -1) {
+      LOG(WARNING, "reading from %p (uart) failed with %d: %s\n", uart_, errno,
+          strerror(errno));
+    } else if (bootloader_read == -2) {
+      LOG(WARNING, "timeout reading from uart %p\n", uart_);
+    }
+  }
+
+  while (true) {
+    size_t total_read = 0;
+    bool done = false;
+    while (total_read < sizeof(buffer)) {
+      ssize_t read = file->ReadBytes(buffer, sizeof(buffer));
+      if (read == -2) {
+        if (total_read == 0) return;
+        done = true;
+        memset(buffer + total_read, 0xFF, sizeof(buffer) - total_read);
+        total_read = sizeof(buffer);
+      } else if (read == -1) {
+        LOG(FATAL, "reading from file %p returned error %d: %s\n", file, errno,
+            strerror(errno));
+      } else {
+        total_read += read;
+      }
+    }
+
+    WriteBuffer(buffer, sizeof(buffer));
+    if (done) return;
+  }
+}
+
+void CapeFlasher::WriteBuffer(uint8_t *buffer, size_t size) {
+  while (true) {
+    uart_->WriteBytes(buffer, size);
+    uint32_t checksum = cape::CalculateChecksum(buffer, size);
+    uart_->WriteBytes(reinterpret_cast<uint8_t *>(&checksum),
+                      sizeof(checksum));
+
+    uint8_t bootloader_receive;
+    ssize_t bootloader_read = uart_->ReadBytes(
+        &bootloader_receive, 1, ::aos::time::Time::InSeconds(2));
+    if (bootloader_read == -1) {
+      LOG(WARNING, "reading from %p (uart) failed with %d: %s\n", uart_,
+          errno, strerror(errno));
+    } else if (bootloader_read == -2) {
+      LOG(WARNING, "timeout reading from uart %p\n", uart_);
+      do {
+        uint8_t byte = 0;
+        uart_->WriteBytes(&byte, 1);
+        bootloader_read = uart_->ReadBytes(&bootloader_receive, 1,
+                                           ::aos::time::Time::InSeconds(0.1));
+      } while (bootloader_read <= 0 || bootloader_receive != command::kNack);
+      switch (bootloader_receive) {
+        case command::kAck:
+          return;
+        case command::kNack:
+          break;
+        default:
+          LOG(WARNING, "unknown bootloader response %" PRIu8 "\n",
+              bootloader_receive);
+      }
+    }
+  }
+}
+
+}  // namespace bbb
diff --git a/bbb_cape/src/bbb/cape_flasher.h b/bbb_cape/src/bbb/cape_flasher.h
new file mode 100644
index 0000000..852321e
--- /dev/null
+++ b/bbb_cape/src/bbb/cape_flasher.h
@@ -0,0 +1,35 @@
+#ifndef BBB_CAPE_SRC_BBB_CAPE_FLASHER_H_
+#define BBB_CAPE_SRC_BBB_CAPE_FLASHER_H_
+
+#include <string>
+
+#include "aos/common/macros.h"
+
+namespace bbb {
+
+class ByteReaderInterface;
+class ByteReaderWriterInterface;
+
+// Talks to the custom bootloader on the cape MCU. Expects for the cape to be
+// freshly booted into the bootloader when initialized.
+class CapeFlasher {
+ public:
+  explicit CapeFlasher(ByteReaderWriterInterface *uart);
+
+  // Downloads a file.
+  // file should give the raw bytes to download, starting at the start of the
+  // main code. All reads will be done with a timeout of 0. Stops on the first
+  // timeout.
+  void Download(ByteReaderInterface *file);
+
+ private:
+  void WriteBuffer(uint8_t *buffer, size_t size);
+
+  ByteReaderWriterInterface *const uart_;
+
+  DISALLOW_COPY_AND_ASSIGN(CapeFlasher);
+};
+
+}  // namespace bbb
+
+#endif  // BBB_CAPE_SRC_BBB_CAPE_FLASHER_H_
diff --git a/bbb_cape/src/bbb/cape_manager.cc b/bbb_cape/src/bbb/cape_manager.cc
new file mode 100644
index 0000000..d9b1e28
--- /dev/null
+++ b/bbb_cape/src/bbb/cape_manager.cc
@@ -0,0 +1,35 @@
+#include "bbb/cape_manager.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include "aos/common/time.h"
+
+#include "bbb/cape_flasher.h"
+#include "bbb/hex_byte_reader.h"
+
+namespace bbb {
+
+CapeManager::CapeManager()
+    : uart_(750000), reset_(2, 5, true), custom_bootloader_(2, 3, false) {}
+
+void CapeManager::DownloadHex(const ::std::string &filename) {
+  HexByteReader file(filename);
+  CapeFlasher flasher(&uart_);
+  DoReset(true);
+  flasher.Download(&file);
+  DoReset(false);
+}
+
+void CapeManager::DoReset(bool bootloader) {
+  static constexpr ::aos::time::Time kTimeout =
+      ::aos::time::Time::InSeconds(0.1);
+  reset_.Set(false);
+  ::aos::time::SleepFor(kTimeout);
+  custom_bootloader_.Set(bootloader);
+  ::aos::time::SleepFor(kTimeout);
+  reset_.Set(true);
+  ::aos::time::SleepFor(kTimeout);
+}
+
+}  // namespace bbb
diff --git a/bbb_cape/src/bbb/cape_manager.h b/bbb_cape/src/bbb/cape_manager.h
new file mode 100644
index 0000000..1cc1a57
--- /dev/null
+++ b/bbb_cape/src/bbb/cape_manager.h
@@ -0,0 +1,39 @@
+#ifndef BBB_CAPE_SRC_BBB_CAPE_MANAGER_H_
+#define BBB_CAPE_SRC_BBB_CAPE_MANAGER_H_
+
+#include <string>
+
+#include "aos/common/macros.h"
+
+#include "bbb/gpo.h"
+#include "bbb/uart_reader.h"
+
+namespace bbb {
+
+// Manages the connection to the cape (including GPIOs, running the bootloader,
+// setting up the serial connection, etc).
+class CapeManager {
+ public:
+  CapeManager();
+
+  // Downloads a .hex file using the custom bootloader.
+  void DownloadHex(const ::std::string &filename);
+
+  // Resets the cape.
+  void Reset() { DoReset(false); }
+
+  UartReader *uart() { return &uart_; }
+
+ private:
+  void DoReset(bool bootloader);
+
+  UartReader uart_;
+
+  Gpo reset_, custom_bootloader_;
+
+  DISALLOW_COPY_AND_ASSIGN(CapeManager);
+};
+
+}  // namespace bbb
+
+#endif  // BBB_CAPE_SRC_BBB_CAPE_MANAGER_H_
diff --git a/bbb_cape/src/bbb/crc.cc b/bbb_cape/src/bbb/crc.cc
index b16eb94..7c14706 100644
--- a/bbb_cape/src/bbb/crc.cc
+++ b/bbb_cape/src/bbb/crc.cc
@@ -1,6 +1,13 @@
 #include "bbb/crc.h"
 
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
 #include "aos/common/once.h"
+#include "aos/common/logging/logging.h"
+
+#include "bbb/byte_io.h"
 
 // There are various implementations that look a lot like this scattered around
 // the internet.
@@ -26,11 +33,13 @@
 
 }  // namespace
 
-uint32_t CalculateChecksum(uint8_t *data, size_t length) {
+uint32_t CalculateChecksum(uint8_t *data, size_t length, uint32_t initial) {
+  assert((length % 4) == 0);
+
   static ::aos::Once<const uint32_t> table_once(GenerateTable);
   const uint32_t *const table = table_once.Get();
 
-  uint32_t r = 0xFFFFFFFF;
+  uint32_t r = initial;
 
   for (size_t i = 0; i < (length / 4); ++i) {
     for (int ii = 3; ii >= 0; --ii) {
@@ -41,4 +50,23 @@
   return r;
 }
 
+uint32_t CalculateChecksum(::bbb::ByteReaderInterface *reader) {
+  uint8_t buffer[256];
+  int remainder_bytes = 0;
+  uint32_t checksum = 0xFFFFFFFF;
+  while (true) {
+    ssize_t read = reader->ReadBytes(&buffer[remainder_bytes],
+                                     sizeof(buffer) - remainder_bytes);
+    if (read == -2) return checksum;
+    if (read == -1) {
+      LOG(FATAL, "reader %p failed to read with %d: %s\n",
+          reader, errno, strerror(errno));
+    }
+    size_t checksum_bytes = (read / 4) * 4;
+    checksum = CalculateChecksum(buffer, checksum_bytes, checksum);
+    remainder_bytes = read - checksum_bytes;
+    memmove(buffer, &buffer[checksum_bytes], remainder_bytes);
+  }
+}
+
 }  // namespace cape
diff --git a/bbb_cape/src/bbb/crc.h b/bbb_cape/src/bbb/crc.h
index 7a8eb95..4d1dea7 100644
--- a/bbb_cape/src/bbb/crc.h
+++ b/bbb_cape/src/bbb/crc.h
@@ -4,11 +4,24 @@
 #include <string.h>
 #include <stdint.h>
 
+namespace bbb {
+
+class ByteReaderInterface;
+
+}  // namespace bbb
 namespace cape {
 
 // Calculates a CRC32 checksum for data. This is definitely the same one as the
 // cape MCU does in hardware which seems to be the same one as Ethernet etc use.
-uint32_t CalculateChecksum(uint8_t *data, size_t length);
+// length is the number of bytes of data to read. It must be a multiple of 4.
+// initial can be a previous return value to continue the same checksum over
+// more data.
+uint32_t CalculateChecksum(uint8_t *data, size_t length,
+                           uint32_t initial = 0xFFFFFFFF);
+// Reads all data out of reader and does a checksum over all of it in reasonably
+// sized pieces. Does all of the reads with a timeout of 0. Stops on the first
+// timeout.
+uint32_t CalculateChecksum(::bbb::ByteReaderInterface *reader);
 
 }  // namespace cape
 
diff --git a/bbb_cape/src/bbb/gpi.cc b/bbb_cape/src/bbb/gpi.cc
index 5bb4902..520d985 100644
--- a/bbb_cape/src/bbb/gpi.cc
+++ b/bbb_cape/src/bbb/gpi.cc
@@ -12,6 +12,7 @@
 }
 
 bool Gpi::Read() {
+  rewind(value_handle_);
   int value = fgetc(value_handle_);
   if (value < 0) {
     LOG(FATAL, "fgetc(%p) for pin (%d,%d) failed with %d: %s\n",
diff --git a/bbb_cape/src/bbb/gpios.cc b/bbb_cape/src/bbb/gpios.cc
index 79d38bc..0aca731 100644
--- a/bbb_cape/src/bbb/gpios.cc
+++ b/bbb_cape/src/bbb/gpios.cc
@@ -5,7 +5,7 @@
 #include <errno.h>
 #include <string.h>
 
-#include "aos/common/logging/logging_impl.h"
+#include "aos/common/logging/logging.h"
 
 namespace bbb {
 
diff --git a/bbb_cape/src/bbb/gpo.cc b/bbb_cape/src/bbb/gpo.cc
index b43c4b0..f02ded3 100644
--- a/bbb_cape/src/bbb/gpo.cc
+++ b/bbb_cape/src/bbb/gpo.cc
@@ -12,6 +12,7 @@
     : GpioPin(bank, pin, false, initial_value) {}
 
 void Gpo::Set(bool high) {
+  rewind(value_handle_);
   if (fputc(high ? '1' : '0', value_handle_) < 0) {
     LOG(FATAL, "fputc(%c, %p) for pin (%d,%d) failed with %d: %s\n",
         high ? '1': '0', value_handle_, bank_, pin_, errno, strerror(errno));
diff --git a/bbb_cape/src/bbb/gpo.h b/bbb_cape/src/bbb/gpo.h
index 893fe4a..493ddd3 100644
--- a/bbb_cape/src/bbb/gpo.h
+++ b/bbb_cape/src/bbb/gpo.h
@@ -1,7 +1,7 @@
 #ifndef BBB_CAPE_SRC_BBB_GPO_H_
 #define BBB_CAPE_SRC_BBB_GPO_H_
 
-#include "gpios.h"
+#include "bbb/gpios.h"
 
 namespace bbb {
 
diff --git a/bbb_cape/src/bbb/hex_byte_reader.cc b/bbb_cape/src/bbb/hex_byte_reader.cc
new file mode 100644
index 0000000..968e771
--- /dev/null
+++ b/bbb_cape/src/bbb/hex_byte_reader.cc
@@ -0,0 +1,52 @@
+#include "bbb/hex_byte_reader.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include "stm32flash/parsers/parser.h"
+#include "stm32flash/parsers/hex.h"
+
+#include "aos/common/logging/logging.h"
+
+namespace bbb {
+namespace {
+
+const parser_t kParser = PARSER_HEX;
+
+__attribute__((noreturn)) void DieParserError(parser_err_t perr) {
+  if (perr == PARSER_ERR_SYSTEM) {
+    LOG(ERROR, "%d: %s\n", errno, strerror(errno));
+  }
+  LOG(FATAL, "%s error: %s\n", kParser.name, parser_errstr(perr));
+}
+
+}  // namespace
+
+HexByteReader::HexByteReader(const ::std::string &filename)
+    : parser_status_(kParser.init()) {
+  if (parser_status_ == NULL) {
+    LOG(FATAL, "%s parser failed to initialize.\n", kParser.name);
+  }
+  parser_err_t perr = kParser.open(parser_status_, filename.c_str(), 0);
+  if (perr != PARSER_ERR_OK) {
+    DieParserError(perr);
+  }
+}
+
+ssize_t HexByteReader::ReadBytes(uint8_t *dest, size_t max_bytes,
+                                 const ::aos::time::Time &/*timeout*/) {
+  unsigned int bytes = max_bytes;
+  parser_err_t perr = kParser.read(parser_status_, dest, &bytes);
+  if (perr != PARSER_ERR_OK) {
+    if (perr == PARSER_ERR_SYSTEM) return -1;
+    DieParserError(perr);
+  }
+  if (bytes == 0) return -2;
+  return bytes;
+}
+
+unsigned int HexByteReader::GetSize() {
+  return kParser.size(parser_status_);
+}
+
+}  // namespace bbb
diff --git a/bbb_cape/src/bbb/hex_byte_reader.h b/bbb_cape/src/bbb/hex_byte_reader.h
new file mode 100644
index 0000000..8f44c9a
--- /dev/null
+++ b/bbb_cape/src/bbb/hex_byte_reader.h
@@ -0,0 +1,30 @@
+#ifndef BBB_CAPE_SRC_BBB_HEX_BYTE_READER_H_
+#define BBB_CAPE_SRC_BBB_HEX_BYTE_READER_H_
+
+#include <string>
+
+#include "aos/common/time.h"
+
+#include "bbb/byte_io.h"
+
+namespace bbb {
+
+// Reads bytes from a .hex file.
+class HexByteReader : public ByteReaderInterface {
+ public:
+  explicit HexByteReader(const ::std::string &filename);
+  virtual ~HexByteReader() {}
+
+  virtual ssize_t ReadBytes(uint8_t *dest, size_t max_bytes,
+                            const ::aos::time::Time &timeout) override;
+
+  // Returns the total number of bytes that we will eventually read out.
+  unsigned int GetSize();
+
+ private:
+  void *const parser_status_;
+};
+
+}  // namespace bbb
+
+#endif  // BBB_CAPE_SRC_BBB_HEX_BYTE_READER_H_
diff --git a/bbb_cape/src/bbb/packet_finder.cc b/bbb_cape/src/bbb/packet_finder.cc
index 713ea69..aac6a0f 100644
--- a/bbb_cape/src/bbb/packet_finder.cc
+++ b/bbb_cape/src/bbb/packet_finder.cc
@@ -3,18 +3,21 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include <algorithm>
 
 #include "aos/common/logging/logging.h"
-#include "bbb_cape/src/cape/cows.h"
+
+#include "cape/cows.h"
 #include "bbb/crc.h"
+#include "bbb/byte_io.h"
 
 using ::aos::time::Time;
 
 namespace bbb {
 
-PacketFinder::PacketFinder(ByteReader *reader, size_t packet_size)
+PacketFinder::PacketFinder(ByteReaderInterface *reader, size_t packet_size)
     : reader_(reader),
       packet_size_(packet_size),
       buf_(new AlignedChar[packet_size_]),
@@ -32,8 +35,9 @@
   int zeros_found = 0;
   while (true) {
     size_t already_read = ::std::max(0, packet_bytes_);
-    ssize_t new_bytes = reader_->ReadBytes(
-        buf_ + already_read, packet_size_ - already_read, timeout_time);
+    ssize_t new_bytes =
+        reader_->ReadBytes((uint8_t *)(buf_ + already_read),
+                           packet_size_ - already_read, timeout_time);
     if (new_bytes < 0) {
       if (new_bytes == -1) {
         LOG(ERROR, "ReadBytes(%p, %zd) failed with %d: %s\n",
diff --git a/bbb_cape/src/bbb/packet_finder.h b/bbb_cape/src/bbb/packet_finder.h
index 9b74583..80cb8b8 100644
--- a/bbb_cape/src/bbb/packet_finder.h
+++ b/bbb_cape/src/bbb/packet_finder.h
@@ -8,15 +8,15 @@
 #include "aos/common/time.h"
 #include "aos/common/macros.h"
 
-#include "bbb/byte_reader.h"
-
 namespace bbb {
 
+class ByteReaderInterface;
+
 class PacketFinder {
  public:
   // *reader has to stay alive for the entire lifetime of this object but this
   // object does not take ownership.
-  explicit PacketFinder(ByteReader *reader, size_t packet_size);
+  PacketFinder(ByteReaderInterface *reader, size_t packet_size);
   ~PacketFinder();
 
   // Returns true if it succeeds or false if it gets an I/O error (or timeout)
@@ -37,7 +37,8 @@
   }
 
  private:
-  typedef ByteReader::AlignedChar AlignedChar;
+  // We have 64-bit ints in some of our data.
+  typedef char __attribute__((aligned(8))) AlignedChar;
 
   // Reads bytes until there are 4 zeros and then fills up buf_.
   // Returns true if it finds one or false if it gets an I/O error first or the
@@ -50,7 +51,7 @@
   // data.
   bool ProcessPacket();
 
-  ByteReader *const reader_;
+  ByteReaderInterface *const reader_;
   const size_t packet_size_;
 
   AlignedChar *const buf_;
diff --git a/bbb_cape/src/bbb/packet_finder_test.cc b/bbb_cape/src/bbb/packet_finder_test.cc
index 437bf8d..b9dd97a 100644
--- a/bbb_cape/src/bbb/packet_finder_test.cc
+++ b/bbb_cape/src/bbb/packet_finder_test.cc
@@ -5,7 +5,7 @@
 #include "aos/common/queue_testutils.h"
 #include "aos/common/time.h"
 
-#include "bbb/byte_reader.h"
+#include "bbb/byte_io.h"
 
 namespace bbb {
 namespace testing {
@@ -17,12 +17,12 @@
   }
 };
 
-class TestByteReader : public ByteReader {
+class TestByteReader : public ByteReaderInterface {
  public:
   TestByteReader(const void *data, size_t data_size)
       : data_(data), data_size_(data_size), bytes_left_(data_size) {}
 
-  virtual ssize_t ReadBytes(AlignedChar *dest, size_t max_bytes,
+  virtual ssize_t ReadBytes(uint8_t *dest, size_t max_bytes,
                             const ::aos::time::Time &/*timeout_time*/)
       override {
     size_t to_transfer = ::std::min(max_bytes, bytes_left_);
diff --git a/bbb_cape/src/bbb/sensor_reader.cc b/bbb_cape/src/bbb/sensor_reader.cc
index a685881..13d6add 100644
--- a/bbb_cape/src/bbb/sensor_reader.cc
+++ b/bbb_cape/src/bbb/sensor_reader.cc
@@ -5,62 +5,30 @@
 #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"
+#include "bbb/hex_byte_reader.h"
 
 namespace bbb {
 namespace {
 
-__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;
+uint32_t ReadChecksum(const ::std::string &filename) {
+  HexByteReader reader(filename);
+  return ::cape::CalculateChecksum(&reader);
 }
 
 }  // namespace
 
 SensorReader::SensorReader(const ::std::string &cape_code)
-    : reader_(750000),
-      packet_finder_(&reader_, DATA_STRUCT_SEND_SIZE - 4),
-      expected_checksum_(ReadChecksum(cape_code)) {
+    : hex_filename_(::std::string(::aos::configuration::GetRootDirectory()) +
+                    "/main_" + cape_code + ".hex"),
+      manager_(),
+      packet_finder_(manager_.uart(), DATA_STRUCT_SEND_SIZE - 4),
+      expected_checksum_(ReadChecksum(hex_filename_)) {
   static_assert(sizeof(SensorGeneration::reader_pid) >= sizeof(pid_t),
-                "pid_t is really big");
+                "pid_t is really big?");
   ResetHappened();
 }
 
@@ -72,12 +40,20 @@
     ::aos::time::Time next_timeout = last_received_time_ + kTimeout;
     if (next_timeout <= ::aos::time::Time::Now()) {
       LOG(WARNING, "too long since good packet received\n");
-      Reset(false);
+      manager_.Reset();
+      ResetHappened();
     }
     if (packet_finder_.ReadPacket(next_timeout)) {
       last_received_time_ = ::aos::time::Time::Now();
       const DataStruct *data = packet_finder_.get_packet<DataStruct>();
-      // TODO(brians): Check the flash checksum and reflash it if necessary.
+      if (data->flash_checksum != expected_checksum_) {
+        LOG(WARNING, "Cape code checksum is %" PRIu32 ". Expected %" PRIu32
+                     ". Reflashing.\n",
+            data->flash_checksum, expected_checksum_);
+        manager_.DownloadHex(hex_filename_);
+        ResetHappened();
+        continue;
+      }
       if (data->timestamp < last_cape_timestamp_) {
         LOG(WARNING, "cape timestamp decreased: %" PRIu64 " to %" PRIu64 "\n",
             last_cape_timestamp_, data->timestamp);
@@ -94,12 +70,6 @@
   return ::aos::time::Time::InUS(last_cape_timestamp_ * 10);
 }
 
-void SensorReader::Reset(bool reflash) {
-  LOG(INFO, "Reset(%s)\n", reflash ? "true" : "false");
-  // TODO(brians): Actually reset it and maybe reflash it.
-  ResetHappened();
-}
-
 void SensorReader::ResetHappened() {
   sensor_generation.MakeWithBuilder().reader_pid(getpid())
       .cape_resets(cape_resets_++).Send();
diff --git a/bbb_cape/src/bbb/sensor_reader.h b/bbb_cape/src/bbb/sensor_reader.h
index a1b489d..48c863a 100644
--- a/bbb_cape/src/bbb/sensor_reader.h
+++ b/bbb_cape/src/bbb/sensor_reader.h
@@ -8,9 +8,9 @@
 #include "aos/common/time.h"
 #include "aos/common/macros.h"
 
-#include "bbb/uart_reader.h"
 #include "bbb/packet_finder.h"
 #include "bbb/data_struct.h"
+#include "bbb/cape_manager.h"
 
 namespace bbb {
 
@@ -25,7 +25,7 @@
 
   // cape_code is the name of the code that should be deployed to the cape if
   // it's not already there.
-  SensorReader(const ::std::string &cape_code);
+  explicit SensorReader(const ::std::string &cape_code);
 
   // Reads in 1 data packet, handles the gyro data in it, and returns a pointer
   // to it.
@@ -40,7 +40,9 @@
   // Called after a reset happens.
   void ResetHappened();
 
-  UartReader reader_;
+  const ::std::string hex_filename_;
+
+  CapeManager manager_;
   PacketFinder packet_finder_;
 
   const uint32_t expected_checksum_;
diff --git a/bbb_cape/src/bbb/uart_reader.cc b/bbb_cape/src/bbb/uart_reader.cc
index cebe127..a82faa1 100644
--- a/bbb_cape/src/bbb/uart_reader.cc
+++ b/bbb_cape/src/bbb/uart_reader.cc
@@ -8,11 +8,11 @@
 #include "aos/common/logging/logging.h"
 
 // This is the code for receiving data from the cape via UART.
-// NOTE: In order for this to work, you MUST HAVE
-// "capemgr.enable_partno=BB_UART1"
-// in your BBB's /media/BEAGLEBONE/uEnv.txt file!
-// `su -c "echo BB-UART1 > /sys/devices/bone_capemgr.*/slots"` works too, but
-// you have to do it every time.
+// NOTE: In order for this to work, you must have the BB-UART1 device tree
+// fragment active.
+// `su -c "echo BB-UART1 > /sys/devices/bone_capemgr.*/slots"` works, but
+// you have to do it every time. It is also possible to set it up to do that
+// every time it boots.
 
 namespace bbb {
 namespace {
@@ -53,7 +53,7 @@
   if (fd_ > 0) close(fd_);
 }
 
-ssize_t UartReader::ReadBytes(AlignedChar *dest, size_t max_bytes,
+ssize_t UartReader::ReadBytes(uint8_t *dest, size_t max_bytes,
                               const ::aos::time::Time &timeout_time) {
   do {
     ::aos::time::Time timeout = timeout_time - ::aos::time::Time::Now();
@@ -76,4 +76,14 @@
   return -1;
 }
 
+bool UartReader::WriteBytes(uint8_t *bytes, size_t number_bytes) {
+  size_t written = 0;
+  while (written < number_bytes) {
+    ssize_t r = write(fd_, &bytes[written], number_bytes - written);
+    if (r == -1) return false;
+    written += r;
+  }
+  return true;
+}
+
 }  // namespace bbb
diff --git a/bbb_cape/src/bbb/uart_reader.h b/bbb_cape/src/bbb/uart_reader.h
index c441167..a6cf34c 100644
--- a/bbb_cape/src/bbb/uart_reader.h
+++ b/bbb_cape/src/bbb/uart_reader.h
@@ -8,17 +8,18 @@
 #include "aos/common/time.h"
 #include "aos/common/macros.h"
 
-#include "bbb/byte_reader.h"
+#include "bbb/byte_io.h"
 
 namespace bbb {
 
-class UartReader : public ByteReader {
+class UartReader : public ByteReaderWriterInterface {
  public:
-  UartReader(int32_t baud_rate);
+  explicit UartReader(int32_t baud_rate);
   virtual ~UartReader();
 
-  virtual ssize_t ReadBytes(AlignedChar *dest, size_t max_bytes,
+  virtual ssize_t ReadBytes(uint8_t *dest, size_t max_bytes,
                             const ::aos::time::Time &timeout_time) override;
+  virtual bool WriteBytes(uint8_t *bytes, size_t number_bytes) override;
 
  private:
   const int fd_;
diff --git a/bbb_cape/src/cape/bootloader.c b/bbb_cape/src/cape/bootloader.c
index 199a3d1..39f3080 100644
--- a/bbb_cape/src/cape/bootloader.c
+++ b/bbb_cape/src/cape/bootloader.c
@@ -4,6 +4,7 @@
 
 #include "cape/bootloader_handoff.h"
 #include "cape/led.h"
+#include "cape/util.h"
 
 // Actually runs the bootloader code.
 // Implemented in bootloader_impl.c.
@@ -64,6 +65,7 @@
       RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;
   led_init();
   led_set(LED_HB, 1);
+  gpio_set_pupd(GPIOC, 2, 2);
 
   setup_main_clock();
 
@@ -72,8 +74,8 @@
   while (!(SYSCFG->CMPCR & SYSCFG_CMPCR_READY)) {}  // wait for it to be ready
 
   if (GPIOC->IDR & (1 << 2)) {
-    jump_to_main();
-  } else {
     bootloader_start();
+  } else {
+    jump_to_main();
   }
 }
diff --git a/bbb_cape/src/cape/bootloader_impl.c b/bbb_cape/src/cape/bootloader_impl.c
index 1c1fde4..15d28b1 100644
--- a/bbb_cape/src/cape/bootloader_impl.c
+++ b/bbb_cape/src/cape/bootloader_impl.c
@@ -12,7 +12,7 @@
 // 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
+// The bootloader sends a NACK 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
@@ -22,7 +22,6 @@
 
 #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;
@@ -67,7 +66,7 @@
   int error = 0;
   int bytes_received = 0;
 
-  uart_byte_send(READY);
+  uart_byte_send(NACK);
 
   while (1) {
     // Receive with a 1 second timeout.
diff --git a/bbb_cape/src/cape/util.h b/bbb_cape/src/cape/util.h
index d19a7e9..9188adb 100644
--- a/bbb_cape/src/cape/util.h
+++ b/bbb_cape/src/cape/util.h
@@ -57,6 +57,11 @@
   SET_BITS(port->MODER, 2, 0 /* input */, pin);
 }
 
+// dir: 0 => none, 1 => up, 2 => down
+static inline void gpio_set_pupd(GPIO_TypeDef *port, int pin, int dir) {
+  SET_BITS(port->PUPDR, 2, dir, pin);
+}
+
 // exti is which EXTI line to set
 // port is 0 for A, 1 for B, etc
 static inline void EXTI_set(int exti, int port) {