diff --git a/devices/geiger/geiger.go b/devices/geiger/geiger.go
index 7599d56ce942a54296a4a8fbb6aeaecad52b3450..74648310595da52354c39e2fd74d66558459469e 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 18b6b537518a2d2507ba903df8720b0b2d98fa0d..77c04d3112d3c71c61e854b8803faa137036a3f2 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
}