Merge "Add a program which returns 0 if it sees 50 a's, and 1 elsewhise"
diff --git a/y2019/vision/BUILD b/y2019/vision/BUILD
index 7300850..0221254 100644
--- a/y2019/vision/BUILD
+++ b/y2019/vision/BUILD
@@ -11,8 +11,8 @@
 
 cc_library(
     name = "constants",
-    hdrs = ["constants.h"],
     srcs = ["constants.cc"],
+    hdrs = ["constants.h"],
 )
 
 cc_library(
@@ -70,40 +70,53 @@
         "//y2019/jevois:serial",
         "//y2019/jevois:structures",
         "//y2019/jevois:uart",
-        "//y2019/jevois/camera:reader",
         "//y2019/jevois/camera:image_stream",
+        "//y2019/jevois/camera:reader",
         "@com_google_ceres_solver//:ceres",
     ],
 )
 
 cc_binary(
+    name = "serial_waiter",
+    srcs = ["serial_waiter.cc"],
+    restricted_to = VISION_TARGETS,
+    deps = [
+        "//aos/time",
+        "//y2019/jevois:serial",
+    ],
+)
+
+cc_binary(
     name = "debug_serial",
     srcs = ["debug_serial.cc"],
     deps = [
+        "//aos/logging",
+        "//aos/logging:implementations",
         "//y2019/jevois:serial",
         "//y2019/jevois:structures",
         "//y2019/jevois:uart",
-         "//aos/logging",
-         "//aos/logging:implementations",
     ],
 )
 
 cc_binary(
     name = "global_calibration",
-    srcs = ["global_calibration.cc", "constants_formatting.cc"],
-    deps = [
-         ":target_finder",
-         "//aos/logging",
-         "//aos/logging:implementations",
-         "//aos/vision/blob:find_blob",
-         "//aos/vision/blob:codec",
-         "//aos/vision/events:epoll_events",
-         "//aos/vision/events:socket_types",
-         "//aos/vision/events:udp",
-         "//aos/vision/image:image_stream",
-         "//aos/vision/image:image_dataset",
-         "//aos/vision/image:reader",
-         "@com_google_ceres_solver//:ceres",
+    srcs = [
+        "constants_formatting.cc",
+        "global_calibration.cc",
     ],
     restricted_to = VISION_TARGETS,
+    deps = [
+        ":target_finder",
+        "//aos/logging",
+        "//aos/logging:implementations",
+        "//aos/vision/blob:codec",
+        "//aos/vision/blob:find_blob",
+        "//aos/vision/events:epoll_events",
+        "//aos/vision/events:socket_types",
+        "//aos/vision/events:udp",
+        "//aos/vision/image:image_dataset",
+        "//aos/vision/image:image_stream",
+        "//aos/vision/image:reader",
+        "@com_google_ceres_solver//:ceres",
+    ],
 )
diff --git a/y2019/vision/serial_waiter.cc b/y2019/vision/serial_waiter.cc
new file mode 100644
index 0000000..551e179
--- /dev/null
+++ b/y2019/vision/serial_waiter.cc
@@ -0,0 +1,48 @@
+#include <unistd.h>
+
+#include <chrono>
+
+#include "y2019/jevois/serial.h"
+#include "aos/time/time.h"
+
+using ::aos::monotonic_clock;
+using ::y2019::jevois::open_via_terminos;
+
+namespace chrono = ::std::chrono;
+
+int main(int /*argc*/, char ** /*argv*/) {
+  int serial_fd = open_via_terminos("/dev/ttyS0");
+
+  // Print out a startup message.  The Teensy will ignore it as a corrupt
+  // packet, but it gives us some warm fuzzies that things are booting right.
+  (void)write(
+      serial_fd,
+      "Starting target_sender in 3 seconds.  Press 10 a's to interrupt.\r\n",
+      66);
+
+  // Give the user 3 seconds to press 10 a's.  If they don't do it in time,
+  // return 0 to signal that we should boot, or 1 that we are asked not to boot.
+  constexpr int kACount = 10;
+  int a_count = 0;
+  const monotonic_clock::time_point end_time =
+      monotonic_clock::now() + chrono::seconds(3);
+  do {
+    constexpr size_t kBufferSize = 16;
+    char data[kBufferSize];
+    ssize_t n = ::read(serial_fd, &data[0], kBufferSize);
+    for (int i = 0; i < n; ++i) {
+      if (data[i] == 'a') {
+        ++a_count;
+        if (a_count > kACount) {
+          ::close(serial_fd);
+          return 1;
+        }
+      } else {
+        a_count = 0;
+      }
+    }
+  } while (monotonic_clock::now() < end_time);
+
+  ::close(serial_fd);
+  return 0;
+}