From fafc6ef25419eaa64af655add411f162a6b45e82 Mon Sep 17 00:00:00 2001
From: Kevin Lyda <kevin@ie.suberic.net>
Date: Sun, 29 Jan 2017 15:02:50 +0000
Subject: [PATCH] Function stubs done.

---
 devices/geiger/geiger.go |  17 +-
 devices/geiger/gqgmc.go  | 936 ++++++++-------------------------------
 2 files changed, 194 insertions(+), 759 deletions(-)

diff --git a/devices/geiger/geiger.go b/devices/geiger/geiger.go
index 7599d56..7464831 100644
--- a/devices/geiger/geiger.go
+++ b/devices/geiger/geiger.go
@@ -18,7 +18,22 @@ func New(c Config) (Counter, error) {
 
 // Counter is an interface for Geiger Counters
 type Counter interface {
-	GetReading() (*Reading, error)
+	Clear() error
+	Version() (string, error)
+	SerialNum() (string, error)
+	GetCPM() (uint16, error)
+	GetCPS() (uint16, error)
+	GetVoltage() (int16, error)
+	GetHistoryData()
+	TurnOnCPS() error
+	TurnOffCPS() error
+	GetAutoCPS() (uint16, error)
+	TurnOffPower()
+	GetConfiguration()
+	SetConfiguration()
+	ResetConfiguration()
+	SetDate(date string)
+	SetTime(time string)
 }
 
 // Config contain the configuration for a Geiger Counter
diff --git a/devices/geiger/gqgmc.go b/devices/geiger/gqgmc.go
index 18b6b53..77c04d3 100644
--- a/devices/geiger/gqgmc.go
+++ b/devices/geiger/gqgmc.go
@@ -8,11 +8,18 @@
 package geiger
 
 import (
+	"fmt"
 	"time"
 
 	"github.com/tarm/serial"
 )
 
+// The Ideal and Low voltage threshholds
+const (
+	VoltageIdeal  = 98
+	VoltageTooLow = 75
+)
+
 const (
 	powerOnOff                    = 0
 	alarmOnOff                    = 1
@@ -87,745 +94,72 @@ const (
 	saveMax
 )
 
-const (
-	key1 = iota
-	key2
-	key3
-	key4
-)
-
 const (
 	historyDataMaxSize = 0x1000
 	historyAddrMaxSize = 0x10000
-	forFirmware = 2.23
-	kNVMSize = 256
+	forFirmware        = 2.23
+	nvmSize            = 256
 )
 
 const (
-cmdGetSerial        = "<GETSERIAL>>"
-cmdGetVersion       = "<GETVER>>"
-cmdGetVoltage       = "<GETVOLT>>"
-cmdGetCpm           = "<GETCPM>>"
-cmdGetCps           = "<GETCPS>>"
-cmdGetCfg           = "<GETCFG>>"
-cmdEraseCfg         = "<ECFG>>"
-cmdUpdateCfg        = "<CFGUPDATE>>"
-cmdTurnOnCps       = "<HEARTBEAT1>>"
-cmdTurnOffCps      = "<HEARTBEAT0>>"
-cmdTurnOffPwr      = "<POWEROFF>>"
+	cmdGetSerial  = "<GETSERIAL>>"
+	cmdGetVersion = "<GETVER>>"
+	cmdGetVoltage = "<GETVOLT>>"
+	cmdGetCpm     = "<GETCPM>>"
+	cmdGetCps     = "<GETCPS>>"
+	cmdGetCfg     = "<GETCFG>>"
+	cmdEraseCfg   = "<ECFG>>"
+	cmdUpdateCfg  = "<CFGUPDATE>>"
+	cmdTurnOnCps  = "<HEARTBEAT1>>"
+	cmdTurnOffCps = "<HEARTBEAT0>>"
+	cmdTurnOffPwr = "<POWEROFF>>"
 )
 
 const (
-    errOpen = "Failed to open USB port"
-    errFirmware = "GQ GMC has older firmware.  Some commands may not work."
-    errVersion = "Failed to read the version number of the firmware"
-    errSerialNumber = "Failed to read the serial number "
-    errCPM = "Failed to read the counts per minute "
-    errCPS = "Failed to read the counts per second "
-    errAutoCPS = "Failed to read auto counts per second "
-    errCFG = "Failed to get configuration data "
-    errEraseCFG = "Failed to erase configuration data "
-    errUpdateCFG = "Failed to update configuration data "
-    errClear = "Failed to clear USB input buffer. You should power cycle GQ GMC."
-    errBatteryVoltage = "Failed to read the battery voltage "
-    errHistoryData = "Failed to read the history data "
-    errHistoryDataLengthFmt = "Requested data length of the history command cannot exceed %d bytes"
-    errHistoryDataAddressFmt = "Address of the history command cannot exceed %d bytes"
-    errHistoryDataOverrunFmt = "History data length added to the address cannot exceed %d bytes"
-    errYear = "Failed to set year"
-    errMonth = "Failed to set month"
-    errDay = "Failed to set day"
-    errHour = "Failed to set hour"
-    errMinute = "Failed to set minute"
-    errSecond = "Failed to set second"
-    )
-
-// CONFIGURATION DATA
-//
-// Setting the configuration data of the GQ GMC is not a
-// straight forward procedure. The following statement is the
-// principle reason why: the GQ GMC does not use or keep a RAM copy
-// of its non-volatile memory (NVM) (configuration data is in NVM).
-// That condition coupled with the fact that the EEPROM
-// used by the GQ GMC can only be reprogrammed
-// all 256 bytes at a shot means that if you write a byte of
-// configuration data and then read back the configuration data,
-// you will not see your data changed as expected.
-//
-// All this means that in order to change the configuration
-// parameters and have the GQ GMC change its operations accordingly,
-// 1) the host computer must keep its own copy of NVM,
-// 2) update its own copy of NVM,
-// 3) issue the erase configuration command,
-// 4) write all 256 bytes of configuration data at one shot,
-// 5) follow immediately with an update configuration command.
-//
-// When the GQ GMC receives the update configuration command,
-// then and only then does it re-configure its operation in
-// accordance with the NVM configuration data. Keeping the host
-// computer's copy of the NVM accurate and up to date can be
-// problematic since behind the back of the host computer,
-// the GQ GMC can be changed manually.
-//
-// The GQGMC software makes a valiant attempt to hide all this
-// from the user. First, immediately following the opening of
-// the USB port, the software silently reads the configuration
-// data from the GQ GMC to obtain its own copy of NVM.
-// From that point on, it is assumed that no manual changes
-// to the GQ GMC will occur. The GQGMC software then reads/writes
-// it own local copy of NVM. When the user issues the Update CFG
-// command, the GQGMC software silently
-// 1) issues the erase configuraton command,
-// 2) writes all 256 bytes of NVM,
-// 3) issues the update configuration command.
-
-// The user software may at any time cause a Get configuration
-// command in which case, the user must be aware that the GQGMC's
-// local copy of NVM will be overwritten.
-
-
-// getConfigurationData public method reads configuration data. You
-// don't request pieces of the configuration data, all 256 bytes
-// are returned, although, there are currently only about 60
-// bytes used (corresponding to about 50 parameters). The command is
-// get_cfg_cmd (see GQ GMC COMMANDS above).
-void
-func (gc *GQGMCCounter) getConfigurationData()
-{
-  // Issue command to get configuration and read returned data.
-  communicate(get_cfg_cmd, reinterpret_cast<char *>(&mCFG_Data),
-                           sizeof(mCFG_Data));
-
-  // If read of returned data failed, set error code.
-  if (mRead_status == false)
-  {
-    mError_code = eGet_CFG;
-  }
-
-  //  debugging code
-  /*
-  uint8_t * inp = (uint8_t *)&mCFG_Data;
-  for(uint32_t i=0; i<64; i++)
-  {
-    cout << Hex(inp[i]) << "-";
-    if (i > 0)
-      if (((i+1)%16) == 0) cout << endl;
-    if (i > 62)break;
-  }
-  cout << endl;
-  */
-  // end debug code
-
-  return;
-} // end getConfigurationData()
-
-// The following get methods are provided in order to access the
-// configuration data, not all configuration data, but only those
-// parameters which are needed to retrieve and parse the history data.
-
-// getSaveDataType is a get method to retrieve the saved data type
-// parameter in the configuration data. Note that SaveDataType is
-// retrieved from the host computer's local copy of NVM
-// configuration data. The return type is an enumeration
-// which matches the following possibilities:
-// 0 = logging is off,
-// 1 = counts per second,
-// 2 = counts per minute,
-// 3 = CPM averaged per hour.
-enum saveDataType_t
-func (gc *GQGMCCounter) getSaveDataType()
-{
-  return ((enum saveDataType_t)(mCFG_Data.saveDataType));
-} // end getSaveDataType()
-
-
-// setSaveDataType is a set method to reconfigure the data type
-// logged in the history buffer. This method is provided as a
-// convenience instead of using the writeConfigurationData method
-// since changing the saved data type is considered to be
-// more commonly used configuration change. The passed argument
-// is an enumeration (see definition in gqgmc.hh) whose value
-// is to be the new value of the saveDataType configuration
-// parameter. Note that only the host computer's local copy
-// of NVM is updated.  The user must issue a update configuration
-// command to cause the GQGMC to implement the NVM changes.
-void
-func (gc *GQGMCCounter) setSaveDataType(enum saveDataType_t newSaveDataType)
-{
-  uint8_t  saveData = uint8_t(newSaveDataType);
-
-  // Use writeConfigurationData method
-  writeConfigurationData(eSaveDataType, eSaveDataType_bytecnt, &saveData);
-  // error condition will be handled by writeConfigurationData()
-
-  return;
-} // end setSaveDatatype method
-
-
-// getDataSaveAddress is get method to retrieve the address
-// in the history buffer where the logged data begins. See
-// getHistoryData method for an explanation of the
-// dataSaveAddress in the configuration data structure.
-// The returned value is a 32 bit address, although the
-// dataSaveAddress maximum value cannot exceed 24 bits worth.
-// Note that the DataSaveAddress is retrieved from the host
-// computer's local copy of the GQ GMC's NVM configuration data.
-uint32_t
-func (gc *GQGMCCounter) getDataSaveAddress()
-{
-  uint32_t      address(0);
-
-  address |= (uint32_t(mCFG_Data.dataSaveAddress2) << 16);
-  address |= (uint32_t(mCFG_Data.dataSaveAddress1) <<  8);
-  address |= (uint32_t(mCFG_Data.dataSaveAddress0) <<  0);
-
-  return address;
-}  // end getDataSaveAddress()
-
-// The resetDataSaveAddress sets the dataSaveAddress configuration
-// parameter to the value of 0x10.  This is provided as a
-// convenience instead of using writeConfigurationData() directly
-// because setting the start of the history buffer back to zero
-// would be a common thing to do. Note that the DataSaveAddress
-// is being updated in the host computer's local copy of the
-// GQ GMC's NVM configuration data. The user must issue the
-// update configuration command to force the GQ GMC to implement
-// the NVM configuration data changes.
-void
-func (gc *GQGMCCounter) resetDataSaveAddress()
-{
-  uint32_t     address(0x10); // 0x10 provides room for date/timestamp
-
-  // Use writeConfigurationData() method
-  writeConfigurationData(eDataSaveAddress, eDataSaveAddress_bytecnt,
-                         (uint8_t *)(&address));
-  // error condition handled by writeConfigurationData()
-
-  return;
-} // end resetDataSaveAddress()
-
-
-// writeConfiguratonData is the public method to write configuration
-// data. It takes the following parameters.
-//
-// cfgParameter is actually the offset from the beginning of the
-// configuration data of the desired parameter. It was explicitly
-// created this way and assigned the offset as its enumeration value so
-// that cfgParameter serves both as an enumeration and as the actual
-// address of the configuration parameter.
-//
-// cfgData is a pointer to an array of raw binary for the parameter.
-//
-// cfgDataCount is the number of bytes of cfgData. Since cfgData is
-// passed as pointer, we need to supply the array length separately
-// as the cfgDataCount parameter.
-//
-// Note that this method updates the local copy of the GQ GMC's
-// NVM configuration data. As noted previously in the documentation,
-// the GQ GMC does not support a direct method of updating its
-// NVM configuration data and having it take effect immediately.
-//
-// This method is not an exact reflection of the native GQ GMC write
-// configuration command. The native GQ GMC write configuration
-// only writes one byte at a time. So writing multibyte configuration
-// parameters would take multiple writes, one per data byte. Instead,
-// we abstract the write configuration command so the user does not
-// have to know this much detail. We can do this because we
-// know a priori the base address of each parameter and how many
-// bytes each parameter needs. So this method is intended to
-// handle parameters with multibyte data by requiring the user
-// to only pass the parameter enumeration, its byte count, and
-// value.
-//
-// Note that changes to the configuration data do not have
-// immediate effect since we writing to the local copy of the
-// GQ GMC's NVM configuration data. To take effect, the user must
-// call the updateConfigurationData() method. Before doing so, all
-// changes to the configuration data should be completed so that
-// interdependent configuration parameters are updated
-// simultaneously.
-void
-func (gc *GQGMCCounter) writeConfigurationData(enum cfg_param_t       cfgParameter,
-                              enum cfg_bytecnt_t     cfgDataCount,
-                              uint8_t const * const  cfgData)
-{
-  uint8_t * pCfg_Data = (uint8_t *)&mCFG_Data + uint8_t(cfgParameter);
-  for(int i=0; i<cfgDataCount; i++)
-  {
-    // Convert little endian to big endian which GQ GMC wants.
-    if (mBig_endian)
-      pCfg_Data[i] = cfgData[i];
-    else
-      pCfg_Data[i] = cfgData[cfgDataCount-1-i];
-  } // end for loop
-
-  return;
-} // end writeConfigurationData()
-
-// loadConfigurationData private method writes all 256 bytes
-// of the configuration data to the GQ GMC. This will take
-// over a minute to complete. This is a practice in patience.
-// The GQ GMC's write_cfg_cmd only transmits a single byte
-// at a time. So it takes 256 transmits and each transmit
-// has to wait for a 0xAA return.  The user obviously should
-// not update the NVM configuration too often.
-void
-func (gc *GQGMCCounter) loadConfigurationData()
-{
-  const
-  uint32_t     retsize = 1;
-  char         ret_char[retsize+1];
-
-  // Need a pointer to the local host computer's copy
-  // of the NVM configuration data.
-  uint8_t *    pCfg_Data = (uint8_t *)&mCFG_Data;
-
-  // Begin formulating the write configuration data command.
-  // "AD" is just a place holder for the address byte and data byte
-  // that will be dynamically derived and inserted.
-  string write_cfg_cmd = "<WCFGAD>>";
-
-  // The parameter and its data have to be dynamically derived.
-  // write_cfg_cmd[5] is parameter enumeration (aka address offset)
-  // write_cfg_cmd[6] is parameter data
-
-  // Pack address and data into write_cfg_data_cmd with big
-  // endian byte order.
-
-  // Address (ie, parameter) is less than 256 since configuration
-  // data has a fixed size of 256 bytes, so address is one byte.
-
-  // pack address and data into command
-  for(uint16_t i=0; i<kNVMSize; i++)
-  {
-    // Increment the address for each additional data byte.
-    write_cfg_cmd[5] = uint8_t(i);
-
-    // Load data one byte at a time
-    write_cfg_cmd[6] = pCfg_Data[i];
-
-/*    // debug code
-    if (i < 64)
-    {
-      for(int i=0; i<5; i++)
-        cout << write_cfg_cmd[i];
-      cout << Hex(write_cfg_cmd[5]);
-      cout << Hex(write_cfg_cmd[6]);
-      cout << write_cfg_cmd[7];
-      cout << write_cfg_cmd[8];
-      cout << endl;
-    }
-*/    // end debug code
-
-    // Issue command to write configuration data, one byte
-    // at a time because that is the native write configuration
-    // command of the GQ GMC.
-    communicate(write_cfg_cmd, ret_char, retsize);
-
-    // if read of returned data succeeded, convert raw data to float
-    if (mRead_status == true)
-    {
-      // We really don't care about the return value of 0xAA. If the
-      // GQ GMC does not recognize the command, it returns nothing and
-      // we get a read_status error.
-    }
-    else  // else for failure, set error code
-    {
-      mError_code = eWrite_CFG;
-      break;                    // break out of for loop
-    } // end (read_status == true)
-  } // end for loop
-
-  return;
-} // loadConfigurationData()
-
-// eraseConfigurationData public method erases the configuration data
-// in its entirety. The configuration returns to its factory default
-// setting. It might have been better to call this the reset
-// configuration data command. The command is erase_cfg_cmd
-// (see GQ GMC COMMANDS above).
-void
-func (gc *GQGMCCounter) eraseConfigurationData()
-{
-  const
-  uint32_t     retsize = 1;
-  char         ret_char[retsize+1];
-
-  // Issue command to erase NVM configuration.
-  communicate(erase_cfg_cmd, ret_char, retsize);
-
-  // If read of returned data succeeded, convert raw data to float,
-  if (mRead_status == true)
-  {
-    // We really don't care about the return value of 0xAA. If the
-    // GQ GMC does not recognize the command, it returns nothing and
-    // we get a read_status error.
-  }
-  else  // else for failure, set the error code.
-  {
-    mError_code = eErase_CFG;
-  }
-
-  return;
-} // end eraseConfigurationData()
-
-
-// The updateConfigurationdata public method is called to make changes
-// to configuration data take effect. All other methods to modify
-// the configuration data do not cause the GQ GMC to immediately
-// change operation. This would not be desireable since various
-// changes to the configuration may be interdependent and so
-// we would want the changes to take effect simultaneously to
-// insure proper operation. There are no arguments to call.
-// The command is update_cfg_cmd (see GQ GMC COMMANDS above).
-// The user who calls this method may want to pop-up a window
-// stating that this will take about one minute. That is about
-// how long it will take to write all 256 bytes to the GQ GMC.
-// This method calls eraseConfigurationData() and
-// loadConfigurationData() as part of the procedure needed to
-// force the GQ GMC to implement operational changes per the
-// new NVM configuration data.
-void
-func (gc *GQGMCCounter) updateConfigurationData()
-{
-  const
-  uint32_t     retsize = 1;
-  char         ret_char[retsize+1];
-
-    // 1st, we have to erase configuration data
-  // cout << erase_cfg_cmd << endl; // debug
-  eraseConfigurationData();
-  // 2nd, write all 256 bytes of NVM to GQ GMC
-  // cout << "load cfg" << endl; // debug
-  loadConfigurationData();
-
-  // cout << update_cfg_cmd << endl; // debug
-  // Issue command to update NVM and force GQ GMC to change
-  // operation in accordance to new configuration data.
-  communicate(update_cfg_cmd, ret_char, retsize);
-
-  // If read of returned data succeeded, convert raw data to float,
-  if (mRead_status == true)
-  {
-    // We really don't care about the return value of 0xAA. If the
-    // GQ GMC does not recognize the command, it returns nothing and
-    // we get a read_status error.
-  }
-  else  // else for failure, set the error code.
-  {
-    mError_code = eUpdate_CFG;
-  }
-
-  return;
-} // end updateConfigurationData()
-
-
-// sendKey is the public method to emulate any one of the 4 keys on
-// the front panel of the GQ GMC. The front panel has a 'left arrow',
-// 'up arrow, 'down arrow', and 'enter' keys. These are used to
-// navigate through the GQ GMC's menu system. In principle, the
-// menu system can be used to set virtually any and all of the
-// configuration data (although this is not recommended for
-// such configuration data as the calibration values).
-// So instead of the writeConfigurationData method,
-// the proper sequence of sending the keys would do the
-// same thing. The command is derived dynamically, but the actual
-// command is "<KEY0>>" or "<KEY1>>" or "<KEY2>>" or "<KEY3>>".
-// So for the purpose that the user need not know the actual
-// command string, the softkey_t enumeration is created and used
-// as the passed argument to the method. See the enum softkey_t
-// declaration in gqgmc.hh for more discussion.
-//
-// For successive sendKey calls there is a trick to know when
-// using the sendKey method. The trick is you can't
-// transmit sendKey too fast and you can't do it too slow.
-// Another thing is that the Save Data option menu starts with
-// the current data type and then cycles through the other options.
-// So you have to know what is the current data type and
-// then send the Enter key the proper number of times to cycle
-// to the desired option. When moving through the menu we use
-// 0.5 seconds, but when moving through a pop up options menu
-// we have to move faster and so should use 0.25 seconds
-// between sendKey in that context.
-void
-func (gc *GQGMCCounter) sendKey(enum softkey_t key)
-{
-  char   inp[1]; // This will not be used, just needed as dummy arg
-
-  // Begin formulating the send key command.
-  string keycmd = "<KEY";
-
-  // Append key number which is an enumeration equal to the
-  // ASCII value of the key, ie, '0', '1', '2', or '3'.
-  keycmd += uint8_t(key);
-  // Append ">>"
-  keycmd += ">>";
-
-  communicate(keycmd, inp, 0);  // no return data
-  // Since the sendkey command returns no data there is no way to
-  // test success of communication.
-
-  // Debug code
-/*
-  for(int i=0; i<7; i++)
-    cout << Hex(keycmd[i]);
-  cout << endl;
-*/
-  return;
-} // end sendKey()
-
-
-// setDate is the public method to set the date. The date is passed as
-// an ASCII string with the format of <month><day><year>, for example,
-// 112312 is November (11th month) 12, 2012.  The year is specified
-// as the last two digits of the century since presumably the date is
-// is being set to the current date. In reality, the GQ GMC has a separate
-// command for setting each of the month, day, and year.
-void
-func (gc *GQGMCCounter) setDate(string date)
-{
-  const
-  uint32_t     retsize = 1;
-  char         ret_char[retsize+1];
-
-  // The date is broken up into three separate commands one each for
-  // month, day, and year as supported by the GQ GMC.
-
-  // Set the month, <SETDATEMMXX>> where XX = month byte.
-  {
-    // uint16_t is necessary since ss >> uint8_t does not work.
-    uint16_t      month=0;
-    string        setMonthCmd;
-    stringstream  ss;
-    ss << date[0] << date[1];
-    ss >> month;
-    //cout << "month = " << Hex(uint8_t(month)) << endl;
-    setMonthCmd  = "<SETDATEMM";
-    setMonthCmd += uint8_t(month);
-    setMonthCmd += ">>";
-    communicate(setMonthCmd, ret_char, retsize);
-  }
-
-  // Set the day, <SETDATEDDXX>> where XX = day byte.
-  {
-    uint16_t      day=0;
-    string        setDayCmd;
-    stringstream  ss;
-    ss << date[2] << date[3];
-    ss >> day;
-    //cout << "day = " << Hex(uint8_t(day)) << endl;
-    setDayCmd  = "<SETDATEDD";
-    setDayCmd += uint8_t(day);
-    setDayCmd += ">>";
-    communicate(setDayCmd, ret_char, retsize);
-  }
-
-  // Set the year, <SETDATEYYXX>> where XX = year byte.
-  {
-    uint16_t      year=0;
-    string        setYearCmd;
-    stringstream  ss;
-    ss << date[4] << date[5];
-    ss >> year;
-    //cout << "year = " << Hex(uint8_t(year)) << endl;
-    setYearCmd  = "<SETDATEYY";
-    setYearCmd += uint8_t(year);
-    setYearCmd += ">>";
-    communicate(setYearCmd, ret_char, retsize);
-  }
-
-  return;
-} // end set Date()
-
-// setTime is the public method to set the time of day. The time is
-// passed as an ASCII string with the format of <hour><minutes><seconds>,
-// for example, 142256 is the 14th hour, 22 minutes after the hour,
-// 56 seconds after the minute.  The hour is given in 24 hour format
-// counting from 0 to 23. In reality, the GQ GMC provides a separate
-// command for setting each of the hour, minutes and seconds.
-void
-func (gc *GQGMCCounter) setTime(string time)
-{
-    const
-    uint32_t     retsize = 1;
-    char         ret_char[retsize+1];
-
-    // The time is broken up into three separate commands one each for
-    // hour, minute, and second as supported by the GQ GMC.
-
-    // Set the hour, <SETTIMEHHXX>> where XX = hour byte.
-    {
-      uint16_t      hour=0;       // stringstream does not convert to uint8_t
-      string        setHourCmd;
-      stringstream  ss;
-      ss << time[0] << time[1];
-      ss >> hour;
-      //cout << "hours = " << Hex(uint8_t(hour)) << endl;
-      setHourCmd  = "<SETTIMEHH";
-      setHourCmd += uint8_t(hour);
-      setHourCmd += ">>";
-      communicate(setHourCmd, ret_char, retsize);
-    }
-
-    // Set the minute, <SETTIMEMMXX>> where XX = minute byte.
-    {
-      uint16_t      minute=0;
-      string        setMinuteCmd;
-      stringstream  ss;
-      ss << time[2] << time[3];
-      ss >> minute;
-      //cout << "minute = " << Hex(uint8_t(minute)) << endl;
-      setMinuteCmd  = "<SETTIMEMM";
-      setMinuteCmd += uint8_t(minute);
-      setMinuteCmd += ">>";
-      communicate(setMinuteCmd, ret_char, retsize);
-    }
-
-    // Set the seconds, <SETTIMESSXX>> where XX = second byte.
-    {
-      uint16_t      second=0;
-      string        setSecondCmd;
-      stringstream  ss;
-      ss << time[4] << time[5];
-      ss >> second;
-      //cout << "second = " << Hex(uint8_t(second)) << endl;
-      setSecondCmd  = "<SETTIMESS";
-      setSecondCmd += uint8_t(second);
-      setSecondCmd += ">>";
-      communicate(setSecondCmd, ret_char, retsize);
-    }
-
-  return;
-} // end setTime()
-
-
-// PRIVATE METHODS
-
-// communicate private method is used to write/read data to/from
-// the GMC-300. This method is expressedly designed to be called
-// by methods which send an ASCII string and expect to receive
-// returned data. However for flexibility, if the command string
-// is null, no command is transmitted and if the expected number
-// of return bytes is zero, no read is performed.
-// cmd is the ASCII string command.
-// retdata is the repository for the returned data.
-// retbytes is the number of bytes of returned data.
-void
-func (gc *GQGMCCounter) communicate(const string cmd, char * retdata, uint32_t retbytes)
-{
-  // Clear the USB port of any left over data from last exchange. Even
-  // though we know how many bytes the GQ GMC transmits for each
-  // command, experience has shown this is the safe thing to do since
-  // there is no protocol for the returned data.
-  clearUSB();
-
-  //cout << cmd << endl;
-  // 1st, issue the command to the GMC-300, this is always an ASCII
-  // string.
-  // For flexibility, only transmit if cmdbytes is not 'null'.
-  if (cmd.size() > 0) sendCmd(cmd);
-  // 2nd, read the return data, for all commands except get version
-  // this is always raw binary data.
-  // For flexibility, only read if return is not 'null'.
-  if (retbytes > 0) readCmdReturn(retdata, retbytes);
-
-  return;
-} // end communicate()
-
-// sendCmd is the private method (the basic method) to transmit
-// the command to the GMC-300.
-// cmd is the ASCII string to send as the command.
-void
-func (gc *GQGMCCounter) sendCmd(const string cmd)
-{
-  // This is a common place to reset the error code since it is always
-  // called for any command.
-  mError_code = eNoProblem;
-
-  // This is a common place to reset the read status since every read
-  // is always preceeded by a write command (except for turn_on_cps!).
-  mRead_status = true;
-
-  // Call low level C stdio routine to write to USB port.
-  write(mUSB_serial, cmd.c_str(), cmd.size());
-
-  return;
-} // end sendCmd()
-
-// readCmdReturn is the private method (the basic method) to read
-// the return bytes from the command.
-// retdata is the repository for the returned data.
-// retbytes is the number of bytes of the returned data.
-void
-func (gc *GQGMCCounter) readCmdReturn(char * retdata, uint32_t retbytes)
-{
-  uint32_t rcvd = 0;           // the number of received bytes
-  char * inp    = &retdata[0]; // pointer to returned data char array
-                               // start pointer off at beginning of
-                               // repository.
-
-  // Assume read will succeed, replicated here only because of the
-  // nature of the turn_on_cps command which automatically returns
-  // data without a preceeding write.
-  mRead_status = true;
-
-  // Now read the returned raw byte string. Do this by reading one byte
-  // at a time until the requested number of bytes are attained. However,
-  // the serial port has been setup to timeout each read attempt. So if
-  // after N calls to read, we haven't yet read all N bytes, declare
-  // a failure. The read is done this way to avoid an indefinite blocking
-  // situation when 0 bytes are returned by the GQ GMC. The only good thing
-  // about this methodology is that the largest possible read is only 4K
-  // for the history data. So the read never really takes that much time.
-  for(uint32_t i=0; i<retbytes; i++)
-  {
-    rcvd += read(mUSB_serial, inp, 1);
-    inp   = &retdata[rcvd];
-    if (rcvd >= retbytes) break;
-  } // end for loop
-
-  //  debugging code
-  /*
-  inp = &retdata[0];
-  for(uint32_t i=0; i<retbytes; i++)
-  {
-    cout << Hex(inp[i]) << "-";
-    if (i > 0)
-      if (((i+1)%16) == 0) cout << endl;
-    if (i > 62)break;
-  }
-  cout << endl;
-  cout << "rcvd = " << rcvd << endl;
-  */
-  // end debug code
-
-  // Communication is considered a failure if less than the expected
-  // number of bytes is returned by the GMC-300.
-  if (rcvd < retbytes)
-    mRead_status = false;
-
-  return;
-} // readCmdReturn()
+	errOpen                  = "Failed to open USB port"
+	errFirmware              = "GQ GMC has older firmware.  Some commands may not work."
+	errVersion               = "Failed to read the version number of the firmware"
+	errSerialNumber          = "Failed to read the serial number "
+	errCPM                   = "Failed to read the counts per minute "
+	errCPS                   = "Failed to read the counts per second "
+	errAutoCPS               = "Failed to read auto counts per second "
+	errCFG                   = "Failed to get configuration data "
+	errEraseCFG              = "Failed to erase configuration data "
+	errUpdateCFG             = "Failed to update configuration data "
+	errClear                 = "Failed to clear USB input buffer. You should power cycle GQ GMC."
+	errBatteryVoltage        = "Failed to read the battery voltage "
+	errHistoryData           = "Failed to read the history data "
+	errHistoryDataLengthFmt  = "Requested data length of the history command cannot exceed %d bytes"
+	errHistoryDataAddressFmt = "Address of the history command cannot exceed %d bytes"
+	errHistoryDataOverrunFmt = "History data length added to the address cannot exceed %d bytes"
+	errYear                  = "Failed to set year"
+	errMonth                 = "Failed to set month"
+	errDay                   = "Failed to set day"
+	errHour                  = "Failed to set hour"
+	errMinute                = "Failed to set minute"
+	errSecond                = "Failed to set second"
+)
 
 // GQGMCCounter is a GQ GMC Counter
 type GQGMCCounter struct {
-	fh string // TODO: make this a file handle.
-	config serial.Config
+	port   *serial.Port
+	config *serial.Config
 }
 
 // NewGQGMC creates a new GQGMC Counter instance
 func NewGQGMC(c Config) (*GQGMCCounter, error) {
-	var gc GQGMCCounter
-
-	portCfg := serial.Config {
-		Name: c.Device,
-		Baud: 57600,
+	cfg := serial.Config{
+		Name:        c.Device,
+		Baud:        57600,
 		ReadTimeout: 500 * time.Millisecond,
 	}
-	gc.port = OpenPort(portCfg)
+	p, err := serial.OpenPort(&cfg)
+	if err != nil {
+		return nil, err
+	}
 	//vers := getVersion()
-        //getConfigurationData()
-	return &gc, nil
+	//getConfigurationData()
+	return &GQGMCCounter{port: p, config: &cfg}, nil
 }
 
 // Clear clears out any remaining data
@@ -837,22 +171,22 @@ func (gc *GQGMCCounter) Clear() error {
 
 // Version gets the version of the device
 func (gc *GQGMCCounter) Version() (string, error) {
-  //communicate(get_version_cmd, version, versize);
-  // use cmdGetVersion. Returns 14 byte string.
-  return "", nil
+	//communicate(get_version_cmd, version, versize);
+	// use cmdGetVersion. Returns 14 byte string.
+	return "", nil
 }
 
 // SerialNum gets the serial number of the device
 func (gc *GQGMCCounter) SerialNum() (string, error) {
-  //communicate(get_serial_cmd, serial_number, sernumsize);
-  // use cmdGetSerial. Returns 7 bytes.
-  // Turn each 4 bits into corresponging hex char.
-      bs := []byte{0, 0x30, 0, 0xE3, 0x4A, 0x35, 0x1A}
-          for _, b := range bs {
-		          fmt.Printf("%02X", b)
-			      }
-			          fmt.Println("")
-  return "", nil
+	//communicate(get_serial_cmd, serial_number, sernumsize);
+	// use cmdGetSerial. Returns 7 bytes.
+	// Turn each 4 bits into corresponging hex char.
+	bs := []byte{0, 0x30, 0, 0xE3, 0x4A, 0x35, 0x1A}
+	for _, b := range bs {
+		fmt.Printf("%02X", b)
+	}
+	fmt.Println("")
+	return "", nil
 }
 
 // GetCPM returns CPM
@@ -879,12 +213,6 @@ func (gc *GQGMCCounter) GetCPS() (uint16, error) {
 	return 0, nil
 }
 
-
-const (
-	VoltageIdeal = 98
-	VoltageTooLow = 75
-)
-
 // GetVoltage returns current battery voltage
 func (gc *GQGMCCounter) GetVoltage() (int16, error) {
 	// Do this differently - for 9.6 return 96. And if not supported,
@@ -910,32 +238,38 @@ func (gc *GQGMCCounter) GetVoltage() (int16, error) {
 // GetHistoryData Should return history data but is unimplemented for now
 func (gc *GQGMCCounter) GetHistoryData() {
 	// It's not recommended to use this so blank for now.
+	return
 }
 
+// TurnOnCPS turns on CPS collection
 func (gc *GQGMCCounter) TurnOnCPS() error {
-// turnOnCPS is public method to enable automatic reporting of the
-// count per second (CPS) value. First, I would say don't use this
-// command. Since the returned data has no protocol, that is, there
-// is no start/stop marker, no identification, no nothing but a
-// sequence of bytes, any other command issued while CPS is turned on
-// will take extraordinary effort not to confuse its returned data
-// with the CPS data. To handle it correctly, you would have to
-// wait for the CPS data to be returned, and then issue the command.
-// This would be most safely done by creating a new thread to
-// read the CPS and using a mutex to signal opportunity to issue a
-// separate command. The command is turn_on_cps_cmd
-// (see GQ GMC COMMANDS above). The returned data is always two
-// bytes so that samples > 255 can be reported (even though unlikely).
-//sendCmd(turn_on_cps_cmd);
-// There is no pass/fail return from GQ GMC
+	// turnOnCPS is public method to enable automatic reporting of the
+	// count per second (CPS) value. First, I would say don't use this
+	// command. Since the returned data has no protocol, that is, there
+	// is no start/stop marker, no identification, no nothing but a
+	// sequence of bytes, any other command issued while CPS is turned on
+	// will take extraordinary effort not to confuse its returned data
+	// with the CPS data. To handle it correctly, you would have to
+	// wait for the CPS data to be returned, and then issue the command.
+	// This would be most safely done by creating a new thread to
+	// read the CPS and using a mutex to signal opportunity to issue a
+	// separate command. The command is turn_on_cps_cmd
+	// (see GQ GMC COMMANDS above). The returned data is always two
+	// bytes so that samples > 255 can be reported (even though unlikely).
+	//sendCmd(turn_on_cps_cmd);
+	// There is no pass/fail return from GQ GMC
+	return nil
 }
 
+// TurnOffCPS turns off CPS collection
 func (gc *GQGMCCounter) TurnOffCPS() error {
-//sendCmd(turn_off_cps_cmd);
-//call Clear()
+	//sendCmd(turn_off_cps_cmd);
+	//call Clear()
+	return nil
 }
 
-func (gc *GQGMCCounter) getAutoCPS() (uint16, error) {
+// GetAutoCPS gets a reading once auto CPS is turned on
+func (gc *GQGMCCounter) GetAutoCPS() (uint16, error) {
 	//uint32_t cpssize = 2;          // 2 bytes of returned data
 	//read-from-port(cps_char, cpssize);
 	// 1st byte is MSB, but note that upper two bits are reserved bits.
@@ -946,7 +280,93 @@ func (gc *GQGMCCounter) getAutoCPS() (uint16, error) {
 	return 0, nil
 }
 
+// TurnOffPower turns the device off
 func (gc *GQGMCCounter) TurnOffPower() {
-  sendCmd(turn_off_pwr_cmd);
-  // Note that power off cannot fail because the GQ GMC returns nothing.
+	//sendCmd(turn_off_pwr_cmd)
+	// Note that power off cannot fail because the GQ GMC returns nothing.
+	return
+}
+
+// GetConfiguration reads configuration data
+func (gc *GQGMCCounter) GetConfiguration() {
+	// Issue command to get configuration and read returned data.
+	// communicate(get_cfg_cmd, reinterpret_cast<char *>(&mCFG_Data),
+}
+
+// SetConfiguration writes configuration data
+func (gc *GQGMCCounter) SetConfiguration() {
+	// See the ConfigurationData functions in gqgmc.cc
+}
+
+// ResetConfiguration resets to factory default
+func (gc *GQGMCCounter) ResetConfiguration() {
+	//uint32_t     retsize = 1;
+	//char         ret_char[retsize+1];
+	//communicate(erase_cfg_cmd, ret_char, retsize);
+}
+
+// SetDate sets the date - format of YYYYMMDD
+func (gc *GQGMCCounter) SetDate(date string) {
+	//setMonthCmd  = "<SETDATEMM";
+	//setMonthCmd += uint8_t(month);
+	//setMonthCmd += ">>";
+	//communicate(setMonthCmd, ret_char, retsize);
+
+	//setDayCmd  = "<SETDATEDD";
+	//setDayCmd += uint8_t(day);
+	//setDayCmd += ">>";
+	//communicate(setDayCmd, ret_char, retsize);
+
+	// year - last two digits
+	//setYearCmd  = "<SETDATEYY";
+	//setYearCmd += uint8_t(year);
+	//setYearCmd += ">>";
+	//communicate(setYearCmd, ret_char, retsize);
+}
+
+// SetTime sets the time (HH:MM:SS)
+func (gc *GQGMCCounter) SetTime(time string) {
+	//setHourCmd  = "<SETTIMEHH";
+	//setHourCmd += uint8_t(hour);
+	//setHourCmd += ">>";
+	//communicate(setHourCmd, ret_char, retsize);
+
+	//setMinuteCmd  = "<SETTIMEMM";
+	//setMinuteCmd += uint8_t(minute);
+	//setMinuteCmd += ">>";
+	//communicate(setMinuteCmd, ret_char, retsize);
+
+	//setSecondCmd  = "<SETTIMESS";
+	//setSecondCmd += uint8_t(second);
+	//setSecondCmd += ">>";
+	//communicate(setSecondCmd, ret_char, retsize);
+}
+
+func (gc *GQGMCCounter) communicate(cmd []byte, length uint32) ([]byte, error) {
+	//clearUSB();
+	//if (cmd.size() > 0) sendCmd(cmd);
+	//if (retbytes > 0) readCmdReturn(retdata, retbytes);
+	return nil, nil
+}
+
+func (gc *GQGMCCounter) sendCmd(cmd []byte) {
+	// The port write thing
+	gc.port.Write(cmd)
+	return
+}
+
+func (gc *GQGMCCounter) readCmdReturn(length uint32) ([]byte, error) {
+	// Now read the returned raw byte string. Do this by reading one byte
+	// at a time until the requested number of bytes are attained. However,
+	// the serial port has been setup to timeout each read attempt. So if
+	// after N calls to read, we haven't yet read all N bytes, declare
+	// a failure. The read is done this way to avoid an indefinite blocking
+	// situation when 0 bytes are returned by the GQ GMC. The only good thing
+	// about this methodology is that the largest possible read is only 4K
+	// for the history data. So the read never really takes that much time.
+	//for(uint32_t i=0; i<retbytes; i++)
+	//  rcvd += read(mUSB_serial, inp, 1);
+	//  inp   = &retdata[rcvd];
+	//  if (rcvd >= retbytes) break;
+	return nil, nil
 }
-- 
GitLab