PINE64
Display battery charge value with LXDE and lxpanel - Printable Version

+- PINE64 (https://forum.pine64.org)
+-- Forum: Pinebook (https://forum.pine64.org/forumdisplay.php?fid=76)
+--- Forum: Linux (https://forum.pine64.org/forumdisplay.php?fid=79)
+--- Thread: Display battery charge value with LXDE and lxpanel (/showthread.php?tid=7586)



Display battery charge value with LXDE and lxpanel - wlad - 06-11-2019

In my opinion, Armbian with LXDE as window manager is a good fitting combination for the limited power of the Pinebook. LXDE provides only few visual effects, so the system can be used with acceptable performance. Unfortunately, I had an issue with the battery plugin of the lxpanel (the bar where all current windows/processes are stacked into). 

Indication: Battery plugin (batt) of lxpanel does not show the current "capacity" (charged value in percent) of Pinebook's battery. Instead, it displays "0% charged" and continuously fires the low-battery alarm.

Reason: The integrated functionality of the battery plugin calculates the charged value by using the coulomb-based counters "charge_now" and "charge_full" (or alternatively "energy_now" and "energy_full"). These counters are usually provided by the power management unit's Linux kernel module. The kernel module (axp20x) for the AXP803 power management unit of the Pinebook does not provide "charge_now" and "charge_full" information. Instead, a percentage value for the current charge is provided as "capacity" (try 'cat /sys/class/power_supply/axp20x-battery/capacity').

Solution: I have changed the source code for the batt plugin with respect to the above mentioned reasons. Now, the percentage value is directly read from the corresponding sysfs file and not longer calculated indirectly.

The original code (lxpanel version 0.10.0) that you can find in 'lxpanel-0.10.0/plugins/batt/batt_sys.c' looked like this: 

Code:
battery* battery_update(battery *b) {
   gchar *gctmp; int promille;

   /* [...] */

   /* read from sysfs */
   b->charge_now = get_gint_from_infofile(b, "charge_now");
   b->energy_now = get_gint_from_infofile(b, "energy_now");

   b->current_now = get_gint_from_infofile(b, "current_now");
   b->power_now   = get_gint_from_infofile(b, "power_now");

   if (b->current_now < -1)
           b->current_now = - b->current_now;

   b->charge_full = get_gint_from_infofile(b, "charge_full");
   b->energy_full = get_gint_from_infofile(b, "energy_full");

   b->charge_full_design = get_gint_from_infofile(b, "charge_full_design");
   b->energy_full_design = get_gint_from_infofile(b, "energy_full_design");

   b->voltage_now = get_gint_from_infofile(b, "voltage_now");

   gctmp = get_gchar_from_infofile(b, "type");
   b->type_battery = gctmp ? (strcasecmp(gctmp, "battery") == 0) : TRUE;
   g_free(gctmp);

   g_free(b->state);
   b->state = get_gchar_from_infofile(b, "status");
   if (!b->state)
       b->state = get_gchar_from_infofile(b, "state");
   if (!b->state) {
       if (b->charge_now != -1 || b->energy_now != -1
               || b->charge_full != -1 || b->energy_full != -1)
           b->state = g_strdup("available");
       else
           b->state = g_strdup("unavailable");
   }

   if (b->charge_now != -1 && b->charge_full != -1)
       promille = (b->charge_now * 1000) / b->charge_full;
   else if (b->energy_full != -1 && b->energy_now != -1)
       /* no charge data, let try energy instead */
       promille = (b->energy_now * 1000) / b->energy_full;
   else
       promille = 0;

   b->percentage = (promille + 5) / 10; /* round properly */
   if (b->percentage > 100)
       b->percentage = 100;

/* [...] */

   return b;
}

This code has been changed to the following:

Code:
battery* battery_update(battery *b) {
   gchar *gctmp; int promille;
    /* [...] */

   /* read from sysfs */
   b->charge_now = get_gint_from_infofile(b, "charge_now");
   b->energy_now = get_gint_from_infofile(b, "energy_now");

   b->current_now = get_gint_from_infofile(b, "current_now");
   b->power_now   = get_gint_from_infofile(b, "power_now");

   if (b->current_now < -1)
           b->current_now = - b->current_now;

   b->charge_full = get_gint_from_infofile(b, "charge_full");
   b->energy_full = get_gint_from_infofile(b, "energy_full");

   b->charge_full_design = get_gint_from_infofile(b, "charge_full_design");
   b->energy_full_design = get_gint_from_infofile(b, "energy_full_design");

   b->voltage_now = get_gint_from_infofile(b, "voltage_now");
   // changed to direct read of percentage value (supported by pinebook)
   b->percentage = get_norm_gint_from_infofile(b, "capacity");

   gctmp = get_gchar_from_infofile(b, "type");
   b->type_battery = gctmp ? (strcasecmp(gctmp, "battery") == 0) : TRUE;
   g_free(gctmp);

   /* [...] */
   
   if (b->charge_now != -1 && b->charge_full != -1)
       promille = (b->charge_now * 1000) / b->charge_full;
   else if (b->energy_full != -1 && b->energy_now != -1)
       /* no charge data, let try energy instead */
       promille = (b->energy_now * 1000) / b->energy_full;
   else
       promille = 0;

   // percentage was indirectly calculated, this has changed to direct read of value
   //b->percentage = (promille + 5) / 10; /* round properly */
   if (b->percentage > 100)
       b->percentage = 100;

   /* [...] */
   return b;
}

As this requires a new function for reading the numerical value as provided by '/sys/class/power_supply/axp20x-battery/capacity', I introduced the following function which was inserted below the existing function "static gint get_gint_from_infofile(battery *b, gchar *sys_file)" in the same source code file "batt_sys.c":

Code:
/* get_norm_gint_from_infofile():
*         If the sys_file exists, then its value is converted to an int,
*         and returned.
*         Failure is indicated by returning -1. */
static gint get_norm_gint_from_infofile(battery *b, gchar *sys_file) {
   gchar *file_content = parse_info_file(b, sys_file);
   gint value = -1;

   if (file_content != NULL)
       value = atoi(file_content);
   g_free(file_content);

   return value;
}

The most time consuming part of this change was building the plugin's shared library file 'batt.so' because of all the required dependencies. But finally, it worked and I could replace the original file in '/usr/lib/aarch64-linux-gnu/lxpanel/plugins/'. The charge indicator now works as it should (except that I see no practical way how to predict the remaining power-on time).

I provide to you my build of 'batt.so' here (together with the changed 'batt_sys.c'), so that you do not need to build it by yourself: [attachment=1371] 
I use it on Armbian 5.86 Debian stretch with LXDE 9.9 and lxpanel version 0.9.3. It may also work with other versions of LXDE/lxpanel and in combination with different Armbian versions. Please post your message on whether it worked for you by mentioning your system configuration. Thanks!