nut-debian/drivers/phoenixcontact_modbus.c
2022-07-10 09:23:45 +02:00

193 lines
4.9 KiB
C

/* phoenixcontact_modbus.c - Driver for PhoenixContact-QUINT UPS
*
* Copyright (C)
* 2017 Spiros Ioannou <sivann@inaccess.com>
*
* 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 "main.h"
#include <modbus.h>
#define DRIVER_NAME "NUT PhoenixContact Modbus driver"
#define DRIVER_VERSION "0.02"
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
#define MODBUS_SLAVE_ID 192
/* Variables */
static modbus_t *modbus_ctx = NULL;
static int errcount = 0;
static int mrir(modbus_t * arg_ctx, int addr, int nb, uint16_t * dest);
/* driver description structure */
upsdrv_info_t upsdrv_info = {
DRIVER_NAME,
DRIVER_VERSION,
"Spiros Ioannou <sivann@inaccess.com>\n",
DRV_BETA,
{NULL}
};
void upsdrv_initinfo(void)
{
upsdebugx(2, "upsdrv_initinfo");
dstate_setinfo("device.mfr", "Phoenix Contact");
dstate_setinfo("device.model", "QUINT-UPS/24DC");
/* upsh.instcmd = instcmd; */
/* upsh.setvar = setvar; */
}
void upsdrv_updateinfo(void)
{
errcount = 0;
upsdebugx(2, "upsdrv_updateinfo");
uint16_t tab_reg[64];
mrir(modbus_ctx, 29697, 3, tab_reg);
status_init();
if (tab_reg[0])
status_set("OB");
else
status_set("OL");
if (tab_reg[2]) {
status_set("CHRG");
}
if (tab_reg[1]) {
status_set("LB"); /* LB is actually called "shutdown event" on this ups */
}
mrir(modbus_ctx, 29745, 1, tab_reg);
dstate_setinfo("output.voltage", "%d", (int) (tab_reg[0] / 1000));
mrir(modbus_ctx, 29749, 5, tab_reg);
dstate_setinfo("battery.charge", "%d", tab_reg[0]);
/* dstate_setinfo("battery.runtime",tab_reg[1]*60); */ /* also reported on this address, but less accurately */
mrir(modbus_ctx, 29792, 10, tab_reg);
dstate_setinfo("battery.voltage", "%f", (double) (tab_reg[0]) / 1000.0);
dstate_setinfo("battery.temperature", "%d", tab_reg[1] - 273);
dstate_setinfo("battery.runtime", "%d", tab_reg[3]);
dstate_setinfo("battery.capacity", "%d", tab_reg[8] * 10);
dstate_setinfo("output.current", "%f", (double) (tab_reg[6]) / 1000.0);
/* ALARMS */
mrir(modbus_ctx, 29840, 1, tab_reg);
alarm_init();
if (CHECK_BIT(tab_reg[0], 4) && CHECK_BIT(tab_reg[0], 5))
alarm_set("End of life (Resistance)");
if (CHECK_BIT(tab_reg[0], 6))
alarm_set("End of life (Time)");
if (CHECK_BIT(tab_reg[0], 7))
alarm_set("End of life (Voltage)");
if (CHECK_BIT(tab_reg[0], 9))
alarm_set("No Battery");
if (CHECK_BIT(tab_reg[0], 10))
alarm_set("Inconsistent technology");
if (CHECK_BIT(tab_reg[0], 11))
alarm_set("Overload Cutoff");
/* We don't use those low-battery indicators below.
* No info or configuration exists for those alarm low-bat
*/
if (CHECK_BIT(tab_reg[0], 12))
alarm_set("Low Battery (Voltage)");
if (CHECK_BIT(tab_reg[0], 13))
alarm_set("Low Battery (Charge)");
if (CHECK_BIT(tab_reg[0], 14))
alarm_set("Low Battery (Time)");
if (CHECK_BIT(tab_reg[0], 16))
alarm_set("Low Battery (Service)");
if (errcount == 0) {
alarm_commit();
status_commit();
dstate_dataok();
}
else
dstate_datastale();
}
void upsdrv_shutdown(void)
__attribute__((noreturn));
void upsdrv_shutdown(void)
{
fatalx(EXIT_FAILURE, "shutdown not supported");
}
void upsdrv_help(void)
{
}
/* list flags and values that you want to receive via -x */
void upsdrv_makevartable(void)
{
}
void upsdrv_initups(void)
{
int r;
upsdebugx(2, "upsdrv_initups");
modbus_ctx = modbus_new_rtu(device_path, 115200, 'E', 8, 1);
if (modbus_ctx == NULL)
fatalx(EXIT_FAILURE, "Unable to create the libmodbus context");
r = modbus_set_slave(modbus_ctx, MODBUS_SLAVE_ID); /* slave ID */
if (r < 0) {
modbus_free(modbus_ctx);
fatalx(EXIT_FAILURE, "Invalid modbus slave ID %d",MODBUS_SLAVE_ID);
}
if (modbus_connect(modbus_ctx) == -1) {
modbus_free(modbus_ctx);
fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno));
}
}
void upsdrv_cleanup(void)
{
if (modbus_ctx != NULL) {
modbus_close(modbus_ctx);
modbus_free(modbus_ctx);
}
}
/* Modbus Read Input Registers */
static int mrir(modbus_t * arg_ctx, int addr, int nb, uint16_t * dest)
{
int r;
r = modbus_read_input_registers(arg_ctx, addr, nb, dest);
if (r == -1) {
upslogx(LOG_ERR, "mrir: modbus_read_input_registers(addr:%d, count:%d): %s (%s)", addr, nb, modbus_strerror(errno), device_path);
errcount++;
}
return r;
}