 
 void six_in(int value, byte *target) {
   for (int i = 5; i >= 0; i--, value /= 10) target[i] = '0'+(value%10);
+}
+
+int six_out(byte *target) {
+  int s = 0;
+  for (int i = 0; i < 6; i++) {
+    byte c = target[i];
+    if (c < '0' || c > '9') return -1;
+    s = 10*s + (c-'0');
+  }
+  return s;
+}
+
+void parse_time_error() {
+  halt_channels();
+  memcpy(errormsg, "bad time    ", 12);
+  running = -1;
+}
+
+Dura parse_time(byte *target) {
+  Dura result;
+  result.s = 0;
+  result.u = 0;
+  int s = six_out(target);
+  if (s < 0) parse_time_error();
+  else {
+    byte c = target[6];
+    if (c == 's') result.s = s;
+    else if (c == 'u') result.u = s;
+    else if (c == 'm') { result.s = s/1000; result.u = s%1000; }
+    else parse_time_error();
+  }
+  return result;
 }
 
 volatile int count_me;

  if (running < 0) {
    msg[i++] = '!';
    for (int j = 0; j < errormsgN; j++, i++) msg[i] = errormsg[j];
  }
  else if (running == 0) {
    byte zero[] = "000000s000000u";
    for (int j = 0; j < (int)sizeof(zero); j++, i++) msg[i] = zero[j];
  }
  else {
    six_in(global_clock.s, msg + i);

  ARM_DEMCR |= ARM_DEMCR_TRCENA;
  ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
  for (int i = 0; i < PROT; i++) protocols[i].next = 255;
  for (int j = 0; j < CHAN; j++) {
    channels[j].protocol = 255;
    if (j < DIG) channels[j].disabled = (j >= digiN) ? 1 : 0;
    else channels[j].disabled = (waveN < 1) ? 1 : 0;
  }
  running = 0;
  blinking.s = 0;
  blinking.u = 1000;   // 1 ms delay before doing anything
  pending_shift.s = 0;
  pending_shift.u = 0;
  tick = micros();
  bufi = 0;
}
