This is some quick code I threw together this evening to measure battery level, remaining battery life in hh:mm, and current battery state (charging, charged, discharging). It has been tested under Ubuntu 9.04 Netbook Remix and seems fine; let me know if you try it. It uses a super-simple state machine to iterate around /proc/acpi/battery/BAT0/ looking for the info it needs.


Code:
///////////////////////////////////////////////////////////////////////
// Christopher Mitchell
// Linux Battery Level Checker
// May 13th, 2009
// http://www.cemetech.net
//
// This code isn't terribly groundbreaking, but please be respectful as
// far as reuse and modifications; credit me, or at least drop me an
// email to let me know you found my code helpful
///////////////////////////////////////////////////////////////////////


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <math.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#define PATH_BATT_STATE "/proc/acpi/battery/BAT0/state"
#define PATH_BATT_INFO "/proc/acpi/battery/BAT0/info"
#define BATT_READ_BUFLEN 256

struct battstate {
   short unsigned int powerstate, time_hour, time_min;
   float chargelevel;
};

enum powerstates {      //indicate charging or discharging
   POWER_CHARGING,
   POWER_CHARGED,
   POWER_DISCHARGING
};

int main(int argc, char* argv[]);
struct battstate* getBattState(void);

int main(int argc, char* argv[]) {
   struct battstate* thisstate;

   thisstate = getBattState();
   switch(thisstate->powerstate) {
      case POWER_CHARGING:
         printf("Battery is currently charging.\n");
         break;
      case POWER_CHARGED:
         printf("Battery is charged.\n");
         break;
      case POWER_DISCHARGING:
         printf("Battery is currently discharging.\n");
         break;
      default:
         printf("!!BUG!! Unrecognized power state returned.\n");
         exit(-1);
         break;
   }
   printf("Battery charge level is %f%%.\n",thisstate->chargelevel);
   if (thisstate->powerstate != POWER_CHARGED) {
      printf("Remainging %d:%d\n",thisstate->time_hour,thisstate->time_min);
   }
   return 0;
}

struct battstate *getBattState(void) {
   static struct battstate mybattstate;
   int battStateHandle;
   long int battRate_mA=0;
   long int battMax_mAh=0;
   long int battRemain_mAh=0;
   char buffer[BATT_READ_BUFLEN];
   char tok_unit[8];
   int readoffset;
   short int readstate=0,readlen=0;      //0=reading,1=eol,2=eof

   if (-1 == (battStateHandle = open(PATH_BATT_INFO,O_RDONLY))) {
      perror("Could not open battery info for reading");
      exit(-1);
   }

   while (readstate < 2) {
      readoffset = 0;
      readstate = 0;
      while (!readstate) {
         if (0 > (readlen = read(battStateHandle,buffer+readoffset,1))) {
            perror("Failed to read battery state");
            exit(-1);
         }
         if (!readlen) {
            readstate=2;
            break;
         }
         if ('\n' == *(buffer+readoffset)) {
            readstate++;
            *(buffer+readoffset+1) = '\0';
         }
         readoffset++;
      }
      if (readstate == 2) break;
      if (NULL != strstr(buffer,"last full capacity")) {
         if (0 >= sscanf(buffer+25,"%ld %s",&battMax_mAh,tok_unit)) {
            perror("sscanf for battery capacity");
            exit(-1);
         }
         break;
      }
   }
   close(battStateHandle);

   if (-1 == (battStateHandle = open(PATH_BATT_STATE,O_RDONLY))) {
      perror("Could not open battery state for reading");
      exit(-1);
   }

   readstate = 0;
   while (readstate < 2) {
      readoffset = 0;
      readstate = 0;
      while (!readstate) {
         if (0 > (readlen = read(battStateHandle,buffer+readoffset,1))) {
            perror("Failed to read battery state");
            exit(-1);
         }
         if (0 == readlen) {
            readstate=2;
            break;
         }
         if ('\n' == *(buffer+readoffset)) {
            readstate++;
            *(buffer+readoffset+1) = '\0';
         }
         readoffset++;
      }
      if (readstate == 2) break;
      if (NULL != strstr(buffer,"charging state")) {
         if (NULL != strstr(buffer,"discharging")) mybattstate.powerstate = POWER_DISCHARGING;
         else if (NULL != strstr(buffer,"charged")) mybattstate.powerstate = POWER_CHARGED;
         else mybattstate.powerstate = POWER_CHARGING;
      } else if (NULL != strstr(buffer,"present rate")) {
         if (0 >= sscanf(buffer+25,"%ld %s",&battRate_mA,tok_unit)) {
            perror("sscanf for battery rate failed");
            exit(-1);
         }
      } else if (NULL != strstr(buffer,"remaining capacity")) {
         if (0 >= sscanf(buffer+25,"%ld %s",&battRemain_mAh,tok_unit)) {
            perror("sscanf for battery capacity");
            exit(-1);
         }
      }
   }
   close(battStateHandle);

   //printf("%ld, %ld, %ld\n",battRemain_mAh,battMax_mAh,battRate_mA);
   mybattstate.chargelevel = 100.00*((float)battRemain_mAh/(float)battMax_mAh);
   if (battRate_mA) {
      mybattstate.time_hour = floor(battRemain_mAh/battRate_mA);
      mybattstate.time_min = floor(60*(((float)battRemain_mAh/(float)battRate_mA)-mybattstate.time_hour));
   }
   return &mybattstate;
}
GNOME Power Manager
but...but...what if it has 2 batteries????
elfprince13 wrote:
but...but...what if it has 2 batteries????

then it would tell you the overall battery power left.
elfprince13 wrote:
but...but...what if it has 2 batteries????
Then you need to examine both /proc/acpi/battery/BAT0 and /BAT1 and sum the results.
you should make your program check ALL the BATn's Wink
elfprince13 wrote:
you should make your program check ALL the BATn's Wink
That's easy enough; I only bothered with BAT0 because my eeePC only has BAT0, and I'm writing this as a tiny module in my Master's thesis.
KermMartian wrote:
elfprince13 wrote:
you should make your program check ALL the BATn's Wink
That's easy enough; I only bothered with BAT0 because my eeePC only has BAT0


How is the Eee PC? I was looking into buying it before I bought my Kohjinsha.

KermMartian wrote:
and I'm writing this as a tiny module in my Master's thesis.


What's your thesis on? I noticed that you haven't finished your BSEE yet; are you in a 5 year program?
If you followed my twitter you'd see I finished my BE (none of that BSEE weakness for me, I have a Bachelors of Engineering!) yesterday, although commencement is in 2 weeks. My thesis is on augmented reality applications of some existing/developing technologies.

The 901 is amazing, I'll post some info about it at some point.
KermMartian wrote:
If you followed my twitter you'd see I finished my BE (none of that BSEE weakness for me, I have a Bachelors of Engineering!) yesterday, although commencement is in 2 weeks. My thesis is on augmented reality applications of some existing/developing technologies.


I don't see a front page annoucement Razz BTW, twitter fails Bad Idea
Pretty cool though all it takes is reading from a file and displaying the value, you could do a similar thing with a basic bash script. Now in the case of my craptop I's need to first fix the ACPI table in the bios and then read from the file for it to be any use. Razz
*necrobump* I'm glad I saved this! The 4GB SSD on my netbook died, leaving me to reinstall Ubuntu Netbook Remix (UNR) 10.10 on its 16GB SSD. Now I need to re-copy and re-build this program.
I have to recommend you look at acpitool -B it prints out all this info in a very nice fashion and if you want to strip out a line or two grep or sed would have no problems doing so.
TheStorm wrote:
I have to recommend you look at acpitool -B it prints out all this info in a very nice fashion and if you want to strip out a line or two grep or sed would have no problems doing so.
Ooooh, that's great, thanks for sharing! I didn't have that in UNR by default, but it was a 160KB executable. Thanks again.
I've been using acpitool since I got my laptop, its the best acpi anything I've seen so far in my proding around. Glad I could be of help.
TheStorm wrote:
I've been using acpitool since I got my laptop, its the best acpi anything I've seen so far in my proding around. Glad I could be of help.
Yup, now I have a cool tool to use. Smile I'm looking forward to exploring it more.
Kerm, I tried compiling this, but I got:


Code:
gcc -o "checkBattery" "checkBattery.c" (in directory: /home/david/Documents/C/Random C Stuff)
/tmp/ccCWr6QR.o: In function `getBattState':
checkBattery.c:(.text+0x568): undefined reference to `floor'
checkBattery.c:(.text+0x5b8): undefined reference to `floor'
collect2: ld returned 1 exit status
Compilation failed.


Linux Mint 11, GCC.
ScoutDavid wrote:
Kerm, I tried compiling this, but I got:


Code:
gcc -o "checkBattery" "checkBattery.c" (in directory: /home/david/Documents/C/Random C Stuff)
/tmp/ccCWr6QR.o: In function `getBattState':
checkBattery.c:(.text+0x568): undefined reference to `floor'
checkBattery.c:(.text+0x5b8): undefined reference to `floor'
collect2: ld returned 1 exit status
Compilation failed.


Linux Mint 11, GCC.


Sounds like your Linux distribution doesn't automatically include some of the usual headers that are included by default on other system. Try including math.h at the top of the file.
elfprince, math.h was already included, but by including it at the top of the file (first include) made no difference, the output is the same, as I expected.
You need to add -lm to your gcc line. You always need to link against libm when you're using math.h.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 2
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement