public static void main(String[] args) throws Exception {
    System.out.println(args.length + " parameter(s).");
    if (args.length > 0) {
      if (args[0].equals("-cal")) {
        calibration = true;
        ansiConsole = false;
      }
    }

    LelandPrototype lp = new LelandPrototype();

    if (!calibration) {
      props = new Properties();
      try {
        props.load(new FileInputStream("props.properties"));
      } catch (IOException ioe) {
        displayAppErr(ioe);
        //  ioe.printStackTrace();
      }

      try {
        windowWidth =
            Integer.parseInt(
                LelandPrototype.getAppProperties()
                    .getProperty("smooth.width", "10")); // For smoothing
        alfa =
            Double.parseDouble(
                LelandPrototype.getAppProperties().getProperty("low.pass.filter.alfa", "0.5"));
      } catch (NumberFormatException nfe) {
        nfe.printStackTrace();
      }

      try {
        cleaningDelay =
            Long.parseLong(props.getProperty("cleaning.delay", "86400")); // Default: one day
      } catch (NumberFormatException nfe) {
        nfe.printStackTrace();
      }

      try {
        nbSeenInARow = Integer.parseInt(props.getProperty("seen.in.a.row", "40"));
      } catch (NumberFormatException nfe) {
        nfe.printStackTrace();
      }

      try {
        rangeSensorHeight = Double.parseDouble(props.getProperty("range.sensor.height", "10"));
      } catch (NumberFormatException nfe) {
        nfe.printStackTrace();
      }

      try {
        fileLogger = new BufferedWriter(new FileWriter(LOG_FILE));
      } catch (Exception ex) {
        ex.printStackTrace();
      }

      if (ansiConsole) AnsiConsole.systemInstall();

      final ReadWriteFONA fona;

      if ("true".equals(props.getProperty("with.fona", "false"))) {
        System.setProperty("baud.rate", props.getProperty("baud.rate"));
        System.setProperty("serial.port", props.getProperty("serial.port"));

        fona = new ReadWriteFONA(lp);
        fona.openSerialInput();
        fona.startListening();
        while (!fonaReady) {
          System.out.println("Waiting for the FONA device to come up...");
          try {
            Thread.sleep(1000L);
          } catch (InterruptedException ie) {
          }
        }
        fona.requestBatteryState();
        fona.requestNetworkStatus();
        displayAppMess(">>> FONA Ready, moving on");
        smsProvider = fona;
      } else {
        System.out.println("Will simulate the phone calls.");
      }
      delay(1);

      wsUri = props.getProperty("ws.uri", "");
      phoneNumber_1 = props.getProperty("phone.number.1", "14153505547");
      phoneNumber_2 = props.getProperty("phone.number.2", "14153505547");
      phoneNumber_3 = props.getProperty("phone.number.3", "14153505547");
      boatName = props.getProperty("boat.name", "Never Again XXIII");

      try {
        rm = new RelayManager();
        rm.set("00", RelayManager.RelayState.ON);
      } catch (Exception ex) {
        System.err.println("You're not on the PI, hey?");
        ex.printStackTrace();
      }

      if (wsUri.trim().startsWith("ws://")) {
        log(">>> Connecting to the WebSocket server [" + wsUri + "]");
        initWebSocketConnection(wsUri);
      } else {
        log(">>> No WebSocket server");
        delay(1);
      }
    }

    final SevenADCChannelsManager sacm = (calibration ? null : new SevenADCChannelsManager(lp));
    final SurfaceDistanceManager sdm = new SurfaceDistanceManager(lp);
    sdm.startListening();

    final Thread me = Thread.currentThread();

    Runtime.getRuntime()
        .addShutdownHook(
            new Thread() {
              public void run() {
                // Cleanup
                System.out.println();
                if (sacm != null) sacm.quit();
                if (smsProvider != null) smsProvider.closeChannel();
                synchronized (me) {
                  me.notify();
                }
                gpio.shutdown();
                if (channelLogger != null) {
                  try {
                    for (BufferedWriter bw : channelLogger) {
                      bw.close();
                    }
                  } catch (Exception ex) {
                    ex.printStackTrace();
                  }
                }
                System.out.println("Program stopped by user's request.");
              }
            });

    if (!calibration) {
      if ("true".equals(props.getProperty("log.channels", "false"))) {
        channelLogger = new BufferedWriter[SevenADCChannelsManager.getChannel().length];
        for (int i = 0; i < SevenADCChannelsManager.getChannel().length; i++) {
          channelLogger[i] =
              new BufferedWriter(
                  new FileWriter(CHANNEL_PREFIX + CHANNEL_NF.format(i) + CHANNEL_SUFFIX));
        }
      }

      // CLS
      if (ansiConsole) AnsiConsole.out.println(EscapeSeq.ANSI_CLS);
    }
    synchronized (me) {
      System.out.println("Main thread waiting...");
      me.wait();
    }
    System.out.println("Done.");
  }
  /**
   * A Sample Command Line Interface (CLI) for FONA/Arduino
   *
   * @param args
   * @throws InterruptedException
   */
  public static void main(String args[]) throws InterruptedException {
    if (args.length > 0) {
      System.out.print("Called with");
      for (String s : args) System.out.println(" " + s);
      System.out.println();
    }
    SampleClient client = new SampleClient();
    final ReadWriteFONA fona = new ReadWriteFONA(client);
    fona.openSerialInput();
    fona.startListening();

    try {
      System.out.println("Hit 'Q' to quit.");
      //    System.out.println("Hit [return] when ready to start.");
      //    userInput("");

      final Thread me = Thread.currentThread();
      Thread userInputThread =
          new Thread() {
            public void run() {
              displayMenu();
              boolean loop = true;
              while (loop) {
                String userInput = "";
                userInput = userInput("So? > ");
                if ("Q".equalsIgnoreCase(userInput)) loop = false;
                else {
                  //  channel.sendSerial(userInput); // Private
                  if ("?".equals(userInput)) displayMenu();
                  else if ("b".equals(userInput)) fona.requestBatteryState();
                  else if ("a".equals(userInput)) fona.requestADC();
                  else if ("C".equals(userInput)) fona.requestSIMCardNumber();
                  else if ("i".equals(userInput)) fona.requestRSSI();
                  else if ("n".equals(userInput)) fona.requestNetworkStatus();
                  else if ("N".equals(userInput)) fona.requestNumberOfMessage();
                  else if ("r".equals(userInput)) {
                    String _smsn = userInput("Mess #:");
                    fona.readMessNum(Integer.parseInt(_smsn));
                  } else if ("d".equals(userInput)) {
                    String _smsn = userInput("Mess #:");
                    fona.deleteMessNum(Integer.parseInt(_smsn));
                  } else if ("s".equals(userInput)) {
                    String to = userInput("Send to > ");
                    String payload = userInput("Message content > ");
                    sendAndWait4Ack(fona, to, payload);
                  } else System.out.println("Duh?");
                }
              }
              synchronized (me) {
                me.notify();
              }
            }
          };
      userInputThread.start();

      synchronized (me) {
        me.wait();
      }
      System.out.println("Bye!");
      fona.closeChannel();
    } catch (SerialPortException ex) {
      System.out.println(" ==>> Serial Setup failed : " + ex.getMessage());
      return;
    }
    System.exit(0);
  }