/* phoenixcontact_modbus.c - Driver for PhoenixContact-QUINT UPS * * Copyright (C) * 2017 Spiros Ioannou * * 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 #define DRIVER_NAME "NUT PhoenixContact Modbus driver" #define DRIVER_VERSION "0.01" #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 \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; }