#if HAVE_DECL_TIOCSRS485 || HAVE_DECL_TIOCM_RTS
#include <sys/ioctl.h>
#define FAST_RTS_DELAY 5000
#endif
// ...
#if HAVE_DECL_TIOCM_RTS
modbus_rtu_t *ctx_rtu = ctx->backend_data;
if (ctx_rtu->rts != MODBUS_RTU_RTS_NONE) {
ssize_t size;
if (ctx->debug) {
fprintf(stderr, "Sending request using RTS signal\n");
}
_modbus_rtu_ioctl_rts(ctx->s, ctx_rtu->rts == MODBUS_RTU_RTS_UP);
usleep(FAST_RTS_DELAY);
size = write(ctx->s, req, req_length);
usleep(ctx_rtu->onebyte_time * req_length + FAST_RTS_DELAY);
_modbus_rtu_ioctl_rts(ctx->s, ctx_rtu->rts != MODBUS_RTU_RTS_UP);
return size;
} else {
#endif
#if HAVE_DECL_TIOCM_RTS
static void _modbus_rtu_ioctl_rts(int fd, int on)
{
//int flags;
int RTS_flag;
RTS_flag = TIOCM_RTS;
if (on) {
ioctl(fd,TIOCMBIS,&RTS_flag);//Set RTS pin
} else {
ioctl(fd,TIOCMBIC,&RTS_flag);//Clear RTS pin
}
}
#endif
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <modbus/modbus.h>
const int EXCEPTION_RC = 2;
enum {
TCP, TCP_PI, RTU
};
#define SERVER_ID 1
int main(int argc, char *argv[])
{
modbus_t *ctx = NULL;
uint16_t *tab_rp_bits = NULL;
uint32_t old_response_to_sec;
uint32_t old_response_to_usec;
int rc;
int server_id = SERVER_ID;
// UPDATE THE DEVICE NAME AS NECESSARY
ctx = modbus_new_rtu("/dev/ttyS0", 9600, 'N', 8, 1);
if (ctx == NULL) {
fprintf(stderr, "Could not connect to MODBUS: %s\n", modbus_strerror(errno));
return -1;
}
printf("Setting slave_id %d\n", server_id);
fflush(stdout);
rc = modbus_set_slave(ctx, server_id);
if (rc == -1) {
fprintf(stderr, "server_id=%d Invalid slave ID: %s\n", server_id, modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
modbus_set_debug(ctx, TRUE);
// Not needed for USB-RS485 adapters
// See: https://github.com/stephane/libmodbus/issues/316
rc = modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_UP);
if (rc == -1) {
fprintf(stderr, "server_id=%d Failed to set serial mode: %s\n", server_id, modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
modbus_set_error_recovery(ctx, MODBUS_ERROR_RECOVERY_LINK | MODBUS_ERROR_RECOVERY_PROTOCOL);
modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec);
if (modbus_connect(ctx) == -1) {
fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
printf("timeout values sec %d, usec %d\n", old_response_to_sec, old_response_to_usec);
tab_rp_bits = (uint16_t *) malloc(2 * sizeof(uint16_t));
memset(tab_rp_bits, 0, 2 * sizeof(uint16_t));
while (TRUE) {
/*
* get charging state
*/
rc = modbus_read_registers(ctx, 0x120, 0x1, tab_rp_bits);
if (rc == -1) {
fprintf(stderr, "Failed to modbus_read_input_registers: %s\n", modbus_strerror(errno));
/* modbus_free(ctx);
return -1; */
}
printf("server_id=%d rc=%d Charge Controller mode=%x \n", rc, server_id, tab_rp_bits[0]);
sleep(1);
}
/* Free the memory */
free(tab_rp_bits);
// free(tab_rp_registers);
/* Close the connection */
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
int8_t controller_work(void)
{
switch (cstate) {
case CLEAR:
clear_2hz();
cstate = INIT;
break;
case INIT:
if (get_2hz(FALSE) > QDELAY) {
#ifdef LOCAL_ECHO
RE_ = 0; // keep receiver active
#else
RE_ = 1; // shutdown receiver
#endif
DE = 1;
V.send_count = 0;
V.recv_count = 0;
cstate = SEND;
clear_500hz();
}
break;
case SEND:
if (get_500hz(FALSE) > TDELAY) {
do {
while (BusyUSART()); // wait for each byte
TXREG = modbus_cc_mode[V.send_count];
} while (++V.send_count < sizeof(modbus_cc_mode));
while (BusyUSART()); // wait for the last byte
cstate = RECV;
clear_500hz();
}
break;
case RECV:
if (get_500hz(FALSE) > TDELAY) {
DE = 0;
RE_ = 0;
if (V.recv_count >= sizeof(re20a_mode)) { // check received data
if (cc_buffer[4]) {
LED1 = ~LED1;
V.pwm_volts = CC_ACTIVE;
} else {
LED1 = OFF;
V.pwm_volts = CC_IDLE;
}
SetDCPWM1(V.pwm_volts);
cstate = CLEAR;
} else {
if (get_500hz(FALSE) > RDELAY) {
LED1 = OFF;
cstate = CLEAR;
V.pwm_volts = CC_OFFLINE;
SetDCPWM1(V.pwm_volts);
}
}
}
break;
default:
break;
}
return 0;
}
void main(void)
{
init_ihcmon();
/* Loop forever */
while (TRUE) { // busy work
controller_work();
}
}
/*
* check received data for size and format
*/
if ((V.recv_count >= sizeof(re20a_mode)) && (cc_buffer[0] == 0x01) && (cc_buffer[1] == 0x03)) {
uint8_t temp;
uint16_t c_crc, c_crc_rec;
static uint8_t volts = CC_OFFLINE;
c_crc = crc16(cc_buffer, 5);
c_crc_rec = ((uint16_t) (cc_buffer[5] << 8)) | ((uint16_t) cc_buffer[6]);
if (c_crc == c_crc_rec) {
if ((temp = cc_buffer[4])) {
LED1 = ~LED1;
switch (temp) {
case 1:
volts = CC_ACT;
break;
case 2:
volts = CC_MPPT;
break;
case 3:
volts = CC_EQUAL;
break;
case 4:
volts = CC_BOOST;
break;
case 5:
volts = CC_FLOAT;
break;
case 6:
volts = CC_LIMIT;
break;
default:
volts = CC_ACT;
break;
}
} else {
LED1 = ON;
volts = CC_DEACT;
}
} else {
crc_error++;
LED1 = OFF;
}
V.pwm_volts = volts;
SetDCPWM1(V.pwm_volts);
cstate = CLEAR;
} else {
if (get_500hz(FALSE) > RDELAY) {
LED1 = OFF;
cstate = CLEAR;
V.pwm_volts = CC_OFFLINE;
SetDCPWM1(V.pwm_volts);
}
}
One day. All I have now is source code and schematics for the boards on github.Hola nsaspook
Coul you post a block diagram of the whole thing? Interesting.
Hola nsaspook
Coul you post a block diagram of the whole thing? Interesting.
/*
* DC PWM diversion for direct PV power before MPPT
*/
bool pv_diversion(bool kill)
{
static uint16_t pwm_val = 0;
static bool pwm_active = false;
if (kill) {
pwm_val = 0;
V.mode_pwm = 0;
pwm_active = false;
diversion_pwm_set(V.mode_pwm); // 10KHz PWM quick stop
return false;
}
if ((((cc_state(C.v_cmode) == M_FLOAT) || (cc_state(C.v_cmode) == M_BOOST)) && (C.calc[V_PV] > DPWM_LOW_VOLTS))) {
if (!pwm_active) {
pwm_active = true;
/*
* setup PWM start conditions
*/
C.start_power = C.p_pv; // save for later possible display
}
if (C.p_pv < PWM_MAX_POWER) { // increase power
if (++pwm_val > DPWM_FULL)
pwm_val = DPWM_FULL; // max PWM limiter
}
if (C.p_pv > PWM_MAX_POWER) { // decrease power
if (V.mode_pwm)
V.mode_pwm--;
}
V.mode_pwm = pwm_val;
diversion_pwm_set(V.mode_pwm); // 10KHz PWM ramp up
return true;
} else {
if (V.mode_pwm)
V.mode_pwm--;
diversion_pwm_set(V.mode_pwm); // 10KHz PWM ramp down
pwm_val = 0;
pwm_active = false;
return false;
}
}
/*
* low-pri interrupt ISR the runs every second for simple coulomb counting
*/
void calc_bsoc(void)
{
uint8_t * log_ptr, lcode = D_CODE;
static uint8_t log_update_wait = 0;
float adj = 1.0;
#ifdef DEBUG_BSOC1
DEBUG1_SetHigh();
#endif
/*
* check for excess power and send to DC dump load
*/
pv_diversion(false);
// rest of code
}
/*
* read from the serial port, check for time T command and log data to storage file
*/
int get_log(int mode)
{
char read_buf[256], msg[256];
int num_bytes, rcode = 0;
num_bytes = read(serial_port, &read_buf[0], sizeof(read_buf));
if (num_bytes < 0) {
printf("Error reading: %s\r\n", strerror(errno));
return errno;
}
file_port = open(LOG_FILE, O_RDWR | O_APPEND);
if (file_port < 0) {
printf("File Error %i from open: %s\r\n", errno, strerror(errno));
return errno;
} else {
if (num_bytes) {
printf("Read %i bytes. Received message: %c\r\n", num_bytes, read_buf[0]);
if (read_buf[0] == 'T') { // process the T server time command
int k = 0;
// Write to serial port
sprintf(msg, "T%lut", time(0)); // format UNIX time in ASCII
do {
write(serial_port, &msg[k], 1); // use gaps between bytes for slow controller time processing
usleep(1000);
} while (k++ < strlen(msg));
printf("Send time %s\r\n", msg);
rcode = 1;
} else { // just write the serial buffer to the log file
write(file_port, &read_buf[0], num_bytes);
}
}
close(file_port);
}
return rcode;
}
by Jake Hertz
by Dale Wilson
by Jeff Child