From 176c5a8c9d9634ff968c4d2d57c6a3fa1d1ea4f1 Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Wed, 12 Aug 2026 11:17:34 +0200 Subject: [PATCH 1/2] dawn: add CO2/TVOC sensor IO classes with IMDS support New IO_CLASS_SENSOR_CO2/TVOC over uORB sensor_co2/sensor_tvoc, with producer counterparts in the aligned sensor/producer ranges like every other scalar sensor, and IMDS bindings with the standard CO2/VOC Concentration characteristics (0x2B8C ppm, 0x2BE7 ppb). Claude-Session: https://claude.ai/code/session_01DCoYWj9PvV4S9KHFBbS12q --- Documentation/components/io/sensor.rst | 4 ++ .../components/proto/nimble/imds.rst | 2 + .../components/proto/nimble/index.rst | 2 + dawn/include/dawn/io/common.hxx | 56 ++++++++++--------- dawn/include/dawn/io/sensor.hxx | 14 +++++ dawn/include/dawn/io/sensor_producer.hxx | 14 +++++ dawn/include/dawn/porting/nuttx/sensors.hxx | 16 +++++- dawn/include/dawn/proto/nimble/iprph.hxx | 28 ++++++++++ dawn/include/dawn/proto/nimble/prph_imds.hxx | 2 + dawn/src/io/capabilities.cxx | 4 ++ dawn/src/io/factory.cxx | 4 ++ dawn/src/io/sensor_producer.cxx | 2 + dawn/src/proto/nimble/prph_imds.cxx | 18 ++++++ tools/dawnpy-ble | 2 +- 14 files changed, 138 insertions(+), 30 deletions(-) diff --git a/Documentation/components/io/sensor.rst b/Documentation/components/io/sensor.rst index 412d7d8d0..34f44875b 100644 --- a/Documentation/components/io/sensor.rst +++ b/Documentation/components/io/sensor.rst @@ -54,6 +54,8 @@ Dawn Type Default Dawn Unit ``ir`` ``float`` or ``b16``, lux ``uv`` ``float`` or ``b16``, UV index ``gas`` ``float`` or ``b16``, kilohms +``co2`` ``float`` or ``b16``, ppm +``tvoc`` ``float`` or ``b16``, ppm ``gnss`` ``float``, 5-vector: latitude, longitude (degrees), altitude (metres), ground speed (m/s), course (degrees) ``gnss_time`` ``uint64``, UTC time in seconds since the Unix epoch ``gnss_info`` ``float``, 5-vector: horizontal/vertical accuracy (metres), HDOP, PDOP, VDOP @@ -107,6 +109,8 @@ underlying NuttX sensor structures: "ir", "sensor_ir", "struct sensor_ir" "uv", "sensor_uv", "struct sensor_uv" "gas", "sensor_gas", "struct sensor_gas" + "co2", "sensor_co2", "struct sensor_co2" + "tvoc", "sensor_tvoc", "struct sensor_tvoc" "gnss", "sensor_gnss", "struct sensor_gnss" "gnss_time", "sensor_gnss", "struct sensor_gnss" "gnss_info", "sensor_gnss", "struct sensor_gnss" diff --git a/Documentation/components/proto/nimble/imds.rst b/Documentation/components/proto/nimble/imds.rst index f1f5c2457..da6d29397 100644 --- a/Documentation/components/proto/nimble/imds.rst +++ b/Documentation/components/proto/nimble/imds.rst @@ -52,6 +52,8 @@ Supported keys: - ``uv_index`` - ``gas_resistance`` (uses non-standard 0x272A UUID; see :ref:`nimble_units`) - ``light`` +- ``co2`` (CO2 Concentration, 0x2B8C) +- ``tvoc`` (VOC Concentration, 0x2BE7) Each measurement may be either a direct IO reference or a mapping with ``data``/``io`` and ``metadata``. Supported metadata: diff --git a/Documentation/components/proto/nimble/index.rst b/Documentation/components/proto/nimble/index.rst index 371c09f24..b3e038c36 100644 --- a/Documentation/components/proto/nimble/index.rst +++ b/Documentation/components/proto/nimble/index.rst @@ -102,6 +102,8 @@ Relative Humidity float, percent (%), scale = 1 sint16, percent (%), PM1P0 float, SI units (ug/m^3), scale = 1 not yet supported in Dawn PH float, pH unit, scale = 1 not yet supported in Dawn Gas resistance float, kilohm, scale = 1 sint16, kilohm, scale = 1 (non-standard UUID 0x272A) +CO2 float, ppm, scale = 1 uint16, ppm, scale = 1 +TVOC float, ppm, scale = 1 uint16, ppb, scale = 1000 Force float, N unit, scale = 1 not yet supported in Dawn Hall int32_t, hall state not yet supported in Dawn IR float, lux unit, scale = 1 uint24, lux, scale = 0.01 diff --git a/dawn/include/dawn/io/common.hxx b/dawn/include/dawn/io/common.hxx index 3b605fad2..12e9e4e07 100644 --- a/dawn/include/dawn/io/common.hxx +++ b/dawn/include/dawn/io/common.hxx @@ -123,30 +123,34 @@ public: IO_CLASS_SENSOR_IR = 20, ///< Infrared sensor IO_CLASS_SENSOR_UV = 21, ///< Ultraviolet sensor IO_CLASS_SENSOR_GAS = 22, ///< Gas sensor + IO_CLASS_SENSOR_CO2 = 23, ///< CO2 concentration sensor + IO_CLASS_SENSOR_TVOC = 24, ///< TVOC concentration sensor // Sensor producers - IO_CLASS_SENSOR_PRODUCER_ACCELEROMETER = 23, ///< Accelerometer publisher - IO_CLASS_SENSOR_PRODUCER_MAGNETICFIELD = 24, ///< Magnetic field publisher - IO_CLASS_SENSOR_PRODUCER_GYROSCOPE = 25, ///< Gyroscope publisher - IO_CLASS_SENSOR_PRODUCER_LIGHT = 26, ///< Light publisher - IO_CLASS_SENSOR_PRODUCER_BAROMETER = 27, ///< Barometer publisher - IO_CLASS_SENSOR_PRODUCER_PROXIMITY = 28, ///< Proximity publisher - IO_CLASS_SENSOR_PRODUCER_HUMIDITY = 29, ///< Humidity publisher - IO_CLASS_SENSOR_PRODUCER_TEMPERATURE = 30, ///< Temperature publisher - IO_CLASS_SENSOR_PRODUCER_ATEMPERATURE = 31, ///< Ambient temperature publisher - IO_CLASS_SENSOR_PRODUCER_RGB = 32, ///< RGB color publisher - IO_CLASS_SENSOR_PRODUCER_IR = 33, ///< Infrared publisher - IO_CLASS_SENSOR_PRODUCER_UV = 34, ///< Ultraviolet publisher - IO_CLASS_SENSOR_PRODUCER_GAS = 35, ///< Gas publisher + IO_CLASS_SENSOR_PRODUCER_ACCELEROMETER = 25, ///< Accelerometer publisher + IO_CLASS_SENSOR_PRODUCER_MAGNETICFIELD = 26, ///< Magnetic field publisher + IO_CLASS_SENSOR_PRODUCER_GYROSCOPE = 27, ///< Gyroscope publisher + IO_CLASS_SENSOR_PRODUCER_LIGHT = 28, ///< Light publisher + IO_CLASS_SENSOR_PRODUCER_BAROMETER = 29, ///< Barometer publisher + IO_CLASS_SENSOR_PRODUCER_PROXIMITY = 30, ///< Proximity publisher + IO_CLASS_SENSOR_PRODUCER_HUMIDITY = 31, ///< Humidity publisher + IO_CLASS_SENSOR_PRODUCER_TEMPERATURE = 32, ///< Temperature publisher + IO_CLASS_SENSOR_PRODUCER_ATEMPERATURE = 33, ///< Ambient temperature publisher + IO_CLASS_SENSOR_PRODUCER_RGB = 34, ///< RGB color publisher + IO_CLASS_SENSOR_PRODUCER_IR = 35, ///< Infrared publisher + IO_CLASS_SENSOR_PRODUCER_UV = 36, ///< Ultraviolet publisher + IO_CLASS_SENSOR_PRODUCER_GAS = 37, ///< Gas publisher + IO_CLASS_SENSOR_PRODUCER_CO2 = 38, ///< CO2 concentration publisher + IO_CLASS_SENSOR_PRODUCER_TVOC = 39, ///< TVOC concentration publisher // GNSS sensor (uORB sensor_gnss). Read-only, so it has no producer // counterpart and lives outside the aligned sensor/producer ranges above. - IO_CLASS_SENSOR_GNSS = 36, ///< GNSS position+velocity (lat/lon/alt/speed/course) - IO_CLASS_SENSOR_GNSS_TIME = 37, ///< GNSS UTC time (seconds since epoch) - IO_CLASS_SENSOR_GNSS_INFO = 38, ///< GNSS accuracy + DOP (eph/epv/hdop/pdop/vdop) - IO_CLASS_SENSOR_GNSS_SATELLITES = 39, ///< GNSS satellites used in fix + IO_CLASS_SENSOR_GNSS = 40, ///< GNSS position+velocity (lat/lon/alt/speed/course) + IO_CLASS_SENSOR_GNSS_TIME = 41, ///< GNSS UTC time (seconds since epoch) + IO_CLASS_SENSOR_GNSS_INFO = 42, ///< GNSS accuracy + DOP (eph/epv/hdop/pdop/vdop) + IO_CLASS_SENSOR_GNSS_SATELLITES = 43, ///< GNSS satellites used in fix // there is 59 sensor types in Nuttx now, most likely it grows later // we still have many free slots, but in the future, it there are no free IO slots @@ -154,16 +158,16 @@ public: // System information - IO_CLASS_SYSTEM_UPTIME = 40, ///< System uptime - IO_CLASS_SYSTEM_CPULOAD = 41, ///< CPU load - IO_CLASS_SYSTEM_RESET = 42, ///< System reset control - IO_CLASS_SYSTEM_RESETCAUSE = 43, ///< Reset cause - IO_CLASS_SYSTEM_POWEROFF = 44, ///< Power off control - IO_CLASS_SYSTEM_HOSTNAME = 45, ///< System hostname - IO_CLASS_SYSTEM_UUID = 46, ///< UUID - IO_CLASS_SYSTEM_SYSTEMTIME = 47, ///< System time + IO_CLASS_SYSTEM_UPTIME = 44, ///< System uptime + IO_CLASS_SYSTEM_CPULOAD = 45, ///< CPU load + IO_CLASS_SYSTEM_RESET = 46, ///< System reset control + IO_CLASS_SYSTEM_RESETCAUSE = 47, ///< Reset cause + IO_CLASS_SYSTEM_POWEROFF = 48, ///< Power off control + IO_CLASS_SYSTEM_HOSTNAME = 49, ///< System hostname + IO_CLASS_SYSTEM_UUID = 50, ///< UUID + IO_CLASS_SYSTEM_SYSTEMTIME = 51, ///< System time - // Slots 48-59 reserved for future system classes (serial number, + // Slots 52-59 reserved for future system classes (serial number, // firmware version, firmware update, etc.). // GPIO diff --git a/dawn/include/dawn/io/sensor.hxx b/dawn/include/dawn/io/sensor.hxx index 3d27366af..62d7f30d0 100644 --- a/dawn/include/dawn/io/sensor.hxx +++ b/dawn/include/dawn/io/sensor.hxx @@ -86,6 +86,10 @@ public: return "uv"; case IO_CLASS_SENSOR_GAS: return "gas"; + case IO_CLASS_SENSOR_CO2: + return "co2"; + case IO_CLASS_SENSOR_TVOC: + return "tvoc"; case IO_CLASS_SENSOR_GNSS: return "gnss"; case IO_CLASS_SENSOR_GNSS_TIME: @@ -212,6 +216,16 @@ public: return CIOSensor::objectId(CIOCommon::IO_CLASS_SENSOR_GAS, dtype, ts, inst); } + constexpr static SObjectId::ObjectId objectIdCo2(uint8_t dtype, bool ts, uint16_t inst) + { + return CIOSensor::objectId(CIOCommon::IO_CLASS_SENSOR_CO2, dtype, ts, inst); + } + + constexpr static SObjectId::ObjectId objectIdTvoc(uint8_t dtype, bool ts, uint16_t inst) + { + return CIOSensor::objectId(CIOCommon::IO_CLASS_SENSOR_TVOC, dtype, ts, inst); + } + constexpr static SObjectId::ObjectId objectIdGnss(uint8_t dtype, bool ts, uint16_t inst) { return CIOSensor::objectId(CIOCommon::IO_CLASS_SENSOR_GNSS, dtype, ts, inst); diff --git a/dawn/include/dawn/io/sensor_producer.hxx b/dawn/include/dawn/io/sensor_producer.hxx index e1e8530fb..c4d3f4f1f 100644 --- a/dawn/include/dawn/io/sensor_producer.hxx +++ b/dawn/include/dawn/io/sensor_producer.hxx @@ -77,6 +77,10 @@ public: return "sprod_uv"; case IO_CLASS_SENSOR_PRODUCER_GAS: return "sprod_gas"; + case IO_CLASS_SENSOR_PRODUCER_CO2: + return "sprod_co2"; + case IO_CLASS_SENSOR_PRODUCER_TVOC: + return "sprod_tvoc"; default: return "sprod"; } @@ -199,6 +203,16 @@ public: return CIOSensorProducer::objectId(CIOCommon::IO_CLASS_SENSOR_PRODUCER_GAS, dtype, ts, inst); } + constexpr static SObjectId::ObjectId objectIdCo2(uint8_t dtype, bool ts, uint16_t inst) + { + return CIOSensorProducer::objectId(CIOCommon::IO_CLASS_SENSOR_PRODUCER_CO2, dtype, ts, inst); + } + + constexpr static SObjectId::ObjectId objectIdTvoc(uint8_t dtype, bool ts, uint16_t inst) + { + return CIOSensorProducer::objectId(CIOCommon::IO_CLASS_SENSOR_PRODUCER_TVOC, dtype, ts, inst); + } + constexpr static SObjectCfg::ObjectCfgId cfgIdQueueSize(uint16_t cls) { return SObjectCfg::objectCfg(SObjectId::OBJTYPE_IO, diff --git a/dawn/include/dawn/porting/nuttx/sensors.hxx b/dawn/include/dawn/porting/nuttx/sensors.hxx index c5e25daba..b617791ac 100644 --- a/dawn/include/dawn/porting/nuttx/sensors.hxx +++ b/dawn/include/dawn/porting/nuttx/sensors.hxx @@ -104,6 +104,16 @@ public: sizeof(struct sensor_gas), 1, offsetof(struct sensor_gas, gas_resistance)}, + {CIOCommon::IO_CLASS_SENSOR_CO2, + "co2", + sizeof(struct sensor_co2), + 1, + offsetof(struct sensor_co2, co2)}, + {CIOCommon::IO_CLASS_SENSOR_TVOC, + "tvoc", + sizeof(struct sensor_tvoc), + 1, + offsetof(struct sensor_tvoc, tvoc)}, // GNSS variants (handled by CIOSensorGnss). All read the same // /dev/uorb/sensor_gnss device; the payload offset is unused because // CIOSensorGnss::getDataImpl assembles the specific fields itself. Only @@ -143,13 +153,13 @@ public: static int16_t producerToSensorClass(int16_t cls) { - static_assert(CIOCommon::IO_CLASS_SENSOR_PRODUCER_GAS - + static_assert(CIOCommon::IO_CLASS_SENSOR_PRODUCER_TVOC - CIOCommon::IO_CLASS_SENSOR_PRODUCER_ACCELEROMETER == - CIOCommon::IO_CLASS_SENSOR_GAS - CIOCommon::IO_CLASS_SENSOR_ACCELEROMETER, + CIOCommon::IO_CLASS_SENSOR_TVOC - CIOCommon::IO_CLASS_SENSOR_ACCELEROMETER, "Sensor and sensor producer class ranges must stay aligned"); if (cls < CIOCommon::IO_CLASS_SENSOR_PRODUCER_ACCELEROMETER || - cls > CIOCommon::IO_CLASS_SENSOR_PRODUCER_GAS) + cls > CIOCommon::IO_CLASS_SENSOR_PRODUCER_TVOC) { return CIOCommon::IO_CLASS_ANY; } diff --git a/dawn/include/dawn/proto/nimble/iprph.hxx b/dawn/include/dawn/proto/nimble/iprph.hxx index 9f0fea8b0..cd458e98d 100644 --- a/dawn/include/dawn/proto/nimble/iprph.hxx +++ b/dawn/include/dawn/proto/nimble/iprph.hxx @@ -345,6 +345,30 @@ public: constexpr static uint16_t UUID16_RESISTANCE = 0x272a; + /** + * @brief CO2 Concentration Characteristic UUID (0x2B8C). + * + * CO2 concentration in 1 ppm units (uint16). + */ + + constexpr static uint32_t UUID_CO2[4] = {0x2b8c0010, 0x7474754e, 0x694e2058, 0x454c426d}; + + /** @brief 16-bit UUID for CO2 Concentration characteristic. */ + + constexpr static uint16_t UUID16_CO2 = 0x2b8c; + + /** + * @brief VOC Concentration Characteristic UUID (0x2BE7). + * + * VOC concentration in 1 ppb units (uint16). + */ + + constexpr static uint32_t UUID_TVOC[4] = {0x2be70010, 0x7474754e, 0x694e2058, 0x454c426d}; + + /** @brief 16-bit UUID for VOC Concentration characteristic. */ + + constexpr static uint16_t UUID16_TVOC = 0x2be7; + /** * @brief Scaling factors for GATT characteristics. * @@ -364,6 +388,10 @@ public: {UUID16_ILLUMINANCE, 100.0f}, /** Illuminance: 0.01 lux units */ + {UUID16_CO2, 1.0f}, /** CO2 concentration: 1 ppm units (no scaling) */ + + {UUID16_TVOC, 1000.0f}, /** VOC concentration: IO ppm -> 1 ppb units */ + }; /** @brief Number of characteristic scaling entries. */ diff --git a/dawn/include/dawn/proto/nimble/prph_imds.hxx b/dawn/include/dawn/proto/nimble/prph_imds.hxx index 081467462..b28b4b85f 100644 --- a/dawn/include/dawn/proto/nimble/prph_imds.hxx +++ b/dawn/include/dawn/proto/nimble/prph_imds.hxx @@ -54,6 +54,8 @@ public: PRPH_IMDS_TYPE_GAS, ///< Industrial gas resistance sensor (non-standard ///< UUID). PRPH_IMDS_TYPE_LIGHT, ///< Industrial illuminance / light sensor. + PRPH_IMDS_TYPE_CO2, ///< Industrial CO2 concentration sensor. + PRPH_IMDS_TYPE_TVOC, ///< Industrial VOC concentration sensor. }; struct diff --git a/dawn/src/io/capabilities.cxx b/dawn/src/io/capabilities.cxx index 3bb4104bd..0a6d9e832 100644 --- a/dawn/src/io/capabilities.cxx +++ b/dawn/src/io/capabilities.cxx @@ -111,6 +111,8 @@ void CIOCapabilities::buildIoBitmap() setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_IR); setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_UV); setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_GAS); + setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_CO2); + setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_TVOC); setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_GNSS); setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_GNSS_TIME); setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_GNSS_INFO); @@ -130,6 +132,8 @@ void CIOCapabilities::buildIoBitmap() setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_PRODUCER_IR); setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_PRODUCER_UV); setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_PRODUCER_GAS); + setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_PRODUCER_CO2); + setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SENSOR_PRODUCER_TVOC); #endif #ifdef CONFIG_DAWN_IO_SYSINFO setBitmapBit(ioBitmap, CIOCommon::IO_CLASS_SYSTEM_UPTIME); diff --git a/dawn/src/io/factory.cxx b/dawn/src/io/factory.cxx index 65435918d..320029b97 100644 --- a/dawn/src/io/factory.cxx +++ b/dawn/src/io/factory.cxx @@ -197,6 +197,8 @@ CIOCommon *CIOFactory::create(CDescObject &desc) case CIOCommon::IO_CLASS_SENSOR_IR: case CIOCommon::IO_CLASS_SENSOR_UV: case CIOCommon::IO_CLASS_SENSOR_GAS: + case CIOCommon::IO_CLASS_SENSOR_CO2: + case CIOCommon::IO_CLASS_SENSOR_TVOC: return new CIOSensor(desc); case CIOCommon::IO_CLASS_SENSOR_GNSS: case CIOCommon::IO_CLASS_SENSOR_GNSS_TIME: @@ -219,6 +221,8 @@ CIOCommon *CIOFactory::create(CDescObject &desc) case CIOCommon::IO_CLASS_SENSOR_PRODUCER_IR: case CIOCommon::IO_CLASS_SENSOR_PRODUCER_UV: case CIOCommon::IO_CLASS_SENSOR_PRODUCER_GAS: + case CIOCommon::IO_CLASS_SENSOR_PRODUCER_CO2: + case CIOCommon::IO_CLASS_SENSOR_PRODUCER_TVOC: return new CIOSensorProducer(desc); #endif diff --git a/dawn/src/io/sensor_producer.cxx b/dawn/src/io/sensor_producer.cxx index f214b7c10..1f6764527 100644 --- a/dawn/src/io/sensor_producer.cxx +++ b/dawn/src/io/sensor_producer.cxx @@ -36,6 +36,8 @@ int CIOSensorProducer::configureDesc(const CDescObject &desc) case CIOCommon::IO_CLASS_SENSOR_PRODUCER_IR: case CIOCommon::IO_CLASS_SENSOR_PRODUCER_UV: case CIOCommon::IO_CLASS_SENSOR_PRODUCER_GAS: + case CIOCommon::IO_CLASS_SENSOR_PRODUCER_CO2: + case CIOCommon::IO_CLASS_SENSOR_PRODUCER_TVOC: { if (item->cfgid.s.cls != getCls()) { diff --git a/dawn/src/proto/nimble/prph_imds.cxx b/dawn/src/proto/nimble/prph_imds.cxx index d2af17ae4..62c54dc73 100644 --- a/dawn/src/proto/nimble/prph_imds.cxx +++ b/dawn/src/proto/nimble/prph_imds.cxx @@ -311,6 +311,24 @@ int CProtoNimblePrphImds::createIMDS() break; } + case PRPH_IMDS_TYPE_CO2: + { + // CO2 Concentration is uint16 in ppm (BT SIG 0x2B8C) + + chr->access_cb = CProtoNimblePrphImds::callback; + char_uuid = IProtoNimblePrphCb::UUID_CO2; + break; + } + + case PRPH_IMDS_TYPE_TVOC: + { + // VOC Concentration is uint16 in ppb (BT SIG 0x2BE7) + + chr->access_cb = CProtoNimblePrphImds::callback; + char_uuid = IProtoNimblePrphCb::UUID_TVOC; + break; + } + default: { DAWNERR("unknown IMDS type %d\n", ioTypeMap[objid]); diff --git a/tools/dawnpy-ble b/tools/dawnpy-ble index 54d2832ab..11ef188dc 160000 --- a/tools/dawnpy-ble +++ b/tools/dawnpy-ble @@ -1 +1 @@ -Subproject commit 54d2832abdf76f4f5b66a97613b7a813d53d2582 +Subproject commit 11ef188dc32eaa9c5a9ef4979a264dba4496f723 From 85e27ed3ef517d90f49a61ec6b2ee3944c28117e Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Wed, 12 Aug 2026 11:17:34 +0200 Subject: [PATCH 2/2] boards/nrf52: add Thingy:52 NimBLE demo (env sensors, air quality, battery, RGB) Add the thingy52 board (nimble_thingy52 config) and demo descriptor: HTS221 temperature/humidity + LPS22HB pressure over ESS, CCS811 eCO2 and TVOC over IMDS, battery level over BAS (raw SAADC -> sampling -> adjust -> BAS) and the lightwell RGB LED writable over a custom characteristic. RTT console; NimBLE pools and RTT buffers trimmed to fit the 64KB nRF52832. Signed-off-by: raiden00pl --- boards/arm/nrf52/thingy52/CMakeLists.txt | 7 + .../configs/nimble_thingy52/defconfig | 105 ++++++++++++ .../configs/nimble_thingy52/defconfig.dawn | 21 +++ .../examples/nimble_thingy52_demo.yaml | 155 ++++++++++++++++++ tools/config-build-all.txt | 2 + 5 files changed, 290 insertions(+) create mode 100644 boards/arm/nrf52/thingy52/CMakeLists.txt create mode 100644 boards/arm/nrf52/thingy52/configs/nimble_thingy52/defconfig create mode 100644 boards/arm/nrf52/thingy52/configs/nimble_thingy52/defconfig.dawn create mode 100644 descriptors/examples/nimble_thingy52_demo.yaml diff --git a/boards/arm/nrf52/thingy52/CMakeLists.txt b/boards/arm/nrf52/thingy52/CMakeLists.txt new file mode 100644 index 000000000..fb3fe00d4 --- /dev/null +++ b/boards/arm/nrf52/thingy52/CMakeLists.txt @@ -0,0 +1,7 @@ +# boards/arm/nrf52/thingy52/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../common + ${CMAKE_BINARY_DIR}/boards/common) diff --git a/boards/arm/nrf52/thingy52/configs/nimble_thingy52/defconfig b/boards/arm/nrf52/thingy52/configs/nimble_thingy52/defconfig new file mode 100644 index 000000000..a5fdedef1 --- /dev/null +++ b/boards/arm/nrf52/thingy52/configs/nimble_thingy52/defconfig @@ -0,0 +1,105 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_NET_ETHERNET is not set +# CONFIG_NET_IPv4 is not set +# CONFIG_STANDARD_SERIAL is not set +# CONFIG_WIRELESS_BLUETOOTH_HOST is not set +CONFIG_ADC=y +CONFIG_ADC_FIFOSIZE=2 +CONFIG_ALLOW_BSDNORDIC_COMPONENTS=y +CONFIG_ALLOW_BSD_COMPONENTS=y +CONFIG_ANALOG=y +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_CUSTOM=y +CONFIG_ARCH_BOARD_CUSTOM_DIR="boards/arm/nrf52/thingy52/" +CONFIG_ARCH_BOARD_CUSTOM_DIR_RELPATH=y +CONFIG_ARCH_BOARD_CUSTOM_NAME="thingy52" +CONFIG_ARCH_CHIP="nrf52" +CONFIG_ARCH_CHIP_NRF52=y +CONFIG_ARCH_CHIP_NRF52832=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARCH_STDARG_H=y +CONFIG_BLUETOOTH_BUFFER_PREALLOC=8 +CONFIG_BOARDCTL=y +CONFIG_BOARD_LOOPSPERMSEC=5500 +CONFIG_CXX_LOCALIZATION=y +CONFIG_CXX_WCHAR=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_ASSERTIONS_EXPRESSION=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_SENSORS=y +CONFIG_DEBUG_SENSORS_ERROR=y +CONFIG_DEBUG_SENSORS_INFO=y +CONFIG_DEBUG_SENSORS_WARN=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEV_GPIO=y +CONFIG_FS_PROCFS=y +CONFIG_GPIO_LOWER_HALF=y +CONFIG_HAVE_CXX=y +CONFIG_I2C=y +CONFIG_I2C_POLLED=y +CONFIG_IDLETHREAD_STACKSIZE=2048 +CONFIG_INIT_ENTRYPOINT="dawn_main" +CONFIG_INTELHEX_BINARY=y +CONFIG_IOEXPANDER=y +CONFIG_IOEXPANDER_PWM=y +CONFIG_IOEXPANDER_SX1509=y +CONFIG_LIBCXX=y +CONFIG_LIBCXXABI=y +CONFIG_LIBC_LOCALE=y +CONFIG_LINE_MAX=64 +CONFIG_MM_REGIONS=2 +CONFIG_NET=y +CONFIG_IOB_NBUFFERS=4 +CONFIG_NETDEV_LATEINIT=y +CONFIG_NETUTILS_NETINIT=y +CONFIG_NET_BLUETOOTH=y +CONFIG_NIMBLE=y +CONFIG_NIMBLE_TRANSPORT_ACL_FROM_HS_COUNT=3 +CONFIG_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=3 +CONFIG_NIMBLE_BLE_ATT_PREFFERED_MTU=128 +CONFIG_NIMBLE_MSYS_1_BLOCK_COUNT=6 +CONFIG_NIMBLE_MSYS_1_BLOCK_SIZE=200 +CONFIG_NIMBLE_ROLE_PERIPHERAL=y +CONFIG_NRF52_GPIOTE=y +CONFIG_NRF52_I2C0_MASTER=y +CONFIG_NRF52_SAADC=y +CONFIG_NRF52_SAADC_RESOLUTION=2 +CONFIG_NRF52_SAADC_CHANNELS=1 +CONFIG_NRF52_SOFTDEVICE_CONTROLLER=y +CONFIG_NRF52_SDC_DLE=y +CONFIG_NRF52_SDC_LE_2M_PHY=y +CONFIG_PREALLOC_MQ_MSGS=8 +CONFIG_PWM=y +CONFIG_PWM_NCHANNELS=3 +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=65536 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RGBLED=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=256 +CONFIG_SENSORS=y +CONFIG_SENSORS_CCS811=y +CONFIG_SENSORS_HTS221=y +CONFIG_SENSORS_LPS22HB=y +CONFIG_SERIAL_RTT0=y +CONFIG_SIG_EVTHREAD=y +CONFIG_START_DAY=26 +CONFIG_START_MONTH=3 +CONFIG_SX1509_LED_ENABLE=y +CONFIG_SYMTAB_ORDEREDBYNAME=y +CONFIG_SYSLOG_RTT=y +CONFIG_TASK_NAME_SIZE=64 +CONFIG_TLS_NELEM=4 +CONFIG_WIRELESS=y +CONFIG_WIRELESS_BLUETOOTH=y +#include "defconfig.dawn" diff --git a/boards/arm/nrf52/thingy52/configs/nimble_thingy52/defconfig.dawn b/boards/arm/nrf52/thingy52/configs/nimble_thingy52/defconfig.dawn new file mode 100644 index 000000000..9e8d8ad20 --- /dev/null +++ b/boards/arm/nrf52/thingy52/configs/nimble_thingy52/defconfig.dawn @@ -0,0 +1,21 @@ +CONFIG_DAWN=y +CONFIG_DAWN_APPS_DAWN=y +CONFIG_DAWN_APPS_EXAMPLE_DESC_FORMAT_YAML=y +CONFIG_DAWN_APPS_EXAMPLE_DESC_YAML_PATH="../../../descriptors/examples/nimble_thingy52_demo.yaml" +CONFIG_DAWN_DEBUG_ERROR=y +CONFIG_DAWN_DEBUG_INFO=y +CONFIG_DAWN_DEBUG_WARN=y +CONFIG_DAWN_IO_NOTIFY=y +CONFIG_DAWN_IO_RGB_LED=y +CONFIG_DAWN_IO_ADC_FETCH=y +CONFIG_DAWN_IO_SENSOR=y +CONFIG_DAWN_IO_VIRT=y +CONFIG_DAWN_PROG=y +CONFIG_DAWN_PROG_ADJUST=y +CONFIG_DAWN_PROG_SAMPLING=y +CONFIG_DAWN_PROTO=y +CONFIG_DAWN_PROTO_NIMBLE=y +CONFIG_DAWN_PROTO_NIMBLE_BAS=y +CONFIG_DAWN_PROTO_NIMBLE_DIS=y +CONFIG_DAWN_PROTO_NIMBLE_ESS=y +CONFIG_DAWN_PROTO_NIMBLE_IMDS=y diff --git a/descriptors/examples/nimble_thingy52_demo.yaml b/descriptors/examples/nimble_thingy52_demo.yaml new file mode 100644 index 000000000..59acef996 --- /dev/null +++ b/descriptors/examples/nimble_thingy52_demo.yaml @@ -0,0 +1,155 @@ +metadata: + title: Thingy:52 Environmental NimBLE Demo + version: "1.0" + description: > + Exposes the Nordic Thingy:52 onboard environmental sensors over the + standard Environmental Sensing (ESS) and Dawn IMDS services via NimBLE. + Temperature and humidity come from the HTS221, pressure from the + LPS22HB and air quality (eCO2 and TVOC) from the CCS811, each published + through the NuttX uORB sensor framework. The + battery voltage is read from the raw SAADC channel (/dev/adc0, AIN4), + sampled periodically and scaled to a Li-Po percentage in Dawn, then + exposed over the standard Battery Service (BAS). The lightwell RGB + LED is writable over a custom characteristic; the rgbled IO uses + 0x00RRGGBB values converted to the #RRGGBB string format expected by + the NuttX /dev/rgbled0 driver. + os_required: + - /dev/rgbled0 + - /dev/uorb/sensor_humi0 + - /dev/uorb/sensor_ambient_temp0 + - /dev/uorb/sensor_baro0 + - /dev/uorb/sensor_co20 + - /dev/uorb/sensor_tvoc0 + - /dev/adc0 + - ble + +ios: + - &rgb_led + id: rgbled0 + type: rgb_led + dtype: uint32 + config: + device: 0 + init_val: 0x000000 + - &humidity + id: humidity1 + type: sensor + dtype: float + config: + device: 0 + subtype: hum + - &temp + id: temp1 + type: sensor + dtype: float + config: + device: 0 + subtype: atemp + - &barometer + id: barometer1 + type: sensor + dtype: float + config: + device: 0 + subtype: baro + - &co2 + id: co21 + type: sensor + dtype: float + config: + device: 0 + subtype: co2 + - &tvoc + id: tvoc1 + type: sensor + dtype: float + config: + device: 0 + subtype: tvoc + + # Battery: raw ADC -> sampling -> adjust -> BAS battery level. The board + # exposes only the raw SAADC channel (/dev/adc0, AIN4). The divider scaling + # and percentage mapping are done in Dawn: sampling polls the ADC, a float + # adjust maps raw counts to a Li-Po percentage, and a uint8 adjust narrows + # it for the standard Battery Service. + - &adc_bat + id: adc_bat + type: adc_fetch + dtype: int32 + config: + device: 0 + - &adc_bat_raw + id: adc_bat_raw + type: virt + dtype: int32 + - &bat_pct + id: bat_pct + type: virt + dtype: float + - &bat_level + id: bat_level + type: virt + dtype: uint8 + +programs: + # Battery: poll the raw ADC once per second, then scale to a percentage. + - id: bat_sampling + type: sampling + config: + sources: + - *adc_bat + outputs: + - *adc_bat_raw + interval: 1000000 + - id: bat_scale + type: adjust + dtype: float + config: + inputs: + - *adc_bat_raw + outputs: + - *bat_pct + params: + scale: 0.1253 # raw counts -> % (empty ~2267, full ~3065 counts) + offset: -284.1 + - id: bat_to_level + type: adjust + dtype: uint8 + config: + inputs: + - *bat_pct + outputs: + - *bat_level + params: + scale: 1 + offset: 0 + +protocols: + - id: nimble1 + type: nimble + config: + gap_name: dawn-thingy52 + services: + dis: + enabled: true + bas: + battery_level: *bat_level + ess: + characteristics: + - type: humidity + data: *humidity + - type: temperature + data: *temp + - type: pressure + data: *barometer + imds: + co2: *co2 + tvoc: *tvoc + custom: + - uuid: 9f2d2b30-74d3-4f34-9f37-ef7d8f6d3000 + characteristics: + - uuid: 9f2d2b30-74d3-4f34-9f37-ef7d8f6d3001 + io: *rgb_led + flags: + - read + - write diff --git a/tools/config-build-all.txt b/tools/config-build-all.txt index 570e470c0..524ee9b14 100644 --- a/tools/config-build-all.txt +++ b/tools/config-build-all.txt @@ -40,6 +40,8 @@ boards/arm/nrf52/nrf52840-dk/configs/nimble_ots boards/arm/nrf52/nrf52840-dk/configs/nimble_sensor_producer boards/arm/nrf52/nrf52840-dk/configs/shell +boards/arm/nrf52/thingy52/configs/nimble_thingy52 -D CMAKE_POLICY_VERSION_MINIMUM=3.5 + boards/arm/nrf53/thingy53/configs/nimble -D CMAKE_POLICY_VERSION_MINIMUM=3.5 boards/arm/nrf53/thingy53/configs/nimble_thingy53 -D CMAKE_POLICY_VERSION_MINIMUM=3.5 boards/arm/nrf53/thingy53/configs/rpmsghci_sdc_cpunet -D CMAKE_POLICY_VERSION_MINIMUM=3.5