removed lcd part implemented serial shift register led pins can be driven directly or through shift register

--- ksp-gegi-mini/ksp-gegi-mini.ino
+++ ksp-gegi-mini/ksp-gegi-mini.ino
@@ -1,37 +1,54 @@

 #include <Arduino.h>
-#include <Wire.h>
-// https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads
-#include <LiquidCrystal_I2C.h> // Using version 1.2.1

 // dł kabelków 15-20cm
 // diody LED: niebieskie (bez rezystora, krótka nóżka) do GND, białe (z rezystorem, długa nóżka) do pinu
 // potencjometr: niebieski z czarną kreską do GND, biały do +5V, niebieski do pinu A0 / A1
 // przełączniki2: niebieski do GND, biały do pinu, wszystkie zamontowane tak, aby niebieski był w środku, biały na dole, a niepodłączone u góry (wtedy gałka w górę==załączenie)
 // przełączniki3: środkowy (ciemny) do GND, boczne do pinów
-// LCD: SDA->A4, SCL->A5
+
+// 595:
+// 15  A1 SCRCK(13) // clock
+// 16  A2 SERIN(3)  // data
+// 17  A3 RCK(12)   // latch
+// /SRCLR(8) -> +5V
+// /G (9) -> GND
+// /DRAINx = GND od LEDa (LED niebieskie, bez rezystora, krótka nożka do drain; białe, z rezystorem, długa nóżka do +5V)
+
+const uint8_t clockPin = A1;  // SCRCK (13)
+const uint8_t dataPin  = A2;  // SERIN (3)
+const uint8_t latchPin = A3;  // RCK (12)

 class digitalPin {
-  public:
+  public: // pinOn/Off - 0-127=hw pin, 128+[0..7]=serial pin
     digitalPin(const uint8_t id, const uint8_t pinSwitch, const uint8_t pinOn, const uint8_t pinOff);
     void updateSwitch();
     void updateLedState(const uint8_t c, const uint8_t val);
-    void updateLed(const bool blink) const ;
+    void updateLed(const bool blink) ;
     const uint8_t getId() const { return(m_id); }
+    const uint8_t getOnOffBits() const; // return object where serial bits are on/off according to state m_OnLed+m_Offled
   private:
     const uint8_t m_id, m_pinSwitch, m_pinOn, m_pinOff;
+    uint8_t m_OnLed { LOW };
+    uint8_t m_OffLed { LOW };
     uint8_t m_lastPinState { HIGH } ;
     uint8_t m_lastLedState { 0 } ; // bits: 0=GxON, 1=RxON, 2=GxBLINK, 3=RxBLINK
 };

 digitalPin digiPins[] = {
-  digitalPin(0,4,0,0),    // stage (id, pin switch, pin green LED (on), pin red LED (off)
-  digitalPin(1,5,13,0),   // RCS
-  digitalPin(2,8,9,0),    // SAS
-  digitalPin(3,12,0,0),   // gear
-  digitalPin(4,0,0,6),    // overheat
-  digitalPin(5,0,0,7),    // low power
-  digitalPin(6,0,0,3)     // low fuel
+  digitalPin(0,2,0,0),    // OLED mode1 switch 2
+  digitalPin(1,3,0,0),    // OLED mode2 switch 3
+  digitalPin(2,4,0,0),    // LCD mode1 switch 4
+  digitalPin(3,5,0,0),    // LCD mode2 switch 5
+  digitalPin(4,6,0+0x80,4+0x80),    // light, switch pin 6, green led 0, red led 4
+  digitalPin(5,7,1+0x80,5+0x80),    // gear, switch pin 7, green led 1, red led 5
+  digitalPin(6,8,2+0x80,6+0x80),    // RCS, switch pin 8, green led 2, red led 6
+  digitalPin(7,9,3+0x80,7+0x80),    // SAS, switch pin 9, green led 3, red led 7
+  digitalPin(8,10,0,0),    // stage, switch pin 10
+  digitalPin(9,11,0,0),    // stagemode, switch pin 11
+  digitalPin(10,0,0,12),    // low fuel, yellow led 12
+  digitalPin(11,0,0,13),    // low fuel, yellow led 13
+  digitalPin(12,0,0,A0),    // low fuel, red led 14=A0
 };

 digitalPin::digitalPin(const uint8_t id, const uint8_t pinSwitch, const uint8_t pinOn, const uint8_t pinOff) :
@@ -40,11 +57,11 @@
   if (m_pinSwitch>0) {
     pinMode(m_pinSwitch,INPUT_PULLUP);
   }
-  if (m_pinOn>0) {
+  if (m_pinOn>0 && m_pinOn<128) {
     pinMode(m_pinOn,OUTPUT);
     digitalWrite(m_pinOn,LOW);
   }
-  if (m_pinOff>0) {
+  if (m_pinOff>0 && m_pinOn<128) {
     pinMode(m_pinOff,OUTPUT);
     digitalWrite(m_pinOff,LOW);
   }
@@ -79,101 +96,59 @@
   m_lastLedState = nval;
 }

-void digitalPin::updateLed(const bool blink) const {
+void digitalPin::updateLed(const bool blink) {
   uint8_t nstate;
-  if (m_pinOn>0) {
-    nstate = (m_lastLedState & 0x01) ? HIGH : LOW;
-    if (nstate==HIGH && (m_lastLedState & 0x02) && blink) {
-      nstate = !digitalRead(m_pinOn);
-    }
-//Serial.print("L"); Serial.print(gPin); Serial.print(nstate);
-    digitalWrite(m_pinOn,nstate);
-  }
-  if (m_pinOff>0) {
-    nstate = (m_lastLedState & 0x10) ? HIGH : LOW;
-    if (nstate==HIGH && (m_lastLedState & 0x20) && blink) {
-      nstate = !digitalRead(m_pinOff);
-    }
-//Serial.print("R"); Serial.print(rPin); Serial.print(nstate);
-    digitalWrite(m_pinOff,nstate);
-  }
-}
-
-class analogInPin {
-  public:
-    analogInPin(const uint8_t id, const uint8_t pin, const int threshold);
-    void update();
-  private:
-    const uint8_t m_id, m_pin;
-    const int m_threshold;
-    int m_lastAValue { 0 };
-};
-
-analogInPin analogInPins[] = {
-  analogInPin(0,A0,2),  // throttle
-  analogInPin(1,A1,2)	  // timewarp
-};
-
-analogInPin::analogInPin(const uint8_t id, const uint8_t pin, const int threshold) :
-	m_id(id), m_pin(pin), m_threshold(threshold)
-{
-}
-
-void analogInPin::update() {
-	// map to the range of the analog out
-	int aValue = map(analogRead(m_pin), 0, 1023, 0, 255);
-	// is it different enough from the last reading?
-	if (abs(aValue-m_lastAValue)>m_threshold) {
-		if (aValue<m_threshold) {
-			aValue = 0;
-		}
-		if (aValue>255-m_threshold) {
-			aValue = 255;
-		}
-    // dump status
-	Serial.print("P");
-	Serial.print(m_id);
-	Serial.print("=");
-	Serial.println(aValue,HEX);
-	m_lastAValue = aValue;
-  }
-}
-
-class analogOutPin {
-  public:
-    analogOutPin(const uint8_t id, const uint8_t pin);
-    void updateState(const uint8_t val) const ;
-    const uint8_t getId() const { return(m_id); }
-  private:
-    const uint8_t m_id, m_pin;
-};
-
-analogOutPin::analogOutPin(const uint8_t id, const uint8_t pin) :
-	m_id(id), m_pin(pin)
-{
-}
-
-void analogOutPin::updateState(const uint8_t val) const {
-	analogWrite(m_pin,val);
-}
-
-analogOutPin analogOutPins[] = {
-  analogOutPin(0,10),	// g-force
-  analogOutPin(0,11)  // electrical power
-};
+  nstate = (m_lastLedState & 0x01) ? HIGH : LOW;
+  if (nstate==HIGH && (m_lastLedState & 0x02) && blink) {
+    nstate = !m_OnLed;
+  }
+  m_OnLed = nstate;
+  if (m_pinOn>0 && m_pinOn<128) {
+    digitalWrite(m_pinOn,m_OnLed);
+  }
+
+  nstate = (m_lastLedState & 0x10) ? HIGH : LOW;
+  if (nstate==HIGH && (m_lastLedState & 0x20) && blink) {
+    nstate = !m_OffLed;
+  }
+  m_OffLed = nstate;
+  if (m_pinOff>0 && m_pinOff<128) {
+    digitalWrite(m_pinOff,m_OffLed);
+  }
+}
+
+const uint8_t digitalPin::getOnOffBits() const {
+  uint8_t nstate = 0;
+  if (m_pinOn>=128 && m_OnLed) {
+    nstate |= 1 << (m_pinOn & 0x07);
+  }
+  if (m_pinOff>=128 && m_OffLed) {
+    nstate |= 1 << (m_pinOff & 0x07);
+  }
+  return(nstate);
+}

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

-// Set the pins on the I2C chip used for LCD connections:
-//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
-LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
+void serialShiftWrite(uint8_t data) {
+  digitalWrite(latchPin, LOW);
+  delayMicroseconds(100);
+  // shift the bytes out:
+  shiftOut(dataPin, clockPin, MSBFIRST, data);
+  delayMicroseconds(100);
+  digitalWrite(latchPin, HIGH);
+}

 // serial port
 #define SERIAL_SPEED 115200

 void setup() {
+  //set pins to output so you can control the shift register
+  pinMode(latchPin, OUTPUT);
+  pinMode(clockPin, OUTPUT);
+  pinMode(dataPin, OUTPUT);
   // initialize serial communications at 9600 bps:
   Serial.begin(SERIAL_SPEED);
   // set timeout to 200ms
@@ -182,22 +157,12 @@
   Serial.println("I");
   // start blink counter
   lastBlink = millis();
-  lcd.begin(16,2);   // initialize the lcd for 16 chars 2 lines, turn on backlight
-  lcd.setCursor(0,0);
-  lcd.print("KSP-Gegi");
-  lcd.setCursor(0,1);
-  lcd.print("Ready for action");
-}
-
-void updateAnalogs() {
-  for (auto &p : analogInPins) {
-    p.update();
-  }
 }

 void updatePins() {
   const unsigned long currentMillis = millis();
   bool blink = false;
+  uint8_t shiftLeds = 0;
   // is it time for tick?
   if (((unsigned long)(currentMillis - lastBlink) >= blinkInterval)) {
     blink = true;
@@ -206,7 +171,9 @@
   for (auto &p : digiPins) {
     p.updateLed(blink);
     p.updateSwitch();
-  }
+    shiftLeds |= p.getOnOffBits();
+  }
+  serialShiftWrite(shiftLeds);
 }

 // commands like: ^L{GR}{id}={val}$
@@ -223,64 +190,18 @@
     }
 }

-// commands like ^A{id}={val}$
-void handleSerialInputAnalogOut() {
-    const uint8_t id = Serial.parseInt();  // skip '='
-    const uint8_t val = Serial.parseInt(); // skip newline
-    for (auto &p : analogOutPins) {
-		  if (p.getId()==id) {
-			  p.updateState(val);
-		  }
-	  }
-}
-
-// commands like ^P{line:0,1}={text0..15}$ (consume all characters until end of the line)
-void handleSerialInputLCD() {
-    uint8_t i=0, c;
-    const uint8_t id = Serial.parseInt();
-    Serial.read(); // skip '='
-//  Serial.print("LCDin"); Serial.println(id);
-	  if (id==0 || id==1) {
-		  lcd.setCursor(0,id);
-		  c = -1;
-		  while (c!='\r' && c!='\n') {
-			  if (Serial.available()>0) {
-				  c = Serial.read();
-			  } else {
-				  c = -1;
-			  }
-			  if (c>0 && c!='\r' && c!='\n' && i<16) {
-          i++;
-				  lcd.write(c);
-          //Serial.print(c,HEX);
-			  }
-		  }
-      for (;i<16;i++) {
-        lcd.write(' ');
-      }
-//    Serial.println("endLCD");
-	}
-}
-
 void checkSerialInput() {
   if (Serial.available()>0) {
     const uint8_t c = Serial.read();
     if (c == 'L') {
 	    handleSerialInputLed();
 	  }
-    if (c == 'A') {
-	    handleSerialInputAnalogOut();
-	  }
-    if (c == 'P') {
-	    handleSerialInputLCD();
-	  }
   }
 }

 void loop() {
   checkSerialInput();
   updatePins();
-  updateAnalogs();
   delay(100);
 }


Basic external command mechanism started

--- DigitalVFO.ino
+++ DigitalVFO.ino
@@ -11,6 +11,7 @@
 // with overflow or underflow propagating to the left.
 ////////////////////////////////////////////////////////////////////////////////

+#include <string.h>
 #include <LiquidCrystal.h>
 #include <EEPROM.h>

@@ -20,7 +21,7 @@
 // Digital VFO program name & version
 const char *ProgramName = "DigitalVFO";
 const char *Version = "1.1";
-const char *MinorVersion = ".1";
+const char *MinorVersion = ".2";
 const char *Callsign = "vk4fawr";

 // display constants - below is for ubiquitous small HD44780 16x2 display
@@ -162,6 +163,11 @@
 // macro to get number of elements in an array
 #define ALEN(a)    (sizeof(a)/sizeof((a)[0]))

+// buffer, etc, to gather external command strings
+#define MAX_COMMAND_LEN   16
+#define COMMAND_END_CHAR    ';'
+char CommandBuffer[MAX_COMMAND_LEN+1];
+int CommandIndex = 0;

 //##############################################################################
 // The VFO state variables and typedefs
@@ -345,6 +351,19 @@
   }

   return "UNKNOWN MODE";
+}
+
+//----------------------------------------
+// Convert a string to uppercase in situ.
+//----------------------------------------
+
+void str2upper(char *str)
+{
+  while (*str)
+  {
+    *str = toupper(*str);
+    ++str;
+  }
 }

@@ -2217,8 +2236,50 @@
 // Standard Arduino loop() function.
 //----------------------------------------

+char * do_external_cmd(char *cmd, int index)
+{
+  char end_char = cmd[index];
+  static char message[128];
+
+  // ensure everything is uppercase
+  str2upper(cmd);
+
+  // if command too long it's illegal
+  if (end_char != COMMAND_END_CHAR)
+  {
+    strcpy(message, "Ignored illegal command: '");
+    strcat(message, cmd);
+    strcat(message, "'");
+    return message;
+  }
+
+  // process the command
+  strcpy(message, "Would execute '");
+  strcat(message, cmd);
+  strcat(message, "'  command");
+  return message;
+}
+
 void loop(void)
 {
+  // gather any commands from controller
+  while (Serial.available())
+  {
+    char ch = Serial.read();
+
+    if (CommandIndex < MAX_COMMAND_LEN)
+    {
+      CommandBuffer[CommandIndex++] = ch;
+    }
+
+    if (ch == COMMAND_END_CHAR)   // if end of command, execute it
+    {
+      CommandBuffer[CommandIndex] = '\0';
+      Serial.printf(F("%s\n"), do_external_cmd(CommandBuffer, CommandIndex-1));
+      CommandIndex = 0;
+    }
+  }
+
   // handle all events in the queue
   while (event_pending() > 0)
   {
@@ -2266,11 +2327,10 @@
         break;
     }

-    // update the display, write changes to EEPROM
+    // update the display
     display_sel_value(VfoFrequency, VfoSelectDigit, NumFreqChars, NumCols - NumFreqChars - 2, 0);
-//    save_to_eeprom();         // FIXME: worry about frequent writes?
-
-    // if online, update DDS-60
+
+    // if online, update DDS-60 and write changes to EEPROM
     if (VfoMode == vfo_Online)
     {
       save_to_eeprom();


Still cleaning up menu handling

Still cleaning up menu handling

--- DigitalVFO.ino
+++ DigitalVFO.ino
@@ -15,7 +15,7 @@
 #include <EEPROM.h>

-//#define DEBUG   // define if debugging
+#define DEBUG   // define if debugging

 // Digital VFO program name & version
@@ -197,7 +197,12 @@
 // Code to handle the menus.
 ////////////////////////////////////////////////////////////////////////////////

+// typedef for a menu display handler
+// this handler draws the entire current screen
 typedef void (*MenuDisplay)(Menu *menu, int slot_num);
+
+// typedef for a menu item action handler
+// this handles selecting a particular item
 typedef void (*MenuAction)(Menu *menu, int slot_num);

 struct Menu
@@ -214,14 +219,16 @@
   int item_index = 0;

   Serial.print("show_menu("); Serial.print(menu->title); Serial.println(")");
-
-  lcd.clear();
-  lcd.setCursor(0, 0);
-  lcd.write(menu->title);
+  // update the menu display
+  Serial.print("Calling menu->display: menu->title=");
+  Serial.print(menu->title);
+  Serial.print(", item_index=");
+  Serial.println(item_index);
   menu->display(menu, item_index);

   while (true)
   {
+    // handle any pending event
     if (queue_len() > 0)
     {
       // get next event and handle it
@@ -253,7 +260,8 @@
           Serial.println("Menu: vfo_Click");
           if (menu->action)
             (*menu->action)(menu, item_index);
-          break;
+          flush_queue();
+          return;
         case vfo_HoldClick:
           Serial.println("Menu: vfo_HoldClick, exit menu");
           flush_queue();
@@ -262,8 +270,8 @@
           Serial.print("Menu: unrecognized event "); Serial.println(event);
           break;
       }
-
-      // update the item display
+
+      // update the menu display
       Serial.print("Calling menu->display: menu->title=");
       Serial.print(menu->title);
       Serial.print(", item_index=");
@@ -658,26 +666,26 @@
 // Define the menus to be used, plus handler code
 //-----

-const char *main_menu_items[2] = {"Save", "Restore"};
-#define NumMainMenuItems (sizeof(main_menu_items)/sizeof(const char *))
-
-Menu main_menu = {"Menu", menu_item_display, menu_item_action,
-                  NumMainMenuItems, main_menu_items};
-
-Menu save_menu = {"Save", save_restore_item_display, save_item_action,
+const char *menu_items[2] = {"Save", "Restore"};
+#define NumMainMenuItems (sizeof(menu_items)/sizeof(const char *))
+
+Menu main_menu = {"Menu", menu_display, menu_select,
+                  NumMainMenuItems, menu_items};
+
+Menu save_menu = {"Save", save_restore_display, save_select,
                   NumSaveSlots, NULL};

-Menu restore_menu = {"Restore", save_restore_item_display, restore_item_action,
+Menu restore_menu = {"Restore", save_restore_display, restore_select,
                      NumSaveSlots, NULL};

-void menu_item_display(Menu *menu, int slot_num)
-{
-  Serial.print("menu_item_display: menu="); Serial.println(menu->title);
-  Serial.print("menu_item_display: slot_num="); Serial.println(slot_num);
-
+void menu_display(Menu *menu, int slot_num)
+{
+  Serial.println("menu_display() called");
+
   // figure out max length of item strings
   int max_len = 0;
+
   Serial.print("menu->num_items="); Serial.println(menu->num_items);
   for (int i = 0; i < menu->num_items; ++ i)
   {
@@ -685,24 +693,29 @@

     if (new_len > max_len)
         max_len = new_len;
+
     Serial.print("i="); Serial.print(i);
     Serial.print(", new_len="); Serial.print(new_len);
     Serial.print(", max_len="); Serial.println(max_len);
   }
   Serial.print("Max item len="); Serial.println(max_len);

+  // write menu title on upper row
+  lcd.clear();
+  lcd.setCursor(0, 0);
+  lcd.write(menu->title);
+
   // write indexed item on lower row, right-justified
   lcd.setCursor(0, 1);
-  lcd.write(BlankRow);
+  lcd.setCursor(NUM_COLS - max_len, 1);
+  lcd.write(menu->items[slot_num]);
   Serial.print("Setting cursor to column "); Serial.println(NUM_COLS - max_len);
   Serial.print("item text="); Serial.println(menu->items[slot_num]);
-  lcd.setCursor(NUM_COLS - max_len, 1);
-  lcd.write(menu->items[slot_num]);
-}
-
-void menu_item_action(Menu *menu, int slot_num)
-{
-  Serial.print("menu_item_action: slot_num="); Serial.println(slot_num);
+}
+
+void menu_select(Menu *menu, int slot_num)
+{
+  Serial.print("menu_select: slot_num="); Serial.println(slot_num);
   const char *action_text = menu->items[slot_num];

   if (strcmp(action_text, "Save") == 0)
@@ -715,25 +728,31 @@
   }
 }

-void save_restore_item_display(Menu *menu, int slot_num)
-{
-  // display data for save frequency slot
-  Serial.print("save_restore_item_display: menu="); Serial.println(menu->title);
-  Serial.print("save_restore_item_display: slot_num="); Serial.println(slot_num);
+// draw the Save or Restore screen
+void save_restore_display(Menu *menu, int slot_num)
+{
+  Serial.print("save_restore_display: menu="); Serial.println(menu->title);
+  Serial.print("save_restore_display: slot_num="); Serial.println(slot_num);

   // max length of item strings is "0: xxxxxxxxHz"
   const int max_len = 13;
   char buf[MAX_FREQ_CHARS + 1];
+
+  // get saved frequency, convert to a display buffer
   int address = SaveSlotBase + slot_num * sizeof(unsigned long);
   unsigned long freq;
-
-  // get saved frequency, convert to a display buffer
+
   EEPROM.get(address, freq);
-  Serial.print("save_restore_item_display: freq="); Serial.println(freq);
   ltobbuff(buf, MAX_FREQ_CHARS, freq);
   buf[MAX_FREQ_CHARS] = 0;
-  Serial.print("save_restore_item_display: buf="); Serial.write(buf, MAX_FREQ_CHARS);
+  Serial.print("save_restore_display: freq="); Serial.println(freq);
+  Serial.print("save_restore_display: buf="); Serial.write(buf, MAX_FREQ_CHARS);
   Serial.println("");
+
+  // write menu title on first row
+  lcd.clear();
+  lcd.setCursor(0, 0);
+  lcd.write(menu->title);

   // write indexed item on lower row, right-justified
   lcd.setCursor(0, 1);
@@ -745,20 +764,25 @@
   lcd.write("Hz");
 }

-void save_item_action(Menu *menu, int slot_num)
+void save_select(Menu *menu, int slot_num)
 {
   int address = SaveSlotBase + slot_num * sizeof(unsigned long);

-  Serial.print("save_item_action: saving Frequency "); Serial.print(VfoFrequency);
+  Serial.print("save_select: saving Frequency "); Serial.print(VfoFrequency);
   Serial.print(" to slot "); Serial.print(slot_num);
-  Serial.print("="); Serial.println(address);
-
+  Serial.print("="); Serial.println(address, HEX);
   EEPROM.put(address, VfoFrequency);
 }

-void restore_item_action(Menu *menu, int slot_num)
-{
-  Serial.print("restore_item_action: slot_num="); Serial.println(slot_num);
+void restore_select(Menu *menu, int slot_num)
+{
+  Serial.print("restore_select: slot_num="); Serial.println(slot_num);
+  int address = SaveSlotBase + slot_num * sizeof(unsigned long);
+
+  EEPROM.get(address, VfoFrequency);
+  Serial.print("restore_select: restoring frequency "); Serial.print(VfoFrequency);
+  Serial.print(" from slot "); Serial.print(slot_num);
+  Serial.print("="); Serial.println(address, HEX);
 }

 //----------------------------------------------------------


Calibrated the battery monitoring, a bit

--- DigitalVFO.ino
+++ DigitalVFO.ino
@@ -17,6 +17,14 @@

 //-----------------
+// #defines controlling configuration
+// set to "1" to turn on feature
+//-----------------
+
+// show voltage on screen next to battery symbol
+#define SHOW_VOLTAGE  0
+
+//-----------------
 // debug bit masks
 //-----------------
 #define DEBUG_DDS     (1 << 0)  // DDS debug
@@ -30,8 +38,8 @@
 #define DEBUG_BATT    (1 << 8)  // battery

 // DEBUG word for debugging program - bitmask values
-#define DEBUG         (DEBUG_BATT)
-//#define DEBUG         0
+//#define DEBUG         (DEBUG_BATT)
+#define DEBUG         0

 // Digital VFO program name & version
 const char *ProgramName = "DigitalVFO";
@@ -204,8 +212,10 @@
 const int MaxOffsetDigits = 5;

 // battery voltage limits
-const float MaxVoltage = 8.0;   // battery voltage for "100% full"
-const float MinVoltage = 6.4;   // battery voltage for "0% full"
+const float OverVoltage = 8.35;   // battery voltage for CHARGING FULL
+const float MaxVoltage = 7.8;     // battery voltage for "100% full"
+const float MinVoltage = 6.4;     // battery voltage for "0% full"
+const float NoBattVoltage = 4.6;  // if at or below this, no battery

 // macro to get number of elements in an array
 #define ALEN(a)    (sizeof(a)/sizeof((a)[0]))
@@ -1160,6 +1170,13 @@

 void display_battery(void)
 {
+#if SHOW_VOLTAGE
+  // display measured voltage
+  sprintf(FreqBuffer, "%.2fv", MeasuredVoltage);
+  lcd.setCursor(9, 1);
+  lcd.print(FreqBuffer);
+#endif
+
   // create special battery character
   lcd.createChar(BatteryChar, BatterySymbol);

@@ -2700,8 +2717,10 @@
 // If DEBUG reporting is turned on, we report every "ReportVoltageDelay" measurements.
 //----------------------------------------

-int batt_report_count = 0;
-
+#if (DEBUG & DEBUG_BATT)
+  int batt_report_count = 0;
+#endif
+
 void measure_battery(void)
 {
   // get the time since the last measurement
@@ -2718,56 +2737,58 @@
   last_volts_time = now_milli;

   // measure voltage, we will get a value of 1023 for 3.3 volts
-  // adjust the divider (32.0/9.92) to calibrate
+  // adjust the divider to calibrate
+  // with my crappy multimeter:  (32.11/9.90)
   UINT measured = analogRead(mc_BattVolts);
-  MeasuredVoltage = (3.3 * measured) / 1023 * 32.0/9.92;
+  MeasuredVoltage = (3.3 * measured) / 1023 * 32.11/9.90;
+
+  int percent = (int) ((MeasuredVoltage - MinVoltage) / (MaxVoltage - MinVoltage) * 100.0);

 #if (DEBUG & DEBUG_BATT)
   if (++batt_report_count > 0)
   {
-    Serial.printf(F("raw volts=%d, "), measured);
+    Serial.printf(F("raw volts=%d, %.2fv, %d%%, "), measured, MeasuredVoltage, percent);
   }
 #endif

   // figure out which battery symbol to use
-  if (MeasuredVoltage < MinVoltage)
-  {
-    if (MeasuredVoltage < 1.0)
-    {
-      BatterySymbol = battnone;
+  if (MeasuredVoltage < NoBattVoltage)
+  {
+    BatterySymbol = battnone;
 #if (DEBUG & DEBUG_BATT)
-      if (batt_report_count > 0)
-        Serial.printf(F("no battery\n"));
+    if (batt_report_count > 0)
+      Serial.printf(F("no battery\n"));
+      batt_report_count = -ReportVoltageDelay;
 #endif
-    }
-    else
-    {
-      BatterySymbol = battunder;
+  }
+  else if (MeasuredVoltage < MinVoltage)
+  {
+    BatterySymbol = battunder;
 #if (DEBUG & DEBUG_BATT)
-      if (batt_report_count > 0)
-        Serial.printf(F("battery under voltage\n"));
+    if (batt_report_count > 0)
+      Serial.printf(F("battery under voltage\n"));
+      batt_report_count = -ReportVoltageDelay;
 #endif
-    }
-  }
-  else if (MeasuredVoltage > MaxVoltage)
+  }
+  else if (MeasuredVoltage > OverVoltage)
   {
     BatterySymbol = battover;
 #if (DEBUG & DEBUG_BATT)
     if (batt_report_count > 0)
-      Serial.printf(F("battery over voltage\n"));
+      Serial.printf(F("OVER VOLTAGE\n"));
+      batt_report_count = -ReportVoltageDelay;
 #endif
   }
   else
   {
-    int percent = (int) ((MeasuredVoltage - MinVoltage) / (MaxVoltage - MinVoltage) * 100.0);
-    int batt_bucket = (int) (percent/20);
-    BatterySymbol = batt_syms[batt_bucket + 2];
+    // calculate the battery symbol depending on %full
+    int batt_bucket = min((int) (percent/(100/6)), 5);
+    BatterySymbol = batt_syms[batt_bucket + 1];
 #if (DEBUG & DEBUG_BATT)
     if (batt_report_count > 0)
     {
-      Serial.printf(F("actual volts=%f, percent=%d, batt_bucket=%d\n"),
-                      MeasuredVoltage, percent, batt_bucket);
-    batt_report_count = -ReportVoltageDelay;
+      Serial.printf(F("batt_bucket=%d\n"), batt_bucket+1);
+      batt_report_count = -ReportVoltageDelay;
     }
 #endif
   }