   lcd.print("Ready for action");
 }
 
-void updateAnalogPin(uint8_t id) {
-	// read in analog value
-	aValue[id] = analogRead(aPins[id]);
-	// map to the range of the analog out
-	aValue[id] = map(aValue[id], 0, 1023, 0, 255);
-	// is it different enough from the last reading?
-	if (abs(aValue[id]-lastAValue[id])>aThreshold[id]) {
-		if (aValue[id]<aThreshold[id]) {
-			aValue[id] = 0;
-		}
-		if (aValue[id]>255-aThreshold[id]) {
-			aValue[id] = 255;
-		}
-    // dump status
-	Serial.print("P");
-	Serial.print(id);
-	Serial.print("=");
-	Serial.println(aValue[id],HEX);
-	lastAValue[id] = aValue[id];
-  }
-}
-
 void updateAnalogs() {
-	for (uint8_t i=0; i<nAPins; i++) {
-		updateAnalogPin(i);
-	}
+  for (auto &p : analogInPins) {
+    p.update();
+  }
 }
 
 void updatePins() {
+  const unsigned long currentMillis = millis();
+  bool blink = false;
+  // is it time for tick?
+  if (((unsigned long)(currentMillis - lastBlink) >= blinkInterval)) {
+    blink = true;
+    lastBlink = currentMillis;
+  }
   for (auto &p : digiPins) {
+    p.updateLed(blink);
     p.updateSwitch();
   }
 }

  }
}

// blinker control
unsigned long lastBlink = 0;
const unsigned long blinkInterval = 500;

// analog control input
const uint8_t nAPins = 2;
const uint8_t aPins[] = {A0, A1};	// pot: throttle, timewarp
int lastAValue[] = {0, 0};
int aValue[] = {0, 0};
const int aThreshold[] = {2, 2};

// analog control output
const uint8_t nAOutPins = 1;
const uint8_t aOutPins[] = {10};	// vmeter: g-force

// Set the pins on the I2C chip used for LCD connections:
//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol

// commands like: ^L{GR}{id}={val}$
// format: "^LG1=0$" turn off green led 1 (RCS), "^LR2=1$" turn off red led 2 (SCS)
void handleSerialInputLed() {
    const uint8_t c = Serial.read();
    const uint8_t id = Serial.parseInt();  // skip '='
    const uint8_t val = Serial.parseInt(); // skip newline
    for (auto &p : digiPins) {
      if (p.getId()==id) {
        p.updateLedState(c, val);
