Hubo Library
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
BatteryTest.cpp
Go to the documentation of this file.
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <string>
4 
5 #include "../hubolib.h"
6 #include "../hubocfg.h" // Required for changing default I2C device.
7 
8 using namespace HuboLib;
9 using namespace std;
10 
11 /*
12 Compile and link:
13  g++ BatteryTest.cpp -L../ -lhubo -lpthread -lrt -o BatteryTest
14 Run:
15  sudo ./BatteryTest
16 Purpose:
17  This little app allows for charging/discharging a battery up/down to a given value while capturing the voltage at a given rate.
18  The Hubo relays will be used for switching the power supply as well as the load.
19  Charging/dischargiong can be interrupted for a given time in order to measure the relaxation voltage (hence estimating capacity).
20 
21  # Discharging down to 2152 digits relaxation voltage (relaxation time is 900s)
22  # AD channel: 0
23  # Sampling rate [ms]: 1000ms
24  # Relay: 7 (128)
25  # Duty count: 3600s -> 60min
26  # Relax count: 900s -> 15min
27  # Charge/Discharge: d
28  # Digits after relaxation: 2152
29  ./BatteryTest 0 1000 128 3600 900 d 2152 discharge_test.csv
30 
31  # Charging up to 2710 digit relaxation voltage (relaxation time is 900s)
32  # AD channel: 0
33  # Sampling rate [ms]: 1000ms
34  # Relay: 6 (64)
35  # Duty count: 3600s -> 60min
36  # Relax count: 900s -> 15min
37  # Charge/Discharge: c
38  # Digits after relaxation: 2710
39  ./BatteryTest 0 1000 64 3600 900 c 2710 charge_test.csv
40 
41  Note - in this example a voltage devider 1:8 was used in front of the Hubo AD-channel in order to measure voltages up to 20V.
42 */
43 
44 string GetDateTime (char seperator, char timeSeperator);
45 bool AppendFile (string sFile, string sText);
46 unsigned long GetADValueAndLog (unsigned char adPin, string sFile);
47 string Convert2String (long value);
48 char g_buffer [1024];
49 
50 int main(int argc, char* argv[])
51 {
52  if (argc != 9)
53  {
54  printf ("Usage: \n BatteryTest <AD pins> <sample rate [ms]> <DO pins> <duty count> <relax count> <charge/discharge {c, d}> <stop at AD> <filename>\n");
55  printf (" example: BatteryTest 0 1000 128 3600 900 d 2152 discharge_test.csv\n");
56  return 0;
57  }
58 
59  unsigned char adPin = 0; // 0..7
60  long logSpeed = 1000; // [ms]
61  unsigned char doPins = 0; // 0..255
62  long dutyCount = 1; // 1..n
63  long relaxCount = 1; // 1..n
64  char chargeDischarge = 'c'; // c, d
65  unsigned long stopAtADValue = 0; // 0..n
66  string sFile = ""; // log file name
67 
68  // 1 adPin
69  if (sscanf (argv[1], "%hhu", &adPin) != 1 || adPin>7 )
70  {
71  printf ("AD Pin could not be determined!\n");
72  return 0;
73  }
74 
75  // 2 logSpeed
76  if (sscanf (argv[2], "%ld", &logSpeed) != 1)
77  {
78  printf ("Logspeed could not be determined!\n");
79  return 0;
80  }
81 
82  // 3 doPins
83  if (sscanf (argv[3], "%hhu", &doPins) != 1)
84  {
85  printf ("DO Pin(s) could not be determined!\n");
86  return 0;
87  }
88 
89  // 4 dutyCount
90  if (sscanf (argv[4], "%ld", &dutyCount) != 1 || dutyCount<1)
91  {
92  printf ("Duty count could not be determined!\n");
93  return 0;
94  }
95 
96  // 5 relaxCount
97  if (sscanf (argv[5], "%ld", &relaxCount) != 1 || relaxCount<1)
98  {
99  printf ("Relax count could not be determined!\n");
100  return 0;
101  }
102 
103  // 6 chargeDischarge
104  if (sscanf (argv[6], "%c", &chargeDischarge) != 1 || ! (chargeDischarge=='c' || chargeDischarge=='d'))
105  {
106  printf ("Charge discahrge at could not be determined!\n");
107  return 0;
108  }
109 
110  // 7 stopAtADValue
111  if (sscanf (argv[7], "%lu", &stopAtADValue) != 1)
112  {
113  printf ("Stop at AD value could not be determined!\n");
114  return 0;
115  }
116 
117  // 8 file name
118  if (sscanf (argv[8], "%s", g_buffer) != 1)
119  {
120  printf ("File name could not be determined!\n");
121  return 0;
122  }
123  sFile = g_buffer;
124 
125  // If required - set the I2C device to work with. The Raspberry Pi uses "/dev/i2c-1" which is default, the Banana Pi uses "/dev/i2c-0"
126  #ifdef BPI
127  g_I2CConfig.m_sI2CDevice = "/dev/i2c-0";
128  #endif
129 
130  // Initialize the library once in your program.
131  if (!Initialize())
132  {
133  printf ("Error: Initialize\n");
134  return 1;
135  }
136 
137  // Log settings.
138  sprintf (g_buffer, "%s %d %ld 0x%02X %ld %ld %c %ld %s\n", argv[0], (int) adPin, logSpeed, (int) doPins, dutyCount, relaxCount, chargeDischarge, stopAtADValue, sFile.c_str());
139  string sLine = string(g_buffer) + "\n";
140  AppendFile(sFile, sLine);
141  printf ("%s", g_buffer);
142 
143  // For simplicity define all channel to be read from the ADC.
144  unsigned short overSampling[MAX_MCP3x08_CHANNELS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
145  Set_MCP3x08_Oversampling (overSampling);
146 
147  // Wait as long until we have valid values in the buffers.
150 
151 
152  unsigned long adcCount = 0;
153  bool bStop = false;
154  unsigned long long relaxTime = 0; // [ms]
155  unsigned long long dutyTime = 0; // [ms]
156  unsigned long long adcDutySum = 0; // [digit]
157  unsigned long long adcDutyCount = 0; // [count]
158 
159  while (1)
160  {
161  // Log short note on relaxing phase.
162  sLine = "Relaxing";
163  printf ("%s\n", sLine.c_str());
164  AppendFile(sFile, sLine);
165  Set_DO_Channels(0x00);
166 
167  for (int count=0; count<relaxCount; count++)
168  {
169  adcCount = GetADValueAndLog(adPin, sFile);
170  usleep(1000L * logSpeed);
171  relaxTime += logSpeed;
172  }
173  // At the end of a relaxing phase....
174  if (chargeDischarge=='c')
175  {
176  // ... if charging check the voltage to be less than overcharge voltage level.
177  if (adcCount>stopAtADValue)
178  {
179  sLine = "End of relaxing phase and voltage above limit.";
180  printf ("%s\n", sLine.c_str());
181  AppendFile(sFile, sLine);
182  bStop = true;
183  break;
184  }
185  else
186  printf ("adcCount=%ld stopAtADValue=%ld\n", adcCount, stopAtADValue);
187  }
188 
189  // Log short note on duty phase.
190  sLine = chargeDischarge=='c' ? "Charging" : "Discharging";
191  printf ("%s\n", sLine.c_str());
192  AppendFile(sFile, sLine);
193  Set_DO_Channels(doPins);
194 
195  for (int count=0; count<dutyCount; count++)
196  {
197  adcCount = GetADValueAndLog(adPin, sFile);
198 
199  // Check analog value and stop if trigger level is reached.
200  if (chargeDischarge=='d')
201  {
202  // Currently actively discharging.
203  if (adcCount<stopAtADValue)
204  {
205  sLine = "Passing lower limit while discharging.";
206  printf ("%s\n", sLine.c_str());
207  AppendFile(sFile, sLine);
208  bStop = true;
209  break;
210  }
211  }
212 
213  usleep(1000L * logSpeed);
214  dutyTime += logSpeed;
215 
216  // Track average values of ADC count (hence average voltage).
217  adcDutySum += adcCount;
218  adcDutyCount++;
219  }
220  // Trigger level reached?
221  if (bStop)
222  break;
223  }
224 
225  // Clear any outputs.
226  Set_DO_Channels(0x00);
227 
228  // Log for another "relax time" period.
229  for (int count=0; count<relaxCount; count++)
230  {
231  adcCount = GetADValueAndLog(adPin, sFile);
232  usleep(1000L * logSpeed);
233  relaxTime += logSpeed;
234  }
235 
236  // Print total relaxing time.
237  sprintf (g_buffer, "Total relax time: %llds = %lfh\n", relaxTime/1000L, ((double)(relaxTime/1000L))/3600.0);
238  printf ("%s", g_buffer);
239  AppendFile(sFile, g_buffer);
240 
241  // Print total duty time.
242  sprintf (g_buffer, "Total duty time: %llds = %lfh\n", dutyTime/1000L, ((double)(dutyTime/1000L))/3600.0);
243  printf ("%s", g_buffer);
244  AppendFile(sFile, g_buffer);
245 
246  // Print average adc duty value.
247  if (adcDutyCount)
248  {
249  sprintf (g_buffer, "Average ADC duty value: %ld digit\n", (long)(adcDutySum / adcDutyCount));
250  printf ("%s", g_buffer);
251  AppendFile(sFile, g_buffer);
252  }
253 
254  // More for beautiness than that it would still have any effect.
256 
257  // Free library resources.
258  Uninitialize();
259 
260  return 0;
261 }
262 
263 unsigned long GetADValueAndLog(unsigned char adPin, string sFile)
264 {
265  string sLine = GetDateTime(' ', ':');
266  sLine += " ";
267 
268  // Read channel and output its value.
269  unsigned long adcCount = 0;
270  double volt = 0.0;
271  if (Get_AI_Channel (adPin, adcCount, volt))
272  sprintf (g_buffer, "%04ld", adcCount);
273  else
274  sprintf (g_buffer, "????");
275  sLine += g_buffer;
276 
277  sLine +="\n";
278  printf ("%s", sLine.c_str());
279  AppendFile(sFile, sLine);
280 
281  return adcCount;
282 }
283 
284 string GetDateTime(char seperator, char timeSeperator)
285 {
286  time_t t = time(0); // get time now
287  struct tm *pNow = localtime (&t);
288  sprintf (g_buffer, "%02d.%02d.%d%c%02d%c%02d%c%02d", pNow->tm_mday, (pNow->tm_mon + 1), (pNow->tm_year + 1900), seperator, pNow->tm_hour, timeSeperator, pNow->tm_min, timeSeperator, pNow->tm_sec);
289  // printf ("Time = %s\n", g_buffer);
290  return string(g_buffer);
291 }
292 
293 bool AppendFile(string sFile, string sText)
294 {
295  FILE* pFile = fopen (sFile.c_str(), "a+");
296  if (!pFile)
297  return false;
298  bool bResult = fputs (sText.c_str(), pFile) > 0;
299  fflush (pFile);
300  fclose(pFile);
301  return bResult;
302 }
303 
304 string Convert2String(long value)
305 {
306  if (sprintf(g_buffer, "%ld", value) != 1)
307  return string("");
308 
309  string s (g_buffer);
310  return s;
311 }
const char * m_sI2CDevice
Definition: hubocfg.h:48
unsigned long GetADValueAndLog(unsigned char adPin, string sFile)
bool Wait_For_MCP3x08_Buffered_Values()
Waits until the input buffer of the MCP3x08 are initialized from the hardware.
char g_buffer[1024]
Definition: BatteryTest.cpp:48
#define MAX_MCP3x08_CHANNELS
Definition: hubolib.h:39
int main(int argc, char *argv[])
Definition: BatteryTest.cpp:50
bool Initialize()
Initializes the library.
bool Set_DO_Channels(unsigned char value)
Requests the background thread to update all 8 bits of the digital output to the value specified...
bool AppendFile(string sFile, string sText)
I2C_Config g_I2CConfig
bool Get_AI_Channel(int channel, unsigned long &count, double &volt)
Get the buffered and oversampled data from the MCP3x08.
string Convert2String(long value)
bool Set_MCP3x08_Oversampling(unsigned short overSampling[MAX_MCP3x08_CHANNELS])
Specifies the ADC channels to be sampled as well as the number they get oversampled.
void Uninitialize()
Releases any resources bound to the library.
bool Wait_For_MCP23017_Buffered_Values()
Waits until input and output buffers of the MCP23017 master are initialized from the hardware...
string GetDateTime(char seperator, char timeSeperator)