This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm whether you accept or reject these cookies being set.

A cookie will be stored in your browser regardless of choice to prevent you being asked this question again. You will be able to change your cookie settings at any time using the link in the footer.

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Raspi UPS Hat in Android
#1
Hi All,
I'm wondering if anyone can help me get started on the last part of my project.
Essentially I have one of these battery packs for Raspberry Pi, and I would like to read the capacity over /dev/i2c-1 and present this as the battery level in android.
http://www.raspberrypiwiki.com/index.php..._HAT_Board

From what i have read the best way would be to have this built as a .so and pushed into /system/lib.
however struggling to find any tutorials on how to interface with i2c using the built in libraries (and not Android Things) and while i can find loads of guides on reading the battery level, I cant see any for how to set this.

Update.
I have an almost working version now. It reads the driver values and is loaded by the OS, I just need to get the values into the correct format. (mV, mA, etc)

I've uploaded the file and instructions below if anyone wants it.
https://github.com/lostangel556/RaspiUPS-Tinker
Reply
#2
Hi lostangel556,
you can take look at the kernel source. E.g. the /drivers/power/stc3100_battery.c can give you an idea how to read from i2c and provide the battery interface.
Reply
#3
Cheers lobo, will have a look at how that's structured.
Managed to get something compiled thats reads the voltage and capacity, just need to make it into a driver and then ill pop something on github for those on here that want it
Reply
#4
Yes, I would benefit from your work. As I already thought of measure the voltage of my own tinkered ups with a small attiny chip via i2c. Don't hesitate to ask here if you need some support.
E.g. you can take this source code and modify to your needs, Then you would need to add your driver to the makefile in /drivers/power and dependent also is needed to modify the Kconfig. Also would be needed a entry in the devicetree under the "&i2c1 {" with the driver id and the i2c address.
Reply
#5
Ok all,
Here is what I've got so far.

Code:
/* RaspiUPS battery driver
* http://www.raspberrypiwiki.com/index.php/Raspi_UPS_HAT_Board
* --(1)--Save this file as "drivers/power/raspiups_battery.c"
* --(2)--Define device under i2c1 devicetree in "arch/arm/boot/dts/rk3288-miniarm.dts" e.g
* &i2c1 {
*       raspiups@36 {
*               reg = <0x36>;
*       };
* };
* --(3)--Add following line to /drivers/power/Makefile
* obj-$(CONFIG_RASPIUPS_BATTERY)  += raspiups_battery.o
* --(4)--Add following to /drivers/power/Kconfig
* config RASPIUPS_BATTERY
*       tristate "RaspiUPS Battery HAT"
*       default y
*       help
*         say Y to enable support for the RaspiUPS HAT
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/idr.h>
#include <linux/i2c.h>
#include <asm/unaligned.h>
#include <linux/swab.h>
#include <linux/slab.h>

#define DRIVER_VERSION            "1.0.0"

#define BATTERY_CAPACITY_MAH    2500 //  define battery capacity mah

#define RaspiUPS_REG_RSOCL        0x04 // Relative State-of-Charge/Capacity
#define RaspiUPS_REG_VOLTL        0x02

#define RaspiUPS_SPEED     100000

struct RaspiUPS_device_info {
    struct device         *dev;
    struct power_supply    bat;
    struct delayed_work work;
    unsigned int interval;
    struct i2c_client    *client;
};

static enum power_supply_property RaspiUPS_battery_props[] = {
    //POWER_SUPPLY_PROP_PRESENT,
    POWER_SUPPLY_PROP_VOLTAGE_NOW,
    POWER_SUPPLY_PROP_CAPACITY,
};

/*
* Return the battery Voltage in millivolts
* Or < 0 if something fails.
*/
static int RaspiUPS_battery_voltage(struct RaspiUPS_device_info *di)
{
   int decimal_number;
   decimal_number = i2c_smbus_read_word_swapped(di->client,RaspiUPS_REG_VOLTL);
   return (decimal_number * 78.125) / 1000000;  //voltage (mV)
}

/*
* Return the battery Relative State-of-Charge
* Or < 0 if something fails.
*/
static int RaspiUPS_battery_rsoc(struct RaspiUPS_device_info *di)
{
   int decimal_number;
   decimal_number = i2c_smbus_read_word_swapped(di->client,RaspiUPS_REG_VOLTL);
   return decimal_number / 256;  //charge capacity (%)
}
/*
//Get whether the Battery is charging by comparing voltage with previous value
//If Higher than previous then it is charging.
static int dc_charge_status(struct RaspiUPS_device_info *di)
{
       int volts;
       volts = (i2c_smbus_read_word_swapped(di->client,RaspiUPS_REG_VOLTL) * 78.125) / 1000000;
    if(volts > (di.get_property(POWER_SUPPLY_PROP_VOLTAGE_NOW)))
        return POWER_SUPPLY_STATUS_CHARGING;
    else
        return POWER_SUPPLY_STATUS_NOT_CHARGING;
}
*/
static int RaspiUPS_battery_get_property(struct power_supply *psy,
                    enum power_supply_property psp,
                    union power_supply_propval *val)
{
    struct RaspiUPS_device_info *di = container_of(psy, struct RaspiUPS_device_info, bat);

    switch (psp) {
    /*case POWER_SUPPLY_PROP_STATUS:
        val->intval = dc_charge_status(di);
        break;*/
    case POWER_SUPPLY_PROP_VOLTAGE_NOW:
        val->intval = RaspiUPS_battery_voltage(di);
        break;
    case POWER_SUPPLY_PROP_CAPACITY:
        val->intval = RaspiUPS_battery_rsoc(di);
        break;
    default:
        return -EINVAL;
    }

    return 0;
}

static void RaspiUPS_powersupply_init(struct RaspiUPS_device_info *di)
{
    di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
    di->bat.properties = RaspiUPS_battery_props;
    di->bat.num_properties = ARRAY_SIZE(RaspiUPS_battery_props);
    di->bat.get_property = RaspiUPS_battery_get_property;
    di->bat.external_power_changed = NULL;
}

static void RaspiUPS_battery_update_status(struct RaspiUPS_device_info *di)
{
    power_supply_changed(&di->bat);
}

static void RaspiUPS_battery_work(struct work_struct *work)
{
    struct RaspiUPS_device_info *di = container_of(work, struct RaspiUPS_device_info, work.work);

    RaspiUPS_battery_update_status(di);
    /* reschedule for the next time */
    schedule_delayed_work(&di->work, di->interval);
}

static int RaspiUPS_battery_probe(struct i2c_client *client,
                 const struct i2c_device_id *id)
{
    struct RaspiUPS_device_info *di;
    int retval = 0;
    //u8 regs[2] = {0x10,0x1d};  ///init regs mode ctrl

    di = kzalloc(sizeof(*di), GFP_KERNEL);
    if (!di) {
        dev_err(&client->dev, "failed to allocate device info data\n");
        retval = -ENOMEM;
        goto batt_failed_2;
    }

    i2c_set_clientdata(client, di);
    di->dev = &client->dev;
    di->bat.name = "RaspiUPS-battery";
    di->client = client;
    /* 4 seconds between monitor runs interval */
    di->interval = msecs_to_jiffies(4 * 1000);
    RaspiUPS_powersupply_init(di);

    retval = power_supply_register(&client->dev, &di->bat);
    if (retval) {
        dev_err(&client->dev, "failed to register battery\n");
        goto batt_failed_3;
    }
    
    INIT_DELAYED_WORK(&di->work, RaspiUPS_battery_work);
    schedule_delayed_work(&di->work, di->interval);
    
    dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);

    return 0;

batt_failed_3:
    kfree(di);
batt_failed_2:
    return retval;
}

static int RaspiUPS_battery_remove(struct i2c_client *client)
{
    struct RaspiUPS_device_info *di = i2c_get_clientdata(client);

    cancel_delayed_work_sync(&di->work);
    power_supply_unregister(&di->bat);

    kfree(di->bat.name);

    kfree(di);

    return 0;
}

/*
* Module stuff
*/

static const struct i2c_device_id RaspiUPS_id[] = {
    { "RaspiUPS", 0 },
    {},
};

static struct i2c_driver RaspiUPS_battery_driver = {
    .driver = {
        .name = "RaspiUPS-battery",
    },
    .probe = RaspiUPS_battery_probe,
    .remove = RaspiUPS_battery_remove,
    .id_table = RaspiUPS_id,
};

static int __init RaspiUPS_battery_init(void)
{
    int ret;

    ret = i2c_add_driver(&RaspiUPS_battery_driver);
    if (ret)
        printk(KERN_ERR "Unable to register RaspiUPS driver\n");

    return ret;
}
module_init(RaspiUPS_battery_init);

static void __exit RaspiUPS_battery_exit(void)
{
    i2c_del_driver(&RaspiUPS_battery_driver);
}
module_exit(RaspiUPS_battery_exit);

MODULE_AUTHOR("lostangel556");
MODULE_DESCRIPTION("RaspiUPS battery monitor driver");
MODULE_LICENSE("GPL");


This driver itself appears to compile fine and produce raspiups_battery.so, but gives me the following errors right at the end.

Code:
drivers/built-in.o: In function `RaspiUPS_battery_voltage':
/home/steve/Android/android_kernel/drivers/power/raspiups_battery.c:65: undefined reference to `__aeabi_i2d'
/home/steve/Android/android_kernel/drivers/power/raspiups_battery.c:65: undefined reference to `__aeabi_dmul'
/home/steve/Android/android_kernel/drivers/power/raspiups_battery.c:65: undefined reference to `__aeabi_ddiv'
/home/steve/Android/android_kernel/drivers/power/raspiups_battery.c:65: undefined reference to `__aeabi_d2iz'
make: *** [vmlinux] Error 1
Makefile:815: recipe for target 'vmlinux' failed

I've put the build instructions I've used at the top of the file if anyone more experienced could take a look and spot what I've done wrong.
Also note that it wont detect whether it is charging or not yet as still working on getting the function to read the structure so it can tell the direction the voltage is moving (positive is charging, negative discharging).
Reply
#6
Think i found my own answer and will try tonight.
https://stackoverflow.com/questions/2649...stdlib-but

Yeah, i used a double-precision floating point on line 64 in the return,,, Doh :facepalm:
Tinker kernel looks to only support single-precision range. so line 64, 78.125 becomes 78.125f
Reply
#7
ok, it builds and kernel can be written successfully. Unfortunately it looks like something else is overriding the battery as it always thinks it is using AC power (using CPU-Z app)
Reply
#8
Hello lostangel556,
have you tried to remove step by step the other drivers from the config that are compiled into the kernel? I see that there are a lot of object files in the power directory and for sure only some are required by the system.
Reply
#9
Just stumbled across the following information.
Below appears that it is the datasheet for the IC as its hosted by the same site that offer the only instructions for the HAT.
http://www.raspberrypiwiki.com/images/0/...asheet.pdf

Now, Looking inside the kernel there is already a driver for the cw2015 fuel gauge, but doesnt appear to be active.

I've gone to the devicetree (rk3288-miniarm.dts) and there is an entry for it on the wrong i2c bus, so created the same on i2c-1 but with the address 0x36 and flashed back to sdcard.

if you do "dmesg | grep -i cw201" via adb you get the following

[    1.014652] cw201x: support DC  charger
[    1.015655] cw201x: probe of 0-0062 failed with error -11
[    1.015742] cw201x: support  USB charger
[    1.015765] cw201x 1-0036: failed to request dc_det_pin gpio
[    1.015777] cw201x 1-0036: cw_bat_gpio_init error
[    1.015792] cw201x: probe of 1-0036 failed with error -16

Looks like some probing may be in order to find what the following need to be set to. Then it should work :fingerscrossed:
dc_det_gpio = <&gpio0 GPIO_B0 GPIO_ACTIVE_LOW>;
bat_low_gpio = <&gpio0 GPIO_A7 GPIO_ACTIVE_LOW>;
chg_ok_gpio = <&gpio0 GPIO_B1 GPIO_ACTIVE_HIGH>;
Reply
#10
ok, looks like im not the only one to try with this particular board (https://brousant.nl/jm3/elektronica/104-...spberry-pi). Long story short;
This "UPS" Hat uses an Maxim MAX17048 IC for Fuel Gauge reporting back to the Host. However due to the other limitations of the board (it doesnt cut the power to the board when no load, so if powered restored while battery not empty then Tinker wont start back up) it probably wont work for what we hope to use it for. Least not without some modifications to cut the power.

I've got a Sparkfun Battery Babysitter on order which uses a BQ27441 Fuel Gauge and should be covered under the bq27541 driver (he hopes). otherwise I might dig out my breadboard and try to wire up a true UPS.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)