From 5716802da4778c3c7507e401bba09686e23ceb60 Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 14 Aug 2020 23:14:03 +0200 Subject: Initial VISCA backend implementation --- backends/visca.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 backends/visca.c (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c new file mode 100644 index 0000000..f8bdae1 --- /dev/null +++ b/backends/visca.c @@ -0,0 +1,222 @@ +#define BACKEND_NAME "visca" +#define DEBUG + +#include +#include "visca.h" +#include "libmmbackend.h" + +/* TODO + * VISCA server + */ + +MM_PLUGIN_API int init(){ + backend ptz = { + .name = BACKEND_NAME, + .conf = ptz_configure, + .create = ptz_instance, + .conf_instance = ptz_configure_instance, + .channel = ptz_channel, + .handle = ptz_set, + .process = ptz_handle, + .start = ptz_start, + .shutdown = ptz_shutdown + }; + + //register backend + if(mm_backend_register(ptz)){ + LOG("Failed to register backend"); + return 1; + } + return 0; +} + +static int ptz_configure(char* option, char* value){ + LOG("No backend configuration possible"); + return 1; +} + +static int ptz_configure_instance(instance* inst, char* option, char* value){ + char* host = NULL, *port = NULL, *options = NULL; + ptz_instance_data* data = (ptz_instance_data*) inst->impl; + uint8_t mode = 0; + + if(!strcmp(option, "id")){ + data->cam_address = strtoul(value, NULL, 10); + return 0; + } + if(!strcmp(option, "connect")){ + if(data->fd >= 0){ + LOGPF("Instance %s already connected", inst->name); + return 1; + } + + mmbackend_parse_hostspec(value, &host, &port, &options); + if(!host || !port){ + LOGPF("Invalid destination address specified for instance %s", inst->name); + return 1; + } + + if(options && !strcmp(options, "udp")){ + mode = 1; + } + + data->fd = mmbackend_socket(host, port, mode ? SOCK_DGRAM : SOCK_STREAM, 0, 0, 1); + if(data->fd < 0){ + LOGPF("Failed to connect instance %s", inst->name); + return 1; + } + return 0; + } + + LOGPF("Unknown instance configuration parameter %s for instance %s", option, inst->name); + return 1; +} + +static int ptz_instance(instance* inst){ + ptz_instance_data* data = calloc(1, sizeof(ptz_instance_data)); + if(!data){ + LOG("Failed to allocate memory"); + return 1; + } + + data->fd = -1; + data->cam_address = 1; + //start with maximum speeds + data->panspeed = ptz_channels[panspeed].max; + data->tiltspeed = ptz_channels[tiltspeed].max; + + inst->impl = data; + return 0; +} + +static channel* ptz_channel(instance* inst, char* spec, uint8_t flags){ + uint64_t ident = pan; + size_t command = 0; + + if(flags & mmchannel_input){ + LOG("This backend currently only supports output channels"); + return NULL; + } + + for(command = 0; command < sentinel; command++){ + if(!strncmp(spec, ptz_channels[command].name, strlen(ptz_channels[command].name))){ + ident = command; + } + } + + if(ident == sentinel){ + LOGPF("Unknown channel spec %s", spec); + return NULL; + } + + if(ident == call){ + ident |= (strtoul(spec + strlen(ptz_channels[call].name), NULL, 10) << 8); + } + + return mm_channel(inst, ident, 1); +} + +static size_t ptz_set_pantilt(instance* inst, channel* c, channel_value* v, uint8_t* msg){ + ptz_instance_data* data = (ptz_instance_data*) inst->impl; + uint32_t* x = (uint32_t*) msg + 6; + uint32_t* y = (uint32_t*) msg + 10; + + if(c->ident == pan){ + data->x = ((ptz_channels[pan].max - ptz_channels[pan].min) * v->normalised) + ptz_channels[pan].min; + } + else{ + data->y = ((ptz_channels[tilt].max - ptz_channels[tilt].min) * v->normalised) + ptz_channels[tilt].min; + } + + msg[4] = data->panspeed; + msg[5] = data->tiltspeed; + *x = htobe32(data->x); + *y = htobe32(data->y); + + return ptz_channels[pan].bytes; +} + +static size_t ptz_set_ptspeed(instance* inst, channel* c, channel_value* v, uint8_t* msg){ + ptz_instance_data* data = (ptz_instance_data*) inst->impl; + if(c->ident == panspeed){ + data->panspeed = ((ptz_channels[panspeed].max - ptz_channels[panspeed].min) * v->normalised) + ptz_channels[panspeed].min; + } + else{ + data->tiltspeed = ((ptz_channels[tiltspeed].max - ptz_channels[tiltspeed].min) * v->normalised) + ptz_channels[tiltspeed].min; + } + return 0; +} + +static size_t ptz_set_zoom(instance* inst, channel* c, channel_value* v, uint8_t* msg){ + uint32_t* position = (uint32_t*) msg + 4; + *position = htobe32(((ptz_channels[zoom].max - ptz_channels[zoom].min) * v->normalised) + ptz_channels[zoom].min); + return ptz_channels[zoom].bytes; +} + +static size_t ptz_set_focus(instance* inst, channel* c, channel_value* v, uint8_t* msg){ + uint32_t* position = (uint32_t*) msg + 4; + *position = htobe32(((ptz_channels[focus].max - ptz_channels[focus].min) * v->normalised) + ptz_channels[focus].min); + return ptz_channels[focus].bytes; +} + +static size_t ptz_set_memory(instance* inst, channel* c, channel_value* v, uint8_t* msg){ + if(v->normalised < 0.9){ + return 0; + } + + msg[5] = (c->ident >> 8); + return ptz_channels[call].bytes; +} + +static int ptz_set(instance* inst, size_t num, channel** c, channel_value* v){ + ptz_instance_data* data = (ptz_instance_data*) inst->impl; + size_t n = 0, bytes = 0; + uint8_t tx[VISCA_BUFFER_LENGTH] = ""; + uint8_t command = 0; + + for(n = 0; n < num; n++){ + bytes = 0; + command = c[n]->ident & 0xFF; + + if(ptz_channels[command].bytes){ + memcpy(tx, ptz_channels[command].pattern, ptz_channels[command].bytes); + } + tx[0] = 0x80 | (data->cam_address & 0xF); + + if(ptz_channels[command].set){ + bytes = ptz_channels[command].set(inst, c[n], v + n, tx); + } + + if(bytes && mmbackend_send(data->fd, tx, bytes)){ + LOGPF("Failed to push %s command on instance %s", ptz_channels[command].name, inst->name); + } + } + return 0; +} + +static int ptz_handle(size_t num, managed_fd* fds){ + //no events generated here + return 0; +} + +static int ptz_start(size_t n, instance** inst){ + //no startup needed yet + return 0; +} + +static int ptz_shutdown(size_t n, instance** inst){ + size_t u; + ptz_instance_data* data = NULL; + + for(u = 0; u < n; u++){ + data = (ptz_instance_data*) inst[u]->impl; + if(data->fd >= 0){ + close(data->fd); + } + free(data); + inst[u]->impl = NULL; + } + + LOG("Backend shut down"); + return 0; +} -- cgit v1.2.3 From 15826f63185211ff3974b29370d04b8082be9c53 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 15 Aug 2020 13:45:23 +0200 Subject: Add VISCA documentation, fix some issues --- backends/visca.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index f8bdae1..ae586a7 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -44,7 +44,7 @@ static int ptz_configure_instance(instance* inst, char* option, char* value){ data->cam_address = strtoul(value, NULL, 10); return 0; } - if(!strcmp(option, "connect")){ + else if(!strcmp(option, "connect")){ if(data->fd >= 0){ LOGPF("Instance %s already connected", inst->name); return 1; @@ -67,6 +67,24 @@ static int ptz_configure_instance(instance* inst, char* option, char* value){ } return 0; } + else if(!strcmp(option, "device")){ + if(data->fd >= 0){ + LOGPF("Instance %s already connected", inst->name); + return 1; + } + + #ifdef _WIN32 + LOGPF("Direct device connections are not possible on Windows"); + return 1; + #else + data->fd = open(value, O_NONBLOCK); + if(data->fd < 0){ + LOGPF("Failed to connect instance %s to device %s", inst->name, value); + return 1; + } + return 0; + #endif + } LOGPF("Unknown instance configuration parameter %s for instance %s", option, inst->name); return 1; @@ -109,6 +127,7 @@ static channel* ptz_channel(instance* inst, char* spec, uint8_t flags){ return NULL; } + //store the memory to be called above the command type if(ident == call){ ident |= (strtoul(spec + strlen(ptz_channels[call].name), NULL, 10) << 8); } @@ -118,8 +137,8 @@ static channel* ptz_channel(instance* inst, char* spec, uint8_t flags){ static size_t ptz_set_pantilt(instance* inst, channel* c, channel_value* v, uint8_t* msg){ ptz_instance_data* data = (ptz_instance_data*) inst->impl; - uint32_t* x = (uint32_t*) msg + 6; - uint32_t* y = (uint32_t*) msg + 10; + uint32_t* x = (uint32_t*) (msg + 6); + uint32_t* y = (uint32_t*) (msg + 10); if(c->ident == pan){ data->x = ((ptz_channels[pan].max - ptz_channels[pan].min) * v->normalised) + ptz_channels[pan].min; @@ -144,17 +163,18 @@ static size_t ptz_set_ptspeed(instance* inst, channel* c, channel_value* v, uint else{ data->tiltspeed = ((ptz_channels[tiltspeed].max - ptz_channels[tiltspeed].min) * v->normalised) + ptz_channels[tiltspeed].min; } + return 0; } static size_t ptz_set_zoom(instance* inst, channel* c, channel_value* v, uint8_t* msg){ - uint32_t* position = (uint32_t*) msg + 4; + uint32_t* position = (uint32_t*) (msg + 4); *position = htobe32(((ptz_channels[zoom].max - ptz_channels[zoom].min) * v->normalised) + ptz_channels[zoom].min); return ptz_channels[zoom].bytes; } static size_t ptz_set_focus(instance* inst, channel* c, channel_value* v, uint8_t* msg){ - uint32_t* position = (uint32_t*) msg + 4; + uint32_t* position = (uint32_t*) (msg + 4); *position = htobe32(((ptz_channels[focus].max - ptz_channels[focus].min) * v->normalised) + ptz_channels[focus].min); return ptz_channels[focus].bytes; } -- cgit v1.2.3 From 4d7db4128985229ebdbaae66be25b2710360f464 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sun, 16 Aug 2020 19:37:48 +0200 Subject: Fix VISCA windows build --- backends/visca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index ae586a7..619211d 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -74,7 +74,7 @@ static int ptz_configure_instance(instance* inst, char* option, char* value){ } #ifdef _WIN32 - LOGPF("Direct device connections are not possible on Windows"); + LOG("Direct device connections are not possible on Windows"); return 1; #else data->fd = open(value, O_NONBLOCK); -- cgit v1.2.3 From 9f2deded624e1a934b52ac0d4d33e3612a7fb469 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 12 Sep 2020 19:34:26 +0200 Subject: Fix VISCA dialect --- backends/visca.c | 88 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 17 deletions(-) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index 619211d..d408b80 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -7,6 +7,7 @@ /* TODO * VISCA server + * Rate limiting */ MM_PLUGIN_API int init(){ @@ -128,8 +129,8 @@ static channel* ptz_channel(instance* inst, char* spec, uint8_t flags){ } //store the memory to be called above the command type - if(ident == call){ - ident |= (strtoul(spec + strlen(ptz_channels[call].name), NULL, 10) << 8); + if(ident == call || ident == store){ + ident |= (strtoul(spec + strlen(ptz_channels[ident].name), NULL, 10) << 8); } return mm_channel(inst, ident, 1); @@ -137,20 +138,27 @@ static channel* ptz_channel(instance* inst, char* spec, uint8_t flags){ static size_t ptz_set_pantilt(instance* inst, channel* c, channel_value* v, uint8_t* msg){ ptz_instance_data* data = (ptz_instance_data*) inst->impl; - uint32_t* x = (uint32_t*) (msg + 6); - uint32_t* y = (uint32_t*) (msg + 10); - + if(c->ident == pan){ - data->x = ((ptz_channels[pan].max - ptz_channels[pan].min) * v->normalised) + ptz_channels[pan].min; + data->x = ((ptz_channels[pan].max - ptz_channels[pan].min) * v->normalised) + ptz_channels[pan].min - ptz_channels[pan].offset; } else{ - data->y = ((ptz_channels[tilt].max - ptz_channels[tilt].min) * v->normalised) + ptz_channels[tilt].min; + data->y = ((ptz_channels[tilt].max - ptz_channels[tilt].min) * v->normalised) + ptz_channels[tilt].min - ptz_channels[tilt].offset; } msg[4] = data->panspeed; msg[5] = data->tiltspeed; - *x = htobe32(data->x); - *y = htobe32(data->y); + + //either i'm doing this wrong or visca is just weird. + msg[6] = ((data->x & 0xF000) >> 12); + msg[7] = ((data->x & 0x0F00) >> 8); + msg[8] = ((data->x & 0xF0) >> 4); + msg[9] = (data->x & 0x0F); + + msg[10] = ((data->y & 0xF000) >> 12); + msg[11] = ((data->y & 0x0F00) >> 8); + msg[12] = ((data->y & 0xF0) >> 4); + msg[13] = (data->y & 0x0F); return ptz_channels[pan].bytes; } @@ -158,24 +166,30 @@ static size_t ptz_set_pantilt(instance* inst, channel* c, channel_value* v, uint static size_t ptz_set_ptspeed(instance* inst, channel* c, channel_value* v, uint8_t* msg){ ptz_instance_data* data = (ptz_instance_data*) inst->impl; if(c->ident == panspeed){ - data->panspeed = ((ptz_channels[panspeed].max - ptz_channels[panspeed].min) * v->normalised) + ptz_channels[panspeed].min; + data->panspeed = ((ptz_channels[panspeed].max - ptz_channels[panspeed].min) * v->normalised) + ptz_channels[panspeed].min - ptz_channels[panspeed].offset; } else{ - data->tiltspeed = ((ptz_channels[tiltspeed].max - ptz_channels[tiltspeed].min) * v->normalised) + ptz_channels[tiltspeed].min; + data->tiltspeed = ((ptz_channels[tiltspeed].max - ptz_channels[tiltspeed].min) * v->normalised) + ptz_channels[tiltspeed].min - ptz_channels[tiltspeed].offset; } return 0; } static size_t ptz_set_zoom(instance* inst, channel* c, channel_value* v, uint8_t* msg){ - uint32_t* position = (uint32_t*) (msg + 4); - *position = htobe32(((ptz_channels[zoom].max - ptz_channels[zoom].min) * v->normalised) + ptz_channels[zoom].min); + uint16_t position = ((ptz_channels[zoom].max - ptz_channels[zoom].min) * v->normalised) + ptz_channels[zoom].min - ptz_channels[zoom].offset; + msg[4] = ((position & 0xF000) >> 12); + msg[5] = ((position & 0x0F00) >> 8); + msg[6] = ((position & 0xF0) >> 4); + msg[7] = (position & 0x0F); return ptz_channels[zoom].bytes; } static size_t ptz_set_focus(instance* inst, channel* c, channel_value* v, uint8_t* msg){ - uint32_t* position = (uint32_t*) (msg + 4); - *position = htobe32(((ptz_channels[focus].max - ptz_channels[focus].min) * v->normalised) + ptz_channels[focus].min); + uint16_t position = ((ptz_channels[focus].max - ptz_channels[focus].min) * v->normalised) + ptz_channels[focus].min - ptz_channels[focus].offset; + msg[4] = ((position & 0xF000) >> 12); + msg[5] = ((position & 0x0F00) >> 8); + msg[6] = ((position & 0xF0) >> 4); + msg[7] = (position & 0x0F); return ptz_channels[focus].bytes; } @@ -188,6 +202,15 @@ static size_t ptz_set_memory(instance* inst, channel* c, channel_value* v, uint8 return ptz_channels[call].bytes; } +static size_t ptz_set_memory_store(instance* inst, channel* c, channel_value* v, uint8_t* msg){ + if(v->normalised < 0.9){ + return 0; + } + + msg[5] = (c->ident >> 8); + return ptz_channels[store].bytes; +} + static int ptz_set(instance* inst, size_t num, channel** c, channel_value* v){ ptz_instance_data* data = (ptz_instance_data*) inst->impl; size_t n = 0, bytes = 0; @@ -215,12 +238,43 @@ static int ptz_set(instance* inst, size_t num, channel** c, channel_value* v){ } static int ptz_handle(size_t num, managed_fd* fds){ - //no events generated here + uint8_t recv_buf[VISCA_BUFFER_LENGTH]; + size_t u; + ssize_t bytes_read; + instance* inst = NULL; + + //read and ignore any responses for now + for(u = 0; u < num; u++){ + inst = (instance*) fds[u].impl; + bytes_read = recv(fds[u].fd, recv_buf, sizeof(recv_buf), 0); + if(bytes_read <= 0){ + LOGPF("Failed to receive on signaled fd for instance %s", inst->name); + //TODO handle failure + } + else{ + DBGPF("Ignored %" PRIsize_t " incoming bytes for instance %s", bytes_read, inst->name); + } + } + return 0; } static int ptz_start(size_t n, instance** inst){ - //no startup needed yet + size_t u, fds = 0; + ptz_instance_data* data = NULL; + + for(u = 0; u < n; u++){ + data = (ptz_instance_data*) inst[u]->impl; + if(data->fd >= 0){ + if(mm_manage_fd(data->fd, BACKEND_NAME, 1, inst[u])){ + LOGPF("Failed to register descriptor for instance %s", inst[u]->name); + return 1; + } + fds++; + } + } + + LOGPF("Registered %" PRIsize_t " descriptors to core", fds); return 0; } -- cgit v1.2.3 From d45ca2422fd5bedf48d68a3a537bae924b0cbae7 Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 18 Sep 2020 20:12:53 +0200 Subject: Implement additional VISCA channels --- backends/visca.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index d408b80..32f11c9 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -8,6 +8,7 @@ /* TODO * VISCA server * Rate limiting + * Inquiry */ MM_PLUGIN_API int init(){ @@ -175,6 +176,13 @@ static size_t ptz_set_ptspeed(instance* inst, channel* c, channel_value* v, uint return 0; } +static size_t ptz_set_stop(instance* inst, channel* c, channel_value* v, uint8_t* msg){ + ptz_instance_data* data = (ptz_instance_data*) inst->impl; + msg[4] = data->panspeed; + msg[5] = data->tiltspeed; + return ptz_channels[stop].bytes; +} + static size_t ptz_set_zoom(instance* inst, channel* c, channel_value* v, uint8_t* msg){ uint16_t position = ((ptz_channels[zoom].max - ptz_channels[zoom].min) * v->normalised) + ptz_channels[zoom].min - ptz_channels[zoom].offset; msg[4] = ((position & 0xF000) >> 12); @@ -193,6 +201,24 @@ static size_t ptz_set_focus(instance* inst, channel* c, channel_value* v, uint8_ return ptz_channels[focus].bytes; } +static size_t ptz_set_focus_mode(instance* inst, channel* c, channel_value* v, uint8_t* msg){ + msg[4] = (v->normalised > 0.9) ? 2 : 3; + return ptz_channels[focus_mode].bytes; +} + +static size_t ptz_set_wb_mode(instance* inst, channel* c, channel_value* v, uint8_t* msg){ + msg[4] = (v->normalised > 0.9) ? 0 : 5; + return ptz_channels[wb_mode].bytes; +} + +static size_t ptz_set_wb(instance* inst, channel* c, channel_value* v, uint8_t* msg){ + uint8_t command = c->ident & 0xFF; + uint8_t value = ((ptz_channels[command].max - ptz_channels[command].min) * v->normalised) + ptz_channels[command].min - ptz_channels[command].offset; + msg[6] = value >> 4; + msg[7] = value & 0x0F; + return ptz_channels[command].bytes; +} + static size_t ptz_set_memory(instance* inst, channel* c, channel_value* v, uint8_t* msg){ if(v->normalised < 0.9){ return 0; @@ -223,6 +249,8 @@ static int ptz_set(instance* inst, size_t num, channel** c, channel_value* v){ if(ptz_channels[command].bytes){ memcpy(tx, ptz_channels[command].pattern, ptz_channels[command].bytes); + //if no handler function set, assume a parameterless command and send verbatim + bytes = ptz_channels[command].bytes; } tx[0] = 0x80 | (data->cam_address & 0xF); -- cgit v1.2.3 From 7d3bde27e412ebb6783c2c94f52a08f0e8d8ba49 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 19 Sep 2020 01:09:43 +0200 Subject: Fix VISCA channel parser --- backends/visca.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index 32f11c9..8465c14 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -110,8 +110,7 @@ static int ptz_instance(instance* inst){ } static channel* ptz_channel(instance* inst, char* spec, uint8_t flags){ - uint64_t ident = pan; - size_t command = 0; + uint64_t command = 0; if(flags & mmchannel_input){ LOG("This backend currently only supports output channels"); @@ -120,21 +119,23 @@ static channel* ptz_channel(instance* inst, char* spec, uint8_t flags){ for(command = 0; command < sentinel; command++){ if(!strncmp(spec, ptz_channels[command].name, strlen(ptz_channels[command].name))){ - ident = command; + break; } } - if(ident == sentinel){ + DBGPF("Matched spec %s as %s", spec, ptz_channels[command].name ? ptz_channels[command].name : "sentinel"); + + if(command == sentinel){ LOGPF("Unknown channel spec %s", spec); return NULL; } //store the memory to be called above the command type - if(ident == call || ident == store){ - ident |= (strtoul(spec + strlen(ptz_channels[ident].name), NULL, 10) << 8); + if(command == call || command == store){ + command |= (strtoul(spec + strlen(ptz_channels[command].name), NULL, 10) << 8); } - return mm_channel(inst, ident, 1); + return mm_channel(inst, command, 1); } static size_t ptz_set_pantilt(instance* inst, channel* c, channel_value* v, uint8_t* msg){ -- cgit v1.2.3 From 588503226ff61c2a440f19e050691af93cef0f5f Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 9 Oct 2020 23:17:41 +0200 Subject: Implement VISCA relative movement --- backends/visca.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index 8465c14..2e82515 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -2,6 +2,7 @@ #define DEBUG #include +#include #include "visca.h" #include "libmmbackend.h" @@ -87,6 +88,10 @@ static int ptz_configure_instance(instance* inst, char* option, char* value){ return 0; #endif } + else if(!strcmp(option, "deadzone")){ + data->deadzone = strtod(value, NULL); + return 0; + } LOGPF("Unknown instance configuration parameter %s for instance %s", option, inst->name); return 1; @@ -104,6 +109,8 @@ static int ptz_instance(instance* inst){ //start with maximum speeds data->panspeed = ptz_channels[panspeed].max; data->tiltspeed = ptz_channels[tiltspeed].max; + //start with a bit of slack/deadzone in relative movement axes + data->deadzone = 0.1; inst->impl = data; return 0; @@ -135,6 +142,32 @@ static channel* ptz_channel(instance* inst, char* spec, uint8_t flags){ command |= (strtoul(spec + strlen(ptz_channels[command].name), NULL, 10) << 8); } + //store relative move direction + else if(command == relmove){ + if(!strcmp(spec + strlen(ptz_channels[relmove].name), ".up") + || !strcmp(spec + strlen(ptz_channels[relmove].name), ".y")){ + command |= (rel_up << 8); + } + else if(!strcmp(spec + strlen(ptz_channels[relmove].name), ".left") + || !strcmp(spec + strlen(ptz_channels[relmove].name), ".x")){ + command |= (rel_left << 8); + } + + if(!strcmp(spec + strlen(ptz_channels[relmove].name), ".down") + || !strcmp(spec + strlen(ptz_channels[relmove].name), ".y")){ + command |= (rel_down << 8); + } + else if(!strcmp(spec + strlen(ptz_channels[relmove].name), ".right") + || !strcmp(spec + strlen(ptz_channels[relmove].name), ".x")){ + command |= (rel_right << 8); + } + + if(command >> 8 == 0){ + LOGPF("Could not parse relative movement command %s", spec); + return NULL; + } + } + return mm_channel(inst, command, 1); } @@ -177,11 +210,53 @@ static size_t ptz_set_ptspeed(instance* inst, channel* c, channel_value* v, uint return 0; } -static size_t ptz_set_stop(instance* inst, channel* c, channel_value* v, uint8_t* msg){ +static size_t ptz_set_relmove(instance* inst, channel* c, channel_value* v, uint8_t* msg){ ptz_instance_data* data = (ptz_instance_data*) inst->impl; + + uint8_t direction = c->ident >> 8; + double speed_factor = v->normalised; + + if(direction == rel_x + || direction == rel_y){ + //select only one move event + direction &= (speed_factor > 0.5) ? (rel_up | rel_left) : (rel_down | rel_right); + + //scale event value to full axis + speed_factor = fabs((speed_factor - 0.5) * 2); + + //clamp to deadzone + speed_factor = (speed_factor < 2 * data->deadzone) ? 0 : speed_factor; + } + + //clear modified axis + if(direction & rel_x){ + data->relative_movement &= ~rel_x; + } + else{ + data->relative_movement &= ~rel_y; + } + + if(speed_factor){ + data->relative_movement |= direction; + } + + //set stored axis speed msg[4] = data->panspeed; msg[5] = data->tiltspeed; - return ptz_channels[stop].bytes; + + //update motor control from movement data + msg[6] |= (data->relative_movement & (rel_left | rel_right)) >> 2; + msg[7] |= data->relative_movement & (rel_up | rel_down); + + //stop motors if unset + msg[6] = msg[6] ? msg[6] : 3; + msg[7] = msg[7] ? msg[7] : 3; + + DBGPF("Moving axis %d with factor %f, total movement now %02X, commanding %d / %d, %d / %d", + direction, speed_factor, data->relative_movement, + msg[6], msg[4], msg[7], msg[5]); + + return ptz_channels[relmove].bytes; } static size_t ptz_set_zoom(instance* inst, channel* c, channel_value* v, uint8_t* msg){ -- cgit v1.2.3 From 70f1ca5bbb3156d9d2b90a115ba88495e5151b55 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 10 Oct 2020 17:30:15 +0200 Subject: Update some comments --- backends/visca.c | 1 + 1 file changed, 1 insertion(+) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index 2e82515..9f398a2 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -241,6 +241,7 @@ static size_t ptz_set_relmove(instance* inst, channel* c, channel_value* v, uint } //set stored axis speed + //TODO find a way to do relative axis speed via speed_factor, without overwriting a set absolute speed msg[4] = data->panspeed; msg[5] = data->tiltspeed; -- cgit v1.2.3 From bb122923379f3439c62241652b82ed403ad835f7 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 24 Oct 2020 12:31:27 +0200 Subject: Add generic lua pkg-config data --- backends/visca.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index 9f398a2..ba81f8d 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -8,8 +8,9 @@ /* TODO * VISCA server - * Rate limiting + * Command output rate limiting / deduplication * Inquiry + * Reconnect on connection close */ MM_PLUGIN_API int init(){ -- cgit v1.2.3 From db76143dfff9aa69318273010fe2922c1e60ea4c Mon Sep 17 00:00:00 2001 From: cbdev Date: Sun, 16 May 2021 17:09:23 +0200 Subject: Fix VISCA serial output (Fixes #91) --- backends/visca.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index ba81f8d..611d142 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -2,7 +2,10 @@ #define DEBUG #include -#include +#include +#include +#include + #include "visca.h" #include "libmmbackend.h" @@ -81,11 +84,41 @@ static int ptz_configure_instance(instance* inst, char* option, char* value){ LOG("Direct device connections are not possible on Windows"); return 1; #else - data->fd = open(value, O_NONBLOCK); + + struct termios2 device_config; + + options = strchr(value, ' '); + if(options){ + //terminate port name + *options = 0; + options++; + } + + data->fd = open(value, O_RDWR | O_NONBLOCK); if(data->fd < 0){ LOGPF("Failed to connect instance %s to device %s", inst->name, value); return 1; } + data->direct_device = 1; + + //configure baudrate + if(options){ + //get current port config + if(ioctl(data->fd, TCGETS2, &device_config)){ + LOGPF("Failed to get port configuration data for %s: %s", value, strerror(errno)); + return 0; + } + + device_config.c_cflag &= ~CBAUD; + device_config.c_cflag |= BOTHER; + device_config.c_ispeed = strtoul(options, NULL, 10); + device_config.c_ospeed = strtoul(options, NULL, 10); + + //set updated config + if(ioctl(data->fd, TCSETS2, &device_config)){ + LOGPF("Failed to set port configuration data for %s: %s", value, strerror(errno)); + } + } return 0; #endif } @@ -315,6 +348,21 @@ static size_t ptz_set_memory_store(instance* inst, channel* c, channel_value* v, return ptz_channels[store].bytes; } +static int ptz_write_serial(int fd, uint8_t* data, size_t bytes){ + ssize_t total = 0, sent; + + while(total < bytes){ + sent = write(fd, data + total, bytes - total); + if(sent < 0){ + LOGPF("Failed to write to serial port: %s", strerror(errno)); + return 1; + } + total += sent; + } + + return 0; +} + static int ptz_set(instance* inst, size_t num, channel** c, channel_value* v){ ptz_instance_data* data = (ptz_instance_data*) inst->impl; size_t n = 0, bytes = 0; @@ -336,7 +384,10 @@ static int ptz_set(instance* inst, size_t num, channel** c, channel_value* v){ bytes = ptz_channels[command].set(inst, c[n], v + n, tx); } - if(bytes && mmbackend_send(data->fd, tx, bytes)){ + if(data->direct_device && bytes && ptz_write_serial(data->fd, tx, bytes)){ + LOGPF("Failed to write %s command on instance %s", ptz_channels[command].name, inst->name); + } + else if(!data->direct_device && bytes && mmbackend_send(data->fd, tx, bytes)){ LOGPF("Failed to push %s command on instance %s", ptz_channels[command].name, inst->name); } } -- cgit v1.2.3 From e28bfea439d09bd0e9fabc865c83ce73e6b16b37 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 26 Jun 2021 19:15:53 +0200 Subject: Fix VISCA windows build includes --- backends/visca.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index 611d142..a43d74f 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -3,8 +3,11 @@ #include #include -#include -#include + +#ifndef _WIN32 + #include + #include +#endif #include "visca.h" #include "libmmbackend.h" -- cgit v1.2.3 From 9be900acd86e03c73266c552db133562005f5607 Mon Sep 17 00:00:00 2001 From: cbdev Date: Mon, 28 Jun 2021 21:46:55 +0200 Subject: Enhance visca relative movement --- backends/visca.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) (limited to 'backends/visca.c') diff --git a/backends/visca.c b/backends/visca.c index a43d74f..a36b139 100644 --- a/backends/visca.c +++ b/backends/visca.c @@ -1,5 +1,5 @@ #define BACKEND_NAME "visca" -#define DEBUG +//#define DEBUG #include #include @@ -17,6 +17,8 @@ * Command output rate limiting / deduplication * Inquiry * Reconnect on connection close + * Speed updates should send motor outputs + * */ MM_PLUGIN_API int init(){ @@ -144,8 +146,8 @@ static int ptz_instance(instance* inst){ data->fd = -1; data->cam_address = 1; //start with maximum speeds - data->panspeed = ptz_channels[panspeed].max; - data->tiltspeed = ptz_channels[tiltspeed].max; + data->max_pan = ptz_channels[panspeed].max; + data->max_tilt = ptz_channels[tiltspeed].max; //start with a bit of slack/deadzone in relative movement axes data->deadzone = 0.1; @@ -218,8 +220,9 @@ static size_t ptz_set_pantilt(instance* inst, channel* c, channel_value* v, uint data->y = ((ptz_channels[tilt].max - ptz_channels[tilt].min) * v->normalised) + ptz_channels[tilt].min - ptz_channels[tilt].offset; } - msg[4] = data->panspeed; - msg[5] = data->tiltspeed; + //absolute movements happen with maximum speed + msg[4] = data->max_pan; + msg[5] = data->max_tilt; //either i'm doing this wrong or visca is just weird. msg[6] = ((data->x & 0xF000) >> 12); @@ -238,10 +241,10 @@ static size_t ptz_set_pantilt(instance* inst, channel* c, channel_value* v, uint static size_t ptz_set_ptspeed(instance* inst, channel* c, channel_value* v, uint8_t* msg){ ptz_instance_data* data = (ptz_instance_data*) inst->impl; if(c->ident == panspeed){ - data->panspeed = ((ptz_channels[panspeed].max - ptz_channels[panspeed].min) * v->normalised) + ptz_channels[panspeed].min - ptz_channels[panspeed].offset; + data->max_pan = ((ptz_channels[panspeed].max - ptz_channels[panspeed].min) * v->normalised) + ptz_channels[panspeed].min - ptz_channels[panspeed].offset; } else{ - data->tiltspeed = ((ptz_channels[tiltspeed].max - ptz_channels[tiltspeed].min) * v->normalised) + ptz_channels[tiltspeed].min - ptz_channels[tiltspeed].offset; + data->max_tilt = ((ptz_channels[tiltspeed].max - ptz_channels[tiltspeed].min) * v->normalised) + ptz_channels[tiltspeed].min - ptz_channels[tiltspeed].offset; } return 0; @@ -250,7 +253,7 @@ static size_t ptz_set_ptspeed(instance* inst, channel* c, channel_value* v, uint static size_t ptz_set_relmove(instance* inst, channel* c, channel_value* v, uint8_t* msg){ ptz_instance_data* data = (ptz_instance_data*) inst->impl; - uint8_t direction = c->ident >> 8; + uint8_t direction = c->ident >> 8, movement = data->relative_movement; double speed_factor = v->normalised; if(direction == rel_x @@ -267,24 +270,31 @@ static size_t ptz_set_relmove(instance* inst, channel* c, channel_value* v, uint //clear modified axis if(direction & rel_x){ - data->relative_movement &= ~rel_x; + movement &= ~rel_x; + data->factor_tilt = speed_factor; } else{ - data->relative_movement &= ~rel_y; + movement &= ~rel_y; + data->factor_pan = speed_factor; } if(speed_factor){ - data->relative_movement |= direction; + movement |= direction; } + //only transmit if something actually changed + if(!movement && !data->relative_movement){ + return 0; + } + data->relative_movement = movement; + //set stored axis speed - //TODO find a way to do relative axis speed via speed_factor, without overwriting a set absolute speed - msg[4] = data->panspeed; - msg[5] = data->tiltspeed; + msg[4] = data->max_pan * data->factor_pan; + msg[5] = data->max_tilt * data->factor_tilt; //update motor control from movement data - msg[6] |= (data->relative_movement & (rel_left | rel_right)) >> 2; - msg[7] |= data->relative_movement & (rel_up | rel_down); + msg[6] |= (movement & (rel_left | rel_right)) >> 2; + msg[7] |= movement & (rel_up | rel_down); //stop motors if unset msg[6] = msg[6] ? msg[6] : 3; -- cgit v1.2.3