From 8260c2ff1e9f2a556c8cb80dc0f960c74a700088 Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Sat, 25 Nov 2023 00:14:59 +0100 Subject: [PATCH] Add host decoder to test the slave communication protocol --- host/scialys_uart_comm_decode/.gitignore | 2 + host/scialys_uart_comm_decode/Makefile | 32 ++ host/scialys_uart_comm_decode/README | 2 + host/scialys_uart_comm_decode/main.c | 356 +++++++++++++++++++ host/scialys_uart_comm_decode/serial_utils.c | 59 +++ host/scialys_uart_comm_decode/serial_utils.h | 32 ++ 6 files changed, 483 insertions(+) create mode 100644 host/scialys_uart_comm_decode/.gitignore create mode 100644 host/scialys_uart_comm_decode/Makefile create mode 100644 host/scialys_uart_comm_decode/README create mode 100644 host/scialys_uart_comm_decode/main.c create mode 100644 host/scialys_uart_comm_decode/serial_utils.c create mode 100644 host/scialys_uart_comm_decode/serial_utils.h diff --git a/host/scialys_uart_comm_decode/.gitignore b/host/scialys_uart_comm_decode/.gitignore new file mode 100644 index 0000000..6570fae --- /dev/null +++ b/host/scialys_uart_comm_decode/.gitignore @@ -0,0 +1,2 @@ +scialys_info_armhf +scialys_info diff --git a/host/scialys_uart_comm_decode/Makefile b/host/scialys_uart_comm_decode/Makefile new file mode 100644 index 0000000..2f8f03b --- /dev/null +++ b/host/scialys_uart_comm_decode/Makefile @@ -0,0 +1,32 @@ +#CROSS_COMPILE ?= arm-linux-gnueabihf- +CC = $(CROSS_COMPILE)gcc + +CFLAGS = -Wall -O2 -Wextra + +EXEC = scialys_info + +all: $(EXEC) + + +OBJDIR = objs +SRC = $(shell find . -name \*.c) +OBJS = ${SRC:%.c=${OBJDIR}/%.o} +INCLUDES = includes/ + +${OBJDIR}/%.o: %.c + @mkdir -p $(dir $@) + @echo "-- compiling" $< + @$(CC) -MMD -MP -MF ${OBJDIR}/$*.d $(CFLAGS) $< -c -o $@ -I$(INCLUDES) + +$(EXEC): $(OBJS) + @echo "Linking $@ ..." + @$(CC) $(LDFLAGS) -o $@ $(OBJS) + @echo Done. + + +clean: + find ${OBJDIR} -name "*.o" -exec rm {} \; + find ${OBJDIR} -name "*.d" -exec rm {} \; + +mrproper: clean + rm -f $(EXEC) diff --git a/host/scialys_uart_comm_decode/README b/host/scialys_uart_comm_decode/README new file mode 100644 index 0000000..30c1fb0 --- /dev/null +++ b/host/scialys_uart_comm_decode/README @@ -0,0 +1,2 @@ +This host app decodes the frames continuously sent by the scialys module on his +serial line (UART1) towards extentions diff --git a/host/scialys_uart_comm_decode/main.c b/host/scialys_uart_comm_decode/main.c new file mode 100644 index 0000000..4cc7892 --- /dev/null +++ b/host/scialys_uart_comm_decode/main.c @@ -0,0 +1,356 @@ +/**************************************************************************** + * Receive and decode data from Scialys module + * + * main.c + * + * + * Copyright 2013-2023 Nathael Pajani + * + * + * 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 3 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, see . + * + ****************************************************************************/ + + +/* + * This host app decodes the frames continuously sent by the scialys module on his + * serial line (UART) + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "serial_utils.h" + + +#define PROG_NAME "Sensors polling and decode" +#define VERSION "0.2" + + +void help(char *prog_name) +{ + fprintf(stderr, "---------------- "PROG_NAME" --------------------------------\n"); + fprintf(stderr, "Usage: %s [options]\n" \ + " Available options:\n" \ + " \t -d | --device : Serial device to use for serial communication with the module\n" \ + " \t -l | --log : Base filename to use for log files\n" \ + " \t -h | --help : Print this help\n" \ + " \t -v | --version : Print programm version\n" \ + " All other arguments are data for the command, handled depending on the command.\n", prog_name); + fprintf(stderr, "-----------------------------------------------------------------------\n"); +} + +#define MAX_LOGNAME_LEN 200 + + +/* Scialys data, as sent on UART1 or stored on SD card. + * + * Header fields : + * The start byte is dedicated to UART communication and is always '#" + * Note that this byte may be repeated within the data packet, so the rest of the header + * should be checked before handling a packet + * The first checksum byte is so that the sum of all header bytes is zero + * The version byte indicates the version of this structure. + * The length is fixed so knowing the version gives the length. + * The data checksum is equal to the sum of all the data bytes + * + * Data fields : + * ... + * The flags can be expanded to a "struct flags" (see below) by shifting one bit to each byte. + * + */ + +#define DATA_START '#' +#define DATA_VERSION 0x01 +#define DATA_HEADER_LEN 4 + +struct scialys_data { + /* Header */ + uint8_t start; + uint8_t cksum; + uint8_t version; + uint8_t data_cksum; + /* Data */ + uint32_t solar_prod_value; + uint32_t home_conso_value; + int water_centi_degrees; + int deci_degrees_power; + int deci_degrees_disp; + uint16_t load_power_lowest; + uint16_t load_power_highest; + uint8_t command_val; + uint8_t act_cmd; + uint8_t mode; + uint8_t flags; +} __attribute__ ((__packed__)); + + +struct flags { + uint8_t fan_on; + uint8_t force_fan; + uint8_t error_shutdown; + uint8_t temp_shutdown; + uint8_t overvoltage; + uint8_t external_disable; + uint8_t forced_heater_mode; + uint8_t manual_activation_request; +}; + + + +#define BUF_SIZE 80 +#define MSG_LEN 32 +uint8_t data[BUF_SIZE]; +int data_idx = 0; + +int protocol_decode(uint8_t c) +{ + /* Start of packet */ + if (data_idx == 0) { + if (c == '#') { + data[0] = c; + data_idx = 1; + } + return 0; + } + if (data_idx > 0) { + data[data_idx++] = c; + /* Full header received, check for header validity */ + if (data_idx == 4) { + uint8_t sum = data[0] + data[1] + data[2] + data[3]; + if (sum != 0) { + data_idx = 0; + return -1; + } + } + /* Full message received, validate data checksum */ + if (data_idx >= MSG_LEN) { + int i = 0; + uint8_t sum = 0; + for (i = 4; i < MSG_LEN; i++) { + sum += data[i]; + } + data_idx = 0; + if (sum != data[3]) { + return -1; + } + return 1; + } + } + return 0; +} + +int main(int argc, char* argv[]) +{ + /* Serial */ + char* device = NULL; + int slave_fd = 0; + + /* Log file */ + char* log_base_name = NULL; + int data_log_fd = 0; + + /* Timestamp */ + struct timeval now; + int start_sec = 0; + + while(1) { + int option_index = 0; + int c = 0; + + struct option long_options[] = { + {"device", required_argument, 0, 'd'}, + {"log", required_argument, 0, 'l'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "d:l:hv", long_options, &option_index); + + /* no more options to parse */ + if (c == -1) break; + switch (c) { + + /* d, device */ + case 'd': + device = optarg; + break; + + /* l, base name for log file */ + case 'l': + log_base_name = optarg; + break; + + /* v, version */ + case 'v': + printf("%s Version %s\n", PROG_NAME, VERSION); + return 0; + break; + + /* h, help */ + case 'h': + default: + help(argv[0]); + return 0; + } + } + + + /* Need Serial port as parameter */ + if (device == NULL) { + printf("Error, need serial (tty) device\n"); + help(argv[0]); + return -1; + } + + /* Open tty */ + slave_fd = serial_setup(device); + if (slave_fd < 0) { + printf("Unable to open specified serial port %s\n", device); + return -2; + } + + /* Log files ? */ + if (log_base_name != NULL) { + char name[MAX_LOGNAME_LEN]; + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + + snprintf(name, MAX_LOGNAME_LEN, "%s.data", log_base_name); + data_log_fd = open(name, O_RDWR|O_CREAT|O_APPEND, mode); + if (data_log_fd <= 0) { + printf("Unable to open Data log file '%s' : %s", name, strerror(errno)); + } + snprintf(name, MAX_LOGNAME_LEN, "Mode Sol Home Teau Tpow Tdis Pmin/Max Cmd/act Fan/fo Terr TMos OV Dis Force Manual\n"); + write(data_log_fd, name, strlen(name)); + } + + /* Save start second */ + gettimeofday(&now, NULL); + start_sec = now.tv_sec; + + + /* ************************************************* */ + /* And never stop handling data ! */ + while (1) { + int len = 0, ret = 0; + char buf[BUF_SIZE]; + int idx = 0; + int sec = 0, msec = 0; + + memset(buf, 0, BUF_SIZE); + /* Get serial data and try to build a packet */ + len = read(slave_fd, buf, BUF_SIZE); + if (len <= 0) { + if (len < 0) { + perror("serial read error"); + } else { + /* Read returnd 0 ... EOF */ + printf("Serial disapeared ... closing and re-opening\n"); + } + close(slave_fd); + do { + slave_fd = serial_setup(device); + if (slave_fd < 0) { + printf("Waiting for serial port %s\n", device); + usleep(10 * 1000); + } + } while (slave_fd < 0); + continue; + } else { + write(2, buf, len); + } + + /* timestamp */ + gettimeofday(&now, NULL); + sec = now.tv_sec - start_sec; + msec = now.tv_usec / 1000; + + while (idx < len) { + /* Extract frames from data stream */ + ret = protocol_decode(buf[idx]); + /* Check return code to know if we have a valid packet */ + if (ret == 1) { + /* Valid packet received, parse data */ + struct scialys_data sc; + struct flags f; + float diff; + + memcpy(&sc, data, sizeof(struct scialys_data)); + + diff = ((float)((int)sc.solar_prod_value - (int)sc.home_conso_value) / 1000); + f.fan_on = (sc.flags & (0x01 << 0)) >> 0; + f.force_fan = (sc.flags & (0x01 << 1)) >> 1; + f.error_shutdown = (sc.flags & (0x01 << 2)) >> 2; + f.temp_shutdown = (sc.flags & (0x01 << 3)) >> 3; + f.overvoltage = (sc.flags & (0x01 << 4)) >> 4; + f.external_disable = (sc.flags & (0x01 << 5)) >> 5; + f.forced_heater_mode = (sc.flags & (0x01 << 6)) >> 6; + f.manual_activation_request = (sc.flags & (0x01 << 7)) >> 7; + + /* Display data */ + printf(""); /* Goto terminal home (top left) */ + printf("Mode: %c\n", sc.mode); + printf("Prod: % 2d.%02dA, Conso: % 2d.%02dA, delta: %.2fA\n", + (sc.solar_prod_value / 1000), ((sc.solar_prod_value % 1000) / 10), + (sc.home_conso_value / 1000), ((sc.home_conso_value % 1000) / 10), + diff); + printf("Cmd: %03d/%03d, Fan:%d/%d\n", sc.command_val, sc.act_cmd, f.fan_on, f.force_fan); + printf("Water: % 2d.%02d°C\n", (sc.water_centi_degrees / 100), (sc.water_centi_degrees % 100)); + printf("Internal TempP: % 2d.%d°C, TempD: % 2d.%d°C\n", + (sc.deci_degrees_power / 10), (sc.deci_degrees_power % 10), + (sc.deci_degrees_disp / 10), (sc.deci_degrees_disp % 10)); + printf("Load: %03d - %03d\n", sc.load_power_lowest, sc.load_power_highest); + printf("Extdis: %d, Forced: %d, Manual: %d, ErrSD: %d, TempSD: %d, OverV: %d\n", + f.external_disable, f.forced_heater_mode, f.manual_activation_request, + f.error_shutdown, f.temp_shutdown, f.overvoltage); + printf("\n"); + + /* Log ? */ + if (data_log_fd > 0) { + char buf[256]; + snprintf(buf, 13, "% 5d.%03d : ", sec, msec); + write(data_log_fd, buf, 12); + snprintf(buf, 256, "%c:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + sc.mode, sc.solar_prod_value, sc.home_conso_value, + sc.water_centi_degrees, sc.deci_degrees_power, sc.deci_degrees_disp, + sc.load_power_lowest, sc.load_power_highest, sc.command_val, + sc.act_cmd, f.fan_on, f.force_fan, f.error_shutdown, f.temp_shutdown, f.overvoltage, + f.external_disable, f.forced_heater_mode, f.manual_activation_request); + write(data_log_fd, buf, strnlen(buf, 256)); + } + } + + idx++; + } + + } /* End of infinite loop */ + + close(slave_fd); + return 0; +} + + diff --git a/host/scialys_uart_comm_decode/serial_utils.c b/host/scialys_uart_comm_decode/serial_utils.c new file mode 100644 index 0000000..5e17e42 --- /dev/null +++ b/host/scialys_uart_comm_decode/serial_utils.c @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Serial utility functions + * + * + * Copyright 2012-2014 Nathael Pajani + * + * 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 3 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, see . + * + *********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define SERIAL_BAUD B115200 + + +int serial_setup(char* name) +{ + struct termios tio; + int fd = -1; + + /* Open serial port */ + fd = open(name, O_RDWR); + if (fd < 0) { + perror("Unable to open communication with companion chip"); + return -1; + } + /* Setup serial port */ + memset(&tio, 0, sizeof(tio)); + tio.c_cflag = CS8 | CREAD | CLOCAL; /* 8n1, see termios.h for more information */ + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 5; + cfsetospeed(&tio, SERIAL_BAUD); + cfsetispeed(&tio, SERIAL_BAUD); + tcsetattr(fd, TCSANOW, &tio); + + return fd; +} + diff --git a/host/scialys_uart_comm_decode/serial_utils.h b/host/scialys_uart_comm_decode/serial_utils.h new file mode 100644 index 0000000..7c17bcf --- /dev/null +++ b/host/scialys_uart_comm_decode/serial_utils.h @@ -0,0 +1,32 @@ +/********************************************************************* + * + * Serial utility functions + * + * + * Copyright 2012 Nathael Pajani + * + * 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 3 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, see . + * + *********************************************************************/ +#ifndef SERIAL_UTILS_H +#define SERIAL_UTILS_H + +/* Setup serial comunication, using name if given or saved name if name is NULL + * SERIAL_BAUD B38400 + * c_cflag (CS7 | PARENB | CREAD | CLOCAL) (7e1) + */ +int serial_setup(char* name); + +#endif /* SERIAL_UTILS_H */ + -- 2.43.0