From 7b7c8236f9e1c2b91437def2adf494c681dbc539 Mon Sep 17 00:00:00 2001 From: Andrew Anderson Date: Thu, 12 Sep 2019 19:37:55 -0400 Subject: [PATCH 01/15] Add pijuice I2C driver for PiJuice HAT 1/4 Add pijuice driver --- drivers/pijuice.c | 769 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 769 insertions(+) create mode 100644 drivers/pijuice.c diff --git a/drivers/pijuice.c b/drivers/pijuice.c new file mode 100644 index 0000000000..ae6fb1cca1 --- /dev/null +++ b/drivers/pijuice.c @@ -0,0 +1,769 @@ +/* pijuice.c Driver for the PiJuice HAT (www.pijuice.com), addressed via i2c. + + Copyright (C) 2019 Andrew Anderson + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#ifndef _LINUX_I2C_H +# include +#endif +#ifndef _LINUX_I2C_DEV_H +# include +#endif + +#include "main.h" + +#define STATUS_CMD 0x40 +#define CHARGE_LEVEL_CMD 0x41 +#define CHARGE_LEVEL_HI_RES_CMD 0x42 +#define FAULT_EVENT_CMD 0x44 +#define BUTTON_EVENT_CMD 0x45 +#define BATTERY_TEMPERATURE_CMD 0x47 +#define BATTERY_VOLTAGE_CMD 0x49 +#define BATTERY_CURRENT_CMD 0x4b +#define IO_VOLTAGE_CMD 0x4d +#define IO_CURRENT_CMD 0x4f + +#define CHARGING_CONFIG_CMD 0x51 +#define BATTERY_PROFILE_ID_CMD 0x52 +#define BATTERY_PROFILE_CMD 0x53 +#define BATTERY_EXT_PROFILE_CMD 0x54 +#define BATTERY_TEMP_SENSE_CONFIG_CMD 0x5D + +#define POWER_INPUTS_CONFIG_CMD 0x5E +#define RUN_PIN_CONFIG_CMD 0x5F +#define POWER_REGULATOR_CONFIG_CMD 0x60 +#define WATCHDOG_ACTIVATION_CMD 0x61 +#define POWER_OFF_CMD 0x62 +#define WAKEUP_ON_CHARGE_CMD 0x63 +#define SYSTEM_POWER_SWITCH_CTRL_CMD 0x64 + +#define LED_STATE_CMD 0x66 +#define LED_BLINK_CMD 0x68 +#define LED_CONFIGURATION_CMD 0x6A +#define BUTTON_CONFIGURATION_CMD 0x6E + +#define IO1_CONFIGURATION_CMD 0x72 +#define IO1_PIN_ACCESS_CMD 0x75 + +#define IO2_CONFIGURATION_CMD 0x77 +#define IO2_PIN_ACCESS_CMD 0x7A + +#define I2C_ADDRESS_CMD 0x7C + +#define ID_EEPROM_WRITE_PROTECT_CTRL_CMD 0x7E +#define ID_EEPROM_ADDRESS_CMD 0x7F + +#define RTC_TIME_CMD 0xB0 +#define RTC_ALARM_CMD 0xB9 +#define RTC_CTRL_STATUS_CMD 0xC2 + +#define RESET_TO_DEFAULT_CMD 0xF0 +#define FIRMWARE_VERSION_CMD 0xFD + +#define BATT_NORMAL 0 +#define BATT_CHARGING_FROM_IN 1 +#define BATT_CHARGING_FROM_5V 2 +#define BATT_NOT_PRESENT 3 + +#define POWER_NOT_PRESENT 0 +#define POWER_BAD 1 +#define POWER_WEAK 2 +#define POWER_PRESENT 3 + +#define LOW_BATTERY_THRESHOLD 25.0 +#define HIGH_BATTERY_THRESHOLD 75.0 + +#define DRIVER_NAME "PiJuice UPS driver" +#define DRIVER_VERSION "0.7" + +static uint8_t i2c_address = 0x14; +static uint8_t shutdown_delay = 30; + +/* + * Flags used to indicate a change in power status + */ +static uint8_t usb_power = 0; +static uint8_t gpio_power = 0; +static uint8_t battery_power = 0; + +/* + * Smooth out i2c read errors by holding the most recent + * battery charge level reading + */ +static float battery_charge_level = 0; + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Andrew Anderson ", + DRV_EXPERIMENTAL, + { NULL } +}; + +#define I2C_READ_BYTE(fd, cmd, label) \ + if ((data = i2c_smbus_read_byte_data(upsfd, cmd)) < 0 ) { \ + upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ + return; \ + } + +#define I2C_WRITE_BYTE(fd, cmd, value, label) \ + if ((data = i2c_smbus_write_byte_data(upsfd, cmd, value)) < 0 ) { \ + upsdebugx(2, "Failure writing to the i2c bus [%s]", label); \ + return; \ + } + +#define I2C_READ_WORD(fd, cmd, label) \ + if ((data = i2c_smbus_read_word_data(upsfd, cmd)) < 0 ) { \ + upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ + return; \ + } + +#define I2C_READ_BLOCK(fd, cmd, size, block, label) \ + if ((i2c_smbus_read_i2c_block_data(upsfd, cmd, size, block)) < 0 ) { \ + upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ + return; \ + } + +/* Not all i2c-tools installs include the appropriate headers/libraries */ +#ifndef LIB_I2C_SMBUS_H +static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_smbus_ioctl_data args; + __s32 err; + + args.read_write = read_write; + args.command = command; + args.size = size; + args.data = data; + + err = ioctl(file, I2C_SMBUS, &args); + if (err == -1) + err = -errno; + return err; +} + + +static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) +{ + union i2c_smbus_data data; + int err; + + if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data)) < 0) + return err; + else + return 0x0FF & data.byte; +} + +static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value) +{ + union i2c_smbus_data data; + int err; + + data.byte = value; + if ((err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data)) < 0) + return err; + else + return 0x0FF & data.byte; +} + + +static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) +{ + union i2c_smbus_data data; + int err; + + if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, + I2C_SMBUS_WORD_DATA, &data)) < 0) + return err; + else + return 0x0FFFF & data.word; +} + +static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value) +{ + union i2c_smbus_data data; + int err; + + data.word = value; + if ((err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command, + I2C_SMBUS_WORD_DATA, &data)) < 0) + return err; + else + return 0x0FFFF & data.word; +} + +static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length, __u8 *values) +{ + union i2c_smbus_data data; + int err; + + if ( length > I2C_SMBUS_BLOCK_MAX) + { + length = I2C_SMBUS_BLOCK_MAX; + } + + data.block[0] = length; + memcpy(data.block + 1, values, length); + + if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data)) < 0) + return NULL; + else + memcpy(values, &data.block[1], data.block[0]); + return values; +} +#endif + +static inline int open_i2c_bus(char *path, uint8_t addr) +{ + int file; + + if ((file = open(path, O_RDWR)) < 0) + { + fatal_with_errno(EXIT_FAILURE, "Failed to open the i2c bus on %s", path); + } + + if (ioctl(file, I2C_SLAVE, addr) < 0) + { + fatal_with_errno(EXIT_FAILURE, "Failed to acquire the i2c bus and/or talk to the UPS"); + } + + return file; +} + +static void get_charge_level_hi_res() +{ + uint8_t cmd = CHARGE_LEVEL_HI_RES_CMD; + uint16_t data; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) + + /* + * Use an external variable to allow for missed i2c bus + * reads; the charge level data may be slightly stale, + * but no other options seem reasonable: + * + * 1) return 0 / NULL + * leads to a false report of a depleted battery, possibly + * triggering an immediate shutdown if on battery power only + * 2) retry the read immediately + * could tie up the i2c bus and make matters exponentially worse + */ + battery_charge_level = data / 10.0; + + upsdebugx( 1, "battery.charge: %02.1f%%", battery_charge_level ); + dstate_setinfo( "battery.charge", "%02.1f", battery_charge_level ); +} + +static void get_status() +{ + uint8_t cmd = STATUS_CMD; + uint8_t data; + char status_buf[ST_MAX_VALUE_LEN]; + + upsdebugx( 3, __FUNCTION__ ); + + memset( status_buf, 0, sizeof( status_buf )); + + I2C_READ_BYTE( upsfd, cmd, __FUNCTION__ ) + + uint8_t batteryStatus = data >> 2 & 0x03; + switch( batteryStatus ) + { + case BATT_NORMAL: + upsdebugx( 1, "Battery Status: Normal" ); + dstate_setinfo( "battery.packs", "%d", 1 ); + dstate_setinfo( "battery.packs.bad", "%d", 0 ); + break; + case BATT_CHARGING_FROM_IN: + upsdebugx( 1, "Battery Status: Charging from IN" ); + dstate_setinfo( "battery.packs", "%d", 1 ); + dstate_setinfo( "battery.packs.bad", "%d", 0 ); + break; + case BATT_CHARGING_FROM_5V: + upsdebugx( 1, "Battery Status: Charging from 5V" ); + dstate_setinfo( "battery.packs", "%d", 1 ); + dstate_setinfo( "battery.packs.bad", "%d", 0 ); + break; + case BATT_NOT_PRESENT: + upsdebugx( 1, "Battery Status: Not Present" ); + dstate_setinfo( "battery.packs", "%d", 0 ); + dstate_setinfo( "battery.packs.bad", "%d", 1 ); + break; + default: + upsdebugx( 1, "battery.status: UNKNOWN" ); + } + + uint8_t powerInput = data >> 4 & 0x03; + switch( powerInput ) + { + case POWER_NOT_PRESENT: + upsdebugx( 1, "Power Input: Not Present" ); + break; + case POWER_BAD: + upsdebugx( 1, "Power Input: Bad" ); + break; + case POWER_WEAK: + upsdebugx( 1, "Power Input: Weak" ); + break; + case POWER_PRESENT: + upsdebugx( 1, "Power Input: Present" ); + break; + default: + upsdebugx( 1, "Power Input: UNKNOWN" ); + } + + uint8_t powerInput5vIo = data >> 6 & 0x03; + switch( powerInput5vIo ) + { + case POWER_NOT_PRESENT : + upsdebugx(1, "Power Input 5v: Not Present"); + break; + case POWER_BAD: + upsdebugx(1, "Power Input 5v: Bad"); + break; + case POWER_WEAK: + upsdebugx(1, "Power Input 5v: Weak"); + break; + case POWER_PRESENT: + upsdebugx(1, "Power Input 5v: Present"); + break; + default: + upsdebugx(1, "Power Input 5v: UNKNOWN"); + } + + if ( batteryStatus == BATT_NORMAL || + batteryStatus == BATT_CHARGING_FROM_IN || + batteryStatus == BATT_CHARGING_FROM_5V ) + { + get_charge_level_hi_res(); + + upsdebugx( 1, "battery_charge_level: %02.1f", battery_charge_level ); + + if ( battery_charge_level <= LOW_BATTERY_THRESHOLD ) + { + upsdebugx( 1, "Low Battery Status" ); + snprintfcat( status_buf, ST_MAX_VALUE_LEN, "LB " ); + } + else if ( battery_charge_level > HIGH_BATTERY_THRESHOLD ) + { + upsdebugx( 1, "High Battery Status" ); + snprintfcat( status_buf, ST_MAX_VALUE_LEN, "HB " ); + } + } + else if ( batteryStatus == BATT_NOT_PRESENT ) + { + snprintfcat( status_buf, ST_MAX_VALUE_LEN, "RB " ); + } + + if ( batteryStatus <= BATT_NOT_PRESENT && + powerInput <= POWER_PRESENT && + powerInput5vIo <= POWER_PRESENT ) + { + if ( powerInput == POWER_NOT_PRESENT && + ( powerInput5vIo != POWER_NOT_PRESENT && + powerInput5vIo <= POWER_PRESENT )) + { + if ( usb_power != 1 || gpio_power != 0 ) + { + upslogx( LOG_NOTICE, "On USB power" ); + } + usb_power = 1; + gpio_power = 0; + battery_power = 0; + upsdebugx( 1, "On USB power [%d:%d:%d]", usb_power, gpio_power, battery_power ); + + snprintfcat( status_buf, sizeof(status_buf), "OL" ); + if ( batteryStatus == BATT_CHARGING_FROM_5V ) + { + snprintfcat( status_buf, sizeof( status_buf ), " CHRG" ); + upsdebugx( 1, "battery charging" ); + dstate_setinfo( "battery.charger.status", "%s", "charging" ); + } + else if ( batteryStatus == BATT_NORMAL ) + { + upsdebugx( 1, "battery resting" ); + dstate_setinfo( "battery.charger.status", "%s", "resting" ); + } + status_set( status_buf ); + } + else if ( powerInput5vIo == POWER_NOT_PRESENT && + ( powerInput != POWER_NOT_PRESENT && + powerInput <= POWER_PRESENT )) + { + if ( gpio_power != 1 || usb_power != 0 ) + { + upslogx( LOG_NOTICE, "On 5V_GPIO power" ); + } + usb_power = 0; + gpio_power = 1; + battery_power = 0; + upsdebugx( 1, "On 5V_GPIO power [%d:%d:%d]", usb_power, gpio_power, battery_power ); + + snprintfcat( status_buf, sizeof(status_buf), "OL" ); + if ( batteryStatus == BATT_CHARGING_FROM_IN ) + { + snprintfcat( status_buf, sizeof(status_buf), " CHRG" ); + status_set( status_buf ); + upsdebugx( 1, "battery charging"); + dstate_setinfo( "battery.charger.status", "%s", "charging" ); + } + else if ( batteryStatus == BATT_NORMAL ) + { + status_set( status_buf ); + upsdebugx(1, "battery resting"); + dstate_setinfo( "battery.charger.status", "%s", "resting" ); + } + } + else if ( ( powerInput != POWER_NOT_PRESENT && powerInput <= POWER_PRESENT ) && + ( powerInput5vIo != POWER_NOT_PRESENT && powerInput5vIo <= POWER_PRESENT )) + { + if ( usb_power != 1 || gpio_power != 1 ) + { + upslogx( LOG_NOTICE, "On USB and 5V_GPIO power" ); + } + usb_power = 1; + gpio_power = 1; + battery_power = 0; + upsdebugx( 1, "On USB and 5V_GPIO power [%d:%d:%d]", usb_power, gpio_power, battery_power ); + + snprintfcat( status_buf, sizeof( status_buf ), "OL" ); + if ( batteryStatus == BATT_CHARGING_FROM_IN || + batteryStatus == BATT_CHARGING_FROM_5V ) + { + snprintfcat( status_buf, sizeof(status_buf), " CHRG"); + status_set( status_buf ); + upsdebugx(1, "battery charging"); + dstate_setinfo("battery.charger.status", "%s", "charging"); + } + else if ( batteryStatus == BATT_NORMAL ) + { + status_set( status_buf ); + upsdebugx( 1, "battery resting" ); + dstate_setinfo( "battery.charger.status", "%s", "resting" ); + } + } + else if ( powerInput == POWER_NOT_PRESENT && powerInput5vIo == POWER_NOT_PRESENT ) + { + if ( usb_power != 0 || gpio_power != 0 ) + { + upslogx( LOG_NOTICE, "On Battery power" ); + } + usb_power = 0; + gpio_power = 0; + battery_power = 1; + upsdebugx( 1, "On Battery power [%d:%d:%d]", usb_power, gpio_power, battery_power ); + + snprintfcat( status_buf, sizeof(status_buf), "OB DISCHRG" ); + status_set( status_buf ); + } + } +} + +static void get_battery_temperature() +{ + uint8_t cmd = BATTERY_TEMPERATURE_CMD; + int16_t data; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) + + upsdebugx( 1, "battery.temperature: %d°C", data ); + dstate_setinfo( "battery.temperature", "%d", data ); + /* Use this for UPS temperature as well, since there is not a separate sensor available */ + dstate_setinfo( "ups.temperature", "%d", data ); +} + +static void get_battery_voltage() +{ + uint8_t cmd = BATTERY_VOLTAGE_CMD; + int16_t data; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) + + upsdebugx( 1, "battery.voltage: %0.3fV", data / 1000.0 ); + dstate_setinfo( "battery.voltage", "%0.3f", data / 1000.0 ); + dstate_setinfo( "battery.voltage.nominal", "%0.3f", 4.1 ); +} + +static void get_battery_current() +{ + uint8_t cmd = BATTERY_CURRENT_CMD; + int16_t data; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) + + upsdebugx( 1, "battery.current: %dmA", data ); + dstate_setinfo( "battery.current", "%d", data ); +} + +static void get_io_voltage() +{ + uint8_t cmd = IO_VOLTAGE_CMD; + int16_t data; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) + + upsdebugx( 1, "input.voltage: %.3fV", data / 1000.0 ); + dstate_setinfo( "input.voltage", "%.3f", data / 1000.0 ); +} + +static void get_io_current() +{ + uint8_t cmd = IO_CURRENT_CMD; + int16_t data; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) + + if ( data & ( 1 << 15 ) ) + { + data = data - ( 1 << 16 ); + } + + upsdebugx( 1, "input.current: %dmA", data ); + dstate_setinfo( "input.current", "%d", data ); +} + +static void get_firmware_version() +{ + uint8_t cmd = FIRMWARE_VERSION_CMD; + uint16_t data; + uint8_t major, minor; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) + + major = data >> 4; + minor = ( data << 4 & 0xf0 ) >> 4; + + if (( major != 1 ) || ( minor > 3 )) + { + upslogx( LOG_WARNING, "Unknown Firmware release: %d.%d", major, minor ); + } + + upsdebugx( 1, "ups.firmware: %d.%d", major, minor ); + dstate_setinfo( "ups.firmware", "%d.%d", major, minor ); +} + +static void get_battery_profile() +{ + uint8_t cmd = BATTERY_PROFILE_CMD; + __u8 block[I2C_SMBUS_BLOCK_MAX]; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_BLOCK( upsfd, cmd, 14, block, __FUNCTION__ ) + + upsdebugx( 1, "battery.capacity: %0.3fAh", ( block[1] << 8 | block[0] ) / 1000.0 ); + dstate_setinfo( "battery.capacity", "%0.3f", ( block[1] << 8 | block[0] ) / 1000.0 ); +} + +static void get_battery_profile_ext() +{ + uint8_t cmd = BATTERY_EXT_PROFILE_CMD; + __u8 block[I2C_SMBUS_BLOCK_MAX]; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_BLOCK( upsfd, cmd, 17, block, __FUNCTION__ ) + + switch( block[0] & 0xFF00 ) + { + case 0: + upsdebugx( 1, "battery.type: LiPO" ); + dstate_setinfo( "battery.type", "%s", "LiPO" ); + break; + case 1: + upsdebugx( 1, "battery.type: LiFePO4" ); + dstate_setinfo( "battery.type", "%s", "LiFePO4" ); + break; + default: + upsdebugx( 1, "battery.type: UNKNOWN" ); + dstate_setinfo( "battery.type", "%s", "UNKNOWN" ); + } +} + +static void get_power_off() +{ + uint8_t cmd = POWER_OFF_CMD; + uint8_t data; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_BYTE( upsfd, cmd, __FUNCTION__ ) + + upsdebugx( 1, "Power Off: %d", data ); +} + +static void set_power_off() +{ + uint8_t cmd = POWER_OFF_CMD; + uint8_t data; + + upsdebugx( 3, __FUNCTION__ ); + + /* + * Acceptable values for shutdown_delay are 1-250, + * use 255 to clear a scheduled power off command + */ + + if ( shutdown_delay > 255 ) + shutdown_delay = 250; + + if ( shutdown_delay == 0 ) + shutdown_delay++; + + I2C_WRITE_BYTE( upsfd, cmd, shutdown_delay, __FUNCTION__ ) +} + +static void get_time() +{ + uint8_t cmd = RTC_TIME_CMD; + __u8 block[I2C_SMBUS_BLOCK_MAX]; + uint8_t second, minute, hour, day, month, subsecond; + uint16_t year; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_BLOCK( upsfd, cmd, 9, block, __FUNCTION__ ) + + second = (( (block[0] >> 4 ) & 0x07) * 10 ) + ( block[0] & 0x0F ); + minute = (( (block[1] >> 4 ) & 0x07) * 10 ) + ( block[1] & 0x0F ); + hour = (( (block[2] >> 4 ) & 0x03) * 10 ) + ( block[2] & 0x0F ); + day = (( (block[4] >> 4 ) & 0x03) * 10 ) + ( block[4] & 0x0F ); + month = (( (block[5] >> 4 ) & 0x01) * 10 ) + ( block[5] & 0x0F ); + year = (( (block[6] >> 4 ) & 0x0F) * 10 ) + ( block[6] & 0x0F ) + 2000; + subsecond = block[7] * 100 / 256; + + upsdebugx( 1, "ups.time: %02d:%02d:%02d.%02d", hour, minute, second, subsecond ); + dstate_setinfo( "ups.time", "%02d:%02d:%02d.%02d", hour, minute, second, subsecond ); + + upsdebugx( 1, "ups.date: %04d-%02d-%02d", year, month, day ); + dstate_setinfo( "ups.date", "%04d-%02d-%02d", year, month, day ); +} + +static void get_i2c_address() +{ + uint8_t cmd = I2C_ADDRESS_CMD; + uint8_t data; + + upsdebugx( 3, __FUNCTION__ ); + + I2C_READ_BYTE( upsfd, cmd, __FUNCTION__ ) + + upsdebugx( 1, "I2C Address: 0x%0x", data ); + + if ( data == i2c_address ) + { + upsdebugx( 1, "Found device '0x%0x' on port '%s'", + (unsigned int) i2c_address, device_path ); + } + else + { + fatalx( EXIT_FAILURE, + "Could not find PiJuice HAT at I2C address 0x%0x", + i2c_address ); + } +} + +void upsdrv_initinfo(void) +{ + + dstate_setinfo( "ups.mfr", "%s", "PiJuice" ); + dstate_setinfo( "ups.type", "%s", "HAT" ); + + /* note: for a transition period, these data are redundant */ + + dstate_setinfo( "device.mfr", "%s", "PiJuice" ); + dstate_setinfo( "device.type", "%s", "HAT" ); + + dstate_setinfo( "battery.charge.low", "%0.0f", LOW_BATTERY_THRESHOLD ); + + get_i2c_address(); + get_battery_profile(); + get_battery_profile_ext(); +} + +void upsdrv_updateinfo(void) +{ + status_init(); + + get_status(); + get_battery_temperature(); + get_battery_voltage(); + get_battery_current(); + get_io_voltage(); + get_io_current(); + get_time(); + get_power_off(); + + status_commit(); + dstate_dataok(); +} + +void upsdrv_shutdown(void) +{ + set_power_off(); +} + +void upsdrv_help(void) +{ + printf("\nThe default I2C address is 20 [0x14]\n"); + printf("\n"); +} + +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "i2c_address", "Override i2c address setting"); +} + +void upsdrv_initups(void) +{ + upsfd = open_i2c_bus( device_path, i2c_address ); + + /* probe ups type */ + get_firmware_version(); + + /* get variables and flags from the command line */ + + if (getval("i2c_address")) + i2c_address = atoi(getval("i2c_address")); +} + +void upsdrv_cleanup(void) +{ + close(upsfd); +} From 0d6389047c4a4885e430b242808b8b10f44251d1 Mon Sep 17 00:00:00 2001 From: Andrew Anderson Date: Thu, 12 Sep 2019 19:39:02 -0400 Subject: [PATCH 02/15] Add pijuice I2C driver for PiJuice HAT 2/4 Update the driver Makefile.am to add pijuice driver to the LINUX_I2C_DRIVERLIST --- drivers/Makefile.am | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 1251ff2850..0f21c71dd7 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -45,7 +45,7 @@ SERIAL_USB_DRIVERLIST = \ NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups MODBUS_DRIVERLIST = phoenixcontact_modbus -LINUX_I2C_DRIVERLIST = asem +LINUX_I2C_DRIVERLIST = asem pijuice # distribute all drivers, even ones that are not built by default EXTRA_PROGRAMS = $(SERIAL_DRIVERLIST) $(SNMP_DRIVERLIST) $(USB_DRIVERLIST) $(SERIAL_USB_DRIVERLIST) $(NEONXML_DRIVERLIST) $(MACOSX_DRIVERLIST) @@ -128,6 +128,7 @@ mge_utalk_SOURCES = mge-utalk.c microdowell_SOURCES = microdowell.c oneac_SOURCES = oneac.c optiups_SOURCES = optiups.c +pijuice_SOURCES = pijuice.c powercom_SOURCES = powercom.c powercom_LDADD = $(LDADD) -lm powerpanel_SOURCES = powerpanel.c powerp-bin.c powerp-txt.c @@ -241,9 +242,11 @@ macosx_ups_SOURCES = macosx-ups.c phoenixcontact_modbus_SOURCES = phoenixcontact_modbus.c phoenixcontact_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) -# Asem +# Linux I2C drivers asem_LDADD = $(LDADD_DRIVERS) asem_SOURCES = asem.c +pijuice_LDADD = $(LDADD_DRIVERS) +pijuice_SOURCES = pijuice.c # nutdrv_qx USB/Serial nutdrv_qx_SOURCES = nutdrv_qx.c From 3b4f94f8e1cec6b5b5183c5141fc62db3e76bccc Mon Sep 17 00:00:00 2001 From: Andrew Anderson Date: Thu, 12 Sep 2019 19:41:23 -0400 Subject: [PATCH 03/15] Add pijuice I2C driver for PiJuice HAT 3/4 Check for both the i2c-tools 3.x and 4.x include files (i2c-dev.h/smbus.h) and set flags accordingly. Link with the i2c library file from i2c-tools 4.x if it is available. --- configure.ac | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/configure.ac b/configure.ac index ca5212050f..26cf086108 100644 --- a/configure.ac +++ b/configure.ac @@ -523,14 +523,30 @@ NUT_ARG_WITH([linux_i2c], [build and install i2c drivers], [auto]) if test "${nut_with_linux_i2c}" != no; then case ${target_os} in linux* ) + AC_CHECK_HEADER( + [linux/i2c-dev.h], + [AC_DEFINE([HAVE_LINUX_I2C_DEV_H], [1], + [Define to 1 if you have .])] + ) + AC_CHECK_HEADER( + [i2c/smbus.h], + [AC_DEFINE([HAVE_LINUX_SMBUS_H], [1], + [Define to 1 if you have .])] + ) AC_CHECK_DECLS( [i2c_smbus_read_word_data, i2c_smbus_write_word_data, i2c_smbus_read_block_data], [nut_with_linux_i2c="yes"], [nut_with_linux_i2c="no"], [#include + #ifdef HAVE_LINUX_I2C_DEV_H #include + #endif + #ifdef HAVE_LINUX_SMBUS_H + #include + #endif ] ) + AC_SEARCH_LIBS([i2c_smbus_read_byte], [i2c]) ;; * ) nut_with_linux_i2c="no" From 0ed5f98052a833d59a0ad9cb3a17059b875b6757 Mon Sep 17 00:00:00 2001 From: Andrew Anderson Date: Thu, 12 Sep 2019 19:42:14 -0400 Subject: [PATCH 04/15] Add pijuice I2C driver for PiJuice HAT 4/4 Update asem.c to complie cleanly by adding an include for --- drivers/asem.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/asem.c b/drivers/asem.c index d5d6ea511a..fd0d39225c 100644 --- a/drivers/asem.c +++ b/drivers/asem.c @@ -30,13 +30,14 @@ so you need to boot with acpi_enforce_resources=lax option. */ -/* Depends on i2c-dev.h, Linux only */ +#include "main.h" + #include #include #include +/* Depends on i2c-dev.h, Linux only */ #include - -#include "main.h" +#include #ifndef __STR__ # define __STR__(x) #x From 2980a3cd57ab2f5d46f36f43b3dc74a884e78c49 Mon Sep 17 00:00:00 2001 From: Andrew Anderson Date: Thu, 12 Sep 2019 20:09:18 -0400 Subject: [PATCH 05/15] Fix the length passed into memset() --- drivers/pijuice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index ae6fb1cca1..ea42b42ea0 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -286,7 +286,7 @@ static void get_status() upsdebugx( 3, __FUNCTION__ ); - memset( status_buf, 0, sizeof( status_buf )); + memset( status_buf, 0, ST_MAX_VALUE_LEN ); I2C_READ_BYTE( upsfd, cmd, __FUNCTION__ ) From 7272bd1102408e9c5dbe30e1eab412fb0ef43c7b Mon Sep 17 00:00:00 2001 From: Andrew Anderson Date: Thu, 12 Sep 2019 20:17:05 -0400 Subject: [PATCH 06/15] Pull over the latest development version Clean up the upsdebugX() labels Pull the conditional I2C support code closer to the includes block Don't report battery temp as UPS temp Warn if we adjust shutdown_delay --- drivers/pijuice.c | 362 ++++++++++++++++++++++++++-------------------- 1 file changed, 207 insertions(+), 155 deletions(-) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index ea42b42ea0..75418fba29 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -17,17 +17,128 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "main.h" + #include -#include #include -#ifndef _LINUX_I2C_H -# include + +/* + * Linux I2C userland is a bit of a mess until distros refresh to + * the i2c-tools 4.x release that profides i2c/smbus.h for userspace + * instead of (re)using linux/i2c-dev.h, which conflicts with a + * kernel header of the same name. + * + * See: + * https://i2c.wiki.kernel.org/index.php/Plans_for_I2C_Tools_4 + */ +#if HAVE_LINUX_SMBUS_H +# include #endif -#ifndef _LINUX_I2C_DEV_H -# include +#if HAVE_LINUX_I2C_DEV_H +# include /* for I2C_SLAVE */ #endif -#include "main.h" +/* + * i2c-tools pre-4.0 has a userspace header with a name that conflicts + * with a kernel header, so it may be ignored/removed by distributions + * when packaging i2c-tools. + * + * This will cause the driver to be un-buildable on certain + * configurations, so include the necessary bits here to handle this + * situation. + */ +#if WITH_LINUX_I2C +#if !HAVE_LINUX_SMBUS_H +static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_smbus_ioctl_data args; + __s32 err; + + args.read_write = read_write; + args.command = command; + args.size = size; + args.data = data; + + err = ioctl(file, I2C_SMBUS, &args); + if (err == -1) + err = -errno; + return err; +} + + +static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) +{ + union i2c_smbus_data data; + int err; + + if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data)) < 0) + return err; + else + return 0x0FF & data.byte; +} + +static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value) +{ + union i2c_smbus_data data; + int err; + + data.byte = value; + if ((err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data)) < 0) + return err; + else + return 0x0FF & data.byte; +} + +static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) +{ + union i2c_smbus_data data; + int err; + + if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, + I2C_SMBUS_WORD_DATA, &data)) < 0) + return err; + else + return 0x0FFFF & data.word; +} + +static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value) +{ + union i2c_smbus_data data; + int err; + + data.word = value; + if ((err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command, + I2C_SMBUS_WORD_DATA, &data)) < 0) + return err; + else + return 0x0FFFF & data.word; +} + +static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length, __u8 *values) +{ + union i2c_smbus_data data; + int err; + + if ( length > I2C_SMBUS_BLOCK_MAX) + { + length = I2C_SMBUS_BLOCK_MAX; + } + + data.block[0] = length; + memcpy(data.block + 1, values, length); + + if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data)) < 0) + return NULL; + else + memcpy(values, &data.block[1], data.block[0]); + return values; +} +#endif +#endif #define STATUS_CMD 0x40 #define CHARGE_LEVEL_CMD 0x41 @@ -77,23 +188,24 @@ #define RESET_TO_DEFAULT_CMD 0xF0 #define FIRMWARE_VERSION_CMD 0xFD -#define BATT_NORMAL 0 -#define BATT_CHARGING_FROM_IN 1 -#define BATT_CHARGING_FROM_5V 2 -#define BATT_NOT_PRESENT 3 +#define BATT_NORMAL 0 +#define BATT_CHARGING_FROM_IN 1 +#define BATT_CHARGING_FROM_5V 2 +#define BATT_NOT_PRESENT 3 -#define POWER_NOT_PRESENT 0 -#define POWER_BAD 1 -#define POWER_WEAK 2 -#define POWER_PRESENT 3 +#define POWER_NOT_PRESENT 0 +#define POWER_BAD 1 +#define POWER_WEAK 2 +#define POWER_PRESENT 3 -#define LOW_BATTERY_THRESHOLD 25.0 -#define HIGH_BATTERY_THRESHOLD 75.0 +#define LOW_BATTERY_THRESHOLD 25.0 +#define HIGH_BATTERY_THRESHOLD 75.0 +#define NOMINAL_BATTERY_VOLTAGE 4.18 -#define DRIVER_NAME "PiJuice UPS driver" -#define DRIVER_VERSION "0.7" +#define DRIVER_NAME "PiJuice UPS driver" +#define DRIVER_VERSION "0.9" -static uint8_t i2c_address = 0x14; +static uint8_t i2c_address = 0x14; static uint8_t shutdown_delay = 30; /* @@ -142,99 +254,6 @@ upsdrv_info_t upsdrv_info = { return; \ } -/* Not all i2c-tools installs include the appropriate headers/libraries */ -#ifndef LIB_I2C_SMBUS_H -static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, - int size, union i2c_smbus_data *data) -{ - struct i2c_smbus_ioctl_data args; - __s32 err; - - args.read_write = read_write; - args.command = command; - args.size = size; - args.data = data; - - err = ioctl(file, I2C_SMBUS, &args); - if (err == -1) - err = -errno; - return err; -} - - -static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) -{ - union i2c_smbus_data data; - int err; - - if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, - I2C_SMBUS_BYTE_DATA, &data)) < 0) - return err; - else - return 0x0FF & data.byte; -} - -static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value) -{ - union i2c_smbus_data data; - int err; - - data.byte = value; - if ((err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command, - I2C_SMBUS_BYTE_DATA, &data)) < 0) - return err; - else - return 0x0FF & data.byte; -} - - -static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) -{ - union i2c_smbus_data data; - int err; - - if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, - I2C_SMBUS_WORD_DATA, &data)) < 0) - return err; - else - return 0x0FFFF & data.word; -} - -static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value) -{ - union i2c_smbus_data data; - int err; - - data.word = value; - if ((err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command, - I2C_SMBUS_WORD_DATA, &data)) < 0) - return err; - else - return 0x0FFFF & data.word; -} - -static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length, __u8 *values) -{ - union i2c_smbus_data data; - int err; - - if ( length > I2C_SMBUS_BLOCK_MAX) - { - length = I2C_SMBUS_BLOCK_MAX; - } - - data.block[0] = length; - memcpy(data.block + 1, values, length); - - if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, - I2C_SMBUS_I2C_BLOCK_DATA, &data)) < 0) - return NULL; - else - memcpy(values, &data.block[1], data.block[0]); - return values; -} -#endif - static inline int open_i2c_bus(char *path, uint8_t addr) { int file; @@ -266,15 +285,18 @@ static void get_charge_level_hi_res() * reads; the charge level data may be slightly stale, * but no other options seem reasonable: * - * 1) return 0 / NULL - * leads to a false report of a depleted battery, possibly + * 1) store 0 + * Leads to a false report of a depleted battery, possibly * triggering an immediate shutdown if on battery power only - * 2) retry the read immediately - * could tie up the i2c bus and make matters exponentially worse + * 2) store -1 + * Adds a lot of logic to "skip" over negative charge levels, + * which effectively accomplishes the same thing + * 3) retry the read immediately + * Could tie up the i2c bus and make matters exponentially worse */ battery_charge_level = data / 10.0; - upsdebugx( 1, "battery.charge: %02.1f%%", battery_charge_level ); + upsdebugx( 1, "Battery Charge Level: %02.1f%%", battery_charge_level ); dstate_setinfo( "battery.charge", "%02.1f", battery_charge_level ); } @@ -361,16 +383,14 @@ static void get_status() { get_charge_level_hi_res(); - upsdebugx( 1, "battery_charge_level: %02.1f", battery_charge_level ); - if ( battery_charge_level <= LOW_BATTERY_THRESHOLD ) { - upsdebugx( 1, "Low Battery Status" ); + upsdebugx( 1, "Battery Charge Status: LOW" ); snprintfcat( status_buf, ST_MAX_VALUE_LEN, "LB " ); } else if ( battery_charge_level > HIGH_BATTERY_THRESHOLD ) { - upsdebugx( 1, "High Battery Status" ); + upsdebugx( 1, "Battery Charge Status: HIGH" ); snprintfcat( status_buf, ST_MAX_VALUE_LEN, "HB " ); } } @@ -383,9 +403,9 @@ static void get_status() powerInput <= POWER_PRESENT && powerInput5vIo <= POWER_PRESENT ) { - if ( powerInput == POWER_NOT_PRESENT && - ( powerInput5vIo != POWER_NOT_PRESENT && - powerInput5vIo <= POWER_PRESENT )) + if ( powerInput == POWER_NOT_PRESENT && + ( powerInput5vIo != POWER_NOT_PRESENT && + powerInput5vIo <= POWER_PRESENT )) { if ( usb_power != 1 || gpio_power != 0 ) { @@ -400,12 +420,12 @@ static void get_status() if ( batteryStatus == BATT_CHARGING_FROM_5V ) { snprintfcat( status_buf, sizeof( status_buf ), " CHRG" ); - upsdebugx( 1, "battery charging" ); + upsdebugx( 1, "Battery Charger Status: charging" ); dstate_setinfo( "battery.charger.status", "%s", "charging" ); } else if ( batteryStatus == BATT_NORMAL ) { - upsdebugx( 1, "battery resting" ); + upsdebugx( 1, "Battery Charger Status: resting" ); dstate_setinfo( "battery.charger.status", "%s", "resting" ); } status_set( status_buf ); @@ -428,17 +448,17 @@ static void get_status() { snprintfcat( status_buf, sizeof(status_buf), " CHRG" ); status_set( status_buf ); - upsdebugx( 1, "battery charging"); + upsdebugx( 1, "Battery Charger Status: charging" ); dstate_setinfo( "battery.charger.status", "%s", "charging" ); } else if ( batteryStatus == BATT_NORMAL ) { status_set( status_buf ); - upsdebugx(1, "battery resting"); + upsdebugx( 1, "Battery Charger Status: resting" ); dstate_setinfo( "battery.charger.status", "%s", "resting" ); } } - else if ( ( powerInput != POWER_NOT_PRESENT && powerInput <= POWER_PRESENT ) && + else if ( ( powerInput != POWER_NOT_PRESENT && powerInput <= POWER_PRESENT ) && ( powerInput5vIo != POWER_NOT_PRESENT && powerInput5vIo <= POWER_PRESENT )) { if ( usb_power != 1 || gpio_power != 1 ) @@ -451,18 +471,17 @@ static void get_status() upsdebugx( 1, "On USB and 5V_GPIO power [%d:%d:%d]", usb_power, gpio_power, battery_power ); snprintfcat( status_buf, sizeof( status_buf ), "OL" ); - if ( batteryStatus == BATT_CHARGING_FROM_IN || - batteryStatus == BATT_CHARGING_FROM_5V ) + if ( batteryStatus == BATT_CHARGING_FROM_IN ) { snprintfcat( status_buf, sizeof(status_buf), " CHRG"); status_set( status_buf ); - upsdebugx(1, "battery charging"); + upsdebugx( 1, "Battery Charger Status: charging" ); dstate_setinfo("battery.charger.status", "%s", "charging"); } else if ( batteryStatus == BATT_NORMAL ) { status_set( status_buf ); - upsdebugx( 1, "battery resting" ); + upsdebugx( 1, "Battery Charger Status: resting" ); dstate_setinfo( "battery.charger.status", "%s", "resting" ); } } @@ -492,10 +511,8 @@ static void get_battery_temperature() I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) - upsdebugx( 1, "battery.temperature: %d°C", data ); + upsdebugx( 1, "Battery Temperature: %d°C", data ); dstate_setinfo( "battery.temperature", "%d", data ); - /* Use this for UPS temperature as well, since there is not a separate sensor available */ - dstate_setinfo( "ups.temperature", "%d", data ); } static void get_battery_voltage() @@ -507,9 +524,8 @@ static void get_battery_voltage() I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) - upsdebugx( 1, "battery.voltage: %0.3fV", data / 1000.0 ); + upsdebugx( 1, "Battery Voltage: %0.3fV", data / 1000.0 ); dstate_setinfo( "battery.voltage", "%0.3f", data / 1000.0 ); - dstate_setinfo( "battery.voltage.nominal", "%0.3f", 4.1 ); } static void get_battery_current() @@ -519,10 +535,19 @@ static void get_battery_current() upsdebugx( 3, __FUNCTION__ ); - I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) + /* + * The reported current can actually be negative, so we cannot + * check for I2C failure by looking for negative values + */ + data = i2c_smbus_read_word_data(upsfd, cmd); + + if ( data & ( 1 << 15 ) ) + { + data = data - ( 1 << 16 ); + } - upsdebugx( 1, "battery.current: %dmA", data ); - dstate_setinfo( "battery.current", "%d", data ); + upsdebugx( 1, "Battery Current: %0.3fA", data / 1000.0 ); + dstate_setinfo( "battery.current", "%0.3f", data / 1000.0 ); } static void get_io_voltage() @@ -534,7 +559,7 @@ static void get_io_voltage() I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) - upsdebugx( 1, "input.voltage: %.3fV", data / 1000.0 ); + upsdebugx( 1, "Input Voltage: %.3fV", data / 1000.0 ); dstate_setinfo( "input.voltage", "%.3f", data / 1000.0 ); } @@ -545,15 +570,19 @@ static void get_io_current() upsdebugx( 3, __FUNCTION__ ); - I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) + /* + * The reported current can actually be negative, so we cannot + * check for I2C failure by looking for negative values + */ + data = i2c_smbus_read_word_data(upsfd, cmd); if ( data & ( 1 << 15 ) ) { data = data - ( 1 << 16 ); } - upsdebugx( 1, "input.current: %dmA", data ); - dstate_setinfo( "input.current", "%d", data ); + upsdebugx( 1, "Input Current: %.3fA", data / 1000.0 ); + dstate_setinfo( "input.current", "%.3f", data / 1000.0 ); } static void get_firmware_version() @@ -574,7 +603,7 @@ static void get_firmware_version() upslogx( LOG_WARNING, "Unknown Firmware release: %d.%d", major, minor ); } - upsdebugx( 1, "ups.firmware: %d.%d", major, minor ); + upsdebugx( 1, "UPS Firmware Version: %d.%d", major, minor ); dstate_setinfo( "ups.firmware", "%d.%d", major, minor ); } @@ -587,7 +616,7 @@ static void get_battery_profile() I2C_READ_BLOCK( upsfd, cmd, 14, block, __FUNCTION__ ) - upsdebugx( 1, "battery.capacity: %0.3fAh", ( block[1] << 8 | block[0] ) / 1000.0 ); + upsdebugx( 1, "Battery Capacity: %0.3fAh", ( block[1] << 8 | block[0] ) / 1000.0 ); dstate_setinfo( "battery.capacity", "%0.3f", ( block[1] << 8 | block[0] ) / 1000.0 ); } @@ -603,15 +632,15 @@ static void get_battery_profile_ext() switch( block[0] & 0xFF00 ) { case 0: - upsdebugx( 1, "battery.type: LiPO" ); + upsdebugx( 1, "Battery Chemistry: LiPO" ); dstate_setinfo( "battery.type", "%s", "LiPO" ); break; case 1: - upsdebugx( 1, "battery.type: LiFePO4" ); + upsdebugx( 1, "Battery Chemistry: LiFePO4" ); dstate_setinfo( "battery.type", "%s", "LiFePO4" ); break; default: - upsdebugx( 1, "battery.type: UNKNOWN" ); + upsdebugx( 1, "Battery Chemistry: UNKNOWN" ); dstate_setinfo( "battery.type", "%s", "UNKNOWN" ); } } @@ -625,7 +654,14 @@ static void get_power_off() I2C_READ_BYTE( upsfd, cmd, __FUNCTION__ ) - upsdebugx( 1, "Power Off: %d", data ); + if ( data == 255 ) + { + upsdebugx( 1, "Power Off: DISABLED" ); + } + else if ( data <= 250 ) + { + upsdebugx( 1, "Power Off: %d Seconds", data ); + } } static void set_power_off() @@ -637,14 +673,26 @@ static void set_power_off() /* * Acceptable values for shutdown_delay are 1-250, - * use 255 to clear a scheduled power off command + * use 0/255 to clear a scheduled power off command */ if ( shutdown_delay > 255 ) + { + upslogx( + LOG_WARNING, + "shutdown delay of >250 seconds requested, shortening to 250 seconds" + ); shutdown_delay = 250; + } if ( shutdown_delay == 0 ) - shutdown_delay++; + { + upslogx( + LOG_WARNING, + "shutdown delay of 0 seconds requested, using 1 second instead" + ); + shutdown_delay = 1; + } I2C_WRITE_BYTE( upsfd, cmd, shutdown_delay, __FUNCTION__ ) } @@ -668,10 +716,10 @@ static void get_time() year = (( (block[6] >> 4 ) & 0x0F) * 10 ) + ( block[6] & 0x0F ) + 2000; subsecond = block[7] * 100 / 256; - upsdebugx( 1, "ups.time: %02d:%02d:%02d.%02d", hour, minute, second, subsecond ); + upsdebugx( 1, "UPS Time: %02d:%02d:%02d.%02d", hour, minute, second, subsecond ); dstate_setinfo( "ups.time", "%02d:%02d:%02d.%02d", hour, minute, second, subsecond ); - upsdebugx( 1, "ups.date: %04d-%02d-%02d", year, month, day ); + upsdebugx( 1, "UPS Date: %04d-%02d-%02d", year, month, day ); dstate_setinfo( "ups.date", "%04d-%02d-%02d", year, month, day ); } @@ -710,8 +758,12 @@ void upsdrv_initinfo(void) dstate_setinfo( "device.mfr", "%s", "PiJuice" ); dstate_setinfo( "device.type", "%s", "HAT" ); + upsdebugx( 1, "Low Battery Threshold: %0.0f%%", LOW_BATTERY_THRESHOLD ); dstate_setinfo( "battery.charge.low", "%0.0f", LOW_BATTERY_THRESHOLD ); + upsdebugx( 1, "Nominal Battery Voltage: %0.3fV", NOMINAL_BATTERY_VOLTAGE ); + dstate_setinfo( "battery.voltage.nominal", "%0.3f", NOMINAL_BATTERY_VOLTAGE ); + get_i2c_address(); get_battery_profile(); get_battery_profile_ext(); From c09cc4a794e3d015a3234165af30281532fd6ff5 Mon Sep 17 00:00:00 2001 From: Andrew Anderson Date: Thu, 12 Sep 2019 21:01:43 -0400 Subject: [PATCH 07/15] Add more granular I2C support checks Accommodate one more variation of SMBUS/I2C support where both the headers and the libraries exist and are usable. --- configure.ac | 2 +- drivers/pijuice.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 26cf086108..ffc3c302a6 100644 --- a/configure.ac +++ b/configure.ac @@ -534,7 +534,7 @@ if test "${nut_with_linux_i2c}" != no; then [Define to 1 if you have .])] ) AC_CHECK_DECLS( - [i2c_smbus_read_word_data, i2c_smbus_write_word_data, i2c_smbus_read_block_data], + [i2c_smbus_access, i2c_smbus_read_byte_data, i2c_smbus_write_byte_data, i2c_smbus_read_word_data, i2c_smbus_write_word_data, i2c_smbus_read_block_data], [nut_with_linux_i2c="yes"], [nut_with_linux_i2c="no"], [#include diff --git a/drivers/pijuice.c b/drivers/pijuice.c index 75418fba29..f4b5d062de 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -36,6 +36,9 @@ #endif #if HAVE_LINUX_I2C_DEV_H # include /* for I2C_SLAVE */ +#if !HAVE_LINUX_SMBUS_H +# include +#endif #endif /* @@ -48,7 +51,7 @@ * situation. */ #if WITH_LINUX_I2C -#if !HAVE_LINUX_SMBUS_H +#if !HAVE_DECL_I2C_SMBUS_ACCESS static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, int size, union i2c_smbus_data *data) { @@ -65,8 +68,9 @@ static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, err = -errno; return err; } +#endif - +#if !HAVE_DECL_I2C_SMBUS_READ_BYTE_DATA static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) { union i2c_smbus_data data; @@ -78,7 +82,9 @@ static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) else return 0x0FF & data.byte; } +#endif +#if !HAVE_DECL_I2C_SMBUS_WRITE_BYTE_DATA static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value) { union i2c_smbus_data data; @@ -91,7 +97,9 @@ static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value else return 0x0FF & data.byte; } +#endif +#if !HAVE_DECL_I2C_SMBUS_READ_WORD_DATA static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) { union i2c_smbus_data data; @@ -103,7 +111,9 @@ static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) else return 0x0FFFF & data.word; } +#endif +#if !HAVE_DECL_I2C_SMBUS_WRITE_WORD_DATA static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value) { union i2c_smbus_data data; @@ -116,7 +126,9 @@ static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 valu else return 0x0FFFF & data.word; } +#endif +#if !HAVE_DECL_I2C_SMBUS_READ_BLOCK_DATA static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length, __u8 *values) { union i2c_smbus_data data; From 704c156bc1a7354d359db4bb4191cf2b13ab8eeb Mon Sep 17 00:00:00 2001 From: Andrew Anderson Date: Thu, 12 Sep 2019 21:13:46 -0400 Subject: [PATCH 08/15] Conditionally include linux/i2c.h Check for I2C_FUNC_I2C to avoid errors caused by conflicts between linux/i2c-dev.h and linux/i2c.h --- drivers/pijuice.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index f4b5d062de..0c73c9b291 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -37,7 +37,9 @@ #if HAVE_LINUX_I2C_DEV_H # include /* for I2C_SLAVE */ #if !HAVE_LINUX_SMBUS_H -# include +#ifndef I2C_FUNC_I2C +# include +#endif #endif #endif From c10cc1baa319550eafc488e4c026f22d16e3bc8e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 7 Oct 2020 18:59:07 +0200 Subject: [PATCH 09/15] pijuice.c cleanup : trailing whitespaces --- drivers/pijuice.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index 0c73c9b291..ec5eb71c2d 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -23,13 +23,13 @@ #include /* - * Linux I2C userland is a bit of a mess until distros refresh to - * the i2c-tools 4.x release that profides i2c/smbus.h for userspace - * instead of (re)using linux/i2c-dev.h, which conflicts with a + * Linux I2C userland is a bit of a mess until distros refresh to + * the i2c-tools 4.x release that profides i2c/smbus.h for userspace + * instead of (re)using linux/i2c-dev.h, which conflicts with a * kernel header of the same name. * * See: - * https://i2c.wiki.kernel.org/index.php/Plans_for_I2C_Tools_4 + * https://i2c.wiki.kernel.org/index.php/Plans_for_I2C_Tools_4 */ #if HAVE_LINUX_SMBUS_H # include @@ -43,17 +43,17 @@ #endif #endif -/* - * i2c-tools pre-4.0 has a userspace header with a name that conflicts +/* + * i2c-tools pre-4.0 has a userspace header with a name that conflicts * with a kernel header, so it may be ignored/removed by distributions * when packaging i2c-tools. * - * This will cause the driver to be un-buildable on certain - * configurations, so include the necessary bits here to handle this + * This will cause the driver to be un-buildable on certain + * configurations, so include the necessary bits here to handle this * situation. */ #if WITH_LINUX_I2C -#if !HAVE_DECL_I2C_SMBUS_ACCESS +#if !HAVE_DECL_I2C_SMBUS_ACCESS static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, int size, union i2c_smbus_data *data) { @@ -295,8 +295,8 @@ static void get_charge_level_hi_res() I2C_READ_WORD( upsfd, cmd, __FUNCTION__ ) /* - * Use an external variable to allow for missed i2c bus - * reads; the charge level data may be slightly stale, + * Use an external variable to allow for missed i2c bus + * reads; the charge level data may be slightly stale, * but no other options seem reasonable: * * 1) store 0 @@ -391,7 +391,7 @@ static void get_status() upsdebugx(1, "Power Input 5v: UNKNOWN"); } - if ( batteryStatus == BATT_NORMAL || + if ( batteryStatus == BATT_NORMAL || batteryStatus == BATT_CHARGING_FROM_IN || batteryStatus == BATT_CHARGING_FROM_5V ) { @@ -417,8 +417,8 @@ static void get_status() powerInput <= POWER_PRESENT && powerInput5vIo <= POWER_PRESENT ) { - if ( powerInput == POWER_NOT_PRESENT && - ( powerInput5vIo != POWER_NOT_PRESENT && + if ( powerInput == POWER_NOT_PRESENT && + ( powerInput5vIo != POWER_NOT_PRESENT && powerInput5vIo <= POWER_PRESENT )) { if ( usb_power != 1 || gpio_power != 0 ) @@ -444,8 +444,8 @@ static void get_status() } status_set( status_buf ); } - else if ( powerInput5vIo == POWER_NOT_PRESENT && - ( powerInput != POWER_NOT_PRESENT && + else if ( powerInput5vIo == POWER_NOT_PRESENT && + ( powerInput != POWER_NOT_PRESENT && powerInput <= POWER_PRESENT )) { if ( gpio_power != 1 || usb_power != 0 ) @@ -550,7 +550,7 @@ static void get_battery_current() upsdebugx( 3, __FUNCTION__ ); /* - * The reported current can actually be negative, so we cannot + * The reported current can actually be negative, so we cannot * check for I2C failure by looking for negative values */ data = i2c_smbus_read_word_data(upsfd, cmd); @@ -585,7 +585,7 @@ static void get_io_current() upsdebugx( 3, __FUNCTION__ ); /* - * The reported current can actually be negative, so we cannot + * The reported current can actually be negative, so we cannot * check for I2C failure by looking for negative values */ data = i2c_smbus_read_word_data(upsfd, cmd); @@ -686,7 +686,7 @@ static void set_power_off() upsdebugx( 3, __FUNCTION__ ); /* - * Acceptable values for shutdown_delay are 1-250, + * Acceptable values for shutdown_delay are 1-250, * use 0/255 to clear a scheduled power off command */ @@ -750,13 +750,13 @@ static void get_i2c_address() if ( data == i2c_address ) { - upsdebugx( 1, "Found device '0x%0x' on port '%s'", + upsdebugx( 1, "Found device '0x%0x' on port '%s'", (unsigned int) i2c_address, device_path ); } else { - fatalx( EXIT_FAILURE, - "Could not find PiJuice HAT at I2C address 0x%0x", + fatalx( EXIT_FAILURE, + "Could not find PiJuice HAT at I2C address 0x%0x", i2c_address ); } } From de79ac982396049b1f6f6b3e13a3b232ea93f729 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 7 Oct 2020 18:59:38 +0200 Subject: [PATCH 10/15] pijuice.c cleanup : indent stacked #if* macros --- drivers/pijuice.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index ec5eb71c2d..d5e61e5941 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -36,11 +36,11 @@ #endif #if HAVE_LINUX_I2C_DEV_H # include /* for I2C_SLAVE */ -#if !HAVE_LINUX_SMBUS_H -#ifndef I2C_FUNC_I2C +# if !HAVE_LINUX_SMBUS_H +# ifndef I2C_FUNC_I2C # include -#endif -#endif +# endif +# endif #endif /* From 03abe87d0aadf3648f48aae413e8a77229683030 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 7 Oct 2020 19:07:56 +0200 Subject: [PATCH 11/15] pijuice.c cleanup : rectify indentation and blank-line the default return --- drivers/pijuice.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index d5e61e5941..d3659421f1 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -149,7 +149,8 @@ static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 l return NULL; else memcpy(values, &data.block[1], data.block[0]); - return values; + + return values; } #endif #endif From 8d747eaef68d4bd849f8b000ffb48a33ef3ae8dc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 7 Oct 2020 19:09:12 +0200 Subject: [PATCH 12/15] pijuice.c cleanup : comment end of big block of nested #if macros --- drivers/pijuice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index d3659421f1..9f2e585948 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -153,7 +153,7 @@ static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 l return values; } #endif -#endif +#endif // if WITH_LINUX_I2C #define STATUS_CMD 0x40 #define CHARGE_LEVEL_CMD 0x41 From bb2e34685b4e78b37c08fe0bd7167d3896e7b207 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 7 Oct 2020 19:09:31 +0200 Subject: [PATCH 13/15] pijuice.c cleanup : rectify tabulation of definitions --- drivers/pijuice.c | 132 +++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index 9f2e585948..a1cb505b0b 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -155,72 +155,72 @@ static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 l #endif #endif // if WITH_LINUX_I2C -#define STATUS_CMD 0x40 -#define CHARGE_LEVEL_CMD 0x41 -#define CHARGE_LEVEL_HI_RES_CMD 0x42 -#define FAULT_EVENT_CMD 0x44 -#define BUTTON_EVENT_CMD 0x45 -#define BATTERY_TEMPERATURE_CMD 0x47 -#define BATTERY_VOLTAGE_CMD 0x49 -#define BATTERY_CURRENT_CMD 0x4b -#define IO_VOLTAGE_CMD 0x4d -#define IO_CURRENT_CMD 0x4f - -#define CHARGING_CONFIG_CMD 0x51 -#define BATTERY_PROFILE_ID_CMD 0x52 -#define BATTERY_PROFILE_CMD 0x53 -#define BATTERY_EXT_PROFILE_CMD 0x54 -#define BATTERY_TEMP_SENSE_CONFIG_CMD 0x5D - -#define POWER_INPUTS_CONFIG_CMD 0x5E -#define RUN_PIN_CONFIG_CMD 0x5F -#define POWER_REGULATOR_CONFIG_CMD 0x60 -#define WATCHDOG_ACTIVATION_CMD 0x61 -#define POWER_OFF_CMD 0x62 -#define WAKEUP_ON_CHARGE_CMD 0x63 -#define SYSTEM_POWER_SWITCH_CTRL_CMD 0x64 - -#define LED_STATE_CMD 0x66 -#define LED_BLINK_CMD 0x68 -#define LED_CONFIGURATION_CMD 0x6A -#define BUTTON_CONFIGURATION_CMD 0x6E - -#define IO1_CONFIGURATION_CMD 0x72 -#define IO1_PIN_ACCESS_CMD 0x75 - -#define IO2_CONFIGURATION_CMD 0x77 -#define IO2_PIN_ACCESS_CMD 0x7A - -#define I2C_ADDRESS_CMD 0x7C - -#define ID_EEPROM_WRITE_PROTECT_CTRL_CMD 0x7E -#define ID_EEPROM_ADDRESS_CMD 0x7F - -#define RTC_TIME_CMD 0xB0 -#define RTC_ALARM_CMD 0xB9 -#define RTC_CTRL_STATUS_CMD 0xC2 - -#define RESET_TO_DEFAULT_CMD 0xF0 -#define FIRMWARE_VERSION_CMD 0xFD - -#define BATT_NORMAL 0 -#define BATT_CHARGING_FROM_IN 1 -#define BATT_CHARGING_FROM_5V 2 -#define BATT_NOT_PRESENT 3 - -#define POWER_NOT_PRESENT 0 -#define POWER_BAD 1 -#define POWER_WEAK 2 -#define POWER_PRESENT 3 - -#define LOW_BATTERY_THRESHOLD 25.0 -#define HIGH_BATTERY_THRESHOLD 75.0 -#define NOMINAL_BATTERY_VOLTAGE 4.18 - -#define DRIVER_NAME "PiJuice UPS driver" -#define DRIVER_VERSION "0.9" - -static uint8_t i2c_address = 0x14; +#define STATUS_CMD 0x40 +#define CHARGE_LEVEL_CMD 0x41 +#define CHARGE_LEVEL_HI_RES_CMD 0x42 +#define FAULT_EVENT_CMD 0x44 +#define BUTTON_EVENT_CMD 0x45 +#define BATTERY_TEMPERATURE_CMD 0x47 +#define BATTERY_VOLTAGE_CMD 0x49 +#define BATTERY_CURRENT_CMD 0x4b +#define IO_VOLTAGE_CMD 0x4d +#define IO_CURRENT_CMD 0x4f + +#define CHARGING_CONFIG_CMD 0x51 +#define BATTERY_PROFILE_ID_CMD 0x52 +#define BATTERY_PROFILE_CMD 0x53 +#define BATTERY_EXT_PROFILE_CMD 0x54 +#define BATTERY_TEMP_SENSE_CONFIG_CMD 0x5D + +#define POWER_INPUTS_CONFIG_CMD 0x5E +#define RUN_PIN_CONFIG_CMD 0x5F +#define POWER_REGULATOR_CONFIG_CMD 0x60 +#define WATCHDOG_ACTIVATION_CMD 0x61 +#define POWER_OFF_CMD 0x62 +#define WAKEUP_ON_CHARGE_CMD 0x63 +#define SYSTEM_POWER_SWITCH_CTRL_CMD 0x64 + +#define LED_STATE_CMD 0x66 +#define LED_BLINK_CMD 0x68 +#define LED_CONFIGURATION_CMD 0x6A +#define BUTTON_CONFIGURATION_CMD 0x6E + +#define IO1_CONFIGURATION_CMD 0x72 +#define IO1_PIN_ACCESS_CMD 0x75 + +#define IO2_CONFIGURATION_CMD 0x77 +#define IO2_PIN_ACCESS_CMD 0x7A + +#define I2C_ADDRESS_CMD 0x7C + +#define ID_EEPROM_WRITE_PROTECT_CTRL_CMD 0x7E +#define ID_EEPROM_ADDRESS_CMD 0x7F + +#define RTC_TIME_CMD 0xB0 +#define RTC_ALARM_CMD 0xB9 +#define RTC_CTRL_STATUS_CMD 0xC2 + +#define RESET_TO_DEFAULT_CMD 0xF0 +#define FIRMWARE_VERSION_CMD 0xFD + +#define BATT_NORMAL 0 +#define BATT_CHARGING_FROM_IN 1 +#define BATT_CHARGING_FROM_5V 2 +#define BATT_NOT_PRESENT 3 + +#define POWER_NOT_PRESENT 0 +#define POWER_BAD 1 +#define POWER_WEAK 2 +#define POWER_PRESENT 3 + +#define LOW_BATTERY_THRESHOLD 25.0 +#define HIGH_BATTERY_THRESHOLD 75.0 +#define NOMINAL_BATTERY_VOLTAGE 4.18 + +#define DRIVER_NAME "PiJuice UPS driver" +#define DRIVER_VERSION "0.9" + +static uint8_t i2c_address = 0x14; static uint8_t shutdown_delay = 30; /* From 16a16702d45b7d75a5f3869846161391f7027ebf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 7 Oct 2020 19:12:34 +0200 Subject: [PATCH 14/15] pijuice.c cleanup : rectify leading indentations --- drivers/pijuice.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index a1cb505b0b..2211bff82f 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -247,25 +247,25 @@ upsdrv_info_t upsdrv_info = { #define I2C_READ_BYTE(fd, cmd, label) \ if ((data = i2c_smbus_read_byte_data(upsfd, cmd)) < 0 ) { \ - upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ + upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ return; \ } #define I2C_WRITE_BYTE(fd, cmd, value, label) \ if ((data = i2c_smbus_write_byte_data(upsfd, cmd, value)) < 0 ) { \ - upsdebugx(2, "Failure writing to the i2c bus [%s]", label); \ + upsdebugx(2, "Failure writing to the i2c bus [%s]", label); \ return; \ } #define I2C_READ_WORD(fd, cmd, label) \ if ((data = i2c_smbus_read_word_data(upsfd, cmd)) < 0 ) { \ - upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ + upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ return; \ } #define I2C_READ_BLOCK(fd, cmd, size, block, label) \ if ((i2c_smbus_read_i2c_block_data(upsfd, cmd, size, block)) < 0 ) { \ - upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ + upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ return; \ } @@ -275,12 +275,12 @@ static inline int open_i2c_bus(char *path, uint8_t addr) if ((file = open(path, O_RDWR)) < 0) { - fatal_with_errno(EXIT_FAILURE, "Failed to open the i2c bus on %s", path); + fatal_with_errno(EXIT_FAILURE, "Failed to open the i2c bus on %s", path); } if (ioctl(file, I2C_SLAVE, addr) < 0) { - fatal_with_errno(EXIT_FAILURE, "Failed to acquire the i2c bus and/or talk to the UPS"); + fatal_with_errno(EXIT_FAILURE, "Failed to acquire the i2c bus and/or talk to the UPS"); } return file; @@ -419,12 +419,12 @@ static void get_status() powerInput5vIo <= POWER_PRESENT ) { if ( powerInput == POWER_NOT_PRESENT && - ( powerInput5vIo != POWER_NOT_PRESENT && + ( powerInput5vIo != POWER_NOT_PRESENT && powerInput5vIo <= POWER_PRESENT )) { if ( usb_power != 1 || gpio_power != 0 ) { - upslogx( LOG_NOTICE, "On USB power" ); + upslogx( LOG_NOTICE, "On USB power" ); } usb_power = 1; gpio_power = 0; @@ -446,12 +446,12 @@ static void get_status() status_set( status_buf ); } else if ( powerInput5vIo == POWER_NOT_PRESENT && - ( powerInput != POWER_NOT_PRESENT && - powerInput <= POWER_PRESENT )) + ( powerInput != POWER_NOT_PRESENT && + powerInput <= POWER_PRESENT )) { if ( gpio_power != 1 || usb_power != 0 ) { - upslogx( LOG_NOTICE, "On 5V_GPIO power" ); + upslogx( LOG_NOTICE, "On 5V_GPIO power" ); } usb_power = 0; gpio_power = 1; @@ -474,11 +474,11 @@ static void get_status() } } else if ( ( powerInput != POWER_NOT_PRESENT && powerInput <= POWER_PRESENT ) && - ( powerInput5vIo != POWER_NOT_PRESENT && powerInput5vIo <= POWER_PRESENT )) + ( powerInput5vIo != POWER_NOT_PRESENT && powerInput5vIo <= POWER_PRESENT )) { if ( usb_power != 1 || gpio_power != 1 ) { - upslogx( LOG_NOTICE, "On USB and 5V_GPIO power" ); + upslogx( LOG_NOTICE, "On USB and 5V_GPIO power" ); } usb_power = 1; gpio_power = 1; @@ -504,7 +504,7 @@ static void get_status() { if ( usb_power != 0 || gpio_power != 0 ) { - upslogx( LOG_NOTICE, "On Battery power" ); + upslogx( LOG_NOTICE, "On Battery power" ); } usb_power = 0; gpio_power = 0; @@ -604,7 +604,7 @@ static void get_firmware_version() { uint8_t cmd = FIRMWARE_VERSION_CMD; uint16_t data; - uint8_t major, minor; + uint8_t major, minor; upsdebugx( 3, __FUNCTION__ ); @@ -615,7 +615,7 @@ static void get_firmware_version() if (( major != 1 ) || ( minor > 3 )) { - upslogx( LOG_WARNING, "Unknown Firmware release: %d.%d", major, minor ); + upslogx( LOG_WARNING, "Unknown Firmware release: %d.%d", major, minor ); } upsdebugx( 1, "UPS Firmware Version: %d.%d", major, minor ); @@ -693,7 +693,7 @@ static void set_power_off() if ( shutdown_delay > 255 ) { - upslogx( + upslogx( LOG_WARNING, "shutdown delay of >250 seconds requested, shortening to 250 seconds" ); @@ -702,7 +702,7 @@ static void set_power_off() if ( shutdown_delay == 0 ) { - upslogx( + upslogx( LOG_WARNING, "shutdown delay of 0 seconds requested, using 1 second instead" ); @@ -740,19 +740,19 @@ static void get_time() static void get_i2c_address() { - uint8_t cmd = I2C_ADDRESS_CMD; - uint8_t data; + uint8_t cmd = I2C_ADDRESS_CMD; + uint8_t data; upsdebugx( 3, __FUNCTION__ ); I2C_READ_BYTE( upsfd, cmd, __FUNCTION__ ) - upsdebugx( 1, "I2C Address: 0x%0x", data ); + upsdebugx( 1, "I2C Address: 0x%0x", data ); if ( data == i2c_address ) { upsdebugx( 1, "Found device '0x%0x' on port '%s'", - (unsigned int) i2c_address, device_path ); + (unsigned int) i2c_address, device_path ); } else { From 5ac96a6a5956873ff64d59dbe968d0e41fede37c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 7 Oct 2020 19:25:21 +0200 Subject: [PATCH 15/15] Add initial manpage for pijuice driver, hastily ripped off one for asem driver --- docs/man/Makefile.am | 6 ++-- docs/man/index.txt | 1 + docs/man/pijuice.txt | 81 ++++++++++++++++++++++++++++++++++++++++++++ docs/nut.dict | 3 ++ 4 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 docs/man/pijuice.txt diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index fef33f6db0..86434fb33d 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -620,16 +620,16 @@ endif HTML_MODBUS_MANS = phoenixcontact_modbus.html -SRC_LINUX_I2C_PAGES = asem.txt +SRC_LINUX_I2C_PAGES = asem.txt pijuice.txt if WITH_MANS -MAN_LINUX_I2C_PAGES = asem.8 +MAN_LINUX_I2C_PAGES = asem.8 pijuice.8 endif if WITH_LINUX_I2C man8_MANS += $(MAN_LINUX_I2C_PAGES) endif -HTML_LINUX_I2C_MANS = asem.html +HTML_LINUX_I2C_MANS = asem.html pijuice.html # SOME_DRIVERS endif diff --git a/docs/man/index.txt b/docs/man/index.txt index d32175db31..b15c6a4518 100644 --- a/docs/man/index.txt +++ b/docs/man/index.txt @@ -91,6 +91,7 @@ Drivers - linkman:oneac[8] - linkman:optiups[8] - linkman:phoenixcontact_modbus[8] +- linkman:pijuice[8] - linkman:powercom[8] - linkman:powerman-pdu[8] - linkman:powerpanel[8] diff --git a/docs/man/pijuice.txt b/docs/man/pijuice.txt new file mode 100644 index 0000000000..1643208f34 --- /dev/null +++ b/docs/man/pijuice.txt @@ -0,0 +1,81 @@ +PIJUICE(8) +========== + +NAME +---- +pijuice - driver for UPS in PiJuice HAT + +NOTE +---- +This man page only documents the hardware-specific features of the +*pijuice* driver. For information about the core driver, see +linkman:nutupsdrv[8]. + +NOTE: This manual page was hastily adapted from related `asem` driver +manpage based on information from the original pull request, and so +may not fully apply to PiJuice HAT, patches from experts are welcome. + +SUPPORTED HARDWARE +------------------ +The *pijuice* driver supports the portable PiJuice HAT UPS for Raspberry Pi +embedded PCs. + +EXTRA ARGUMENTS +--------------- + +The required parameter for this driver is the I2C bus name: + +*port*='dev-node':: +On the PiJuice HAT, this should be `/dev/i2c-1`. + +INSTALLATION +------------ +NOTE: This section was copied from `asem` driver manpage and may not fully +apply to PiJuice HAT, patches are welcome. + +This driver is specific to the Linux I2C API, and requires the lm_sensors +libi2c-dev or its equivalent to compile. + +Beware that the SystemIO memory used by the I2C controller is reserved by ACPI. +If only a native I2C driver (e.g. i2c_i801, as of 3.5.X Linux kernels) is +available, then you'll need to relax the ACPI resources check. For example, you +can boot with the `acpi_enforce_resources=lax` option. + +////////////////////////////////////////// +Optional: use DIAGNOSTICS to describe troubleshooting techniques that are +longer than what can be conveniently described in the driver error messages. + +DIAGNOSTICS +----------- + +////////////////////////////////////////// + +KNOWN ISSUES AND BUGS +--------------------- +NOTE: This section was copied from `asem` driver manpage and may not fully +apply to PiJuice HAT, patches are welcome. + +The driver shutdown function is not implemented, so other arrangements must be +made to turn off the UPS. + +AUTHORS +------- +Andrew Anderson + +SEE ALSO +-------- + +The core driver: +~~~~~~~~~~~~~~~~ +linkman:nutupsdrv[8] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ +Initial pull requests adding this driver: + +* https://github.com/networkupstools/nut/pull/730 +* https://github.com/PiSupply/PiJuice/issues/124 + +Product home page: https://uk.pi-supply.com/products/pijuice-standard + +The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/nut.dict b/docs/nut.dict index e7795b49cf..921ab16cbc 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -751,6 +751,8 @@ PhaseWin PhoenixContact PhoenixTec Phoenixtec +Pi +PiJuice Plesser PnP Pohle @@ -1970,6 +1972,7 @@ phoenixcontact picocom pid pidpath +pijuice pinout pinouts pkg