Please try again, I have been actively editing it.
I'll try and drop a current copy in here for posterity, too.
Rgds
Damon
/*
Damon Hart-Davis licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the Licence is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the Licence for the
specific language governing permissions and limitations
under the Licence.
Author(s) / Copyright (s): Damon Hart-Davis 2016
*/
static const char *Id = "$Id: ssmppt15l-modbus.cpp 15087 2016-08-12 20:06:20Z dhd $";
/*
Stand-alone code to interrogate SS-MPPT-15L solar controller over MODBUS.
*/
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <modbus/modbus.h>
// Default (USB) wired serial connection to SS-MPPT-15L.
static const char *DEFAULT_MODBUS_DEV = "/dev/serial/by-id/usb-FTDI_UT232R_FTXIEVAK-if00-port0";
// Default address of the SunSaver MPPT.
static const uint8_t SUNSAVERMPPT = 0x01;
// Array volatge, filtered, units of 100*2^-15V.
static const uint16_t SS_REG_Adc_vb_f = 0x0008;
// Load voltage, filtered, units of 100*2^-15V.
static const uint16_t SS_REG_Adc_vl_f = 0x000a;
// Load current, filtered, units of 79.16*2^-15V.
static const uint16_t SS_REG_Adc_il_f = 0x000c;
// Battery voltage, slow filter (~25s), units of 100*2^-15V.
static const uint16_t SS_REG_Vb_f = 0x0013;
// Array Voc, filtered, units of 100*2^-15V.
static const uint16_t SS_REG_Sweep_Voc = 0x002a;
#ifndef __cplusplus
typedef enum { false, true } bool;
#endif
// Read single SS-MPPT-15L register into uint16_t over MODBUS; true if success.
// Potentially somewhat inefficient nominally setting up and tearing down
// connection for each read, but self-contained.
// Can automatically retry the read after a pause.
static bool getMODBUSReg(const uint16_t addr, uint16_t &value,
const int retries = 1)
{
modbus_t * const ctx = modbus_new_rtu(DEFAULT_MODBUS_DEV, 9600, 'N', 8, 2);
if(NULL == ctx)
{
fprintf(stderr, "Unable to create libmodbus context\n");
return(false);
}
modbus_set_slave(ctx, SUNSAVERMPPT);
if(-1 == modbus_connect(ctx))
{
fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
modbus_free(ctx);
return(false);
}
// Request specified register.
for(int i = 0; ; )
{
const int n = modbus_read_input_registers(ctx, addr, 1, &value);
if(1 == n) { break; }
fprintf(stderr, "Bad MODBUS read: %s\n", modbus_strerror(errno));
if(++i > retries)
{
fprintf(stderr, "Giving up.\n");
modbus_free(ctx);
return(false);
}
sleep(1);
}
/* Close the connection and free resources. */
modbus_close(ctx);
modbus_free(ctx);
return(true);
}
// Convert from MODBUS units of 100*2^-15V to mV. */
inline uint16_t fMODBUSTmV(const uint16_t vMB) { return((uint16_t) ((32767u+vMB*100000UL)>>15)); }
int main (const int argc, char *const argv [])
{
int c;
while((c = getopt(argc,argv,"hv")) >= 0)
{
switch(c)
{
case 'h':
case '?':
{
fprintf(stderr, "Version %s, libmodbus " LIBMODBUS_VERSION_STRING "\n", Id);
fprintf(stderr, "%s [options]\n", argv[0]);
fprintf(stderr, " -h this help\n");
fprintf(stderr, " -v verbose\n");
break;
}
}
}
// Get (smoothed/filtered) battery voltage.
uint16_t Vb_f_raw;
if(getMODBUSReg(SS_REG_Vb_f, Vb_f_raw))
{
fprintf(stdout, "Vb_f (V): %f\n", Vb_f_raw*100.0f/32768.0f);
fprintf(stdout, "Vb_f (mV): %u\n", fMODBUSTmV(Vb_f_raw));
}
else { fprintf(stderr, "Failed\n"); exit(1); }
uint16_t Adc_vb_f;
if(getMODBUSReg(SS_REG_Adc_vb_f, Adc_vb_f))
{
fprintf(stdout, "Adc_vb_f (mV): %u\n", fMODBUSTmV(Adc_vb_f));
}
else { fprintf(stderr, "Failed\n"); exit(1); }
uint16_t Sweep_Voc;
if(getMODBUSReg(SS_REG_Sweep_Voc, Sweep_Voc))
{
fprintf(stdout, "Sweep_Voc (mV): %u\n", fMODBUSTmV(Sweep_Voc));
}
else { fprintf(stderr, "Failed\n"); exit(1); }
return(0);
}