   WiFi.mode(WIFI_AP_STA);
   WiFi.softAPConfig(apIP, apIP, netMsk);
   WiFi.softAP(apSSID);
-  Serial.println("Soft AP started");
+  dln(netDBG, "Soft AP started");
 }
 void startDNS() {
   dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
   dnsServer.start(DNS_PORT, "*", apIP);
-  Serial.println("DNS server started");
+  dln(netDBG, "DNS server started");
 }
 void printIPs() {
-  Serial.print("AP SSID: ");
-  Serial.print(apSSID);
-  Serial.print("; IP address(es): local=");
-  Serial.print(WiFi.localIP());
-  Serial.print("; AP=");
-  Serial.println(WiFi.softAPIP());
+  dbg(netDBG, "AP SSID: ");
+  dbg(netDBG, apSSID);
+  dbg(netDBG, "; IP address(es): local=");
+  dbg(netDBG, WiFi.localIP());
+  dbg(netDBG, "; AP=");
+  dln(netDBG, WiFi.softAPIP());
 }
 void startWebServer() {
   webServer.on("/", handle_root);

  char dryMins = 45;            // mins to leave bed drained per cycle
  // in beds with overflows can be 0; else will be cycle time minus fill time
  long startTime = -1;          // when to start cycling (after boot)    
  bool running = false;         // true when cycling started
  bool gotOverflow = false;     // does the growbed have an overflow?
  char fillLevel = 5;           // cms below the level sensor: drain point

  Valve() { number = counter++; }
  void stateChange(bool newState) { // turn on or off
    Serial.print("valve ");
    Serial.print(number);
    int pumpMcpPin = valvePinMap[number][0];
    int solenoidMcpPin = valvePinMap[number][1];


  webServer.on("/valve2", handle_valve2);
  webServer.on("/valve3", handle_valve3);
  webServer.begin();
  dln(netDBG, "HTTP server started");
}
void handleNotFound() {
  dbg(netDBG, "URI Not Found: ");
  dln(netDBG, webServer.uri());
  // TODO send redirect to /? or just use handle_root?
  webServer.send(200, "text/plain", "URI Not Found");
}
void handle_root() {
  dln(netDBG, "serving page notionally at /");
  String toSend = pageTop;
  toSend += pageTop2;
  toSend += pageDefault;
