Commit f176d76e authored by Jeroen Vreeken's avatar Jeroen Vreeken
Browse files

Add VESP protocol.

parent 62edca27
......@@ -14,6 +14,7 @@ $(eval $(call SUBDIR,block))
$(eval $(call SUBDIR,trigger))
$(eval $(call SUBDIR,ec))
$(eval $(call SUBDIR,test))
$(eval $(call SUBDIR,vesp))
ifneq (,$(findstring arm,$(HW)))
$(eval $(call SUBDIR,am335x))
......@@ -52,7 +53,7 @@ test: dt_ctrl_test
DT_CTRL_TESTS := $(CTRL_TESTS)
DT_CTRL := $(DIR)/dt_ctrl
define DT_CTRl_TEST
define DT_CTRL_TEST
$(DT_CTRL) $1
endef
......@@ -60,7 +61,7 @@ endef
dt_ctrl_test: $(DIR)/dt_ctrl
dt_ctrl_test:
echo tests: $(DT_CTRL_TESTS)
$(foreach TESTITEM,$(DT_CTRL_TESTS),$(call DT_CTRl_TEST,$(TESTITEM)))
$(foreach TESTITEM,$(DT_CTRL_TESTS),$(call DT_CTRL_TEST,$(TESTITEM)))
SRCS += $(DT_CTRL_SRCS)
......
......@@ -141,10 +141,10 @@ struct controller_bus_client *controller_bus_add_input_client(
bus->input_client = ic;
bus->input_client[bus->input_clients].block = client;
bus->input_client[bus->input_clients].callback = NULL;
bus->input_client[bus->output_clients].poll = NULL;
bus->input_client[bus->output_clients].recover = NULL;
bus->input_client[bus->input_clients].poll = NULL;
bus->input_client[bus->input_clients].recover = NULL;
bus->input_client[bus->input_clients].offset = 0;
bus->input_client[bus->output_clients].state = CONTROLLER_BUS_STATE_OK;
bus->input_client[bus->input_clients].state = CONTROLLER_BUS_STATE_OK;
bus->input_clients++;
log_send(LOG_T_DEBUG, "Block '%s' is an input client of bus '%s'",
......
......@@ -132,9 +132,17 @@ void controller_dumpdot(char *filename)
}
if (j == 0) {
char *bgcolor;
if (block->calculate) {
bgcolor="yellow";
} else {
bgcolor="lightyellow";
}
fprintf(fddot,
"\t\t\t\t<TD PORT=\"block_port\" ROWSPAN=\"%d\" BGCOLOR=\"yellow\">%s<BR/><I>%s</I></TD>\n",
rows, block->name, block->type);
"\t\t\t\t<TD PORT=\"block_port\" ROWSPAN=\"%d\" BGCOLOR=\"%s\">%s<BR/><I>%s</I></TD>\n",
rows, bgcolor, block->name, block->type);
}
if (block->outputs > j) {
......
......@@ -433,6 +433,7 @@ static int check_one_argument_type_list(char *expected_types,
free(expected_type);
idx++;
}
idx--;
log_send(LOG_T_ERROR, "Invalid number of arguments! "
"Expecting %d argument%s instead of %d.", idx,
(idx > 1) ? "s" : "", supplied_argc);
......
all:
@$(MAKE) --no-print-directory -C ../.. targets_controller/vesp
$(MAKECMDGOALS):
@$(MAKE) --no-print-directory -C ../.. $(MAKECMDGOALS)
/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2015
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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <controller/controller_block.h>
#include <controller/controller_bus.h>
#include <log/log.h>
#include <vesp/vesp.h>
#include <vesp/vesp_ids.h>
/*
outputs
nr name
-----------------
| |
| 0 ppm |----
| |
-----------------
*/
struct controller_block_private {
float ppm;
void *data_rx;
void *data_tx;
struct controller_bus *bus;
struct controller_bus_client *client;
};
struct rx_packet {
uint16_t vdiv;
uint8_t status;
} __packed;
struct tx_packet {
uint8_t control;
} __packed;
#define VESP_SENSOR_CO_CMD_CONTROL 4
#define VESP_SENSOR_CO_CTRL_ENABLE 1
static void calculate(struct controller_block *sensor)
{
struct controller_block_private *priv = sensor->private;
struct rx_packet *packet_rx = priv->data_rx;
struct tx_packet *packet_tx = priv->data_tx;
/* no calibration yet, just a direct conversion */
priv->ppm = le16toh(packet_rx->vdiv);
packet_tx->control = VESP_SENSOR_CO_CTRL_ENABLE;
}
static void rx_callback(struct controller_block *block, void *data)
{
block->private->data_rx = data;
}
static void tx_callback(struct controller_block *block, void *data)
{
block->private->data_tx = data;
}
static struct controller_block_outterm_list outterms[] = {
{ "ppm", CONTROLLER_BLOCK_TERM_FLOAT, offsetof(struct controller_block_private, ppm) },
{ NULL }
};
static struct controller_block * block_vesp_sensor_co_create(char *name,
int argc, va_list ap)
{
struct controller_block *sensor;
char *busname = va_arg(ap, char *);
uint8_t address = va_arg(ap, int);
uint8_t vid, dev;
if (!(sensor = controller_block_alloc("vesp_sensor_co", name, sizeof(struct controller_block_private))))
return NULL;
sensor->calculate = calculate;
if (controller_block_outterm_list_init(sensor, outterms))
goto err_block;
sensor->private->bus = controller_bus_find(busname);
if (!sensor->private->bus)
goto err_bus;
//TODO: check device presence
if (vesp_command_get_details(sensor->private->bus, address, &vid, &dev)) {
log_send(LOG_T_ERROR, "%s: Could not get details of device 0x%02x",
name, address);
goto err_dev;
}
if (vid != VESP_VID_VREEKEN || dev != VESP_DEV_VREEKEN_CO) {
log_send(LOG_T_ERROR,
"%s: VID/DEV ids do not match: (0x%02x, 0x%02x) != (0x%02x, 0x%02x)",
name, vid, dev, VESP_VID_VREEKEN, VESP_DEV_VREEKEN_CO);
goto err_dev;
}
if (vesp_bus_add_input(sensor, sensor->private->bus,
address, VESP_RESP_OOB, sizeof(struct rx_packet), rx_callback))
goto err_bus_input;
if (vesp_bus_add_output(sensor, sensor->private->bus,
address, VESP_SENSOR_CO_CMD_CONTROL,
sizeof(struct tx_packet), tx_callback))
goto err_bus_output;
if (controller_block_add(sensor))
goto err_add;
return sensor;
err_add:
err_bus_output:
err_bus_input:
err_dev:
err_bus:
err_block:
controller_block_free(sensor);
return NULL;
}
BLOCK_CREATE(vesp_sensor_co) = {
.create = block_vesp_sensor_co_create,
.args = { "char*,int", NULL },
};
/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2015
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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <controller/controller_block.h>
#include <controller/controller_bus.h>
#include <controller/controller_mem.h>
#include <controller/controller_sample.h>
#include <log/log.h>
#include <vesp/vesp.h>
/*
outputs
nr name
------------------
| |
| 0 operational |----
| |
------------------
*/
struct controller_block_private {
bool operational;
struct controller_bus *bus;
};
static struct controller_block_outterm_list outterms[] = {
{ "operational", CONTROLLER_BLOCK_TERM_BOOL, offsetof(struct controller_block_private, operational) },
{ NULL }
};
static void vesp_sim_tx_calculate(struct controller_block *vesp)
{
struct controller_block_private *priv = vesp->private;
vesp_output_clients(priv->bus);
}
static int vesp_sim_recv(struct controller_bus *bus, struct timespec *timeout)
{
return 0;
}
static int vesp_sim_send(struct controller_bus *bus, void *data, size_t size)
{
struct controller_block *vesp = bus->owner;
log_send(LOG_T_DEBUG, "%s: Send frame with %zd bytes", vesp->name, size);
return 0;
}
static void vesp_sample_start(void *arg)
{
struct controller_bus *bus = arg;
vesp_client_callbacks(bus);
}
static struct controller_block * block_vesp_sim_create(char *name, int argc,
va_list val)
{
struct controller_block *vesp;
struct controller_bus *bus;
char *name_tx;
struct controller_block *vesp_tx;
struct vesp_bus_create_arg bus_arg;
asprintf(&name_tx, "%s_tx", name);
if (!name_tx)
return NULL;
vesp = controller_block_alloc("vesp_sim", name, sizeof(struct controller_block));
if (!vesp)
goto err_block;
vesp_tx = controller_block_alloc("vesp_sim_tx", name_tx, 0);
if (!vesp)
goto err_block_tx;
vesp_tx->private = vesp->private;
free(name_tx);
name_tx = NULL;
vesp->private->operational = true;
if (controller_block_outterm_list_init(vesp, outterms))
goto err_outterm;
bus_arg.owner = vesp;
bus_arg.send = vesp_sim_send;
bus_arg.recv = vesp_sim_recv;
bus_arg.max_command = 256;
bus_arg.max_response = 256;
bus = vesp_bus_create(name, &bus_arg);
if (!bus)
goto err_bus;
controller_bus_output_set(bus, vesp_tx);
vesp->private->bus = bus;
vesp_tx->calculate = vesp_sim_tx_calculate;
if (controller_block_add(vesp))
goto err_add;
if (controller_block_add(vesp_tx))
goto err_add;
controller_sample_start_hook(vesp_sample_start, vesp->private->bus);
return vesp;
err_add:
err_bus:
err_outterm:
controller_block_free(vesp_tx);
err_block_tx:
controller_block_free(vesp);
err_block:
free(name_tx);
return NULL;
}
BLOCK_CREATE(vesp_sim) = {
.create = block_vesp_sim_create,
.args = { "", NULL },
};
/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2015
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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/serial.h>
#include <controller/controller_block.h>
#include <controller/controller_bus.h>
#include <controller/controller_mem.h>
#include <controller/controller_sample.h>
#include <log/log.h>
#include <vesp/vesp.h>
/*
outputs
nr name
------------------
| |
| 0 operational |----
| |
------------------
*/
struct controller_block_private {
bool operational;
void *rx_buffer;
size_t rx_size;
struct controller_bus *bus;
char *devname;
int fd;
speed_t speed;
bool rts;
};
static struct controller_block_outterm_list outterms[] = {
{ "operational", CONTROLLER_BLOCK_TERM_BOOL, offsetof(struct controller_block_private, operational) },
{ NULL }
};
static int vesp_tty_fd_recv(struct controller_block *vesp)
{
struct controller_block_private *priv = vesp->private;
ssize_t ret;
int fd = priv->fd;
void *rx_buffer = priv->rx_buffer;
log_send(LOG_T_DEBUG, "%s: try to receive %zd", vesp->name, priv->rx_size);
ret = read(fd, rx_buffer, priv->rx_size);
if (ret > 0) {
log_send(LOG_T_DEBUG, "received %zd bytes", ret);
vesp_input_process(priv->bus, rx_buffer, ret);
} else {
if (ret < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
log_send(LOG_T_ERROR,
"%s: Error reading from device(%zd): %s",
vesp->name, ret, strerror(errno));
close(fd);
priv->fd = -1;
goto err;
}
}
return 0;
err:
return -1;
}
static void vesp_tty_calculate(struct controller_block *vesp)
{
struct controller_block_private *priv = vesp->private;
int fd = priv->fd;
if (fd < 0)
goto err;
if (vesp_tty_fd_recv(vesp))
goto err;
priv->operational = true;
return;
err:
priv->operational = false;
return;
}
static void vesp_tty_tx_calculate(struct controller_block *vesp)
{
struct controller_block_private *priv = vesp->private;
vesp_output_clients(priv->bus);
}
static int vesp_tty_recv(struct controller_bus *bus, struct timespec *timeout)
{
struct controller_block *vesp = bus->owner;
struct controller_block_private *priv = vesp->private;
int fd = priv->fd;
if (fd < 0)
goto err;
if (timeout) {
fd_set fdsr;
FD_ZERO(&fdsr);
FD_SET(fd, &fdsr);
if (pselect(fd + 1, &fdsr, NULL, NULL, timeout, NULL) < 0) {
log_send(LOG_T_ERROR,
"%s: Error while waiting for data, %s",
vesp->name, strerror(errno));
close(fd);
priv->fd = -1;
goto err;
}
}
return vesp_tty_fd_recv(vesp);
err:
return -1;
}
static int vesp_tty_send(struct controller_bus *bus, void *data, size_t size)
{
struct controller_block *vesp = bus->owner;
struct controller_block_private *priv = vesp->private;
ssize_t ret;
int fd = priv->fd;
if (fd < 0)
goto err;
log_send(LOG_T_DEBUG, "%s: send %zd bytes", bus->name, size);
ret = write(fd, data, size);
if (ret == size)
return 0;
log_send(LOG_T_ERROR, "%s: failed to send %zd bytes: %s",
vesp->name, ret, strerror(errno));
err:
return -1;
}
static void vesp_sample_start(void *arg)
{
struct controller_block *vesp = arg;
struct controller_block_private *priv = vesp->private;
struct controller_bus *bus = priv->bus;
vesp_client_callbacks(bus);
priv->rx_size = vesp_client_rx_size(bus);
priv->rx_buffer = controller_mem_realloc(
CONTROLLER_MEM_PERIODIC_READ | CONTROLLER_MEM_PERIODIC_WRITE,
priv->rx_buffer, priv->rx_size);
log_send(LOG_T_DEBUG, "%s: Receive buffer: %zd bytes",
vesp->name, priv->rx_size);
}
static int vesp_dev_open(struct controller_block *block)
{
struct controller_block_private *priv = block->private;
struct termios tty;
priv->fd = open(priv->devname, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (priv->fd < 0) {
log_send(LOG_T_ERROR, "%s: Opening VESP device %s failed",
block->name, priv->devname);
goto err;
}
memset (&tty, 0, sizeof tty);
if (tcgetattr (priv->fd, &tty) != 0) {
log_send(LOG_T_ERROR, "%s: tcgetattr(): %s",
block->name, strerror(errno));
goto err;
}
if (cfsetospeed(&tty, priv->speed) ||
cfsetispeed(&tty, priv->speed)) {
log_send(LOG_T_ERROR,
"%s: failed to set baudrate: 0x%x",
block->name, priv->speed);
goto err;
}
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
tty.c_iflag &= ~IGNBRK; // disable break processing
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 0; // seconds read timeout
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (priv->fd, TCSANOW, &tty) != 0) {
log_send(LOG_T_DEBUG, "%s: tcsetattr() %s",
block->name, strerror(errno));
goto err;
}
struct serial_rs485 rs485conf;