system: add sensor streaming tool based on nxscope

This tool allows you to send sensor data via the nxscope interface.
Useful when we test sensors or when we just need a tool to visualize
data from sensors.

Works only with the new sensor framework.
This commit is contained in:
raiden00pl 2024-04-27 10:05:11 +02:00 committed by Xiang Xiao
parent e3110d3ea7
commit 72635a88ca
5 changed files with 715 additions and 0 deletions

View File

@ -0,0 +1,31 @@
# ##############################################################################
# apps/system/sensorscope/CMakeLists.txt
#
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
# license agreements. See the NOTICE file distributed with this work for
# additional information regarding copyright ownership. The ASF licenses this
# file to you under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# ##############################################################################
if(CONFIG_SYSTEM_SENSORSCOPE)
nuttx_add_application(
NAME
${CONFIG_SYSTEM_SENSORSCOPE_PROGNAME}
SRCS
sensorscope_main.c
STACKSIZE
${CONFIG_SYSTEM_SENSORSCOPE_STACKSIZE}
PRIORITY
${CONFIG_SYSTEM_SENSORSCOPE_PRIORITY})
endif()

View File

@ -0,0 +1,72 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
menuconfig SYSTEM_SENSORSCOPE
bool "NxScope sensors stream app"
default n
depends on LOGGING_NXSCOPE
select LOGGING_NXSCOPE_INTF_SERIAL
select LOGGING_NXSCOPE_PROTO_SER
if SYSTEM_SENSORSCOPE
config SYSTEM_SENSORSCOPE_PROGNAME
string "Program name"
default "sensorscope"
config SYSTEM_SENSORSCOPE_PRIORITY
int "nxscope task priority"
default 100
config SYSTEM_SENSORSCOPE_STACKSIZE
int "nxscope stack size"
default DEFAULT_TASK_STACKSIZE
config SYSTEM_SENSORSCOPE_SERIAL_PATH
string "nxscope serial path"
default "/dev/ttyUSB0"
config SYSTEM_SENSORSCOPE_SERIAL_BAUD
int "nxscope serial baud"
default 115200
---help---
Ignored if set to 0 (for example for RTT interface)
config SYSTEM_SENSORSCOPE_CDCACM
bool "nxscope CDCACM device support"
depends on CDCACM
default n
config SYSTEM_SENSORSCOPE_FORCE_ENABLE
bool "nxscope force enable"
default n
config SYSTEM_SENSORSCOPE_STREAMBUF_LEN
int "nxscope stream buffer length"
default 512
config SYSTEM_SENSORSCOPE_RXBUF_LEN
int "nxscope RX buffer length"
default 32
config SYSTEM_SENSORSCOPE_RX_PADDING
int "nxscope RX padding"
default 0
config SYSTEM_SENSORSCOPE_MAIN_INTERVAL
int "nxscope main interval (microseconds)"
default 100000
---help---
This value is responsible for the frequency at which stream
frames will be sent and incoming frames will be received.
config SYSTEM_SENSORSCOPE_FETCH_INTERVAL
int "nxscope sensor fetch interval (microseconds)"
default 1000
---help---
This value is responsible for the frequency at which the sensors
are read
endif # SYSTEM_SENSORSCOPE

View File

@ -0,0 +1,23 @@
############################################################################
# apps/systen/sensorscope/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
ifneq ($(CONFIG_SYSTEM_SENSORSCOPE),)
CONFIGURED_APPS += $(APPDIR)/system/sensorscope
endif

View File

@ -0,0 +1,34 @@
############################################################################
# apps/system/sensorscope/Makefile
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
include $(APPDIR)/Make.defs
# sensorscope built-in application info
PROGNAME = $(CONFIG_SYSTEM_SENSORSCOPE_PROGNAME)
PRIORITY = $(CONFIG_SYSTEM_SENSORSCOPE_PRIORITY)
STACKSIZE = $(CONFIG_SYSTEM_SENSORSCOPE_STACKSIZE)
MODULE = $(CONFIG_SYSTEM_SENSORSCOPE)
MAINSRC = sensorscope_main.c
CSRCS =
include $(APPDIR)/Application.mk

View File

@ -0,0 +1,555 @@
/****************************************************************************
* apps/system/sensorscope/sensorscope_main.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/boardctl.h>
#include <sys/param.h>
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <libgen.h>
#include <nuttx/sensors/sensor.h>
#include "logging/nxscope/nxscope.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define SENSOR_PATH "/dev/uorb/"
#define SENSOR_PATH_MAX 62
#define SENSOR_CHNAME_MAX 16
/****************************************************************************
* Private Type Definition
****************************************************************************/
struct nxsensor_info_s
{
FAR const char *name;
size_t data_size; /* Sensor read data size */
size_t data_offset; /* Read data offset (no timestamp) */
size_t dim; /* Data vector dimenstion */
int dtype; /* Data vector type */
};
struct listen_object_s
{
/* Node of object info list */
struct list_node node;
/* Sensor data */
int fd;
FAR uint8_t *data;
/* Sensor info */
FAR struct nxsensor_info_s *info;
/* nxscope channel data */
int chanid;
char chname[SENSOR_CHNAME_MAX];
};
struct nxscope_thr_env_s
{
struct nxscope_s nxs;
struct list_node objlist;
};
/****************************************************************************
* Private Data
****************************************************************************/
/* This array defines supported sensors. */
struct nxsensor_info_s g_nxsensor[] =
{
{"accel", sizeof(struct sensor_accel), 0, 3, NXSCOPE_TYPE_FLOAT},
{"mag", sizeof(struct sensor_mag), 0, 3, NXSCOPE_TYPE_FLOAT},
{"gyro", sizeof(struct sensor_gyro), 0, 3, NXSCOPE_TYPE_FLOAT},
{"light", sizeof(struct sensor_light), 0, 2, NXSCOPE_TYPE_FLOAT},
{"baro", sizeof(struct sensor_baro), 0, 2, NXSCOPE_TYPE_FLOAT},
{"prox", sizeof(struct sensor_prox), 0, 1, NXSCOPE_TYPE_FLOAT},
{"humi", sizeof(struct sensor_humi), 0, 1, NXSCOPE_TYPE_FLOAT},
{"temp", sizeof(struct sensor_temp), 0, 1, NXSCOPE_TYPE_FLOAT},
{"rgb", sizeof(struct sensor_rgb), 0, 3, NXSCOPE_TYPE_FLOAT},
{"hall", sizeof(struct sensor_hall), 0, 1, NXSCOPE_TYPE_INT32},
{"ir", sizeof(struct sensor_ir), 0, 1, NXSCOPE_TYPE_FLOAT},
{"gas", sizeof(struct sensor_gas), 0, 1, NXSCOPE_TYPE_FLOAT},
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nxscope_samples_thr
****************************************************************************/
static FAR void *nxscope_samples_thr(FAR void *arg)
{
FAR struct nxscope_thr_env_s *envp = arg;
FAR struct listen_object_s *tmp;
FAR float *data;
int ret;
size_t offset;
DEBUGASSERT(envp);
printf("nxscope_samples_thr\n");
while (1)
{
list_for_every_entry(&envp->objlist, tmp, struct listen_object_s, node)
{
/* Read data from sensor */
ret = read(tmp->fd, tmp->data, tmp->info->data_size);
if (ret < 0)
{
printf("ERROR: read failed %d\n", -errno);
}
else
{
/* Get vector from a given offset */
offset = tmp->info->data_offset + sizeof(uint64_t);
data = (float *)&tmp->data[offset];
nxscope_put_vfloat(&envp->nxs, tmp->chanid, data,
tmp->info->dim);
}
}
usleep(CONFIG_SYSTEM_SENSORSCOPE_FETCH_INTERVAL);
}
return NULL;
}
#ifdef CONFIG_SYSTEM_SENSORSCOPE_CDCACM
/****************************************************************************
* Name: nxscope_cdcacm_init
****************************************************************************/
static int nxscope_cdcacm_init(void)
{
struct boardioc_usbdev_ctrl_s ctrl;
FAR void *handle;
int ret;
ctrl.usbdev = BOARDIOC_USBDEV_CDCACM;
ctrl.action = BOARDIOC_USBDEV_CONNECT;
ctrl.instance = 0;
ctrl.handle = &handle;
ret = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl);
if (ret < 0)
{
printf("ERROR: BOARDIOC_USBDEV_CONTROL failed %d\n", ret);
goto errout;
}
errout:
return ret;
}
#endif
/****************************************************************************
* Name: listener_add_object
****************************************************************************/
static FAR struct listen_object_s *
listener_add_object(FAR struct list_node *objlist,
FAR struct nxsensor_info_s *info,
int fd, int chanid, FAR char *chname)
{
FAR struct listen_object_s *tmp;
tmp = malloc(sizeof(struct listen_object_s));
if (tmp == NULL)
{
return NULL;
}
/* Initialize object */
strncpy(tmp->chname, chname, SENSOR_CHNAME_MAX);
tmp->info = info;
tmp->chanid = chanid;
tmp->fd = fd;
/* Allocate space for data */
tmp->data = malloc(info->data_size);
/* Add node to list */
list_add_tail(objlist, &tmp->node);
return tmp;
}
/****************************************************************************
* Name: listener_delete_object_list
****************************************************************************/
static void listener_delete_object_list(FAR struct list_node *objlist)
{
FAR struct listen_object_s *tmp;
FAR struct listen_object_s *next;
list_for_every_entry_safe(objlist, tmp, next, struct listen_object_s, node)
{
free(tmp->data);
list_delete(&tmp->node);
free(tmp);
}
list_initialize(objlist);
}
/****************************************************************************
* Name: sensorscope_chinfo
****************************************************************************/
static int sensorscope_chinfo(FAR char *path,
FAR struct nxsensor_info_s **info)
{
int i;
for (i = 0; i < nitems(g_nxsensor); i++)
{
if (strstr(path, g_nxsensor[i].name) != NULL)
{
*info = &g_nxsensor[i];
return OK;
}
}
return -EINVAL;
}
/****************************************************************************
* Name: nxscope_channels_num
****************************************************************************/
static int nxscope_channels_num(void)
{
FAR struct dirent *entry;
FAR DIR *dir;
int i = 0;
dir = opendir(SENSOR_PATH);
if (!dir)
{
return 0;
}
while ((entry = readdir(dir)) != NULL)
{
if (entry->d_type == DT_CHR)
{
i++;
}
}
closedir(dir);
return i;
}
/****************************************************************************
* Name: nxscope_channels
****************************************************************************/
static int nxscope_channels(FAR struct nxscope_thr_env_s *envp)
{
union nxscope_chinfo_type_u u;
FAR struct nxsensor_info_s *info;
FAR struct dirent *entry;
FAR struct listen_object_s *obj;
FAR DIR *dir;
int chanid;
int ret;
int fd;
char path[SENSOR_PATH_MAX];
/* Initialize objects list */
list_initialize(&envp->objlist);
/* Open sensors direcotry */
dir = opendir(SENSOR_PATH);
if (!dir)
{
return 0;
}
/* Get available sensors */
chanid = 0;
while ((entry = readdir(dir)) != NULL)
{
if (entry->d_type != DT_CHR)
{
continue;
}
/* Get sensor info */
ret = sensorscope_chinfo(entry->d_name, &info);
if (ret != OK)
{
printf("ERROR: not supported sensor %s\n", entry->d_name);
}
else
{
snprintf(path, SENSOR_PATH_MAX, SENSOR_PATH"%s", entry->d_name);
fd = open(path, O_CLOEXEC | O_RDWR | O_NONBLOCK);
if (fd < 0)
{
printf("ERROR: failed to open %s %d\n", entry->d_name, -errno);
}
else
{
/* Add new object */
obj = listener_add_object(&envp->objlist,
info, fd, chanid,
basename(entry->d_name));
if (obj == NULL)
{
printf("ERROR: failed to add listener object %d\n",
chanid);
return -ENOMEM;
}
/* Register nxscope channel */
u.s.dtype = NXSCOPE_TYPE_FLOAT;
u.s._res = 0;
u.s.cri = 0;
nxscope_chan_init(&envp->nxs, obj->chanid, obj->chname,
u.u8, obj->info->dim, 0);
/* Next channel */
chanid++;
}
}
}
return OK;
}
/****************************************************************************
* Name: nxscope_cb_start
****************************************************************************/
static int nxscope_cb_start(FAR void *priv, bool start)
{
UNUSED(priv);
printf("--> nxscope_cb_start: start=%d\n", start);
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxscope_main
****************************************************************************/
int main(int argc, FAR char *argv[])
{
struct nxscope_thr_env_s env;
struct nxscope_cfg_s nxs_cfg;
struct nxscope_intf_s intf;
struct nxscope_proto_s proto;
struct nxscope_ser_cfg_s nxs_ser_cfg;
struct nxscope_callbacks_s cbs;
pthread_t thread;
int ret;
#ifndef CONFIG_NSH_ARCHINIT
/* Perform architecture-specific initialization (if configured) */
boardctl(BOARDIOC_INIT, 0);
# ifdef CONFIG_BOARDCTL_FINALINIT
/* Perform architecture-specific final-initialization (if configured) */
boardctl(BOARDIOC_FINALINIT, 0);
# endif
#endif
#ifdef CONFIG_SYSTEM_SENSORSCOPE_CDCACM
/* Initialize the USB CDCACM device */
ret = nxscope_cdcacm_init();
if (ret < 0)
{
printf("ERROR: nxscope_cdcacm_init failed %d\n", ret);
goto errout_noproto;
}
#endif
/* Default serial protocol */
ret = nxscope_proto_ser_init(&proto, NULL);
if (ret < 0)
{
printf("ERROR: nxscope_proto_ser_init failed %d\n", ret);
goto errout_noproto;
}
/* Configuration */
nxs_ser_cfg.path = CONFIG_SYSTEM_SENSORSCOPE_SERIAL_PATH;
nxs_ser_cfg.nonblock = true;
nxs_ser_cfg.baud = CONFIG_SYSTEM_SENSORSCOPE_SERIAL_BAUD;
/* Initialize serial interface */
ret = nxscope_ser_init(&intf, &nxs_ser_cfg);
if (ret < 0)
{
printf("ERROR: nxscope_ser_init failed %d\n", ret);
goto errout_nointf;
}
/* Connect callbacks */
cbs.start_priv = NULL;
cbs.start = nxscope_cb_start;
/* Initialize nxscope */
nxs_cfg.intf_cmd = &intf;
nxs_cfg.intf_stream = &intf;
nxs_cfg.proto_cmd = &proto;
nxs_cfg.proto_stream = &proto;
nxs_cfg.callbacks = &cbs;
nxs_cfg.channels = nxscope_channels_num();
nxs_cfg.streambuf_len = CONFIG_SYSTEM_SENSORSCOPE_STREAMBUF_LEN;
nxs_cfg.rxbuf_len = CONFIG_SYSTEM_SENSORSCOPE_RXBUF_LEN;
nxs_cfg.rx_padding = CONFIG_SYSTEM_SENSORSCOPE_RX_PADDING;
ret = nxscope_init(&env.nxs, &nxs_cfg);
if (ret < 0)
{
printf("ERROR: nxscope_init failed %d\n", ret);
goto errout_nonxscope;
}
/* Create channels */
ret = nxscope_channels(&env);
if (ret != OK)
{
printf("ERROR: nxscope_channels failed %d\n", ret);
goto errout;
}
/* Create samples thread */
ret = pthread_create(&thread, NULL, nxscope_samples_thr, &env);
if (ret != OK)
{
printf("ERROR: pthread_create failed %d\n", ret);
goto errout;
}
#ifdef CONFIG_SYSTEM_SENSORSCOPE_FORCE_ENABLE
/* Enable channels and enable stream */
nxscope_chan_all_en(&nxs, true);
nxscope_stream_start(&nxs, true);
#endif
/* Main loop */
while (1)
{
/* Flush stream data */
ret = nxscope_stream(&env.nxs);
if (ret < 0)
{
printf("ERROR: nxscope_stream failed %d\n", ret);
}
/* Handle recv data */
ret = nxscope_recv(&env.nxs);
if (ret < 0)
{
printf("ERROR: nxscope_recv failed %d\n", ret);
}
usleep(CONFIG_SYSTEM_SENSORSCOPE_MAIN_INTERVAL);
}
errout:
/* Delete objects */
listener_delete_object_list(&env.objlist);
/* Deinit nxscope */
nxscope_deinit(&env.nxs);
errout_nonxscope:
/* Deinit interface */
nxscope_ser_deinit(&intf);
errout_nointf:
/* Deinit protocol */
nxscope_proto_ser_deinit(&proto);
errout_noproto:
return 0;
}