├── Arduino_BHY2Host ├── README.md ├── examples │ ├── Portenta_BLE_Bridge │ │ ├── arduino_secrets.h │ │ ├── thingProperties.h │ │ └── Portenta_BLE_Bridge.ino │ ├── Nicla_IoT_Bridge │ │ ├── arduino_secrets.h │ │ ├── thingProperties.h │ │ └── Nicla_IoT_Bridge.ino │ ├── BSEC │ │ └── BSEC.ino │ ├── Temperature │ │ └── Temperature.ino │ ├── Orientation │ │ └── Orientation.ino │ ├── Accelerometer │ │ └── Accelerometer.ino │ └── Passthrough │ │ └── Passthrough.ino ├── src │ ├── DFUTypes.h │ ├── sensors │ │ ├── SensorManager.h │ │ ├── SensorXYZ.h │ │ ├── SensorClass.h │ │ ├── SensorQuaternion.h │ │ ├── SensorOrientation.h │ │ ├── SensorClass.cpp │ │ ├── Sensor.h │ │ ├── SensorManager.cpp │ │ ├── SensorBSEC.h │ │ ├── SensorActivity.h │ │ ├── DataParser.h │ │ └── DataParser.cpp │ ├── BLEHandler.h │ ├── Arduino_BHY2Host.cpp │ └── EslovHandler.h ├── library.properties └── docs │ └── readme.md ├── tools └── bhy-controller │ ├── src │ ├── .gitignore │ ├── static │ │ ├── static.go │ │ ├── index.html │ │ └── parse-scheme.json │ ├── util │ │ └── util.go │ ├── dfu │ │ ├── crc.go │ │ ├── packet.go │ │ └── dfu.go │ ├── go.mod │ ├── webserver │ │ └── webserver.go │ └── bhy.go │ └── README.md ├── .gitignore ├── Arduino_BHY2 ├── examples │ ├── DataHarvester │ │ ├── extras │ │ │ └── Commander │ │ │ │ ├── requirements.txt │ │ │ │ └── Commander.py │ │ └── DataHarvester.ino │ ├── BHYFirmwareUpdate │ │ ├── conv.sh │ │ └── BHYFirmwareUpdate.ino │ ├── App │ │ └── App.ino │ ├── AppLowDelay │ │ └── AppLowDelay.ino │ ├── ShowSensorList │ │ └── ShowSensorList.ino │ ├── Pressure │ │ └── Pressure.ino │ ├── ActivityRecognition │ │ └── ActivityRecognition.ino │ ├── Magnetometer │ │ └── Magnetometer.ino │ ├── Fail_Safe_flasher │ │ └── Fail_Safe_flasher.ino │ ├── AccelGyro │ │ └── AccelGyro.ino │ ├── ReadSensorConfiguration │ │ └── ReadSensorConfiguration.ino │ ├── BSEC2GasScannerCollectData │ │ └── BSEC2GasScannerCollectData.ino │ ├── Standalone │ │ └── Standalone.ino │ └── IMURangeSettings │ │ └── IMURangeSettings.ino ├── README.md ├── library.properties ├── src │ ├── sensors │ │ ├── SensorManager.h │ │ ├── SensorXYZ.h │ │ ├── SensorBSEC2Collector.h │ │ ├── SensorQuaternion.h │ │ ├── SensorBSEC2.h │ │ ├── SensorOrientation.h │ │ ├── SensorManager.cpp │ │ ├── Sensor.h │ │ ├── SensorClass.cpp │ │ ├── SensorBSEC.h │ │ ├── SensorActivity.h │ │ ├── SensorClass.h │ │ ├── DataParser.cpp │ │ └── DataParser.h │ ├── BoschParser.h │ ├── EslovHandler.h │ ├── bosch │ │ ├── common │ │ │ └── common.h │ │ ├── bhy2_parse.c │ │ └── bhy2_parse.h │ ├── DFUManager.h │ ├── BLEHandler.h │ └── DFUManager.cpp └── docs │ └── readme.md ├── bootloader ├── mbed-os.lib ├── BHY2-Sensor-API │ ├── firmware │ │ ├── bha260 │ │ │ ├── DataInject.fw │ │ │ ├── DataInject-flash.fw │ │ │ ├── DataInject_nobsx.fw │ │ │ ├── Bosch_SHUTTLE_BHA260.fw │ │ │ ├── DataInject_nobsx-flash.fw │ │ │ ├── Bosch_SHUTTLE_BHA260_BMG250.fw │ │ │ ├── Bosch_SHUTTLE_BHA260_BMM150.fw │ │ │ ├── Bosch_SHUTTLE_BHA260_turbo.fw │ │ │ ├── Bosch_SHUTTLE_BHA260_AK09915.fw │ │ │ ├── Bosch_SHUTTLE_BHA260_BMG250_AK09915.fw │ │ │ └── Bosch_SHUTTLE_BHA260_BMG250_BMM150.fw │ │ └── bhi260 │ │ │ ├── DataInject.fw │ │ │ ├── DataInject-flash.fw │ │ │ ├── DataInject_nobsx.fw │ │ │ ├── Bosch_SHUTTLE_BHI260.fw │ │ │ ├── DataInject_nobsx-flash.fw │ │ │ ├── Bosch_SHUTTLE_BHI260-flash.fw │ │ │ ├── Bosch_SHUTTLE_BHI260_BME280.fw │ │ │ ├── Bosch_SHUTTLE_BHI260_BMM150.fw │ │ │ ├── Bosch_SHUTTLE_BHI260_turbo.fw │ │ │ ├── Bosch_SHUTTLE_BHI260_AK09915.fw │ │ │ ├── Bosch_SHUTTLE_BHI260_turbo-flash.fw │ │ │ ├── Bosch_SHUTTLE_BHI260_AK09915-flash.fw │ │ │ ├── Bosch_SHUTTLE_BHI260_AK09915_BME280.fw │ │ │ ├── Bosch_SHUTTLE_BHI260_BME280-flash.fw │ │ │ ├── Bosch_SHUTTLE_BHI260_BMM150-flash.fw │ │ │ ├── Bosch_SHUTTLE_BHI260_AK09915_BME280-flash.fw │ │ │ └── Bosch_SHUTTLE_BHI260-flash.fw.h │ ├── README.md │ ├── LICENSE │ ├── common │ │ └── common.h │ ├── bhy2_parse.c │ └── bhy2_parse.h ├── resources │ └── official_armmbed_example_badge.png ├── src │ ├── file_utils.h │ ├── bhy_upload.h │ ├── BQ25120A.h │ ├── pmic_driver.cpp │ ├── file_utils.cpp │ ├── led_driver.cpp │ └── IS31FL3194.h ├── CONTRIBUTING.md ├── examples │ ├── Fail_safe_sketch │ │ └── Fail_safe_sketch.ino │ ├── Blink_flasher_unisense │ │ └── Blink_flasher_unisense.ino │ └── Blink_Unisense │ │ └── Blink_Unisense.ino └── mbed_app.json ├── .vscode └── easycode.ignore ├── pyproject.toml ├── .codespellrc ├── .github ├── dependabot.yml ├── pull_request_template.md ├── workflows │ ├── report-size-deltas.yml │ ├── spell-check-task.yml │ ├── check-arduino.yml │ ├── update-libraries.yml │ └── check-license.yml └── ISSUE_TEMPLATE │ └── bug_report.md └── Taskfile.yml /Arduino_BHY2Host/README.md: -------------------------------------------------------------------------------- 1 | # Arduino_BHY2Host -------------------------------------------------------------------------------- /tools/bhy-controller/src/.gitignore: -------------------------------------------------------------------------------- 1 | bhy 2 | dist/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | .mbed 3 | projectfiles 4 | *.py* 5 | 6 | dist/* 7 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/DataHarvester/extras/Commander/requirements.txt: -------------------------------------------------------------------------------- 1 | pyserial 2 | cobs 3 | -------------------------------------------------------------------------------- /bootloader/mbed-os.lib: -------------------------------------------------------------------------------- 1 | git@github.com:bcmi-labs/mbed-os-h747/#5be0357687292f51422bb42bb89f0cc08a71b9e2 2 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/Portenta_BLE_Bridge/arduino_secrets.h: -------------------------------------------------------------------------------- 1 | #define SECRET_SSID "" 2 | #define SECRET_PASS "" -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/Nicla_IoT_Bridge/arduino_secrets.h: -------------------------------------------------------------------------------- 1 | #define SECRET_SSID "" 2 | #define SECRET_PASS "" 3 | -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/DataInject.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/DataInject.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/DataInject.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/DataInject.fw -------------------------------------------------------------------------------- /bootloader/resources/official_armmbed_example_badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/resources/official_armmbed_example_badge.png -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/DataInject-flash.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/DataInject-flash.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/DataInject_nobsx.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/DataInject_nobsx.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/DataInject-flash.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/DataInject-flash.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/DataInject_nobsx.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/DataInject_nobsx.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260.fw -------------------------------------------------------------------------------- /.vscode/easycode.ignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | vendor/ 4 | cache/ 5 | .*/ 6 | *.min.* 7 | *.test.* 8 | *.spec.* 9 | *.bundle.* 10 | *.bundle-min.* 11 | *.*.js 12 | *.*.ts 13 | *.log -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/DataInject_nobsx-flash.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/DataInject_nobsx-flash.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/DataInject_nobsx-flash.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/DataInject_nobsx-flash.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_BMG250.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_BMG250.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_BMM150.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_BMM150.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_turbo.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_turbo.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260-flash.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260-flash.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_BME280.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_BME280.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_BMM150.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_BMM150.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_turbo.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_turbo.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_AK09915.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_AK09915.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_AK09915.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_AK09915.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_turbo-flash.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_turbo-flash.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_BMG250_AK09915.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_BMG250_AK09915.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_BMG250_BMM150.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bha260/Bosch_SHUTTLE_BHA260_BMG250_BMM150.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_AK09915-flash.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_AK09915-flash.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_AK09915_BME280.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_AK09915_BME280.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_BME280-flash.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_BME280-flash.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_BMM150-flash.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_BMM150-flash.fw -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_AK09915_BME280-flash.fw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/nicla-sense-me-fw/HEAD/bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260_AK09915_BME280-flash.fw -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/DFUTypes.h: -------------------------------------------------------------------------------- 1 | #ifndef DFU_TYPES_H_ 2 | #define DFU_TYPES_H_ 3 | 4 | #include "Arduino.h" 5 | 6 | struct __attribute__((packed)) DFUPacket { 7 | uint8_t last: 1; 8 | union { 9 | uint16_t index: 15; 10 | uint16_t remaining: 15; 11 | }; 12 | uint8_t data[232]; 13 | }; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /tools/bhy-controller/src/static/static.go: -------------------------------------------------------------------------------- 1 | package static 2 | 3 | import "embed" 4 | 5 | // Content holds all the static file necessary 6 | // to serve our local webserver. 7 | // Since we embed them in the final binary we won't need to 8 | // distribute the static file. 9 | //go:embed *.html 10 | //go:embed *.json 11 | var Content embed.FS 12 | -------------------------------------------------------------------------------- /Arduino_BHY2/README.md: -------------------------------------------------------------------------------- 1 | # Bosch BHY2 Sensor Library for Arduino 2 | 3 | The Bosch BHY2 Sensor Library provides an interface with the [Bosch BHY2-Sensor-API](https://github.com/BoschSensortec/BHY2-Sensor-API) for communicating with the BHI260AP and a custom library for the BME688 sensors of the Nicla Sense ME. 4 | 5 | See [docs/readme.md](docs/readme.md) for usage and examples. 6 | -------------------------------------------------------------------------------- /bootloader/src/file_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_UTILS_H_ 2 | #define FILE_UTILS_H_ 3 | 4 | #include "mbed.h" 5 | 6 | #include "common.h" 7 | 8 | class FileUtils 9 | { 10 | public: 11 | 12 | FileUtils(); 13 | 14 | long getFileLen(FILE *file); 15 | char getFileCRC(FILE *file); 16 | char computeCRC(FILE *file); 17 | 18 | private: 19 | 20 | }; 21 | 22 | #endif //FILE_UTILS_H_ 23 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "nicla-sense-me-fw" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Your Name "] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.9" 10 | 11 | [tool.poetry.group.dev.dependencies] 12 | codespell = "^2.2.5" 13 | 14 | [build-system] 15 | requires = ["poetry-core"] 16 | build-backend = "poetry.core.masonry.api" 17 | -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/README.md: -------------------------------------------------------------------------------- 1 | # BHI260AB/BHA260AB Sensor API 2 | 3 | > This package contains BHI260AB/BHA260AB generically clustered as BHy2 sensor API 4 | 5 | Product links 6 | - [BHA260AB](https://www.bosch-sensortec.com/products/smart-sensors/bha260ab.html) 7 | - [BHI260AB](https://www.bosch-sensortec.com/products/smart-sensors/bhi260ab.html) 8 | 9 | --- 10 | #### Copyright (C) 2020 Bosch Sensortec GmbH. All rights reserved 11 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/BHYFirmwareUpdate/conv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -f "$1" ] ; then 4 | cp -a "$1" BHI260AP_NiclaSenseME-flash.fw 5 | echo const > fw.h && xxd -i BHI260AP_NiclaSenseME-flash.fw >> fw.h 6 | else 7 | echo "Usage: $0 " 8 | echo "\t usually has a pattern: xxxx-flash.fw" 9 | echo "\tExample:" 10 | echo "\t\t$0 ./BHI260AP_NiclaSenseME_basic+bsec-flash.fw" 11 | fi 12 | 13 | -------------------------------------------------------------------------------- /bootloader/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Mbed OS 2 | 3 | Mbed OS is an open-source, device software platform for the Internet of Things. Contributions are an important part of the platform, and our goal is to make it as simple as possible to become a contributor. 4 | 5 | To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for [contributing to Mbed OS](https://os.mbed.com/docs/mbed-os/latest/contributing/index.html). 6 | -------------------------------------------------------------------------------- /tools/bhy-controller/src/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "log" 5 | 6 | "go.bug.st/serial" 7 | ) 8 | 9 | func OpenPort(usbPort string, baudRate int) serial.Port { 10 | mode := &serial.Mode{ 11 | BaudRate: baudRate, 12 | Parity: serial.NoParity, 13 | DataBits: 8, 14 | StopBits: serial.OneStopBit, 15 | } 16 | port, err := serial.Open(usbPort, mode) 17 | ErrCheck(err) 18 | return port 19 | } 20 | 21 | func ErrCheck(e error) { 22 | if e != nil { 23 | log.Fatal(e) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Arduino_BHY2/library.properties: -------------------------------------------------------------------------------- 1 | name=Arduino_BHY2 2 | version=1.0.8 3 | author=Arduino 4 | maintainer=Arduino 5 | sentence=Library for BHI260AP and BME688 chip on the Nicla Sense ME 6 | paragraph=Provides the APIs for Nicla Sense ME board to make a DFU or configure/read BHI260AP (BHY2) and BME688 sensors. All these operations can be done either via ESLOV or BLE. 7 | category=Sensors 8 | url=https://github.com/arduino-libraries/Arduino_BHY2 9 | architectures=mbed,mbed_nicla 10 | includes=Arduino_BHY2.h 11 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/SensorManager.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_MANAGER_H_ 2 | #define SENSOR_MANAGER_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | class SensorManager { 7 | public: 8 | SensorManager(); 9 | void process(SensorLongDataPacket &data); 10 | 11 | void subscribe(SensorClass *sensor); 12 | void unsubscribe(SensorClass *sensor); 13 | 14 | private: 15 | SensorClass* _sensors[10]; // array of 256 or list to handle unsubscription 16 | int _sensorsLen; 17 | }; 18 | 19 | extern SensorManager sensorManager; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /tools/bhy-controller/src/dfu/crc.go: -------------------------------------------------------------------------------- 1 | package dfu 2 | 3 | type crc8 struct { 4 | value uint8 5 | } 6 | 7 | func (c *crc8) reset() { 8 | c.value = uint8(0) 9 | } 10 | 11 | func (c *crc8) byte() byte { 12 | return byte(c.value) 13 | } 14 | 15 | func (c *crc8) compute(buf []byte, len int) { 16 | for i := 0; i < len; i++ { 17 | b := buf[i] 18 | c.value = c.value ^ b 19 | } 20 | } 21 | 22 | func CRC8(buf []byte, len int, crc uint8) uint8 { 23 | for i := 0; i < len; i++ { 24 | b := buf[i] 25 | crc = crc ^ b 26 | } 27 | return crc 28 | } 29 | -------------------------------------------------------------------------------- /bootloader/src/bhy_upload.h: -------------------------------------------------------------------------------- 1 | #ifndef BHY_UPLOAD_H_ 2 | #define BHY_UPLOAD_H_ 3 | 4 | #include "mbed.h" 5 | 6 | #include "bhy2.h" 7 | #include "bhy2_parse.h" 8 | #include "common.h" 9 | 10 | #define MOUNT_PATH "fs" 11 | #define BHY_UPDATE_FILE_PATH "/" MOUNT_PATH "/BHY_UPDATE.BIN" 12 | 13 | struct parse_ref 14 | { 15 | struct 16 | { 17 | uint8_t accuracy; 18 | float scaling_factor; 19 | } 20 | sensor[BHY2_SENSOR_ID_MAX]; 21 | uint8_t *verbose; 22 | }; 23 | 24 | int fwupdate_bhi260(void); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /tools/bhy-controller/src/go.mod: -------------------------------------------------------------------------------- 1 | module arduino/bhy 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/mattn/go-runewidth v0.0.13 // indirect 7 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 8 | github.com/schollz/progressbar/v3 v3.8.6 9 | github.com/sirupsen/logrus v1.8.1 10 | go.bug.st/serial v1.3.5 11 | golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58 // indirect 12 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect 13 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect 14 | gopkg.in/yaml.v2 v2.2.2 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/SensorManager.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_MANAGER_H_ 2 | #define SENSOR_MANAGER_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | class SensorManager { 7 | public: 8 | SensorManager(); 9 | void process(SensorLongDataPacket &data); 10 | void process(SensorDataPacket &data); 11 | 12 | void subscribe(SensorClass *sensor); 13 | void unsubscribe(SensorClass *sensor); 14 | 15 | private: 16 | SensorClass* _sensors[10]; // array of 256 or list to handle unsubscription 17 | int _sensorsLen; 18 | }; 19 | 20 | extern SensorManager sensorManager; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/spell-check/.codespellrc 2 | # See: https://github.com/codespell-project/codespell#using-a-config-file 3 | [codespell] 4 | # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: 5 | ignore-words-list = pevent, ot, oce, pres, ser 6 | skip = ./.git,./.licenses,__pycache__,node_modules,./go.mod,./go.sum,./package-lock.json,./poetry.lock,./yarn.lock 7 | builtin = clear,informal,en-GB_to_en-US 8 | check-filenames = 9 | check-hidden = 10 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/library.properties: -------------------------------------------------------------------------------- 1 | name=Arduino_BHY2Host 2 | version=1.0.9 3 | author=Arduino 4 | maintainer=Arduino 5 | sentence=Library for Host boards that interact with BHY2 chip on Nicla Sense ME 6 | paragraph=Provides the APIs for a host board to interact with Nicla Sense ME board. The host board can be used to make a DFU or configure/read BHY sensors on the Nicla Sense ME via Eslov. 7 | category=Communication 8 | url=https://github.com/arduino-libraries/Arduino_BHY2Host 9 | architectures=samd,mbed 10 | depends= ArduinoIoTCloud, Arduino_ConnectionHandler, Arduino_Cellular 11 | includes=Arduino_BHY2Host.h 12 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/Portenta_BLE_Bridge/thingProperties.h: -------------------------------------------------------------------------------- 1 | #include "arduino_secrets.h" 2 | 3 | const char THING_ID[] = ""; 4 | 5 | const char SSID[] = SECRET_SSID; // Network SSID (name) 6 | const char PASS[] = SECRET_PASS; // Network password (use for WPA, or use as key for WEP) 7 | 8 | float temperature; 9 | int seconds; 10 | 11 | void initProperties(){ 12 | 13 | ArduinoCloud.setThingId(THING_ID); 14 | ArduinoCloud.addProperty(temperature, READ, 1 * SECONDS, NULL); 15 | ArduinoCloud.addProperty(seconds, READ, 1 * SECONDS, NULL); 16 | 17 | } 18 | 19 | WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS); -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/Nicla_IoT_Bridge/thingProperties.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "arduino_secrets.h" 4 | 5 | 6 | const char THING_ID[] = ""; 7 | 8 | const char SSID[] = SECRET_SSID; // Network SSID (name) 9 | const char PASS[] = SECRET_PASS; // Network password (use for WPA, or use as key for WEP) 10 | 11 | float temp; 12 | int seconds; 13 | 14 | void initProperties(){ 15 | 16 | ArduinoCloud.setThingId(THING_ID); 17 | ArduinoCloud.addProperty(temp, READ, 1 * SECONDS, NULL); 18 | ArduinoCloud.addProperty(seconds, READ, 1 * SECONDS, NULL); 19 | 20 | } 21 | 22 | WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS); 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#about-the-dependabotyml-file 2 | version: 2 3 | 4 | updates: 5 | # Configure check for outdated GitHub Actions actions in workflows. 6 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/dependabot/README.md 7 | # See: https://docs.github.com/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 8 | - package-ecosystem: github-actions 9 | directory: /.github/workflows/ 10 | schedule: 11 | interval: daily 12 | labels: 13 | - "topic: infrastructure" 14 | -------------------------------------------------------------------------------- /bootloader/examples/Fail_safe_sketch/Fail_safe_sketch.ino: -------------------------------------------------------------------------------- 1 | void setup() { 2 | // put your setup code here, to run once: 3 | pinMode(P0_17, OUTPUT); 4 | pinMode(P0_19, OUTPUT); 5 | } 6 | 7 | void loop() { 8 | // put your main code here, to run repeatedly: 9 | for (int i = 0; i<3; i++) { 10 | digitalWrite(P0_17, LOW); 11 | digitalWrite(P0_19, HIGH); 12 | delay(500); 13 | digitalWrite(P0_17, HIGH); 14 | digitalWrite(P0_19, LOW); 15 | delay(500); 16 | } 17 | for (int j = 0; j<3; j++) { 18 | digitalWrite(P0_17, LOW); 19 | digitalWrite(P0_19, HIGH); 20 | delay(1000); 21 | digitalWrite(P0_17, HIGH); 22 | digitalWrite(P0_19, LOW); 23 | delay(1000); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tools/bhy-controller/src/webserver/webserver.go: -------------------------------------------------------------------------------- 1 | package webserver 2 | 3 | import ( 4 | "arduino/bhy/static" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | 9 | "github.com/pkg/browser" 10 | ) 11 | 12 | func errCheck(e error) { 13 | if e != nil { 14 | log.Fatal(e) 15 | } 16 | } 17 | 18 | func startServer() { 19 | fmt.Printf("Starting server at port 8000\n") 20 | fmt.Printf("Visit webpage at address http://localhost:8000/index.html\n") 21 | 22 | fileServer := http.FileServer(http.FS(static.Content)) 23 | http.Handle("/", fileServer) 24 | http.ListenAndServe(":8000", nil) 25 | } 26 | 27 | func Execute() { 28 | go startServer() 29 | 30 | e := browser.OpenURL("http://localhost:8000/index.html") 31 | 32 | errCheck(e) 33 | 34 | select {} 35 | } 36 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/SensorXYZ.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_XYZ_H_ 2 | #define SENSOR_XYZ_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | class SensorXYZ : public SensorClass { 7 | public: 8 | SensorXYZ() {} 9 | SensorXYZ(uint8_t id) : SensorClass(id), _data() {} 10 | 11 | int16_t x() 12 | { 13 | return _data.x; 14 | } 15 | int16_t y() 16 | { 17 | return _data.y; 18 | } 19 | int16_t z() 20 | { 21 | return _data.z; 22 | } 23 | 24 | void setData(SensorDataPacket &data) 25 | { 26 | DataParser::parse3DVector(data, _data); 27 | } 28 | 29 | void setData(SensorLongDataPacket &data) {} 30 | 31 | String toString() 32 | { 33 | return _data.toString(); 34 | } 35 | 36 | private: 37 | DataXYZ _data; 38 | 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/SensorXYZ.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_XYZ_H_ 2 | #define SENSOR_XYZ_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | class SensorXYZ : public SensorClass { 7 | public: 8 | SensorXYZ() {} 9 | SensorXYZ(uint8_t id) : SensorClass(id), _data() {} 10 | 11 | int16_t x() 12 | { 13 | return _data.x; 14 | } 15 | int16_t y() 16 | { 17 | return _data.y; 18 | } 19 | int16_t z() 20 | { 21 | return _data.z; 22 | } 23 | 24 | void setData(SensorDataPacket &data) 25 | { 26 | DataParser::parse3DVector(data, _data); 27 | } 28 | 29 | void setData(SensorLongDataPacket &data) {} 30 | 31 | String toString() 32 | { 33 | return _data.toString(); 34 | } 35 | 36 | private: 37 | DataXYZ _data; 38 | 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/App/App.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Use this sketch if you want to control nicla from 3 | * an external device acting as a host. 4 | * Here, nicla just reacts to external stimuli coming from 5 | * the eslov port or through BLE 6 | * 7 | * NOTE: Remember to choose your Nicla configuration! 8 | * If Nicla is used as a Shield, provide the NICLA_AS_SHIELD parameter. 9 | * If you want to enable just one between I2C and BLE, 10 | * use NICLA_I2C or NICLA_BLE parameters. 11 | * 12 | */ 13 | 14 | #include "Arduino_BHY2.h" 15 | 16 | // Set DEBUG to true in order to enable debug print 17 | #define DEBUG false 18 | 19 | void setup() 20 | { 21 | #if DEBUG 22 | Serial.begin(115200); 23 | BHY2.debug(Serial); 24 | #endif 25 | 26 | BHY2.begin(); 27 | } 28 | 29 | void loop() 30 | { 31 | // Update and then sleep 32 | BHY2.update(100); 33 | } -------------------------------------------------------------------------------- /Arduino_BHY2/examples/AppLowDelay/AppLowDelay.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Use this sketch if you want to control nicla from 3 | * an external device acting as a host. 4 | * Here, nicla just reacts to external stimuli coming from 5 | * the eslov port or through BLE 6 | * 7 | * NOTE: Remember to choose your Nicla configuration! 8 | * If Nicla is used as a Shield, provide the NICLA_AS_SHIELD parameter. 9 | * If you want to enable just one between I2C and BLE, 10 | * use NICLA_I2C or NICLA_BLE parameters. 11 | * 12 | */ 13 | 14 | #include "Arduino_BHY2.h" 15 | 16 | // Set DEBUG to true in order to enable debug print 17 | #define DEBUG false 18 | 19 | void setup() 20 | { 21 | #if DEBUG 22 | Serial.begin(115200); 23 | BHY2.debug(Serial); 24 | #endif 25 | 26 | BHY2.begin(); 27 | } 28 | 29 | void loop() 30 | { 31 | // Update and then sleep only for 1ms 32 | BHY2.update(1); 33 | } 34 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | 8 | 9 | 10 | ### Pull request type 11 | 12 | 19 | [ ] Fix 20 | [ ] Refactor 21 | [ ] Target update 22 | [ ] Functionality change 23 | [ ] Breaking change 24 | 25 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/ShowSensorList/ShowSensorList.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sketch shows a list of virtual sensors supported on the Nicla Sense Board 3 | * see more details about the definition and specs of virtual sensors in the BHI260AP datasheet on Bosch Sensortec website 4 | 5 | * Dependencies: 6 | * librares: Arduino_BHY2 7 | */ 8 | 9 | 10 | #include "Arduino_BHY2.h" 11 | 12 | void showSensorList() 13 | { 14 | Serial.println("\n"); 15 | Serial.println("Virtual sensors supported on this board:"); 16 | for (int id = 0; id < 255; id++) { 17 | if (BHY2.hasSensor(id)){ 18 | Serial.println(id); 19 | } 20 | } 21 | 22 | Serial.println("end of the list!"); 23 | Serial.flush(); 24 | } 25 | 26 | void setup() 27 | { 28 | Serial.begin(115200); 29 | BHY2.begin(); 30 | 31 | showSensorList(); 32 | } 33 | 34 | void loop() 35 | { 36 | delay(1000); 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/report-size-deltas.yml: -------------------------------------------------------------------------------- 1 | name: Report Size Deltas 2 | 3 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | paths: 7 | - ".github/workflows/report-size-deltas.ya?ml" 8 | schedule: 9 | # Run at the minimum interval allowed by GitHub Actions. 10 | # Note: GitHub Actions periodically has outages which result in workflow failures. 11 | # In this event, the workflows will start passing again once the service recovers. 12 | - cron: "*/5 * * * *" 13 | workflow_dispatch: 14 | repository_dispatch: 15 | 16 | jobs: 17 | report: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Comment size deltas reports to PRs 21 | uses: arduino/report-size-deltas@v1 22 | with: 23 | # Regex matching the names of the workflow artifacts created by the "Compile Examples" workflow 24 | sketches-reports-source: ^sketches-report-.+ 25 | -------------------------------------------------------------------------------- /bootloader/src/BQ25120A.h: -------------------------------------------------------------------------------- 1 | #ifndef BQ25120A_h 2 | #define BQ25120A_h 3 | 4 | #define BQ25120A_ADDRESS 0x6A 5 | 6 | // Register Map 7 | // https://www.ti.com/lit/ds/symlink/bq25120a.pdf?ts=1610608851953&ref_url=https%253A%252F%252Fwww.startpage.com%252F 8 | #define BQ25120A_STATUS 0x00 9 | #define BQ25120A_FAULTS 0x01 10 | #define BQ25120A_TS_CONTROL 0x02 11 | #define BQ25120A_FAST_CHG 0x03 12 | #define BQ25120A_TERMINATION_CURR 0x04 13 | #define BQ25120A_BATTERY_CTRL 0x05 14 | #define BQ25120A_SYS_VOUT_CTRL 0x06 15 | #define BQ25120A_LDO_CTRL 0x07 16 | #define BQ25120A_PUSH_BUTT_CTRL 0x08 17 | #define BQ25120A_ILIM_UVLO_CTRL 0x09 18 | #define BQ25120A_BATT_MON 0x0A 19 | #define BQ25120A_VIN_DPM 0x0B 20 | 21 | class BQ25120A 22 | { 23 | public: 24 | BQ25120A(); 25 | uint8_t getStatus(); 26 | void writeByte(uint8_t address, uint8_t subAddress, uint8_t data); 27 | uint8_t readByte(uint8_t address, uint8_t subAddress); 28 | }; 29 | 30 | #endif -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/SensorBSEC2Collector.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_BSEC2COLLECTOR_H_ 2 | #define SENSOR_BSEC2COLLECTOR_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | 7 | class SensorBSEC2Collector : public SensorClass { 8 | public: 9 | SensorBSEC2Collector() {} 10 | SensorBSEC2Collector(uint8_t id) : SensorClass(id), _data() {} 11 | 12 | 13 | uint64_t timestamp() {return _data.timestamp;} 14 | float temperature() {return _data.raw_temp;} 15 | float pressure() {return _data.raw_pressure;} 16 | float humidity() {return _data.raw_hum;} 17 | float gas() {return _data.raw_gas;} 18 | uint8_t gas_index() {return _data.gas_index;} 19 | 20 | void setData(SensorDataPacket &data) 21 | { 22 | } 23 | 24 | void setData(SensorLongDataPacket &data) 25 | { 26 | if (_id == SENSOR_ID_BSEC2_COLLECTOR ) { 27 | DataParser::parseBSEC2Collector(data, _data); 28 | } 29 | } 30 | 31 | String toString() 32 | { 33 | return _data.toString(); 34 | } 35 | 36 | private: 37 | DataBSEC2Collector _data; 38 | }; 39 | #endif 40 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/BSEC/BSEC.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sketch shows how an arduino board can act as a host for nicla. 3 | * An host board can configure the sensors of nicla and then read their values. 4 | * The host board should be connected to nicla through the eslov connector. 5 | * 6 | * In this example, the BSEC sensor is enabled and its 7 | * values are periodically read and then printed to the serial channel 8 | * 9 | * NOTE: if Nicla is used as a Shield on top of a MKR board, 10 | * please use BHY2Host.begin(false, NICLA_AS_SHIELD) 11 | */ 12 | 13 | #include "Arduino.h" 14 | #include "Arduino_BHY2Host.h" 15 | 16 | SensorBSEC bsec(SENSOR_ID_BSEC); 17 | 18 | void setup() 19 | { 20 | // debug port 21 | Serial.begin(115200); 22 | while(!Serial); 23 | 24 | BHY2Host.begin(); 25 | 26 | bsec.begin(); 27 | } 28 | 29 | void loop() 30 | { 31 | static auto printTime = millis(); 32 | BHY2Host.update(); 33 | 34 | if (millis() - printTime >= 1000) { 35 | printTime = millis(); 36 | Serial.println(bsec.toString()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/SensorQuaternion.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_QUATERNION_H_ 2 | #define SENSOR_QUATERNION_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | class SensorQuaternion : public SensorClass { 7 | public: 8 | SensorQuaternion() {} 9 | SensorQuaternion(uint8_t id) : SensorClass(id), _data(), _factor(0.000061035) {} 10 | 11 | float x() 12 | { 13 | return _data.x; 14 | } 15 | float y() 16 | { 17 | return _data.y; 18 | } 19 | float z() 20 | { 21 | return _data.z; 22 | } 23 | float w() 24 | { 25 | return _data.w; 26 | } 27 | float accuracy() 28 | { 29 | return _data.accuracy; 30 | } 31 | 32 | float getFactor() 33 | { 34 | return _factor; 35 | } 36 | 37 | void setData(SensorDataPacket &data) 38 | { 39 | DataParser::parseQuaternion(data, _data, _factor); 40 | } 41 | 42 | void setData(SensorLongDataPacket &data) {} 43 | 44 | String toString() 45 | { 46 | return _data.toString(); 47 | } 48 | 49 | private: 50 | DataQuaternion _data; 51 | float _factor; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/SensorClass.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_CLASS_H_ 2 | #define SENSOR_CLASS_H_ 3 | 4 | #include "SensorID.h" 5 | #include "DataParser.h" 6 | 7 | class SensorClass { 8 | public: 9 | __attribute__ ((error("Sensor requires an ID"))) SensorClass(); 10 | SensorClass(uint8_t id); 11 | virtual ~SensorClass(); 12 | 13 | uint8_t id(); 14 | /* 15 | * Sample rate: it indicates the frequency at which a sensor is sampled. 16 | * It is expressed in Hz. 17 | * Latency: it indicates how much ms time a new value is retained in its fifo 18 | * before a notification to the host is sent via interrupt. 19 | * It is expressed in ms. 20 | */ 21 | bool begin(float rate = 1000, uint32_t latency = 0); 22 | void configure(float rate, uint32_t latency); 23 | void end(); 24 | 25 | virtual void setData(SensorDataPacket &data) = 0; 26 | virtual void setData(SensorLongDataPacket &data) = 0; 27 | virtual String toString() = 0; 28 | 29 | protected: 30 | uint8_t _id; 31 | bool _subscribed; 32 | }; 33 | 34 | #endif -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/SensorQuaternion.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_QUATERNION_H_ 2 | #define SENSOR_QUATERNION_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | class SensorQuaternion : public SensorClass { 7 | public: 8 | SensorQuaternion() {} 9 | SensorQuaternion(uint8_t id) : SensorClass(id), _data(), _factor(0.000061035) {} 10 | 11 | float x() 12 | { 13 | return _data.x; 14 | } 15 | float y() 16 | { 17 | return _data.y; 18 | } 19 | float z() 20 | { 21 | return _data.z; 22 | } 23 | float w() 24 | { 25 | return _data.w; 26 | } 27 | float accuracy() 28 | { 29 | return _data.accuracy; 30 | } 31 | 32 | float getFactor() 33 | { 34 | return _factor; 35 | } 36 | 37 | void setData(SensorDataPacket &data) 38 | { 39 | DataParser::parseQuaternion(data, _data, _factor); 40 | } 41 | 42 | void setData(SensorLongDataPacket &data) {} 43 | 44 | String toString() 45 | { 46 | return _data.toString(); 47 | } 48 | 49 | private: 50 | float _factor; 51 | DataQuaternion _data; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/Temperature/Temperature.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sketch shows how an arduino board can act as a host for nicla. 3 | * An host board can configure the sensors of nicla and then read their values. 4 | * The host board should be connected to nicla through the eslov connector. 5 | * 6 | * In this example, the temperature sensor is enabled and its 7 | * values are periodically read and then printed to the serial channel 8 | * 9 | * NOTE: if Nicla is used as a Shield on top of a MKR board, 10 | * please use BHY2Host.begin(false, NICLA_AS_SHIELD) 11 | */ 12 | 13 | #include "Arduino.h" 14 | #include "Arduino_BHY2Host.h" 15 | 16 | Sensor temp(SENSOR_ID_TEMP); 17 | 18 | void setup() 19 | { 20 | Serial.begin(115200); 21 | while(!Serial); 22 | 23 | BHY2Host.begin(); 24 | 25 | temp.begin(); 26 | } 27 | 28 | void loop() 29 | { 30 | static auto printTime = millis(); 31 | BHY2Host.update(); 32 | 33 | if (millis() - printTime >= 1000) { 34 | printTime = millis(); 35 | Serial.println(String("Temperature: ") + String(temp.value(), 3)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/Orientation/Orientation.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sketch shows how an arduino board can act as a host for nicla. 3 | * An host board can configure the sensors of nicla and then read their values. 4 | * The host board should be connected to nicla through the eslov connector. 5 | * 6 | * In this example, the orientation sensor is enabled and its 7 | * values are periodically read and then printed to the serial channel 8 | * 9 | * NOTE: if Nicla is used as a Shield on top of a MKR board, 10 | * please use BHY2Host.begin(false, NICLA_AS_SHIELD) 11 | */ 12 | 13 | #include "Arduino.h" 14 | #include "Arduino_BHY2Host.h" 15 | 16 | SensorOrientation ori(SENSOR_ID_ORI); 17 | 18 | void setup() 19 | { 20 | Serial.begin(115200); 21 | while(!Serial); 22 | 23 | BHY2Host.begin(); 24 | 25 | ori.begin(); 26 | } 27 | 28 | void loop() 29 | { 30 | static auto printTime = millis(); 31 | BHY2Host.update(); 32 | 33 | if (millis() - printTime >= 1000) { 34 | printTime = millis(); 35 | Serial.println(String("Orientation values: ") + ori.toString()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/Accelerometer/Accelerometer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sketch shows how an arduino board can act as a host for nicla. 3 | * An host board can configure the sensors of nicla and then read their values. 4 | * The host board should be connected to nicla through the eslov connector. 5 | * 6 | * In this example, the accelerometer sensor is enabled and its 7 | * values are periodically read and then printed to the serial channel 8 | * 9 | * NOTE: if Nicla is used as a Shield on top of a MKR board, 10 | * please use BHY2Host.begin(false, NICLA_AS_SHIELD) 11 | */ 12 | 13 | #include "Arduino.h" 14 | #include "Arduino_BHY2Host.h" 15 | 16 | SensorXYZ accel(SENSOR_ID_ACC); 17 | 18 | void setup() 19 | { 20 | // debug port 21 | Serial.begin(115200); 22 | while(!Serial); 23 | 24 | BHY2Host.begin(); 25 | 26 | accel.begin(); 27 | } 28 | 29 | void loop() 30 | { 31 | static auto printTime = millis(); 32 | BHY2Host.update(); 33 | 34 | if (millis() - printTime >= 1000) { 35 | printTime = millis(); 36 | Serial.println(String("Acceleration values: ") + accel.toString()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/Pressure/Pressure.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example shows how to use the access the pressure data and send it over serial. 3 | * 4 | * Every 1 second, this sketch will send the pressure in hPa over serial. 5 | * SensorID 129 (SENSOR_ID_BARO) is read with the Sensor class from the BMP390. 6 | * 7 | * Instructions: 8 | * 1. Upload this sketch to your Nicla Sense ME board. 9 | * 2. Open the Serial Monitor at a baud rate of 115200. 10 | * 3. The pressure will be printed to the serial monitor. 11 | * 12 | * Initial author: @mcmchris 13 | */ 14 | 15 | #include "Arduino_BHY2.h" 16 | 17 | 18 | unsigned long previousMillis = 0; // will store last time the sensor was updated 19 | const long interval = 1000; 20 | 21 | Sensor pressure(SENSOR_ID_BARO); 22 | 23 | void setup() { 24 | Serial.begin(9600); 25 | BHY2.begin(); 26 | pressure.begin(); 27 | } 28 | 29 | void loop() { 30 | BHY2.update(); 31 | unsigned long currentMillis = millis(); 32 | if (currentMillis - previousMillis >= interval) { 33 | previousMillis = currentMillis; 34 | Serial.println(String(pressure.value()) + " hPa"); 35 | } 36 | } -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/SensorBSEC2.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_BSEC2_H_ 2 | #define SENSOR_BSEC2_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | 7 | class SensorBSEC2 : public SensorClass { 8 | public: 9 | SensorBSEC2() {} 10 | SensorBSEC2(uint8_t id) : SensorClass(id), _data() {} 11 | 12 | bool getNewDataFlag() { return _newDataFlag;} 13 | void setNewDataFlag(bool flag) { _newDataFlag = flag;} 14 | 15 | uint8_t gas_estimates0() {return _data.gas_estimates[0];} 16 | uint8_t gas_estimates1() {return _data.gas_estimates[1];} 17 | uint8_t gas_estimates2() {return _data.gas_estimates[2];} 18 | uint8_t gas_estimates3() {return _data.gas_estimates[3];} 19 | uint8_t accuracy() {return _data.accuracy;} 20 | 21 | 22 | void setData(SensorDataPacket &data) 23 | { 24 | if (_id == SENSOR_ID_BSEC2 ) { 25 | DataParser::parseBSEC2(data, _data); 26 | _newDataFlag = true; 27 | } 28 | } 29 | 30 | void setData(SensorLongDataPacket &data) 31 | { 32 | } 33 | 34 | String toString() 35 | { 36 | return _data.toString(); 37 | } 38 | 39 | private: 40 | DataBSEC2 _data; 41 | bool _newDataFlag; 42 | }; 43 | #endif 44 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/SensorOrientation.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_ORIENTATION_H_ 2 | #define SENSOR_ORIENTATION_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | class SensorOrientation : public SensorClass { 7 | public: 8 | SensorOrientation() {} 9 | SensorOrientation(uint8_t id) : SensorClass(id), _data(), _factor(0) { 10 | for (int i = 0; i < NUM_SUPPORTEND_SENSOR; i++) { 11 | if (SensorList[i].id == id) { 12 | _factor = SensorList[i].scaleFactor; 13 | } 14 | } 15 | } 16 | 17 | float heading() 18 | { 19 | return _data.heading; 20 | } 21 | float pitch() 22 | { 23 | return _data.pitch; 24 | } 25 | float roll() 26 | { 27 | return _data.roll; 28 | } 29 | 30 | void setFactor(float factor) 31 | { 32 | _factor = factor; 33 | } 34 | 35 | float getFactor() 36 | { 37 | return _factor; 38 | } 39 | 40 | void setData(SensorDataPacket &data) 41 | { 42 | DataParser::parseEuler(data, _data, _factor); 43 | } 44 | 45 | void setData(SensorLongDataPacket &data) {} 46 | 47 | String toString() 48 | { 49 | return _data.toString(); 50 | } 51 | 52 | private: 53 | DataOrientation _data; 54 | float _factor; 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/SensorOrientation.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_ORIENTATION_H_ 2 | #define SENSOR_ORIENTATION_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | class SensorOrientation : public SensorClass { 7 | public: 8 | SensorOrientation() {} 9 | SensorOrientation(uint8_t id) : SensorClass(id), _data(), _factor(0) { 10 | for (int i = 0; i < NUM_SUPPORTEND_SENSOR; i++) { 11 | if (SensorList[i].id == id) { 12 | _factor = SensorList[i].scaleFactor; 13 | } 14 | } 15 | } 16 | 17 | float heading() 18 | { 19 | return _data.heading; 20 | } 21 | float pitch() 22 | { 23 | return _data.pitch; 24 | } 25 | float roll() 26 | { 27 | return _data.roll; 28 | } 29 | 30 | void setFactor(float factor) 31 | { 32 | _factor = factor; 33 | } 34 | 35 | float getFactor() 36 | { 37 | return _factor; 38 | } 39 | 40 | void setData(SensorDataPacket &data) 41 | { 42 | DataParser::parseEuler(data, _data, _factor); 43 | } 44 | 45 | void setData(SensorLongDataPacket &data) {} 46 | 47 | String toString() 48 | { 49 | return _data.toString(); 50 | } 51 | 52 | private: 53 | float _factor; 54 | DataOrientation _data; 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/SensorManager.cpp: -------------------------------------------------------------------------------- 1 | #include "SensorManager.h" 2 | 3 | SensorManager::SensorManager() : 4 | _sensors(), 5 | _sensorsLen(0) 6 | { 7 | } 8 | 9 | void SensorManager::process(SensorDataPacket &data) 10 | { 11 | for (int i = 0; i < _sensorsLen; i++) { 12 | if (data.sensorId == _sensors[i]->id()) { 13 | _sensors[i]->setData(data); 14 | _sensors[i]->setDataAvailFlag(); 15 | return; 16 | } 17 | } 18 | } 19 | 20 | void SensorManager::process(SensorLongDataPacket &data) 21 | { 22 | for (int i = 0; i < _sensorsLen; i++) { 23 | if (data.sensorId == _sensors[i]->id()) { 24 | _sensors[i]->setData(data); 25 | _sensors[i]->setDataAvailFlag(); 26 | return; 27 | } 28 | } 29 | } 30 | 31 | void SensorManager::subscribe(SensorClass *sensor) 32 | { 33 | _sensors[_sensorsLen++] = sensor; 34 | } 35 | 36 | void SensorManager::unsubscribe(SensorClass *sensor) 37 | { 38 | for (int i = 0; i < _sensorsLen; i++) { 39 | if (sensor->id() == _sensors[i]->id()) { 40 | _sensors[i] = _sensors[--_sensorsLen]; 41 | _sensors[_sensorsLen] = NULL; 42 | return; // can more sensor objects use the same sensor id? 43 | } 44 | } 45 | } 46 | 47 | SensorManager sensorManager; 48 | -------------------------------------------------------------------------------- /.github/workflows/spell-check-task.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/spell-check-task.md 2 | name: Spell Check 3 | 4 | env: 5 | # See: https://github.com/actions/setup-python/tree/main#available-versions-of-python 6 | PYTHON_VERSION: "3.9" 7 | 8 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 9 | on: 10 | push: 11 | pull_request: 12 | schedule: 13 | # Run every Tuesday at 8 AM UTC to catch new misspelling detections resulting from dictionary updates. 14 | - cron: "0 8 * * TUE" 15 | workflow_dispatch: 16 | repository_dispatch: 17 | 18 | jobs: 19 | spellcheck: 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v6 25 | 26 | - name: Install Python 27 | uses: actions/setup-python@v6 28 | with: 29 | python-version: ${{ env.PYTHON_VERSION }} 30 | 31 | - name: Install Poetry 32 | run: pip install poetry 33 | 34 | - name: Install Task 35 | uses: arduino/setup-task@v2 36 | with: 37 | repo-token: ${{ secrets.GITHUB_TOKEN }} 38 | version: 3.x 39 | 40 | - name: Spell check 41 | run: task general:check-spelling 42 | -------------------------------------------------------------------------------- /bootloader/src/pmic_driver.cpp: -------------------------------------------------------------------------------- 1 | /* 12/23/2017 Copyright Tlera Corporation 2 | * 3 | * Created by Kris Winer 4 | * 5 | This basic sketch is to operate the BQ25120A battery charger 6 | https://www.ti.com/lit/ds/symlink/bq25120a.pdf?ts=1610608851953&ref_url=https%253A%252F%252Fwww.startpage.com%252F 7 | 8 | The sketch uses SDA/SCL pins of I2C0. 9 | Library may be used freely and without limit only with attribution. 10 | 11 | */ 12 | #include 13 | #include 14 | #include "BQ25120A.h" 15 | 16 | BQ25120A::BQ25120A(){ 17 | } 18 | 19 | I2C i2c0(I2C_SDA0, I2C_SCL0); 20 | 21 | extern mbed::DigitalOut cd; 22 | 23 | uint8_t BQ25120A::getStatus() 24 | { 25 | uint8_t c = readByte(BQ25120A_ADDRESS, BQ25120A_STATUS); // Read PRODUCT_ID register for BQ25120A 26 | return c; 27 | } 28 | 29 | void BQ25120A::writeByte(uint8_t address, uint8_t subAddress, uint8_t data) 30 | { 31 | cd = 1; 32 | char command[2]; 33 | command[0] = subAddress; 34 | command[1] = data; 35 | i2c0.write(address << 1, command, 2); 36 | cd = 0; 37 | } 38 | 39 | uint8_t BQ25120A::readByte(uint8_t address, uint8_t subAddress) 40 | { 41 | cd = 1; 42 | char response = 0xFF; 43 | int ret = i2c0.write(address << 1, (const char*)&subAddress, 1); 44 | ret = i2c0.read(address << 1, &response, 1); 45 | cd = 0; 46 | return response; 47 | } -------------------------------------------------------------------------------- /tools/bhy-controller/README.md: -------------------------------------------------------------------------------- 1 | # bhy-controller commands 2 | 3 | When a command is called without arguments, the possible subcommands and/or flags are printed 4 | 5 | Example: 6 | ``` 7 | ./bhy 8 | ``` 9 | will result in: 10 | ``` 11 | A command is required 12 | sensor 13 | to control bhy sensors 14 | dfu 15 | to upload new firmware for nicla or bhy 16 | list 17 | list available serial ports 18 | webserver 19 | start a local webserver and open the webserver 20 | ``` 21 | 22 | List of useful commands: 23 | ```bash 24 | # list available serial ports 25 | ./bhy list 26 | 27 | # start the local webserver and open the main webpage 28 | ./bhy webserver 29 | 30 | # read available sensor data 31 | ./bhy sensor read -p /dev/ttyACM2 32 | 33 | # continuously read sensor data when available 34 | ./bhy sensor read -live -p /dev/ttyACM2 35 | 36 | # configure sensor 10 with a sample rate of 1 Hz and latency of 0ms 37 | ./bhy sensor config -p /dev/ttyACM2 -sensor 10 -rate 1 -latency 0 38 | 39 | # disable sensor 10 40 | ./bhy sensor config -p /dev/ttyACM2 -sensor 10 -rate 0 -latency 0 41 | 42 | # update bhi firmware 43 | ./bhy dfu -t nicla -bin fw.bin -p /dev/ttyACM2 44 | 45 | # update Nicla firmware 46 | ./bhy dfu -t bhi -bin fw.bin -p /dev/ttyACM2 47 | ``` 48 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/Passthrough/Passthrough.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sketch allows to control nicla from a PC. 3 | * Upload this sketch on an arduino board connected to nicla 4 | * through the eslov connector. Then connect the same arduino board 5 | * to your PC. 6 | * Now you can use the arduino-bhy tool, written in golang, 7 | * to control nicla from either the PC command line or from a web page. 8 | * 9 | * NOTE: if Nicla is used as a Shield on top of a MKR board, 10 | * please use BHY2Host.begin(true, NICLA_AS_SHIELD) 11 | */ 12 | 13 | #include "Arduino.h" 14 | #include "Arduino_BHY2Host.h" 15 | 16 | #ifdef ARDUINO_ARCH_MBED 17 | #include "USB/PluggableUSBSerial.h" 18 | arduino::USBSerial SerialUSB2(false); 19 | #else 20 | Serial_ SerialUSB2(USBDevice); 21 | #endif 22 | 23 | // Set DEBUG to true in order to enable debug print 24 | #define DEBUG false 25 | 26 | void setup() 27 | { 28 | Serial.begin(115200); 29 | Serial1.begin(115200); 30 | SerialUSB2.begin(115200); 31 | #if DEBUG 32 | // When the passthrough is enabled, Serial is busy -> 33 | // so it cannot be used for debugging. Serial1 is used instead 34 | BHY2Host.debug(Serial1); 35 | #endif 36 | 37 | BHY2Host.begin(true); 38 | } 39 | 40 | void loop() 41 | { 42 | if (Serial1.available()) { 43 | SerialUSB2.write(Serial1.read()); 44 | } 45 | BHY2Host.update(); 46 | } 47 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/ActivityRecognition/ActivityRecognition.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example shows how to use the Nicla Sense ME library to detect activity and send it over serial. 3 | * 4 | * Every 1 second, this sketch will send the latest activity detected via the BHI260AP sensor to the serial port. 5 | * Without any additional training, it is possible to detect Still, Walking, Running and Tilting activities 6 | * A full description of supported activities is available in Table 93 of the BHI260AP datasheet. 7 | * 8 | * Instructions: 9 | * 1. Upload this sketch to your Nicla Sense ME board. 10 | * 2. Open the Serial Monitor at a baud rate of 115200. 11 | * 3. Observe the detected activity, by moving the Nicla Sense ME board. 12 | * 13 | * Initial author: @mcmchris 14 | */ 15 | 16 | #include "Arduino_BHY2.h" 17 | 18 | SensorActivity active(SENSOR_ID_AR); 19 | 20 | unsigned long previousMillis = 0; // will store last time the sensor was updated 21 | const long interval = 1000; 22 | 23 | 24 | void setup() { 25 | Serial.begin(115200); 26 | BHY2.begin(); 27 | active.begin(); 28 | } 29 | 30 | void loop() { 31 | BHY2.update(); 32 | unsigned long currentMillis = millis(); 33 | if (currentMillis - previousMillis >= interval) { 34 | previousMillis = currentMillis; 35 | Serial.println(String("Activity info: ") + active.toString()); 36 | } 37 | } -------------------------------------------------------------------------------- /Arduino_BHY2/examples/Magnetometer/Magnetometer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example shows how to use the access the magnetometer data and send it over serial. 3 | * 4 | * Every 1 second, this sketch will send the heading of the magnetometer over serial. 5 | * by calculating the arctangent between the x and y axis and multiplied in a conversion 6 | * factor to convert the headings from radians to degrees. 7 | * 8 | * Instructions: 9 | * 1. Upload this sketch to your Nicla Sense ME board. 10 | * 2. Open the Serial Monitor at a baud rate of 115200. 11 | * 3. The heading of the magnetometer will be printed to the serial monitor. 12 | * 13 | * Initial author: @mcmchris 14 | */ 15 | 16 | #include "Arduino_BHY2.h" 17 | #include "math.h" 18 | 19 | SensorXYZ magnetometer(SENSOR_ID_MAG); 20 | 21 | float heading = 0; 22 | unsigned long previousMillis = 0; // will store last time the sensor was updated 23 | const long interval = 1000; 24 | 25 | void setup() { 26 | Serial.begin(115200); 27 | BHY2.begin(); 28 | magnetometer.begin(); 29 | } 30 | 31 | void loop() { 32 | BHY2.update(); 33 | unsigned long currentMillis = millis(); 34 | if (currentMillis - previousMillis >= interval) { 35 | previousMillis = currentMillis; 36 | heading = round(atan2(magnetometer.x(), magnetometer.y()) * 180.0 / PI); 37 | Serial.println(String(heading) + "º"); 38 | } 39 | } -------------------------------------------------------------------------------- /Arduino_BHY2/examples/Fail_Safe_flasher/Fail_Safe_flasher.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Use this sketch to flash a binary (fw.h) that can be used as Fail Safe for Nicla Sense. 3 | * Here a simple colorful blink for the RGB led is used. 4 | * 5 | * To use a different binary, generate a new fw.h: 6 | * xxd -i your_sketch.ino.bin >> fw.h 7 | * 8 | */ 9 | 10 | 11 | #include "SPIFBlockDevice.h" 12 | #include "LittleFileSystem.h" 13 | #include "fw.h" 14 | 15 | SPIFBlockDevice spif(SPI_PSELMOSI0, SPI_PSELMISO0, SPI_PSELSCK0, CS_FLASH); 16 | mbed::LittleFileSystem fs("fs"); 17 | FILE *file; 18 | 19 | void setup() { 20 | Serial.begin(115200); 21 | delay(3000); 22 | 23 | char crc = 0; 24 | for (int i = 0; i < Fail_Safe_bin_len; i++) { 25 | crc = crc ^ Fail_Safe_bin[i]; 26 | } 27 | Serial.print("The computed CRC is 0x"); 28 | Serial.println(crc, HEX); 29 | 30 | Serial.println("Writing Fail Safe binary in SPIFlash..."); 31 | 32 | int err = fs.mount(&spif); 33 | if (err) { 34 | err = fs.reformat(&spif); 35 | Serial.print("Error mounting file system: "); 36 | Serial.println(err); 37 | } 38 | 39 | file = fopen("/fs/FAIL_SAFE.BIN", "wb"); 40 | int ret = fwrite(Fail_Safe_bin, Fail_Safe_bin_len, 1, file); 41 | fwrite(&crc, 1, 1, file); 42 | delay(100); 43 | fclose(file); 44 | 45 | Serial.print("Done!"); 46 | } 47 | 48 | void loop() { 49 | delay(100); 50 | } 51 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/SensorClass.cpp: -------------------------------------------------------------------------------- 1 | #include "SensorClass.h" 2 | #include "SensorManager.h" 3 | #include "Arduino_BHY2Host.h" 4 | #include "EslovHandler.h" 5 | 6 | SensorClass::SensorClass() : 7 | _id(0), 8 | _subscribed(false) 9 | { 10 | } 11 | 12 | SensorClass::SensorClass(uint8_t id) : 13 | _id(id), 14 | _subscribed(false) 15 | { 16 | } 17 | 18 | SensorClass::~SensorClass() 19 | { 20 | end(); 21 | } 22 | 23 | uint8_t SensorClass::id() 24 | { 25 | return _id; 26 | } 27 | 28 | bool SensorClass::begin(float rate, uint32_t latency) 29 | { 30 | configure(rate, latency); 31 | return true; 32 | } 33 | 34 | void SensorClass::configure(float rate, uint32_t latency) 35 | { 36 | SensorConfigurationPacket config {_id, rate, latency}; 37 | 38 | if (BHY2Host.getNiclaConnection() == NICLA_VIA_BLE) { 39 | BHY2Host.configureSensor(config); 40 | } else { 41 | uint8_t ack = 0; 42 | while(ack != 15) { 43 | BHY2Host.configureSensor(config); 44 | ack = BHY2Host.requestAck(); 45 | } 46 | } 47 | 48 | if (rate == 0 && _subscribed) { 49 | // unregister sensor 50 | sensorManager.unsubscribe(this); 51 | _subscribed = false; 52 | } else if (rate > 0 && !_subscribed) { 53 | // register sensor 54 | sensorManager.subscribe(this); 55 | _subscribed = true; 56 | } 57 | 58 | } 59 | 60 | void SensorClass::end() 61 | { 62 | configure(0, 0); 63 | } 64 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/Sensor.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_H_ 2 | #define SENSOR_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | class Sensor : public SensorClass { 7 | public: 8 | Sensor() {} 9 | Sensor(uint8_t id) : SensorClass(id), _value(0.), _factor(0) { 10 | for (int i = 0; i < NUM_SUPPORTEND_SENSOR; i++) { 11 | if (SensorList[i].id == id) { 12 | _factor = SensorList[i].scaleFactor; 13 | _format = SensorList[i].payload; 14 | } 15 | } 16 | } 17 | 18 | float value() 19 | { 20 | if (_format == PEVENT) { 21 | if (_value > 0) { 22 | _value = 0; 23 | return 1; 24 | } 25 | return 0; 26 | } 27 | return _value; 28 | } 29 | 30 | void setFactor(float factor) 31 | { 32 | _factor = factor; 33 | } 34 | 35 | float getFactor() 36 | { 37 | return _factor; 38 | } 39 | 40 | void setData(SensorDataPacket &data) 41 | { 42 | DataParser::parseData(data, _value, _factor, _format); 43 | } 44 | 45 | void setData(SensorLongDataPacket &data) {} 46 | 47 | String toString() 48 | { 49 | if (_format == PEVENT) { 50 | if (value()) { 51 | return (String)("Event detected\n"); 52 | } 53 | return (String)(""); 54 | } 55 | return (String)("Data value: " + String(_value, 3) + "\n"); 56 | } 57 | 58 | private: 59 | float _value; 60 | float _factor; 61 | SensorPayload _format; 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/Sensor.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_H_ 2 | #define SENSOR_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | class Sensor : public SensorClass { 7 | public: 8 | Sensor() {} 9 | Sensor(uint8_t id) : SensorClass(id), _value(0.), _factor(0) { 10 | for (int i = 0; i < NUM_SUPPORTEND_SENSOR; i++) { 11 | if (SensorList[i].id == id) { 12 | _factor = SensorList[i].scaleFactor; 13 | _format = SensorList[i].payload; 14 | } 15 | } 16 | } 17 | 18 | float value() 19 | { 20 | if (_format == PEVENT) { 21 | if (_value > 0) { 22 | _value = 0; 23 | return 1; 24 | } 25 | return 0; 26 | } 27 | return _value; 28 | } 29 | 30 | void setFactor(float factor) 31 | { 32 | _factor = factor; 33 | } 34 | 35 | float getFactor() 36 | { 37 | return _factor; 38 | } 39 | 40 | void setData(SensorDataPacket &data) 41 | { 42 | DataParser::parseData(data, _value, _factor, _format); 43 | } 44 | 45 | void setData(SensorLongDataPacket &data) {} 46 | 47 | String toString() 48 | { 49 | if (_format == PEVENT) { 50 | if (value()) { 51 | return (String)("Event detected\n"); 52 | } 53 | return (String)(""); 54 | } 55 | return (String)("Data value: " + String(_value, 3) + "\n"); 56 | } 57 | 58 | private: 59 | float _factor; 60 | float _value; 61 | SensorPayload _format; 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/BLEHandler.h: -------------------------------------------------------------------------------- 1 | #ifndef BLE_HANDLER_H_ 2 | #define BLE_HANDLER_H_ 3 | 4 | #include "Arduino.h" 5 | #include "sensors/SensorTypes.h" 6 | #include "DFUTypes.h" 7 | #include "ArduinoBLE.h" 8 | 9 | class BLEHandler { 10 | public: 11 | BLEHandler(); 12 | virtual ~BLEHandler(); 13 | /** 14 | * @brief initialize BLE for DFU and sensor data transfer and connect to Nicla device 15 | * 16 | * @return true successful connection to Nicla over BLE 17 | * 18 | */ 19 | bool begin(); 20 | /** 21 | * @brief Read data from the Nicla over BLE to the host device. 22 | * 23 | */ 24 | void update(); 25 | /** 26 | * @brief Disconnected Nicla from host and close BLE connection 27 | * 28 | */ 29 | void end(); 30 | 31 | /** 32 | * @brief Write a configuration packet to the Nicla over BLE. First byte sets the opcode 33 | * 34 | * @param config Instance of @see SensorConfigurationPacket class, with sensorID, sampleRate and latency 35 | */ 36 | void writeConfigPacket(SensorConfigurationPacket& config); 37 | 38 | private: 39 | friend class Arduino_BHY2Host; 40 | 41 | static void receivedSensorData(BLEDevice central, BLECharacteristic characteristic); 42 | static void receivedLongSensorData(BLEDevice central, BLECharacteristic characteristic); 43 | bool connectToNicla(); 44 | 45 | void debug(Stream &stream); 46 | Stream *_debug; 47 | }; 48 | 49 | extern BLEHandler bleHandler; 50 | 51 | #endif -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/SensorManager.cpp: -------------------------------------------------------------------------------- 1 | #include "SensorManager.h" 2 | #include "SensorID.h" 3 | 4 | SensorManager::SensorManager() : 5 | _sensors(), 6 | _sensorsLen(0) 7 | { 8 | } 9 | 10 | void SensorManager::process(SensorLongDataPacket &data) 11 | { 12 | for (int i = 0; i < _sensorsLen; i++) { 13 | if (data.sensorId == _sensors[i]->id()) { 14 | 15 | bool longSensor = false; 16 | 17 | for (int i = 0; i < NUM_LONG_SENSOR; i++) { 18 | if (LongSensorList[i].id == data.sensorId) { 19 | longSensor = true; 20 | break; 21 | } 22 | } 23 | 24 | if (longSensor) { 25 | _sensors[i]->setData(data); 26 | } else { 27 | // All the other sensors have short payloads 28 | SensorDataPacket shortData; 29 | memcpy(&shortData, &data, sizeof(SensorDataPacket)); 30 | _sensors[i]->setData(shortData); 31 | } 32 | 33 | return; 34 | 35 | } 36 | } 37 | } 38 | 39 | void SensorManager::subscribe(SensorClass *sensor) 40 | { 41 | _sensors[_sensorsLen++] = sensor; 42 | } 43 | 44 | void SensorManager::unsubscribe(SensorClass *sensor) 45 | { 46 | for (int i = 0; i < _sensorsLen; i++) { 47 | if (sensor->id() == _sensors[i]->id()) { 48 | _sensors[i] = _sensors[--_sensorsLen]; 49 | _sensors[_sensorsLen] = NULL; 50 | return; // can more sensor objects use the same sensor id? 51 | } 52 | } 53 | } 54 | 55 | SensorManager sensorManager; 56 | -------------------------------------------------------------------------------- /.github/workflows/check-arduino.yml: -------------------------------------------------------------------------------- 1 | name: Check Arduino 2 | 3 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | paths: 7 | - ".github/workflows/check-arduino.ya?ml" 8 | - "Arduino_BHY2/**" 9 | - "Arduino_BHY2Host/**" 10 | pull_request: 11 | paths: 12 | - ".github/workflows/check-arduino.ya?ml" 13 | - "Arduino_BHY2/**" 14 | - "Arduino_BHY2Host/**" 15 | schedule: 16 | # Run every Tuesday at 8 AM UTC to catch breakage caused by new rules added to Arduino Lint. 17 | - cron: "0 8 * * TUE" 18 | workflow_dispatch: 19 | repository_dispatch: 20 | 21 | jobs: 22 | lint: 23 | name: ${{ matrix.project.path }} 24 | runs-on: ubuntu-latest 25 | 26 | strategy: 27 | fail-fast: false 28 | 29 | matrix: 30 | project: 31 | - path: Arduino_BHY2 32 | project-type: library 33 | - path: Arduino_BHY2Host 34 | project-type: library 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v6 39 | 40 | - name: Arduino Lint 41 | uses: arduino/arduino-lint-action@v2 42 | with: 43 | path: ${{ matrix.project.path }} 44 | project-type: ${{ matrix.project.project-type }} 45 | compliance: strict 46 | library-manager: update 47 | # Always use this setting for official repositories. Remove for 3rd party projects. 48 | official: true 49 | -------------------------------------------------------------------------------- /tools/bhy-controller/src/dfu/packet.go: -------------------------------------------------------------------------------- 1 | package dfu 2 | 3 | import "encoding/binary" 4 | 5 | const ( 6 | Ack = (byte)(0x0F) 7 | Nack = (byte)(0x00) 8 | PayloadSize = 232 9 | PackSize = PayloadSize + 4 10 | ) 11 | 12 | type dfuPacket struct { 13 | opcode uint8 14 | last uint8 15 | index uint16 16 | length uint16 17 | payload []byte 18 | } 19 | 20 | func newDFUPacket(target string, i int, len int, p []byte) (d *dfuPacket) { 21 | d = &dfuPacket{ 22 | index: uint16(i), 23 | length: uint16(len), 24 | payload: p, 25 | } 26 | d.setOpcode(target) 27 | return 28 | } 29 | 30 | func (d *dfuPacket) build() []byte { 31 | b := append([]byte{d.opcode}, byte(d.last)) 32 | 33 | if d.last == 1 { 34 | len := make([]byte, 2) 35 | binary.LittleEndian.PutUint16(len, (d.length)) 36 | b = append(b, len...) 37 | } else { 38 | idx := make([]byte, 2) 39 | binary.LittleEndian.PutUint16(idx, (d.index)) 40 | b = append(b, idx...) 41 | } 42 | 43 | b = append(b, d.payload...) 44 | 45 | return b 46 | } 47 | 48 | func (d *dfuPacket) setPayload(p []byte) { 49 | d.payload = p 50 | } 51 | 52 | func (d *dfuPacket) setOpcode(target string) { 53 | oc := 0 54 | if target == "bhi" { 55 | oc = 1 56 | } 57 | d.opcode = uint8(oc) 58 | } 59 | 60 | func (d *dfuPacket) setLast(l int) { 61 | d.last = uint8(l) 62 | } 63 | 64 | func (d *dfuPacket) setIndex(i int) { 65 | d.index = uint16(i) 66 | } 67 | 68 | func (d *dfuPacket) setLength(l int) { 69 | d.length = uint16(l) 70 | } 71 | -------------------------------------------------------------------------------- /bootloader/mbed_app.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": [ 3 | "bare-metal", 4 | "rtos-api", 5 | "spif-driver", 6 | "filesystem", 7 | "littlefs", 8 | "fat_chan", 9 | "flashiap-block-device" 10 | ], 11 | "config": { 12 | "update_file": { 13 | "help": "Path to the application update binary on the SD card", 14 | "value": "\"mbed-os-example-blinky_application.bin\"" 15 | }, 16 | "spif_mosi": { 17 | "help": "MCU pin connected to the SPI MOSI pin of the flash", 18 | "value": "SPI_PSELMOSI0" 19 | }, 20 | "spif_miso": { 21 | "help": "MCU pin connected to the SPI MISO pin of the flash", 22 | "value": "SPI_PSELMISO0" 23 | }, 24 | "spif_sck": { 25 | "help": "MCU pin connected to the SPI SCK pin of the flash", 26 | "value": "SPI_PSELSCK0" 27 | }, 28 | "spif_cs": { 29 | "help": "MCU pin connected to the SPI CS pin of the flash", 30 | "value": "CS_FLASH" 31 | } 32 | }, 33 | "target_overrides": { 34 | "NICLA": { 35 | "target.restrict_size": "0x10000", 36 | "target.components_add" : ["SPIF"], 37 | "target.c_lib": "small", 38 | "platform.stdio-baud-rate": 115200, 39 | "platform.default-serial-baud-rate": 115200, 40 | "target.macros_remove": ["CONFIG_GPIO_AS_PINRESET"] 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/SensorClass.cpp: -------------------------------------------------------------------------------- 1 | #include "SensorClass.h" 2 | #include "SensorManager.h" 3 | 4 | SensorClass::SensorClass() : 5 | _id(0), 6 | _dataAvail(false), 7 | _subscribed(false) 8 | { 9 | } 10 | 11 | SensorClass::SensorClass(uint8_t id) : 12 | _id(id), 13 | _dataAvail(false), 14 | _subscribed(false) 15 | { 16 | } 17 | 18 | SensorClass::~SensorClass() 19 | { 20 | end(); 21 | } 22 | 23 | uint8_t SensorClass::id() 24 | { 25 | return _id; 26 | } 27 | 28 | bool SensorClass::begin(float rate, uint32_t latency) 29 | { 30 | if (sensortec.hasSensor(_id)) { 31 | configure(rate, latency); 32 | return true; 33 | } 34 | return false; 35 | } 36 | 37 | void SensorClass::configure(float rate, uint32_t latency) 38 | { 39 | SensorConfigurationPacket config {_id, rate, latency}; 40 | sensortec.configureSensor(config); 41 | 42 | if (rate == 0 && _subscribed) { 43 | // unregister sensor 44 | sensorManager.unsubscribe(this); 45 | _subscribed = false; 46 | } else if (rate > 0 && !_subscribed) { 47 | // register sensor 48 | sensorManager.subscribe(this); 49 | _subscribed = true; 50 | } 51 | 52 | } 53 | 54 | int SensorClass::setRange(uint16_t range) 55 | { 56 | return sensortec.configureSensorRange(_id, range); 57 | } 58 | 59 | const SensorConfig SensorClass::getConfiguration() 60 | { 61 | SensorConfig config; 62 | 63 | sensortec.getSensorConfiguration(_id, config); 64 | 65 | return config; 66 | } 67 | 68 | void SensorClass::end() 69 | { 70 | configure(0, 0); 71 | } 72 | -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Bosch Sensortec GmbH. All rights reserved. 2 | 3 | BSD-3-Clause 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /bootloader/src/file_utils.cpp: -------------------------------------------------------------------------------- 1 | #include "mbed.h" 2 | #include "common.h" 3 | #include "file_utils.h" 4 | 5 | FileUtils::FileUtils(){} 6 | 7 | long FileUtils::getFileLen(FILE *file) { 8 | 9 | fseek(file, 0, SEEK_END); 10 | long len = ftell(file); 11 | fseek(file, 0, SEEK_SET); 12 | //Decrement len by 1 to remove the CRC from the count 13 | return len - 1; 14 | 15 | } 16 | 17 | char FileUtils::getFileCRC(FILE *file) { 18 | 19 | char crc_file; 20 | 21 | long len = getFileLen(file); 22 | fseek(file, len, SEEK_SET); 23 | fread(&crc_file, 1, 1, file); 24 | 25 | return crc_file; 26 | } 27 | 28 | char FileUtils::computeCRC(FILE *file) { 29 | char buffer[256]; 30 | char crc; 31 | 32 | long len = getFileLen(file); 33 | 34 | //read by chunks of 256 bytes 35 | uint16_t iterations = floor(len/256); 36 | uint8_t spare_bytes = len%256; 37 | 38 | int buffer_index = 2; 39 | 40 | //copy the file content by chunks of 256 bytes into a buffer of chars 41 | for (int i = 0; i < iterations; i++) { 42 | //read 256 bytes into buffer 43 | fread(buffer, 1, 256, file); 44 | 45 | if (i==0) { 46 | //we are at the beginning of the file, so we must compute the first xor between bytes 47 | crc = buffer[0]^buffer[1]; 48 | } 49 | 50 | for(int i=buffer_index; i<256; i++) { 51 | crc = crc^buffer[i]; 52 | } 53 | 54 | buffer_index = 0; 55 | 56 | } 57 | 58 | if (spare_bytes) { 59 | //read the spare bytes, without the CRC 60 | fread(buffer, 1, spare_bytes, file); 61 | 62 | for(int i=0; i> i; 37 | if (maskedVal) { 38 | return _activityArray[i].activityMessage; 39 | } 40 | } 41 | return "Waiting to detect valid activity"; 42 | } 43 | 44 | private: 45 | uint16_t _value; 46 | ActivityBitMask _activityArray[16] = { 47 | {0, "Still activity ended"}, 48 | {1, "Walking activity ended"}, 49 | {2, "Running activity ended"}, 50 | {3, "On bicycle activity ended"}, 51 | {4, "In vehicle activity ended"}, 52 | {5, "Tilting activity ended"}, 53 | {6, "In vehicle still ended"}, 54 | {7, ""}, 55 | {8, "Still activity started"}, 56 | {9, "Walking activity started"}, 57 | {10, "Running activity started"}, 58 | {11, "On bicycle activity started"}, 59 | {12, "IN vehicle activity started"}, 60 | {13, "Tilting activity started"}, 61 | {14, "In vehicle still started"}, 62 | {15, ""} 63 | }; 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/SensorActivity.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_ACTIVITY_H_ 2 | #define SENSOR_ACTIVITY_H_ 3 | 4 | #include "SensorClass.h" 5 | 6 | struct ActivityBitMask{ 7 | uint8_t bit; 8 | String activityMessage; 9 | }; 10 | 11 | class SensorActivity : public SensorClass { 12 | public: 13 | SensorActivity() {} 14 | SensorActivity(uint8_t id) : SensorClass(id), _value(0) {} 15 | 16 | uint16_t value() 17 | { 18 | return _value; 19 | } 20 | 21 | void setData(SensorDataPacket &data) 22 | { 23 | DataParser::parseActivity(data, _value); 24 | } 25 | 26 | void setData(SensorLongDataPacket &data) {} 27 | 28 | String toString() 29 | { 30 | return getActivity(); 31 | } 32 | 33 | String getActivity() 34 | { 35 | for (int i = 0; i < 16; i++) { 36 | uint16_t maskedVal = (_value & (0x0001 << i)) >> i; 37 | if (maskedVal) { 38 | return _activityArray[i].activityMessage; 39 | } 40 | } 41 | return "Waiting to detect valid activity"; 42 | } 43 | 44 | private: 45 | uint16_t _value; 46 | ActivityBitMask _activityArray[16] = { 47 | {0, "Still activity ended"}, 48 | {1, "Walking activity ended"}, 49 | {2, "Running activity ended"}, 50 | {3, "On bicycle activity ended"}, 51 | {4, "In vehicle activity ended"}, 52 | {5, "Tilting activity ended"}, 53 | {6, "In vehicle still ended"}, 54 | {7, ""}, 55 | {8, "Still activity started"}, 56 | {9, "Walking activity started"}, 57 | {10, "Running activity started"}, 58 | {11, "On bicycle activity started"}, 59 | {12, "IN vehicle activity started"}, 60 | {13, "Tilting activity started"}, 61 | {14, "In vehicle still started"}, 62 | {15, ""} 63 | }; 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/Nicla_IoT_Bridge/Nicla_IoT_Bridge.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Upload this sketch to a Host board to use it as an I2C bridge between 3 | * Nicla Sense ME and the Arduino Cloud. 4 | * Nicla Sense ME board needs to run the basic App.ino sketch and to be plugged 5 | * as a shield on top of the Host board. 6 | * 7 | * Before uploading this sketch to the Host board: 8 | * - add your Host device to Arduino Cloud 9 | * - setup temperature and seconds Things in Arduino Cloud 10 | * - get the THING_ID and copy it in thingProperties.h 11 | * - create a Dashboard in Arduino Cloud, like a live chart of the temperature 12 | */ 13 | 14 | #include "thingProperties.h" 15 | 16 | #include "Arduino_BHY2Host.h" 17 | #include 18 | #include 19 | 20 | Sensor tempSensor(SENSOR_ID_TEMP); 21 | 22 | void setup() { 23 | Serial.begin(115200); 24 | while(!Serial) {} 25 | 26 | // Defined in thingProperties.h 27 | initProperties(); 28 | 29 | // Connect to Arduino IoT Cloud 30 | ArduinoCloud.begin(ArduinoIoTPreferredConnection); 31 | 32 | setDebugMessageLevel(2); 33 | ArduinoCloud.printDebugInfo(); 34 | 35 | while(!ArduinoCloud.connected()) { 36 | ArduinoCloud.update(); 37 | delay(10); 38 | } 39 | 40 | Serial.println("Configuring Nicla..."); 41 | #ifdef ARDUINO_ARCH_MBED 42 | BHY2Host.begin(); 43 | #else 44 | BHY2Host.begin(false, NICLA_AS_SHIELD); 45 | #endif 46 | tempSensor.begin(); 47 | } 48 | 49 | void loop() { 50 | static auto printTime = millis(); 51 | BHY2Host.update(); 52 | 53 | if (millis() - printTime > 1000) { 54 | printTime = millis(); 55 | seconds = millis()/1000; 56 | temp = tempSensor.value(); 57 | Serial.print("Temp: "); 58 | Serial.println(temp, 3); 59 | } 60 | 61 | ArduinoCloud.update(); 62 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'type: bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 28 | 29 | ### Description of defect 30 | 31 | 35 | 36 | 37 | #### Target(s) affected by this defect ? 38 | 39 | 40 | #### Toolchain(s) (name and version) displaying this defect ? 41 | 42 | 43 | #### What version of Mbed-os are you using (tag or sha) ? 44 | 53 | 54 | 55 | #### What version(s) of tools are you using. List all that apply (E.g. mbed-cli) 56 | 57 | 58 | #### How is this defect reproduced ? 59 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/AccelGyro/AccelGyro.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example shows how to use the access the IMU data and plot it in real time. 3 | * 4 | * Every 50 milliseconds, this sketch will send the x,y and z accelerometer (SensorID 4) and 5 | * gyroscope (SensorID 22) values over serial from the BHI260AP six axis IMU. 6 | * These six values are visually displayed with the Serial Plotter in the Arduino IDE. 7 | * 8 | * Instructions: 9 | * 1. Upload this sketch to your Nicla Sense ME board. 10 | * 2. Open the Serial Plotter at a baud rate of 115200. 11 | * 3. The three axis values for both the accelerometer and gyroscope will be printed to the Serial Plotter. 12 | * 4. Optionally, you can change the visibility of each data by clicking on the legend. 13 | * 14 | * Initial author: @mcmchris 15 | */ 16 | 17 | #include "Arduino_BHY2.h" 18 | 19 | SensorXYZ accel(SENSOR_ID_ACC); 20 | SensorXYZ gyro(SENSOR_ID_GYRO); 21 | 22 | 23 | void setup() { 24 | Serial.begin(115200); 25 | BHY2.begin(); 26 | accel.begin(); 27 | gyro.begin(); 28 | } 29 | 30 | void loop() { 31 | static auto printTime = millis(); 32 | 33 | // Update function should be continuously polled 34 | BHY2.update(); 35 | 36 | if (millis() - printTime >= 50) { 37 | printTime = millis(); 38 | 39 | // Accelerometer values 40 | Serial.print("acc_X:"); 41 | Serial.print(accel.x()); 42 | Serial.print(","); 43 | Serial.print("acc_Y:"); 44 | Serial.print(accel.y()); 45 | Serial.print(","); 46 | Serial.print("acc_Z:"); 47 | Serial.print(accel.z()); 48 | Serial.print(","); 49 | 50 | // Gyroscope values 51 | Serial.print("gyro_X:"); 52 | Serial.print(gyro.x()); 53 | Serial.print(","); 54 | Serial.print("gyro_Y:"); 55 | Serial.print(gyro.y()); 56 | Serial.print(","); 57 | Serial.print("gyro_Z:"); 58 | Serial.println(gyro.z()); 59 | } 60 | } -------------------------------------------------------------------------------- /bootloader/examples/Blink_flasher_unisense/Blink_flasher_unisense.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Blink 3 | 4 | Turns an LED on for one second, then off for one second, repeatedly. 5 | 6 | Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO 7 | it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to 8 | the correct LED pin independent of which board is used. 9 | If you want to know what pin the on-board LED is connected to on your Arduino 10 | model, check the Technical Specs of your board at: 11 | https://www.arduino.cc/en/Main/Products 12 | 13 | modified 8 May 2014 14 | by Scott Fitzgerald 15 | modified 2 Sep 2016 16 | by Arturo Guadalupi 17 | modified 8 Sep 2016 18 | by Colby Newman 19 | 20 | This example code is in the public domain. 21 | 22 | http://www.arduino.cc/en/Tutorial/Blink 23 | */ 24 | #include "SPIFBlockDevice.h" 25 | #include "LittleFileSystem.h" 26 | #include "fw.h" 27 | 28 | SPIFBlockDevice spif(P0_13, P0_14, P0_12, P0_5); 29 | 30 | mbed::LittleFileSystem fs("fs"); 31 | 32 | FILE *file; 33 | 34 | int timeout = 0; 35 | 36 | // the setup function runs once when you press reset or power the board 37 | void setup() { 38 | Serial.begin(921600); 39 | int err = fs.mount(&spif); 40 | 41 | file = fopen("/fs/ANNA_UPDATE.BIN", "wb"); 42 | // initialize digital pin LED_BUILTIN as an output. 43 | pinMode(P0_19, OUTPUT); 44 | pinMode(P0_20, INPUT_PULLUP); 45 | fwrite(TestCRC_bin, TestCRC_bin_len, 1, file); 46 | } 47 | 48 | // the loop function runs over and over again forever 49 | void loop() { 50 | if (digitalRead(P0_20)==LOW) { 51 | delay(5000); 52 | fclose(file); 53 | system_reset(); 54 | } 55 | /* 56 | while (Serial.available()) { 57 | char c = Serial.read(); 58 | fwrite(&c, 1, 1, file); 59 | timeout = millis(); 60 | } 61 | */ 62 | digitalWrite(P0_19, LOW); 63 | delay(1000); 64 | digitalWrite(P0_19, HIGH); 65 | delay(1000); 66 | } 67 | -------------------------------------------------------------------------------- /tools/bhy-controller/src/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Nicla webserver 12 | 13 | 14 | 15 |







16 | 17 |
18 |
19 |
20 |

Open sensor page

21 | Sensors 22 |
23 |
24 |
25 | 26 |


27 | 28 |






29 | 30 |
31 |
32 |
33 |

Open DFU page

34 | DFU 35 |
36 |
37 |
38 | 39 |


40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/ReadSensorConfiguration/ReadSensorConfiguration.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This sketch shows how to retrieve actual configuration 3 | - sample rate and latency – from sensors. 4 | */ 5 | 6 | #include "Arduino_BHY2.h" 7 | 8 | SensorXYZ accel(SENSOR_ID_ACC); 9 | SensorXYZ gyro(SENSOR_ID_GYRO); 10 | Sensor temp(SENSOR_ID_TEMP); 11 | Sensor gas(SENSOR_ID_GAS); 12 | SensorQuaternion rotation(SENSOR_ID_RV); 13 | 14 | SensorConfig cfg; 15 | 16 | void setup() 17 | { 18 | Serial.begin(115200); 19 | while (!Serial); 20 | 21 | BHY2.begin(); 22 | 23 | accel.begin(); 24 | gyro.begin(); 25 | temp.begin(); 26 | gas.begin(); 27 | rotation.begin(); 28 | 29 | } 30 | 31 | void loop() 32 | { 33 | static auto printTime = millis(); 34 | 35 | // Update function should be continuously polled 36 | BHY2.update(); 37 | 38 | if (millis() - printTime >= 1000) { 39 | printTime = millis(); 40 | 41 | static float rate; 42 | static uint32_t latency; 43 | 44 | cfg = accel.getConfiguration(); 45 | Serial.println(String("acceleration configuration - rate: ") + cfg.sample_rate + String("Hz - latency: ") + cfg.latency + String("ms - range: ") + cfg.range); 46 | 47 | cfg = gyro.getConfiguration(); 48 | Serial.println(String("gyro configuration - rate: ") + cfg.sample_rate + String(" - latency: ") + cfg.latency + String("ms - range: ") + cfg.range); 49 | 50 | cfg = temp.getConfiguration(); 51 | Serial.println(String("temperature configuration - rate: ") + cfg.sample_rate + String(" - latency: ") + cfg.latency + String("ms - range: ") + cfg.range); 52 | 53 | cfg = gas.getConfiguration(); 54 | Serial.println(String("gas configuration - rate: ") + cfg.sample_rate + String(" - latency: ") + cfg.latency + String("ms - range: ") + cfg.range); 55 | 56 | cfg = rotation.getConfiguration(); 57 | Serial.println(String("rotation configuration - rate: ") + cfg.sample_rate + String(" - latency: ") + cfg.latency + String("ms - range: ") + cfg.range); 58 | Serial.println(); 59 | } 60 | } -------------------------------------------------------------------------------- /Arduino_BHY2Host/examples/Portenta_BLE_Bridge/Portenta_BLE_Bridge.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Upload this sketch to Portenta to use it as a BLE bridge between 3 | * Nicla Sense ME and the Arduino Cloud. 4 | * Nicla Sense ME board needs to run the basic App.ino sketch. 5 | * 6 | * Before uploading this sketch to Portenta: 7 | * - add your Portenta device to Arduino Cloud 8 | * - setup temperature and seconds Things in Arduino Cloud 9 | * - get the THING_ID and copy it in thingProperties.h 10 | * - create a Dashboard in Arduino Cloud, like a live chart of the temperature 11 | */ 12 | 13 | #include 14 | #include 15 | #include "thingProperties.h" 16 | 17 | #include "Arduino_BHY2Host.h" 18 | Sensor tempSensor(SENSOR_ID_TEMP); 19 | 20 | int printTime = 0; 21 | 22 | #define DEBUG false 23 | 24 | void setup() { 25 | // Initialize serial and wait for port to open: 26 | Serial.begin(115200); 27 | while(!Serial) {} 28 | #ifndef TARGET_PORTENTA_H7 29 | Serial.println("Unsupported board!"); 30 | while(1); 31 | #endif 32 | 33 | #if DEBUG 34 | BHY2Host.debug(Serial); 35 | #endif 36 | 37 | // Defined in thingProperties.h 38 | initProperties(); 39 | 40 | // Connect to Arduino IoT Cloud 41 | if (!ArduinoCloud.begin(ArduinoIoTPreferredConnection)) { 42 | Serial.println("ArduinoCloud.begin FAILED!"); 43 | } 44 | 45 | setDebugMessageLevel(2); 46 | ArduinoCloud.printDebugInfo(); 47 | 48 | while(!ArduinoCloud.connected()) { 49 | ArduinoCloud.update(); 50 | delay(10); 51 | } 52 | 53 | Serial.println("Configuring Nicla..."); 54 | 55 | while (!BHY2Host.begin(false, NICLA_VIA_BLE)) {} 56 | Serial.println("NICLA device found!"); 57 | 58 | tempSensor.begin(); 59 | 60 | printTime = millis(); 61 | } 62 | 63 | void loop() { 64 | BHY2Host.update(100); 65 | 66 | if (millis() - printTime > 1000) { 67 | printTime = millis(); 68 | seconds = millis()/1000; 69 | temperature = tempSensor.value(); 70 | 71 | Serial.print("Temp: "); 72 | Serial.println(temperature, 3); 73 | 74 | } 75 | 76 | ArduinoCloud.update(); 77 | 78 | } 79 | -------------------------------------------------------------------------------- /.github/workflows/update-libraries.yml: -------------------------------------------------------------------------------- 1 | name: Update Library Mirrors 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - ".github/workflows/update-libraries.ya?ml" 9 | - "Arduino_BHY2/**" 10 | - "Arduino_BHY2Host/**" 11 | 12 | jobs: 13 | update: 14 | if: github.repository == 'arduino/nicla-sense-me-fw' # Workflow would fail in forks 15 | name: ${{ matrix.library.source }} 16 | runs-on: ubuntu-latest 17 | 18 | strategy: 19 | matrix: 20 | library: 21 | - source: Arduino_BHY2 22 | repo: https://github.com/arduino-libraries/Arduino_BHY2 23 | - source: Arduino_BHY2Host 24 | repo: https://github.com/arduino-libraries/Arduino_BHY2Host 25 | 26 | steps: 27 | - name: Checkout filter-repo tool repository 28 | uses: actions/checkout@v6 29 | with: 30 | repository: newren/git-filter-repo 31 | ref: v2.33.0 # See: https://github.com/newren/git-filter-repo/releases 32 | 33 | - name: Install filter-repo 34 | run: | 35 | INSTALL_PATH="${{ runner.temp }}/filter-repo" 36 | mkdir --parents "$INSTALL_PATH" 37 | cp --archive . "$INSTALL_PATH" 38 | # Add installation to PATH: 39 | # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#adding-a-system-path 40 | echo "$INSTALL_PATH" >> "$GITHUB_PATH" 41 | 42 | - name: Checkout repository 43 | uses: actions/checkout@v6 44 | with: 45 | token: ${{ secrets.LIBRARY_REPO_TOKEN }} 46 | fetch-depth: 0 47 | 48 | - name: Install Python for running filter-repo script 49 | uses: actions/setup-python@v6 50 | with: 51 | python-version: 3.9 52 | 53 | - name: Filter repository to library history 54 | run: | 55 | git filter-repo \ 56 | --path "${{ matrix.library.source }}" \ 57 | --path-rename "${{ matrix.library.source }}"/: 58 | 59 | - name: Update library mirror repository 60 | run: git push --set-upstream ${{ matrix.library.repo }} main 61 | -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | includes: 4 | dist: ./DistTasks.yml 5 | 6 | tasks: 7 | build-bhy-controller-tool: 8 | desc: Build the bhy controller tool 9 | dir: tools/bhy-controller/src 10 | cmds: 11 | - go build -v 12 | 13 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/spell-check-task/Taskfile.yml 14 | general:check-spelling: 15 | desc: Check for commonly misspelled words 16 | deps: 17 | - task: poetry:install-deps 18 | cmds: 19 | - poetry run codespell 20 | 21 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/spell-check-task/Taskfile.yml 22 | general:correct-spelling: 23 | desc: Correct commonly misspelled words where possible 24 | deps: 25 | - task: poetry:install-deps 26 | cmds: 27 | - poetry run codespell --write-changes 28 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/poetry-task/Taskfile.yml 29 | poetry:install-deps: 30 | desc: Install dependencies managed by Poetry 31 | cmds: 32 | - poetry install --no-root 33 | 34 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/poetry-task/Taskfile.yml 35 | poetry:update-deps: 36 | desc: Update all dependencies managed by Poetry to their newest versions 37 | cmds: 38 | - poetry update 39 | 40 | vars: 41 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/release-go-task/Taskfile.yml 42 | PROJECT_NAME: bhy 43 | DIST_DIR: "dist" 44 | # build vars 45 | COMMIT: 46 | sh: echo "$(git log --no-show-signature -n 1 --format=%h)" 47 | TIMESTAMP: 48 | sh: echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" 49 | TIMESTAMP_SHORT: 50 | sh: echo "{{now | date "20060102"}}" 51 | TAG: 52 | sh: echo "$(git tag --points-at=HEAD 2> /dev/null | head -n1)" 53 | VERSION: "{{if .NIGHTLY}}nightly-{{.TIMESTAMP_SHORT}}{{else if .TAG}}{{.TAG}}{{else}}{{.PACKAGE_NAME_PREFIX}}git-snapshot{{end}}" 54 | TOOL_DIR: 55 | sh: echo "`pwd`/tools/bhy-controller/src" 56 | -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/firmware/bhi260/Bosch_SHUTTLE_BHI260-flash.fw.h: -------------------------------------------------------------------------------- 1 | const unsigned char bhy2_firmware_image[] = { 2 | 0x2b, 0x66, 0x8, 0x0, 0x0, 0x0, 0x5a, 0x17, 0x24, 0x51, 0x41, 0xb7, 3 | 0x24, 0xcf, 0x22, 0xeb, 0xee, 0x5b, 0xe7, 0xb3, 0x42, 0xba, 0x35, 0xfc, 4 | 0x2c, 0xd5, 0x4, 0x67, 0xc1, 0x3c, 0xa1, 0xcf, 0xae, 0x11, 0x4f, 0x0, 5 | 0xad, 0x37, 0xc6, 0x7c, 0x0, 0x0, 0x0, 0x0, 0x60, 0xb7, 0x27, 0x6a, 6 | 0x63, 0xbf, 0xbe, 0x52, 0xf6, 0xee, 0xca, 0xfe, 0x9b, 0x3d, 0x9b, 0x4a, 7 | 0xe5, 0xa8, 0xe8, 0x4f, 0x0, 0x0, 0x0, 0x0, 0x62, 0x7b, 0xda, 0xa8, 8 | 0x65, 0xe6, 0x68, 0xb6, 0xf7, 0x61, 0xc1, 0xa9, 0x16, 0x44, 0xb0, 0x90, 9 | 0x52, 0xec, 0xd, 0xda, 0xa0, 0x7b, 0x1, 0x0, 0x8e, 0x70, 0x31, 0xe9, 10 | 0x90, 0x2b, 0x1, 0x0, 0x2e, 0x14, 0x0, 0x0, 0x0, 0x0, 0xe0, 0xff, 11 | 0xf8, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 12 | 0x7e, 0x29, 0x2d, 0xdc, 0x8, 0x40, 0x21, 0x0, 0xd0, 0x29, 0x10, 0x0, 13 | 0xd0, 0x29, 0x10, 0x0, 0xd0, 0x29, 0x10, 0x0, 0xd0, 0x29, 0x10, 0x0, 14 | 0xd0, 0x29, 0x10, 0x0, 0xd0, 0x29, 0x10, 0x0, 0xd0, 0x29, 0x10, 0x0, 15 | 0xd0, 0x29, 0x10, 0x0, 0xdc, 0x9a, 0x10, 0x0, 0xd0, 0x29, 0x10, 0x0, 16 | 0xd0, 0x29, 0x10, 0x0, 0xd0, 0x29, 0x10, 0x0, 0xd0, 0x29, 0x10, 0x0, 17 | 0x14, 0x2b, 0x1, 0x0, 0xd0, 0x29, 0x10, 0x0, 0x58, 0x32, 0x20, 0x0, 18 | 0xc, 0xb1, 0x10, 0x0, 0xd0, 0x29, 0x10, 0x0, 0x68, 0xb3, 0x10, 0x0, 19 | 0x68, 0xb3, 0x10, 0x0, 0x68, 0xb3, 0x10, 0x0, 0xa4, 0x22, 0x20, 0x0, 20 | 0x40, 0x3f, 0x20, 0x0, 0x40, 0x3f, 0x20, 0x0, 0x64, 0xb6, 0x10, 0x0, 21 | 0xec, 0xb6, 0x10, 0x0, 0x34, 0xb8, 0x10, 0x0, 0xcc, 0x40, 0x20, 0x0, 22 | 0xd0, 0x29, 0x10, 0x0, 0x2c, 0x1a, 0x10, 0x0, 0xc8, 0xb5, 0x10, 0x0, 23 | 0x10, 0x15, 0x10, 0x0, 0xd0, 0x29, 0x10, 0x0, 0xd0, 0x29, 0x10, 0x0, 24 | 0xb8, 0x18, 0x10, 0x0, 0x6c, 0x19, 0x10, 0x0, 0xe8, 0x3f, 0x20, 0x0, 25 | 0xe8, 0x3f, 0x20, 0x0, 0xe8, 0x3f, 0x20, 0x0, 0x8c, 0xa0, 0x20, 0x0, 26 | 0xe2, 0xc2, 0x8, 0x45, 0x1, 0x80, 0x68, 0x40, 0x84, 0x20, 0x3, 0xc, 27 | 0x19, 0x8, 0x31, 0x4, 0x48, 0x41, 0xe, 0x8d, 0x0, 0x15, 0x6, 0x10, 28 | 0x21, 0x42, 0x1d, 0x78, 0x26, 0x9, 0xef, 0x8c, 0x1, 0x43, 0xc2, 0xc6, 29 | 0x28, 0x4d, 0x4c, 0 30 | }; -------------------------------------------------------------------------------- /Arduino_BHY2/examples/BSEC2GasScannerCollectData/BSEC2GasScannerCollectData.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sketch is used for collecting raw data of BME688, 3 | and the data log after conversion with the helper tools can be used in Bosch Sensortec's AI Studio to train an algorithm 4 | and generate the corresponding config string for the BSEC2 library which can be later used for gas type classification/scanning 5 | */ 6 | 7 | #include "Arduino.h" 8 | #include "Arduino_BHY2.h" 9 | 10 | SensorBSEC2Collector bsec2Collector(SENSOR_ID_BSEC2_COLLECTOR); 11 | 12 | #define CONFIG_BSEC2_USE_DEAULT_HP 1 13 | 14 | #if CONFIG_BSEC2_USE_DEAULT_HP 15 | // Default Heater temperature and time base(Recommendation) 16 | const uint16_t BSEC2HP_TEMP[] = {320, 100, 100, 100, 200, 200, 200, 320, 320, 320}; // HP-354 / 17 | const uint16_t BSEC2HP_DUR[] = {5, 2, 10, 30, 5, 5, 5, 5, 5, 5}; // the duration in steps of 140ms, 5 means 700ms, 2 means 280ms 18 | #else 19 | // customized Heater temperature and time base 20 | const uint16_t BSEC2HP_TEMP[] = {100, 320, 320, 200, 200, 200, 320, 320, 320, 320}; // HP-321 / 21 | const uint16_t BSEC2HP_DUR[] = {43, 2, 2, 2, 21, 21, 2, 14, 14, 14}; // the duration in steps of 140ms, 5 means 700ms, 2 means 280ms 22 | #endif 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | while(!Serial); 28 | 29 | BHY2.begin(); 30 | sensortec.bhy2_bsec2_setHP((uint8_t*)BSEC2HP_TEMP, sizeof(BSEC2HP_TEMP), (uint8_t*)BSEC2HP_DUR, sizeof(BSEC2HP_DUR)); 31 | 32 | bsec2Collector.begin(); 33 | } 34 | 35 | void loop() 36 | { 37 | static auto last_index = 0; 38 | 39 | // Update function should be continuously polled 40 | BHY2.update(); 41 | 42 | if (last_index != bsec2Collector.gas_index()) { 43 | last_index = bsec2Collector.gas_index(); 44 | Serial.println(String((uint32_t)bsec2Collector.timestamp()) + " " 45 | + String(bsec2Collector.temperature()) + " " 46 | + String(bsec2Collector.pressure()) + " " 47 | + String(bsec2Collector.humidity()) + " " 48 | + String(bsec2Collector.gas()) + " " 49 | + String(bsec2Collector.gas_index()) 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/BHYFirmwareUpdate/BHYFirmwareUpdate.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Use this sketch to flash a binary (fw.h) that is used to update the on-flash firmware for BHI260AP on Nicla Sense. 3 | * Steps: 4 | * 1. Generate the "fw.h" desired (see steps below) and put it in the same folder as this sketch 5 | * 2. Upload (program) this sketch to the Nicla Sense Board and immediately open the Serial Monitor from the Arduino IDE 6 | * 3. Check the messages in the Serial Monitor and wait until "BHY FW Upload Done!" shows up 7 | * 4. Upload another sketch such as "Examples->Arduino_BHY2->Standalone" to the Nicla Sense Board and you are all set 8 | * 9 | * Steps to generate the desired "fw.h" 10 | * To use a different BHI260 FW binary, use the command below to generate the "fw.h" 11 | * note: the FW binary should have a name pattern: xxx-flash.fw or xxx_flash.fw 12 | * $ mv xxx-flash.fw BHI260AP_NiclaSenseME-flash.fw 13 | * $ echo const > fw.h && xxd -i BHI260AP_NiclaSenseME-flash.fw >> fw.h 14 | * 15 | */ 16 | 17 | #include "SPIFBlockDevice.h" 18 | #include "LittleFileSystem.h" 19 | #include "fw.h" //echo const > fw.h && xxd -i BHI260AP_NiclaSenseME-flash.fw >> fw.h 20 | 21 | #define BHY_DFU_FW_PATH "/fs/BHY_UPDATE.BIN" 22 | 23 | SPIFBlockDevice spif(SPI_PSELMOSI0, SPI_PSELMISO0, SPI_PSELSCK0, CS_FLASH); 24 | mbed::LittleFileSystem fs("fs"); 25 | FILE *file; 26 | 27 | #define fw_bin BHI260AP_NiclaSenseME_flash_fw 28 | #define fw_bin_len BHI260AP_NiclaSenseME_flash_fw_len 29 | 30 | void setup() { 31 | Serial.begin(115200); 32 | delay(3000); 33 | 34 | char crc = 0; 35 | for (int i = 0; i < fw_bin_len; i++) { 36 | crc = crc ^ fw_bin[i]; 37 | } 38 | 39 | Serial.print("The computed CRC is 0x"); 40 | Serial.println(crc, HEX); 41 | 42 | Serial.println("Writing BHY FW binary into SPIFlash..."); 43 | 44 | int err = fs.mount(&spif); 45 | if (err) { 46 | err = fs.reformat(&spif); 47 | Serial.print("Error mounting file system: "); 48 | Serial.println(err); 49 | } 50 | 51 | file = fopen(BHY_DFU_FW_PATH, "wb"); 52 | int ret = fwrite(fw_bin, fw_bin_len, 1, file); 53 | fwrite(&crc, 1, 1, file); 54 | delay(100); 55 | fclose(file); 56 | 57 | Serial.println("BHY FW Upload Done!"); 58 | } 59 | 60 | void loop() { 61 | delay(100); 62 | } 63 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/SensorClass.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSOR_CLASS_H_ 2 | #define SENSOR_CLASS_H_ 3 | 4 | #include "SensorID.h" 5 | #include "DataParser.h" 6 | #include "BoschSensortec.h" 7 | 8 | class SensorClass { 9 | public: 10 | __attribute__ ((error("Sensor requires an ID"))) SensorClass(); 11 | SensorClass(uint8_t id); 12 | virtual ~SensorClass(); 13 | 14 | uint8_t id(); 15 | /* 16 | * Sample rate: it indicates the frequency at which a sensor is sampled. 17 | * It is expressed in Hz. 18 | * Latency: it indicates how much ms time a new value is retained in its fifo 19 | * before a notification to the host is sent via interrupt. 20 | * It is expressed in ms. 21 | */ 22 | bool begin(float rate = 1000, uint32_t latency = 1); 23 | void configure(float rate, uint32_t latency); 24 | /* 25 | * parameter range: 26 | * for accelerometer, the range parameter is in the units of g (1g = ~9.80665 m/s^2) 27 | * valid values: 28 | * 2 (+/-2g), 4 (+/-4g), 8 (+/-8g), 16 (+/-16g), 29 | * 30 | * for gyroscope, the range parameter is in the units of dps (degrees per second) 31 | * valid values: 32 | * 125 (+/-125 dps) 33 | * 250 (+/-250 dps) 34 | * 500 (+/-500 dps) 35 | * 1000 (+/-1000 dps) 36 | * 2000 (+/-2000 dps) 37 | * 38 | * for other sensors, the range is defined based on the physical driver implementation 39 | * please note that: changing the range of one virtual sensor might affect the other virtual sensors which share the same underlying physical sensor, e.g.: 40 | * changing the accelerometer range will at the same time impact the sensing range for the gravity virtual sensor, 41 | * for more details please refer to the datasheet of BHI260AP, section "Change Sensor Dynamic Range" 42 | */ 43 | int setRange(uint16_t range); 44 | const SensorConfig getConfiguration(); 45 | void end(); 46 | 47 | bool dataAvailable() {return _dataAvail;} 48 | bool clearDataAvailFlag(){_dataAvail = false;} 49 | 50 | 51 | virtual void setData(SensorDataPacket &data) = 0; 52 | virtual void setData(SensorLongDataPacket &data) = 0; 53 | virtual String toString() = 0; 54 | 55 | protected: 56 | uint8_t _id; 57 | bool _subscribed; 58 | 59 | bool _dataAvail; 60 | 61 | void setDataAvailFlag() {_dataAvail = true;} 62 | 63 | friend class SensorManager; 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/Standalone/Standalone.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sketch shows how nicla can be used in standalone mode. 3 | * Without the need for an host, nicla can run sketches that 4 | * are able to configure the bhi sensors and are able to read all 5 | * the bhi sensors data. 6 | */ 7 | 8 | #include "Arduino.h" 9 | #include "Arduino_BHY2.h" 10 | 11 | SensorXYZ accel(SENSOR_ID_ACC); 12 | SensorXYZ gyro(SENSOR_ID_GYRO); 13 | Sensor temp(SENSOR_ID_TEMP); 14 | Sensor gas(SENSOR_ID_GAS); 15 | SensorQuaternion rotation(SENSOR_ID_RV); 16 | 17 | void setup() 18 | { 19 | Serial.begin(115200); 20 | while(!Serial); 21 | 22 | BHY2.begin(); 23 | 24 | accel.begin(); 25 | gyro.begin(); 26 | temp.begin(); 27 | gas.begin(); 28 | rotation.begin(); 29 | 30 | SensorConfig cfg = accel.getConfiguration(); 31 | Serial.println(String("range of accel: +/-") + cfg.range + String("g")); 32 | accel.setRange(2); //this sets the range of accel to +/-4g, 33 | cfg = accel.getConfiguration(); 34 | Serial.println(String("range of accel: +/-") + cfg.range + String("g")); 35 | 36 | cfg = gyro.getConfiguration(); 37 | Serial.println(String("range of gyro: +/-") + cfg.range + String("dps")); 38 | gyro.setRange(1000); 39 | cfg = gyro.getConfiguration(); 40 | Serial.println(String("range of gyro: +/-") + cfg.range + String("dps")); 41 | } 42 | 43 | void loop() 44 | { 45 | static auto printTime = millis(); 46 | 47 | // Update function should be continuously polled 48 | BHY2.update(); 49 | 50 | if (millis() - printTime >= 1000) { 51 | printTime = millis(); 52 | 53 | if(accel.dataAvailable()) { 54 | Serial.println(String("acceleration: ") + accel.toString()); 55 | accel.clearDataAvailFlag(); 56 | } 57 | 58 | if(gyro.dataAvailable()) { 59 | Serial.println(String("gyroscope: ") + gyro.toString()); 60 | gyro.clearDataAvailFlag(); 61 | } 62 | 63 | if (temp.dataAvailable()) { 64 | Serial.println(String("temperature: ") + String(temp.value(),3)); 65 | temp.clearDataAvailFlag(); 66 | } 67 | 68 | if (gas.dataAvailable()) { 69 | Serial.println(String("gas: ") + String(gas.value(),3)); 70 | gas.clearDataAvailFlag(); 71 | } 72 | 73 | if (rotation.dataAvailable()) { 74 | Serial.println(String("rotation: ") + rotation.toString()); 75 | rotation.clearDataAvailFlag(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/BoschParser.h: -------------------------------------------------------------------------------- 1 | #ifndef BOSCH_PARSER_H_ 2 | #define BOSCH_PARSER_H_ 3 | 4 | #include "Arduino.h" 5 | #include "mbed.h" 6 | 7 | #include "bosch/common/common.h" 8 | #include "sensors/SensorTypes.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" 12 | { 13 | #endif 14 | #include "bosch/bhy2.h" 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | 19 | /** 20 | * @brief Class for parsing data obtained from the Bosch BHY2 libraries 21 | * 22 | */ 23 | class BoschParser { 24 | public: 25 | /** 26 | * @brief Convert ticks of the 64 MHz oscillator to time 27 | * 28 | * @param time_ticks Timestamp of the 64 MHz oscillator in ticks 29 | * @param s Timestamp in seconds 30 | * @param ns Timestamp in nanoseconds 31 | */ 32 | static void convertTime(uint64_t time_ticks, uint32_t *s, uint32_t *ns); 33 | /** 34 | * @brief Data payload is stored in FIFO buffer 35 | * 36 | * @param fifoData Pointer to data to be parsed 37 | * @param arg Reserved for future use. 38 | */ 39 | static void parseData(const struct bhy2_fifo_parse_data_info *fifoData, void *arg); 40 | /** 41 | * @brief Parse Meta Events from the Bosch BHI260 Sensor 42 | * 43 | * @param callback_info Pointer containing information to be parsed 44 | * @param callback_ref Reserved for future use 45 | */ 46 | static void parseMetaEvent(const struct bhy2_fifo_parse_data_info *callback_info, void *callback_ref); 47 | /** 48 | * @brief Extracts the timestamp information and prints it with the sensor ID. Also prints contents of callback_info. 49 | * 50 | * @param callback_info Pointer containing information to be parsed 51 | * @param callback_ref Reserved for future use 52 | */ 53 | static void parseGeneric(const struct bhy2_fifo_parse_data_info *callback_info, void *callback_ref); 54 | /** 55 | * @brief Parse debug message 56 | * 57 | * @param callback_info Pointer containing information to be parsed 58 | * @param callback_ref Reserved for future use 59 | */ 60 | static void parseDebugMessage(const struct bhy2_fifo_parse_data_info *callback_info, void *callback_ref); 61 | 62 | private: 63 | /** 64 | * @brief The Arduino_BHY2 class can access both private and public methods of BoschParser 65 | * 66 | */ 67 | friend class Arduino_BHY2; 68 | static void debug(Stream &stream); 69 | static Stream *_debug; 70 | }; 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /tools/bhy-controller/src/dfu/dfu.go: -------------------------------------------------------------------------------- 1 | package dfu 2 | 3 | import ( 4 | "arduino/bhy/util" 5 | "fmt" 6 | "math" 7 | "os" 8 | 9 | log "github.com/sirupsen/logrus" 10 | 11 | "github.com/schollz/progressbar/v3" 12 | "go.bug.st/serial" 13 | ) 14 | 15 | func sendPacket(port serial.Port, packet *dfuPacket) { 16 | for ackReceived := false; ackReceived == false; { 17 | log.Debugf("Sending packet %d\n", packet.index) 18 | _, err := port.Write(packet.build()) 19 | log.Debugln(packet.build()) 20 | util.ErrCheck(err) 21 | 22 | //wait ack from Nicla 23 | log.Debugln("Waiting for ack") 24 | ackBuf := make([]byte, 1) 25 | for n := 0; n != 1; { 26 | n, err = port.Read(ackBuf) 27 | util.ErrCheck(err) 28 | 29 | if n == 0 { 30 | log.Debugln("Ack not received") 31 | } else if n > 1 { 32 | log.Debugln("Ack expected but more than one byte received") 33 | } 34 | } 35 | 36 | ackReceived = (ackBuf[0] == Ack) 37 | } 38 | } 39 | 40 | func readPayload(f *os.File, idx int, buf []byte) int { 41 | //Adjust pointer to fw binary file 42 | p := (int64)(idx * PayloadSize) 43 | _, err := f.Seek(p, 0) 44 | util.ErrCheck(err) 45 | 46 | // Read payload from the binary file 47 | n, err := f.Read(buf) 48 | util.ErrCheck(err) 49 | 50 | return n 51 | } 52 | 53 | func Upload(baud int, target string, usbPort string, bPath string, debug bool) { 54 | if debug { 55 | log.SetLevel(log.DebugLevel) 56 | } 57 | // Open serial port 58 | port := util.OpenPort(usbPort, baud) 59 | 60 | //Open the firmware binary file and get the length 61 | f, err := os.Open(bPath) 62 | defer f.Close() 63 | util.ErrCheck(err) 64 | fi, err := f.Stat() 65 | util.ErrCheck(err) 66 | fwSize := fi.Size() 67 | fmt.Printf("The firmware is %d bytes long\n", fwSize) 68 | 69 | // consider fwSize + 1 for crc byte 70 | nChunks := int(math.Ceil(float64(fwSize+1) / float64(PayloadSize))) 71 | spareBytes := int(fwSize % int64(PayloadSize)) 72 | bar := progressbar.Default(int64(nChunks)) 73 | crc := uint8(0) 74 | buf := make([]byte, PayloadSize) 75 | 76 | for i := 0; i < nChunks; i++ { 77 | if !debug { 78 | bar.Add(1) 79 | } 80 | 81 | n := 0 82 | if i < nChunks-1 || spareBytes > 0 { 83 | n = readPayload(f, i, buf) 84 | crc = CRC8(buf, n, crc) 85 | } 86 | 87 | packet := newDFUPacket(target, i, n, buf) 88 | // if this is the very last packet 89 | if i == (nChunks - 1) { 90 | packet.setLast(1) 91 | // assert n == spareBytes 92 | // append crc to this packet 93 | buf[spareBytes] = crc 94 | packet.setLength(spareBytes + 1) 95 | } 96 | sendPacket(port, packet) 97 | } 98 | 99 | bar.Finish() 100 | fmt.Printf("FW sent!\n") 101 | } 102 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/EslovHandler.h: -------------------------------------------------------------------------------- 1 | #ifndef ESLOV_HANDLER_H_ 2 | #define ESLOV_HANDLER_H_ 3 | 4 | #include "Arduino.h" 5 | #include "Wire.h" 6 | 7 | #define ESLOV_MAX_LENGTH 255 8 | #define ESLOV_DEFAULT_ADDRESS 0x55 9 | 10 | #define I2C_INT_PIN (p24) 11 | 12 | /** 13 | * @brief Enumerator for ESLOV operational state 14 | * 15 | */ 16 | enum EslovOpcode { 17 | ESLOV_DFU_INTERNAL_OPCODE, /*!< ESLOV DFU ANNA-B112 */ 18 | ESLOV_DFU_EXTERNAL_OPCODE, /*!< ESLOV DFU BHY260 */ 19 | ESLOV_SENSOR_CONFIG_OPCODE, /*!< ESLOV Sensor Configuration */ 20 | ESLOV_SENSOR_STATE_OPCODE /*!< ESLOV Sensor State */ 21 | }; 22 | 23 | /** 24 | * @brief Enumeration for various states over ESLOV 25 | * 26 | */ 27 | enum EslovState { 28 | ESLOV_AVAILABLE_SENSOR_STATE = 0x00, 29 | ESLOV_READ_SENSOR_STATE = 0x01, 30 | ESLOV_DFU_ACK_STATE = 0x02, 31 | ESLOV_SENSOR_ACK_STATE = 0x03, 32 | ESLOV_AVAILABLE_LONG_SENSOR_STATE = 0x04, 33 | ESLOV_READ_LONG_SENSOR_STATE = 0x05 34 | }; 35 | 36 | /** 37 | * @brief Class to manage communication over ESLOV 38 | * 39 | */ 40 | class EslovHandler { 41 | public: 42 | EslovHandler(); 43 | virtual ~EslovHandler(); 44 | 45 | /** 46 | * @brief Start I2C communication over ESLOV 47 | * 48 | * @return true I2C communication initialized successfully. 49 | */ 50 | bool begin(); 51 | /** 52 | * @brief Close I2C communication over ESLOV 53 | * 54 | */ 55 | void end(); 56 | 57 | /** 58 | * @brief Manage incoming I2C data based on EslovOpCode state with specified length 59 | * 60 | * @param length 61 | */ 62 | static void onReceive(int length); 63 | /** 64 | * @brief Manage incoming I2C data based on EslovOpCode state 65 | * 66 | */ 67 | static void onRequest(); 68 | 69 | bool eslovActive = false; /*!< Changes to true when ESLOV I2C communication established */ 70 | 71 | protected: 72 | /** 73 | * @brief Nicla Sense ME is mounted as a Shield 74 | * 75 | */ 76 | void niclaAsShield(); 77 | 78 | private: 79 | void receiveEvent(int length); 80 | void requestEvent(); 81 | 82 | void eslovBusy(); 83 | void eslovAvailable(); 84 | 85 | int _rxIndex; 86 | uint8_t _rxBuffer[ESLOV_MAX_LENGTH]; 87 | 88 | EslovState _state; 89 | 90 | private: 91 | /** 92 | * @brief The Arduino_BHY2 class can access both private and public methods of EslovHandler 93 | * 94 | */ 95 | friend class Arduino_BHY2; 96 | void debug(Stream &stream); 97 | void dump(); 98 | Stream *_debug; 99 | bool _lastDfuPack; 100 | 101 | PinName _eslovIntPin; 102 | }; 103 | 104 | /** 105 | * @brief The EslovHandler class can be externally linked to as eslovHandler in your sketch 106 | * 107 | */ 108 | extern EslovHandler eslovHandler; 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/bosch/common/common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Bosch Sensortec GmbH. All rights reserved. 3 | * 4 | * BSD-3-Clause 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * 3. Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | * 33 | * @file common.h 34 | * @date 24 Mar 2020 35 | * @brief Common header file for the BHI260/BHA260 examples 36 | * 37 | */ 38 | 39 | #ifndef _COMMON_H_ 40 | #define _COMMON_H_ 41 | 42 | #include 43 | #include 44 | 45 | #include "bosch/bhy2.h" 46 | 47 | const char *get_coines_error(int16_t rslt); 48 | const char *get_api_error(int8_t error_code); 49 | const char *get_sensor_error_text(uint8_t sensor_error); 50 | const char *get_sensor_name(uint8_t sensor_id); 51 | 52 | void setup_interfaces(bool reset_power, enum bhy2_intf intf); 53 | void close_interfaces(void); 54 | int8_t bhy2_spi_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr); 55 | int8_t bhy2_spi_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr); 56 | int8_t bhy2_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr); 57 | int8_t bhy2_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr); 58 | void bhy2_delay_us(uint32_t us, void *private_data); 59 | bool get_interrupt_status(void); 60 | 61 | #endif /* _COMMON_H_ */ 62 | -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/common/common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Bosch Sensortec GmbH. All rights reserved. 3 | * 4 | * BSD-3-Clause 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * 3. Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | * 33 | * @file common.h 34 | * @date 24 Mar 2020 35 | * @brief Common header file for the BHI260/BHA260 examples 36 | * 37 | */ 38 | 39 | #ifndef _COMMON_H_ 40 | #define _COMMON_H_ 41 | 42 | #include 43 | #include 44 | 45 | #include "bhy2.h" 46 | 47 | char *get_coines_error(int16_t rslt); 48 | char *get_api_error(int8_t error_code); 49 | char *get_sensor_error_text(uint8_t sensor_error); 50 | char *get_sensor_name(uint8_t sensor_id); 51 | 52 | void setup_interfaces(bool reset_power, enum bhy2_intf intf); 53 | void close_interfaces(void); 54 | int8_t bhy2_spi_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr); 55 | int8_t bhy2_spi_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr); 56 | int8_t bhy2_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr); 57 | int8_t bhy2_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, void *intf_ptr); 58 | void bhy2_delay_us(uint32_t us, void *private_data); 59 | bool get_interrupt_status(void); 60 | 61 | #endif /* _COMMON_H_ */ 62 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/IMURangeSettings/IMURangeSettings.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * IMURangeSettings 3 | * 4 | * This sketch demonstrates how to configure the acceleration (SENSOR_ID_ACC) and gyroscope (SENSOR_ID_GYRO) range values. 5 | * In the setup function, the default ranges are printed to the serial for the acceleration and gravity (+/-8g). 6 | * Then, the range is modified to +/-4g, and is confirmed by printing the value of .range method to the Serial monitor. Finally, 7 | * the setup function prints the default range value for the gyroscope (+/-2000 dps) and then modifies this to +/-1000 dps. 8 | * 9 | * Every second, the loop function prints out acceleration and gyroscope values to the Serial port. 10 | * 11 | * 12 | */ 13 | 14 | #include "Arduino_BHY2.h" 15 | 16 | // declare 3D sensor instances 17 | // default ADC range is -32768 to 32767 (16 bit) 18 | SensorXYZ accel(SENSOR_ID_ACC); 19 | SensorXYZ gyro(SENSOR_ID_GYRO); 20 | SensorXYZ gravity(SENSOR_ID_GRA); 21 | 22 | void setup() { 23 | Serial.begin(9600); 24 | while(!Serial); 25 | 26 | BHY2.begin(); 27 | 28 | accel.begin(); 29 | gyro.begin(); 30 | 31 | delay(1000); 32 | 33 | SensorConfig cfg = accel.getConfiguration(); 34 | 35 | Serial.println("Default Range values:"); 36 | Serial.println(String("range of accel: +/-") + cfg.range + String("g")); 37 | cfg = gravity.getConfiguration(); 38 | Serial.println(String("range of gravity: +/-") + cfg.range + String("g")); 39 | 40 | accel.setRange(4); //this sets the range of accel to +/-4g, 41 | Serial.println("Modified Range values:"); 42 | cfg = accel.getConfiguration(); 43 | Serial.println(String("range of accel: +/-") + cfg.range + String("g")); 44 | cfg = gravity.getConfiguration(); 45 | Serial.println(String("range of gravity: +/-") + cfg.range + String("g")); 46 | 47 | cfg = gyro.getConfiguration(); 48 | Serial.println(String("range of gyro: +/-") + cfg.range + String("dps")); 49 | gyro.setRange(1000); //this sets the range of gyro to +/-1000dps, 50 | cfg = gyro.getConfiguration(); 51 | Serial.println(String("range of gyro: +/-") + cfg.range + String("dps")); 52 | } 53 | 54 | void loop() { 55 | static auto printTime = millis(); 56 | 57 | // Update function should be continuously polled 58 | BHY2.update(); 59 | 60 | // print gyroscope/acceleration data once every second 61 | if (millis() - printTime >= 1000) { 62 | printTime = millis(); 63 | Serial.print(String("acceleration (raw): ") + accel.toString()); 64 | 65 | float accelX = ((float)accel.x() / 32768.0) *4; 66 | float accelY = ((float)accel.y() / 32768.0) *4; 67 | float accelZ = ((float)accel.z() / 32768.0) *4; 68 | String accelInG = String("X: ") + String(accelX) + String(" Y: ") + String(accelY) + String(" Z: ") + String(accelZ); 69 | 70 | Serial.println(String("acceleration (g): ") + accelInG); 71 | Serial.println(String("gyroscope: ") + gyro.toString()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/docs/readme.md: -------------------------------------------------------------------------------- 1 | # Bosch BHY2 Host Sensor Library for Arduino 2 | 3 | This library provides capabilities for the Portenta and MKR boards to interact with the Nicla Sense ME and poll the BHI260AP and BME688 sensors. This library is included on sketches from the host board (not the Nicla Sense ME board). 4 | 5 | ## Features 6 | 7 | - Easy access to data from Nicla Sense ME sensors from the Portenta and MKR boards 8 | - Wrapper for [Bosch BHY2-Sensor-API](https://github.com/BoschSensortec/BHY2-Sensor-API) 9 | - Bridge for data to the Arduino IoT Cloud 10 | - All functionality available over both I2C/ESLOV and BLE 11 | 12 | ## Usage 13 | 14 | The ArduinoBLE library needs to be installed, for the BLE features to function. 15 | 16 | To use this library, use this code at the top of your sketch 17 | ``` 18 | #include 19 | ``` 20 | 21 | A UML diagram of the main library classes is provided in the diagram below, provided as an editable SVG file. 22 | 23 | ![Arduino_BHY2 Library UML Diagram](./Arduino_BHY2Host.UML.drawio.svg) 24 | 25 | 26 | 27 | ## Examples 28 | 29 | - [Accelerometer](https://github.com/arduino-libraries/Arduino_BHY2Host/blob/main/examples/Accelerometer/Accelerometer.ino) : Read accelerometer data from a Nicla Sense ME connected to a host board, and print to the Serial monitor 30 | - [BSEC](https://github.com/arduino-libraries/Arduino_BHY2Host/blob/main/examples/BSEC/BSEC.ino) : Read BSEC data from a Nicla Sense ME connected to a host board, and print to the Serial monitor 31 | - [Nicla_IoT_Bridge](https://github.com/arduino-libraries/Arduino_BHY2Host/blob/main/examples/Nicla_IoT_Bridge/Nicla_IoT_Bridge.ino) : Obtain temperature data from a Nicla Sense ME board and send data to the Arduino IoT Cloud 32 | - [Orientation](https://github.com/arduino-libraries/Arduino_BHY2Host/blob/main/examples/Orientation/Orientation.ino) : Read orientation data from a Nicla Sense ME connected to a host board, and print to the Serial monitor 33 | - [Passthrough](https://github.com/arduino-libraries/Arduino_BHY2Host/blob/main/examples/Passthrough/Passthrough.ino) : Control the Nicla Sense ME over Serial commands, via a host board using the arduino-bhy tool 34 | - [Portenta_BLE_Bridge](https://github.com/arduino-libraries/Arduino_BHY2Host/blob/main/examples/Portenta_BLE_Bridge/Portenta_BLE_Bridge.ino) : Obtain temperature data from a Nicla Sense ME board over BLE and send data to the Arduino IoT Cloud via the Portenta H7 35 | - [Portenta_BLE_Bridge](https://github.com/arduino-libraries/Arduino_BHY2Host/blob/main/examples/Portenta_BLE_Bridge/Portenta_BLE_Bridge.ino) : Obtain temperature data from a Nicla Sense ME board over BLE and send data to the Arduino IoT Cloud via the Portenta H7 36 | - [Temperature](https://github.com/arduino-libraries/Arduino_BHY2Host/blob/main/examples/Temperature/Temperature.ino) : Obtain temperature data from a Nicla Sense ME board and send it over Serial 37 | 38 | ## License 39 | 40 | See [LICENSE.txt](LICENSE.txt) -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/DataParser.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_PARSER_H_ 2 | #define DATA_PARSER_H_ 3 | 4 | #include "Arduino.h" 5 | #include "sensors/SensorTypes.h" 6 | #include "SensorID.h" 7 | 8 | struct DataXYZ { 9 | int16_t x; 10 | int16_t y; 11 | int16_t z; 12 | 13 | String toString() { 14 | return (String)("XYZ values - X: " + String(x) 15 | + " Y: " + String(y) 16 | + " Z: " + String(z) + "\n"); 17 | } 18 | }; 19 | 20 | struct DataOrientation { 21 | float heading; 22 | float pitch; 23 | float roll; 24 | 25 | String toString() { 26 | return (String)("Orientation values - heading: " + String(heading, 3) 27 | + " pitch: " + String(pitch, 3) 28 | + " roll: " + String(roll, 3) + "\n"); 29 | } 30 | }; 31 | 32 | struct DataQuaternion { 33 | float x; 34 | float y; 35 | float z; 36 | float w; 37 | float accuracy; 38 | 39 | String toString() { 40 | return (String)("Quaternion values - X: " + String(x, 3) 41 | + " Y: " + String(y, 3) 42 | + " Z: " + String(z, 3) 43 | + " W: " + String(w, 3) 44 | + " Accuracy: " + String(accuracy, 3) 45 | + "\n"); 46 | } 47 | }; 48 | 49 | struct DataBSEC { 50 | uint16_t iaq; //iaq value for regular use case 51 | uint16_t iaq_s; //iaq value for stationary use cases 52 | float b_voc_eq; //breath VOC equivalent (ppm) 53 | uint32_t co2_eq; //CO2 equivalent (ppm) [400,] 54 | float comp_t; //compensated temperature (celsius) 55 | float comp_h; //compensated humidity 56 | uint32_t comp_g; //compensated gas resistance (Ohms) 57 | uint8_t accuracy; //accuracy level: [0-3] 58 | 59 | String toString() { 60 | return (String)("BSEC output values - iaq: " + String(iaq) 61 | + " iaq_s: " + String(iaq_s) 62 | + " b_voc_eq: " + String(b_voc_eq, 2) 63 | + " co2_eq: " + String(co2_eq) 64 | + " accuracy: " + String(accuracy) 65 | + " comp_t: " + String(comp_t, 2) 66 | + " comp_h: " + String(comp_h, 2) 67 | + " comp_g: " + String(comp_g) 68 | + "\n"); 69 | } 70 | }; 71 | 72 | class DataParser { 73 | public: 74 | static void parse3DVector(SensorDataPacket& data, DataXYZ& vector); 75 | static void parseEuler(SensorDataPacket& data, DataOrientation& vector); 76 | static void parseEuler(SensorDataPacket& data, DataOrientation& vector, float scaleFactor); 77 | static void parseQuaternion(SensorDataPacket& data, DataQuaternion& vector, float scaleFactor); 78 | static void parseBSEC(SensorLongDataPacket& data, DataBSEC& vector); 79 | static void parseBSECLegacy(SensorLongDataPacket& data, DataBSEC& vector); 80 | static void parseData(SensorDataPacket& data, float& value, float scaleFactor, SensorPayload format); 81 | static void parseActivity(SensorDataPacket& data, uint16_t& value); 82 | }; 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/DFUManager.h: -------------------------------------------------------------------------------- 1 | #ifndef DFU_MANAGER_H_ 2 | #define DFU_MANAGER_H_ 3 | 4 | #include "Arduino.h" 5 | 6 | #include "SPIFBlockDevice.h" 7 | 8 | #include "LittleFileSystem.h" 9 | 10 | /** 11 | * @brief Enumerator for selecting device for DFU 12 | * 13 | */ 14 | enum DFUType { 15 | DFU_INTERNAL, /*!< ANNA-B112 */ 16 | DFU_EXTERNAL /*!< BHY2 */ 17 | }; 18 | 19 | /** 20 | * @brief Acknowledgment handler for the DFU 21 | * 22 | */ 23 | enum DFUAckCode { 24 | DFUAck = 0x0F, /*!< Acknowledgment */ 25 | DFUNack = 0x00 /*!< Negative Acknowledgment */ 26 | }; 27 | 28 | /** 29 | * @brief Enumeration to define source of DFU data stream 30 | * 31 | */ 32 | enum DFUSource { 33 | bleDFU, /*!< BLE */ 34 | eslovDFU /*!< ESLOV */ 35 | }; 36 | 37 | /** 38 | * @brief Structure of a DFU data packet 39 | * 40 | */ 41 | struct __attribute__((packed)) DFUPacket { 42 | uint8_t last: 1; /*!< Bit to indicate last packet */ 43 | union { 44 | uint16_t index: 15; /*!< Current packet index */ 45 | uint16_t remaining: 15; /*!< Remaining packet index */ 46 | }; 47 | uint8_t data[232]; /*!< 232 byte array to store data */ 48 | }; 49 | 50 | /** 51 | * @brief Class for handling Device Firmware Upgrade (DFU) of both the ANNA-B112 and the BHY2 chip 52 | * 53 | */ 54 | class DFUManager { 55 | public: 56 | DFUManager(); 57 | virtual ~DFUManager(); 58 | 59 | /** 60 | * @brief Mount file system 61 | * 62 | * @return true File system mounted successfully 63 | * @return false Error in mounting file system 64 | */ 65 | bool begin(); 66 | /** 67 | * @brief Method for processing packet data 68 | * 69 | * @param source Selection of DFU data source 70 | * @param dfuType Choose between ANNA-B112 and BHY260 71 | * @param data datastream 72 | */ 73 | void processPacket(DFUSource source, DFUType dfuType, const uint8_t* data); 74 | /** 75 | * @brief Set _transferPending to False 76 | * 77 | */ 78 | void closeDfu(); 79 | 80 | /** 81 | * @brief Return state of _transferPending 82 | * 83 | * @return true _transferPending is true 84 | * @return false _transferPending is false 85 | */ 86 | bool isPending(); 87 | DFUSource dfuSource(); 88 | /** 89 | * @brief Reset DFUAckCode flag 90 | * 91 | * @return uint8_t 92 | */ 93 | uint8_t acknowledgment(); 94 | 95 | 96 | private: 97 | static SPIFBlockDevice _bd; 98 | static mbed::LittleFileSystem _fs; 99 | FILE* _target; 100 | 101 | uint8_t _acknowledgment; 102 | bool _transferPending; 103 | DFUSource _dfuSource; 104 | 105 | private: 106 | /** 107 | * @brief The Arduino_BHY2 class can access both private and public methods of DFUManager 108 | * 109 | */ 110 | friend class Arduino_BHY2; 111 | void debug(Stream &stream); 112 | Stream *_debug; 113 | }; 114 | 115 | /** 116 | * @brief The DFUManager class can be externally linked to as dfuManager in your sketch 117 | * 118 | */ 119 | extern DFUManager dfuManager; 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/BLEHandler.h: -------------------------------------------------------------------------------- 1 | #ifndef BLE_HANDLER_H_ 2 | #define BLE_HANDLER_H_ 3 | 4 | #include "Arduino.h" 5 | #include "ArduinoBLE.h" 6 | 7 | #include "DFUManager.h" 8 | #include "sensors/SensorTypes.h" 9 | 10 | #define BHY2_ENABLE_BLE_BATCH 1 11 | const uint8_t BLE_SENSOR_EVT_BATCH_CNT_MAX = (244 / (SENSOR_DATA_FIXED_LENGTH + 2))/10*10; 12 | 13 | /** 14 | * @brief Class for handling BLE communication for both sensor data transfer and DFU 15 | * 16 | */ 17 | class BLEHandler { 18 | public: 19 | BLEHandler(); 20 | virtual ~BLEHandler(); 21 | 22 | /** 23 | * @brief Set advertised local name and initialize BLE advertising for DFU and sensor data transfer 24 | * 25 | * @return true Successful initialization of BLE device 26 | */ 27 | bool begin(); 28 | /** 29 | * @brief Update sensor data by reading the FIFO buffer on the BHI260 and then pass it to a suitable parser. 30 | * 31 | */ 32 | void update(); 33 | 34 | /** 35 | * @brief Poll BLE data 36 | * 37 | * @param timeout timeout in ms, to wait for event. 38 | */ 39 | void poll(unsigned long timeout); 40 | /** 41 | * @brief Close BLE connection 42 | * 43 | */ 44 | void end(); 45 | 46 | static void debug(Stream &stream); 47 | 48 | bool bleActive = false; 49 | 50 | private: 51 | static Stream *_debug; 52 | 53 | #if BHY2_ENABLE_BLE_BATCH 54 | static SensorDataPacket *_dataBatch; 55 | static uint8_t _idxBatch; 56 | #endif 57 | 58 | bool _lastDfuPack; 59 | 60 | /** 61 | * @brief Method for reading and processing DFU packet. Prints size of DFU packet to debug output 62 | * 63 | * @param dfuType Selects device to update firmware. DFU_INTERNAL for the ANNA-B112 and DFU_EXTERNAL for the BHY2 sensor. 64 | * @param characteristic Selected BLE characteristic. See https://reference.arduino.cc/reference/en/libraries/arduinoble/blecharacteristic/ 65 | */ 66 | void processDFUPacket(DFUType dfuType, BLECharacteristic characteristic); 67 | 68 | /** 69 | * @brief Method for receiving and forwarding BLE packet to ANNA-B112 module 70 | * 71 | * @param central Instance of BLEDevice 72 | * @param characteristic Selected BLE characteristic. See https://reference.arduino.cc/reference/en/libraries/arduinoble/blecharacteristic/ 73 | */ 74 | static void receivedInternalDFU(BLEDevice central, BLECharacteristic characteristic); 75 | /** 76 | * @brief Method for receiving and forwarding BLE packet to BHI260AP sensor 77 | * 78 | * @param central Instance of BLEDevice 79 | * @param characteristic Selected BLE characteristic. See https://reference.arduino.cc/reference/en/libraries/arduinoble/blecharacteristic/ 80 | */ 81 | static void receivedExternalDFU(BLEDevice central, BLECharacteristic characteristic); 82 | /** 83 | * @brief Read sensor configuration data and output to debug 84 | * 85 | * @param central Instance of BLEDevice 86 | * @param characteristic Selected BLE characteristic. See https://reference.arduino.cc/reference/en/libraries/arduinoble/blecharacteristic/ 87 | */ 88 | static void receivedSensorConfig(BLEDevice central, BLECharacteristic characteristic); 89 | }; 90 | 91 | /** 92 | * @brief The BLEHandler class can be externally linked to as bleHandler in your sketch 93 | * 94 | */ 95 | extern BLEHandler bleHandler; 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/sensors/DataParser.cpp: -------------------------------------------------------------------------------- 1 | #include "sensors/DataParser.h" 2 | 3 | void DataParser::parse3DVector(SensorDataPacket& data, DataXYZ& vector) { 4 | vector.x = data.getInt16(0); 5 | vector.y = data.getInt16(2); 6 | vector.z = data.getInt16(4); 7 | } 8 | 9 | void DataParser::parseEuler(SensorDataPacket& data, DataOrientation& vector, float scaleFactor) { 10 | vector.heading = data.getInt16(0) * scaleFactor; 11 | vector.pitch = data.getInt16(2) * scaleFactor; 12 | vector.roll = data.getInt16(4) * scaleFactor; 13 | } 14 | 15 | void DataParser::parseEuler(SensorDataPacket& data, DataOrientation& vector) { 16 | parseEuler(data, vector, 1.f); 17 | } 18 | 19 | void DataParser::parseQuaternion(SensorDataPacket& data, DataQuaternion& vector, float scaleFactor) { 20 | vector.x = data.getInt16(0) * scaleFactor; 21 | vector.y = data.getInt16(2) * scaleFactor; 22 | vector.z = data.getInt16(4) * scaleFactor; 23 | vector.w = data.getInt16(6) * scaleFactor; 24 | vector.accuracy = data.getUint16(8) * scaleFactor; 25 | } 26 | 27 | void DataParser::parseBSEC(SensorLongDataPacket& data, DataBSEC& vector) { 28 | const float SCALE_BSEC_BVOC_EQ = 0.01f; 29 | const float SCALE_BSEC_COMP_T = 1.0f / 256; 30 | const float SCALE_BSEC_COMP_H = 1.0f / 500; 31 | 32 | vector.iaq = data.getUint16(0); 33 | vector.iaq_s = data.getUint16(2); 34 | vector.b_voc_eq = data.getUint16(4) * SCALE_BSEC_BVOC_EQ; //b-VOC-eq in the FIFO frame is scaled up by 100 35 | vector.co2_eq = data.getUint24(6); 36 | vector.accuracy = data.getUint8(9); 37 | vector.comp_t = data.getInt16(10) * SCALE_BSEC_COMP_T; 38 | vector.comp_h = data.getUint16(12) * SCALE_BSEC_COMP_H; 39 | vector.comp_g = (uint32_t)(data.getFloat(14)); 40 | } 41 | 42 | void DataParser::parseBSECLegacy(SensorLongDataPacket& data, DataBSEC& vector) { 43 | vector.comp_t = data.getFloat(0); 44 | vector.comp_h = data.getFloat(4); 45 | //note that: SENSOR_DATA_FIXED_LENGTH is defined as 10 by default, 46 | //so all the fields below are 0 unless it's redefined to 29 and above 47 | vector.comp_g = (uint32_t)(data.getFloat(8)); 48 | vector.iaq = (uint16_t)(data.getFloat(12)); 49 | vector.iaq_s = (uint16_t)(data.getFloat(16)); 50 | vector.co2_eq = (uint32_t)data.getFloat(20); 51 | vector.b_voc_eq = data.getFloat(24); 52 | vector.accuracy = data.getUint8(28); 53 | } 54 | 55 | void DataParser::parseData(SensorDataPacket& data, float& value, float scaleFactor, SensorPayload format) { 56 | uint8_t id = data.sensorId; 57 | switch (format) { 58 | case P8BITSIGNED: 59 | value = data.getInt8(0) * scaleFactor; 60 | break; 61 | case P8BITUNISIGNED: 62 | value = data.getUint8(0) * scaleFactor; 63 | break; 64 | case P16BITSIGNED: 65 | value = data.getInt16(0) * scaleFactor; 66 | break; 67 | case P16BITUNSIGNED: 68 | value = data.getUint16(0) * scaleFactor; 69 | break; 70 | case P24BITUNSIGNED: 71 | value = data.getUint24(0) * scaleFactor; 72 | break; 73 | case P32BITSIGNED: 74 | value = data.getInt32(0) * scaleFactor; 75 | break; 76 | case P32BITUNSIGNED: 77 | value = data.getUint32(0) * scaleFactor; 78 | break; 79 | case PEVENT: 80 | value = 1; 81 | default: 82 | break; 83 | } 84 | 85 | } 86 | 87 | void DataParser::parseActivity(SensorDataPacket& data, uint16_t& value) { 88 | value = data.getUint16(0); 89 | } 90 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/DFUManager.cpp: -------------------------------------------------------------------------------- 1 | #include "DFUManager.h" 2 | 3 | #include "BLEHandler.h" 4 | #include "EslovHandler.h" 5 | 6 | SPIFBlockDevice DFUManager::_bd(SPI_PSELMOSI0, SPI_PSELMISO0, 7 | SPI_PSELSCK0, CS_FLASH, 16000000); 8 | 9 | mbed::LittleFileSystem DFUManager::_fs("fs"); 10 | 11 | DFUManager::DFUManager() : 12 | _target(NULL), 13 | _acknowledgment(DFUNack), 14 | _transferPending(false), 15 | _debug(NULL) 16 | { 17 | } 18 | 19 | DFUManager::~DFUManager() 20 | { 21 | } 22 | 23 | bool DFUManager::begin() 24 | { 25 | int err = _fs.mount(&_bd); 26 | if (err) { 27 | if(_debug) { 28 | _debug->print("Error mounting file system: "); 29 | _debug->println(err); 30 | } 31 | 32 | if (_fs.reformat(&_bd)) { 33 | if(_debug) { 34 | _debug->println("Error reformatting file system"); 35 | } 36 | return false; 37 | } 38 | 39 | } 40 | 41 | return true; 42 | } 43 | 44 | void DFUManager::processPacket(DFUSource source, DFUType dfuType, const uint8_t* data) 45 | { 46 | _transferPending = true; 47 | _dfuSource = source; 48 | 49 | DFUPacket* packet = (DFUPacket*)data; 50 | 51 | if (source == bleDFU) { 52 | //If Eslov is still active, turn it off 53 | if (eslovHandler.eslovActive) { 54 | eslovHandler.eslovActive = false; 55 | eslovHandler.end(); 56 | } 57 | } else { 58 | //If BLE is still active, turn it off 59 | if (bleHandler.bleActive) { 60 | bleHandler.bleActive = false; 61 | bleHandler.end(); 62 | } 63 | } 64 | 65 | if (_debug) { 66 | _debug->print("packet: "); 67 | _debug->println(packet->index); 68 | } 69 | 70 | if (packet->index == 0) { 71 | if (dfuType == DFU_INTERNAL) { 72 | _target = fopen("/fs/ANNA_UPDATE.BIN", "wb"); 73 | } else { 74 | _target = fopen("/fs/BHY_UPDATE.BIN", "wb"); 75 | } 76 | 77 | if(_debug) { 78 | bool target_found = (_target != NULL); 79 | _debug->print("target_found = "); 80 | _debug->println(target_found); 81 | } 82 | } 83 | 84 | if (_target != NULL) { 85 | int ret = fwrite(&packet->data, packet->last ? packet->remaining : sizeof(packet->data), 1, _target); 86 | // Set the acknowledgment flag according to the write return value 87 | if(_debug) { 88 | _debug->print("ret: "); 89 | _debug->println(ret); 90 | } 91 | if (ret > 0) _acknowledgment = DFUAck; 92 | else _acknowledgment = DFUNack; 93 | } 94 | 95 | if (packet->last) { 96 | if(_debug) { 97 | _debug->print("Last packet received. Remaining: "); 98 | _debug->println(packet->remaining); 99 | } 100 | fclose(_target); 101 | _target = NULL; 102 | } 103 | } 104 | 105 | bool DFUManager::isPending() 106 | { 107 | return _transferPending; 108 | } 109 | 110 | DFUSource DFUManager::dfuSource() 111 | { 112 | return _dfuSource; 113 | } 114 | 115 | void DFUManager::closeDfu() 116 | { 117 | _transferPending = false; 118 | } 119 | 120 | // acknowledgment flag is reset when read 121 | uint8_t DFUManager::acknowledgment() 122 | { 123 | uint8_t ack = _acknowledgment; 124 | // Reset acknowledgment 125 | _acknowledgment = DFUNack; 126 | return ack; 127 | } 128 | 129 | void DFUManager::debug(Stream &stream) 130 | { 131 | _debug = &stream; 132 | } 133 | 134 | DFUManager dfuManager; 135 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/DataHarvester/DataHarvester.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This sketch shows how the use the Nicla Sense ME to collect 3 | acceleration data at maximum speed, and how to send them to 4 | a companion app via UART. 5 | 6 | It uses a COBS-based, packed-oriented serial protocol to establish 7 | a reliable communication to the companion app, to receive operation 8 | commands and to send collected data. 9 | 10 | Requirements: 11 | - Nicla Sense ME 12 | - PacketSerial library https://github.com/bakercp/PacketSerial 13 | - Commander.py companion app 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | // Manage the communication with the companion app 20 | #include 21 | PacketSerial myPacketSerial; 22 | 23 | auto capture { false }; 24 | 25 | // Send data as packed struct, ie. as the raw bytes 26 | struct __attribute__((__packed__)) Data { 27 | float ts; 28 | float x; 29 | float y; 30 | float z; 31 | }; 32 | 33 | SensorXYZ accel(SENSOR_ID_ACC_PASS); 34 | 35 | void setup() 36 | { 37 | nicla::begin(); 38 | nicla::leds.begin(); 39 | BHY2.begin(); 40 | accel.begin(); 41 | 42 | // Init the PacketSerial communication 43 | myPacketSerial.begin(115200); 44 | // Set the function for handling commands from the companion app 45 | myPacketSerial.setPacketHandler(&onPacketReceived); 46 | 47 | pinMode(LED_BUILTIN, OUTPUT); 48 | for (auto i = 0u; i < 10; i++) { 49 | nicla::leds.setColor(green); 50 | delay(25); 51 | nicla::leds.setColor(off); 52 | delay(25); 53 | } 54 | } 55 | 56 | void loop() 57 | { 58 | // Update communication-channel and sensors 59 | myPacketSerial.update(); 60 | BHY2.update(); 61 | 62 | // Check for a receive buffer overflow (optional). 63 | if (myPacketSerial.overflow()) { 64 | for (auto i = 0u; i < 5; i++) { 65 | nicla::leds.setColor(green); 66 | delay(25); 67 | nicla::leds.setColor(off); 68 | delay(25); 69 | } 70 | } 71 | 72 | // Capture and send data as soon as we read it 73 | if (capture == true) { 74 | auto now = micros() / 1000.0; 75 | 76 | // Collect data from accel sensor 77 | Data data { now, accel.x(), accel.y(), accel.z() }; 78 | constexpr size_t dataBufLen { sizeof(Data) }; 79 | uint8_t dataBuf[dataBufLen] {}; 80 | 81 | // Convert the Data struct to an array of bytes 82 | memcpy(dataBuf, reinterpret_cast(&data), dataBufLen); 83 | 84 | // Send data 85 | myPacketSerial.send(dataBuf, dataBufLen); 86 | } 87 | } 88 | 89 | 90 | // Parse commands from the companion app 91 | void onPacketReceived(const uint8_t* buffer, size_t size) 92 | { 93 | uint8_t tempBuffer[size]; 94 | 95 | for (auto i = 0u; i < 2; i++) { 96 | nicla::leds.setColor(green); 97 | delay(25); 98 | nicla::leds.setColor(off); 99 | delay(25); 100 | } 101 | 102 | memcpy(tempBuffer, buffer, size); 103 | 104 | switch (tempBuffer[0]) { 105 | case 'R': 106 | nicla::leds.setColor(green); 107 | capture = true; 108 | break; 109 | case 'S': 110 | nicla::leds.setColor(off); 111 | capture = false; 112 | break; 113 | 114 | default: 115 | break; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/bosch/bhy2_parse.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Bosch Sensortec GmbH. All rights reserved. 3 | * 4 | * BSD-3-Clause 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * 3. Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | * 33 | * @file bhy2_parse.c 34 | * @date 2020-03-24 35 | * @version v1.3.0 36 | * 37 | */ 38 | 39 | #include "bhy2.h" 40 | #include "bhy2_parse.h" 41 | 42 | void bhy2_parse_temperature_celsius(const uint8_t *data, bhy2_float *temperature) 43 | { 44 | /* 1 LSB = 1/100 degC */ 45 | float scale_factor = (float)1 / 100; 46 | 47 | *temperature = BHY2_LE2S16(data) * scale_factor; 48 | } 49 | 50 | void bhy2_parse_humidity(const uint8_t *data, bhy2_float *humidity) 51 | { 52 | float scale_factor = (float)1; 53 | 54 | *humidity = data[0] * scale_factor; 55 | } 56 | 57 | void bhy2_parse_pressure(const uint8_t *data, bhy2_float *pressure) 58 | { 59 | /* 1 LSB = 1/128 Pa */ 60 | float scale_factor = (float)1 / 128; 61 | 62 | *pressure = (float)BHY2_LE2U24(data) * scale_factor; 63 | } 64 | 65 | void bhy2_parse_altitude(const uint8_t *data, bhy2_float *altitude) 66 | { 67 | *altitude = (float)(data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24)); 68 | } 69 | 70 | void bhy2_parse_quaternion(const uint8_t *data, struct bhy2_data_quaternion *quaternion) 71 | { 72 | quaternion->x = BHY2_LE2S16(data); 73 | quaternion->y = BHY2_LE2S16(data + 2); 74 | quaternion->z = BHY2_LE2S16(data + 4); 75 | quaternion->w = BHY2_LE2S16(data + 6); 76 | quaternion->accuracy = BHY2_LE2U16(data + 8); 77 | } 78 | 79 | void bhy2_parse_xyz(const uint8_t *data, struct bhy2_data_xyz *vector) 80 | { 81 | vector->x = BHY2_LE2S16(data); 82 | vector->y = BHY2_LE2S16(data + 2); 83 | vector->z = BHY2_LE2S16(data + 4); 84 | } 85 | 86 | void bhy2_parse_orientation(const uint8_t *data, struct bhy2_data_orientation *orientation) 87 | { 88 | orientation->heading = BHY2_LE2S16(data); 89 | orientation->pitch = BHY2_LE2S16(data + 2); 90 | orientation->roll = BHY2_LE2S16(data + 4); 91 | } 92 | -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/bhy2_parse.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Bosch Sensortec GmbH. All rights reserved. 3 | * 4 | * BSD-3-Clause 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * 3. Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | * 33 | * @file bhy2_parse.c 34 | * @date 2020-03-24 35 | * @version v1.3.0 36 | * 37 | */ 38 | 39 | #include "bhy2.h" 40 | #include "bhy2_parse.h" 41 | 42 | void bhy2_parse_temperature_celsius(const uint8_t *data, bhy2_float *temperature) 43 | { 44 | /* 1 LSB = 1/100 degC */ 45 | float scale_factor = (float)1 / 100; 46 | 47 | *temperature = BHY2_LE2S16(data) * scale_factor; 48 | } 49 | 50 | void bhy2_parse_humidity(const uint8_t *data, bhy2_float *humidity) 51 | { 52 | float scale_factor = (float)1; 53 | 54 | *humidity = data[0] * scale_factor; 55 | } 56 | 57 | void bhy2_parse_pressure(const uint8_t *data, bhy2_float *pressure) 58 | { 59 | /* 1 LSB = 1/128 Pa */ 60 | float scale_factor = (float)1 / 128; 61 | 62 | *pressure = (float)BHY2_LE2U24(data) * scale_factor; 63 | } 64 | 65 | void bhy2_parse_altitude(const uint8_t *data, bhy2_float *altitude) 66 | { 67 | *altitude = (float)(data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24)); 68 | } 69 | 70 | void bhy2_parse_quaternion(const uint8_t *data, struct bhy2_data_quaternion *quaternion) 71 | { 72 | quaternion->x = BHY2_LE2S16(data); 73 | quaternion->y = BHY2_LE2S16(data + 2); 74 | quaternion->z = BHY2_LE2S16(data + 4); 75 | quaternion->w = BHY2_LE2S16(data + 6); 76 | quaternion->accuracy = BHY2_LE2U16(data + 8); 77 | } 78 | 79 | void bhy2_parse_xyz(const uint8_t *data, struct bhy2_data_xyz *vector) 80 | { 81 | vector->x = BHY2_LE2S16(data); 82 | vector->y = BHY2_LE2S16(data + 2); 83 | vector->z = BHY2_LE2S16(data + 4); 84 | } 85 | 86 | void bhy2_parse_orientation(const uint8_t *data, struct bhy2_data_orientation *orientation) 87 | { 88 | orientation->heading = BHY2_LE2S16(data); 89 | orientation->pitch = BHY2_LE2S16(data + 2); 90 | orientation->roll = BHY2_LE2S16(data + 4); 91 | } 92 | -------------------------------------------------------------------------------- /bootloader/src/led_driver.cpp: -------------------------------------------------------------------------------- 1 | /* 12/23/2017 Copyright Tlera Corporation 2 | * 3 | * Created by Kris Winer 4 | * 5 | This basic sketch is to operate the IS31FL3194 3-channel led driver 6 | http://www.issi.com/WW/pdf/IS31FL3194.pdf 7 | 8 | The sketch uses default SDA/SCL pins on the Ladybug development board 9 | but should work with almost any Arduino-based board. 10 | Library may be used freely and without limit only with attribution. 11 | 12 | */ 13 | #include 14 | #include 15 | #include "IS31FL3194.h" 16 | 17 | IS31FL3194::IS31FL3194(){ 18 | } 19 | 20 | extern I2C i2c0; 21 | 22 | // Read the Chip ID register, this is a good test of communication 23 | uint8_t IS31FL3194::getChipID() 24 | { 25 | uint8_t c = readByte(IS31FL3194_ADDRESS, IS31FL3194_PRODUCT_ID); // Read PRODUCT_ID register for IS31FL3194 26 | return c; 27 | } 28 | 29 | 30 | void IS31FL3194::reset() 31 | { 32 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_RESET, 0xC5); 33 | } 34 | 35 | 36 | void IS31FL3194::powerDown() 37 | { 38 | uint8_t d = readByte(IS31FL3194_ADDRESS, IS31FL3194_OP_CONFIG); 39 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OP_CONFIG, d & ~(0x01)); //clear bit 0 to shut down 40 | } 41 | 42 | 43 | void IS31FL3194::powerUp() 44 | { 45 | uint8_t d = readByte(IS31FL3194_ADDRESS, IS31FL3194_OP_CONFIG); 46 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OP_CONFIG, d | 0x01); //set bit 0 to enable 47 | } 48 | 49 | 50 | void IS31FL3194::init()// configure rgb led function 51 | { 52 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OP_CONFIG, 0x01); // normal operation in current mode 53 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT_CONFIG, 0x07); // enable all three outputs 54 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_CURRENT_BAND, 0x00); // 10 mA max current 55 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_HOLD_FUNCTION, 0x00); // hold function disable 56 | } 57 | 58 | 59 | void IS31FL3194::ledBlink(uint8_t color, uint32_t duration) 60 | { 61 | if(color == green) { 62 | _out1 = 0x00; 63 | _out2 = 0xFF; 64 | _out3 = 0x00; 65 | } 66 | 67 | if(color == blue) { 68 | _out1 = 0xFF; 69 | _out2 = 0x00; 70 | _out3 = 0x00; 71 | } 72 | 73 | if(color == red) { 74 | _out1 = 0x00; 75 | _out2 = 0x00; 76 | _out3 = 0xFF; 77 | } 78 | 79 | if(color == cyan) { 80 | _out1 = 0x20; 81 | _out2 = 0x20; 82 | _out3 = 0x00; 83 | } 84 | 85 | if(color == magenta) { 86 | _out1 = 0x20; 87 | _out2 = 0x00; 88 | _out3 = 0x20; 89 | } 90 | 91 | if(color == yellow) { 92 | _out1 = 0x00; 93 | _out2 = 0x20; 94 | _out3 = 0x20; 95 | } 96 | 97 | 98 | // set rgb led current 99 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT1, _out1 >> 4); //maximum current 100 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT2, _out2 >> 4); 101 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT3, _out3 >> 4); 102 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_COLOR_UPDATE, 0xC5); // write to color update register for changes to take effect 103 | ThisThread::sleep_for(duration); 104 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT1, 0x00); //maximum current 105 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT2, 0x00); 106 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT3, 0x00); 107 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_COLOR_UPDATE, 0xC5); // write to color update register for changes to take effect 108 | } 109 | 110 | void IS31FL3194::writeByte(uint8_t address, uint8_t subAddress, uint8_t data) 111 | { 112 | char command[2]; 113 | command[0] = subAddress; 114 | command[1] = data; 115 | i2c0.write(address << 1, command, 2); 116 | } 117 | 118 | uint8_t IS31FL3194::readByte(uint8_t address, uint8_t subAddress) 119 | { 120 | char response = 0xFF; 121 | int ret = i2c0.write(address << 1, (const char*)&subAddress, 1); 122 | ret = i2c0.read(address << 1, &response, 1); 123 | return response; 124 | } -------------------------------------------------------------------------------- /Arduino_BHY2/docs/readme.md: -------------------------------------------------------------------------------- 1 | # Bosch BHY2 Sensor Library for Arduino 2 | 3 | This library allows the Nicla Sense ME to interface with the onboard 6-axis IMU (BHI260AP) and environmental sensor (BME688). 4 | ## Features 5 | 6 | - Easy access to data from Nicla Sense ME sensors using sensor ID 7 | - Wrapper for [Bosch BHY2-Sensor-API](https://github.com/BoschSensortec/BHY2-Sensor-API) 8 | - DFU (Device Firmware Update) of the ANNA-B112 and the BHI260AP 9 | - All functionality available over both ESLOV and BLE 10 | 11 | ## Usage 12 | 13 | The ArduinoBLE library needs to be installed, for the BLE features to function. 14 | 15 | To use this library, use this code at the top of your sketch 16 | ``` 17 | #include 18 | ``` 19 | 20 | > **Note** 21 | > If no wired connection is used, we can set the state of `NiclaWiring` to `false`. Also, a modified delay using the millis() function similar to the [BlinkWithoutDelay.ino](https://docs.arduino.cc/built-in-examples/digital/BlinkWithoutDelay) sketch is used. 22 | 23 | Methods interacting with the sensor, such as `configureSensor()` and `readSensorData()`, calls methods defined by the `sensortec` class which are defined in `BoschSensortec.h`. The `sensortec` class itself calls methods developed by the [Bosch BHY2-Sensor-API](https://github.com/boschsensortec/BHY2-Sensor-API) library and mirrored in the `bosch` folder. Motion data is parsed from the `DataParser` class (defined in `sensors/DataParser.h`). The `debug()` method checks the state of `NiclaConfig` and then calls the related method to start `.debug(stream)` using `eslovHandler` or `BLEHandler` class, together with `sensortec`, `dfuManager` and `BoschParser`. 24 | 25 | IMU sensor objects are defined as objects of `SensorXYZ`. IMU readings in quaternion format are defined in `SensorQuaternion.h`. Activity recognition (obtained from the on-chip AI processor) in `SensorActivity.h`. Pressure, temperature and gas values are defined in `Sensor.h`. The relevant IDs are defined in `SensorID.h`. 26 | 27 | A UML diagram of the main library classes are provided in the diagram below, provided as an editable SVG file. 28 | 29 | ![Arduino_BHY2 Library UML Diagram](./Arduino_BHY2.UML.drawio.svg) 30 | 31 | For additional information on the Arduino_BHY2 library (including a list of Sensor IDs) and how you can use it with the Nicla Sense ME, see the [Arduino Nicla Sense ME Cheat Sheet](https://docs.arduino.cc/tutorials/nicla-sense-me/cheat-sheet) page. 32 | 33 | ## Examples 34 | 35 | - [App](https://github.com/arduino-libraries/Arduino_BHY2/blob/main/examples/App/App.ino) : Control Nicla Sense ME from an external device acting as a host via I2C or ESLOV 36 | - [AppLowDelay](https://github.com/arduino-libraries/Arduino_BHY2/blob/main/examples/AppLowDelay/AppLowDelay.ino) : Control Nicla Sense ME from an external device acting as a host via I2C or ESLOV with a lower delay 37 | - [BHYFirmwareUpdate](https://github.com/arduino-libraries/Arduino_BHY2/tree/main/examples/BHYFirmwareUpdate) : Update BHI260 firmware to latest version. Can also be used to upload custom firmware binary. 38 | - [Fail_Safe_flasher](https://github.com/arduino-libraries/Arduino_BHY2/blob/main/examples/Fail_Safe_flasher/Fail_Safe_flasher.ino) : Fail Safe flashing of a RGB blink sketch. Writes binary file to the SPI flash, then performs CRC to ensure successful writing of binary file to flash memory. 39 | - [ReadSensorConfiguration](https://github.com/arduino-libraries/Arduino_BHY2/blob/main/examples/ReadSensorConfiguration/ReadSensorConfiguration.ino) : Send sample rate, latency and range for accelerator, gyroscope, rotation, temperature and gas sensor over Serial 40 | - [ShowSensorList](https://github.com/arduino-libraries/Arduino_BHY2/blob/main/examples/ShowSensorList/ShowSensorList.ino) : List sensors of the Nicla Sense ME board 41 | - [Standalone](https://github.com/arduino-libraries/Arduino_BHY2/blob/main/examples/Standalone/Standalone.ino) : Read motion, temperature and gas data and send over Serial 42 | - [StandaloneFlashStorage](https://github.com/arduino-libraries/Arduino_BHY2/blob/main/examples/StandaloneFlashStorage/StandaloneFlashStorage.ino) : Save accelerator, gryoscope, temperature, gas and rotation sensor values to the onboard SPI flash in a .CSV file. 43 | 44 | ## License 45 | 46 | See [LICENSE.txt](LICENSE.txt) -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/Arduino_BHY2Host.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino_BHY2Host.h" 2 | 3 | #include "EslovHandler.h" 4 | #include "sensors/SensorManager.h" 5 | 6 | Arduino_BHY2Host::Arduino_BHY2Host() : 7 | _passthrough(false), 8 | _wiring(NICLA_VIA_ESLOV), 9 | _debug(NULL) 10 | { 11 | } 12 | 13 | Arduino_BHY2Host::~Arduino_BHY2Host() 14 | { 15 | } 16 | 17 | bool Arduino_BHY2Host::begin(bool passthrough, NiclaWiring niclaConnection) 18 | { 19 | _passthrough = passthrough; 20 | _wiring = niclaConnection; 21 | if (niclaConnection == NICLA_VIA_BLE) { 22 | #ifdef __BHY2_HOST_BLE_SUPPORTED__ 23 | if (_debug) { 24 | _debug->println("NICLA_VIA_BLE selected"); 25 | } 26 | return bleHandler.begin(); 27 | #else 28 | if (_debug) { 29 | _debug->println("Unsupported board!"); 30 | } 31 | return false; 32 | #endif 33 | } 34 | if (niclaConnection == NICLA_AS_SHIELD) { 35 | eslovHandler.niclaAsShield(); 36 | } 37 | return eslovHandler.begin(passthrough); 38 | } 39 | 40 | void Arduino_BHY2Host::update() 41 | { 42 | if (_wiring == NICLA_VIA_BLE) { 43 | #ifdef __BHY2_HOST_BLE_SUPPORTED__ 44 | bleHandler.update(); 45 | #endif 46 | } else { 47 | if (_passthrough){ 48 | eslovHandler.update(); 49 | } else { 50 | uint8_t available = availableSensorData(); 51 | for (int i = 0; i < available; i++) { 52 | SensorDataPacket data; 53 | readSensorData(data); 54 | SensorLongDataPacket longData; 55 | memcpy(&longData, &data, sizeof(SensorDataPacket)); 56 | sensorManager.process(longData); 57 | } 58 | uint8_t availableLong = availableSensorLongData(); 59 | for (int i = 0; i < available; i++) { 60 | SensorLongDataPacket data; 61 | readSensorLongData(data); 62 | sensorManager.process(data); 63 | } 64 | } 65 | } 66 | } 67 | 68 | void Arduino_BHY2Host::update(unsigned long ms) 69 | { 70 | update(); 71 | delay(ms); 72 | } 73 | 74 | void Arduino_BHY2Host::configureSensor(SensorConfigurationPacket& config) 75 | { 76 | if (_wiring == NICLA_VIA_BLE) { 77 | #ifdef __BHY2_HOST_BLE_SUPPORTED__ 78 | bleHandler.writeConfigPacket(config); 79 | #endif 80 | } else { 81 | eslovHandler.writeConfigPacket(config); 82 | } 83 | } 84 | 85 | void Arduino_BHY2Host::configureSensor(uint8_t sensorId, float sampleRate, uint32_t latency) 86 | { 87 | SensorConfigurationPacket config; 88 | config.sensorId = sensorId; 89 | config.sampleRate = sampleRate; 90 | config.latency = latency; 91 | if (_wiring == NICLA_VIA_BLE) { 92 | #ifdef __BHY2_HOST_BLE_SUPPORTED__ 93 | bleHandler.writeConfigPacket(config); 94 | #endif 95 | } else { 96 | eslovHandler.writeConfigPacket(config); 97 | } 98 | } 99 | 100 | uint8_t Arduino_BHY2Host::requestAck() 101 | { 102 | return eslovHandler.requestPacketAck(); 103 | } 104 | 105 | uint8_t Arduino_BHY2Host::availableSensorData() 106 | { 107 | return eslovHandler.requestAvailableData(); 108 | } 109 | 110 | uint8_t Arduino_BHY2Host::availableSensorLongData() 111 | { 112 | return eslovHandler.requestAvailableLongData(); 113 | } 114 | 115 | bool Arduino_BHY2Host::readSensorData(SensorDataPacket &data) 116 | { 117 | return eslovHandler.requestSensorData(data); 118 | } 119 | 120 | bool Arduino_BHY2Host::readSensorLongData(SensorLongDataPacket &data) 121 | { 122 | return eslovHandler.requestSensorLongData(data); 123 | } 124 | 125 | void Arduino_BHY2Host::parse(SensorDataPacket& data, DataXYZ& vector) 126 | { 127 | DataParser::parse3DVector(data, vector); 128 | } 129 | 130 | void Arduino_BHY2Host::parse(SensorDataPacket& data, DataOrientation& vector) 131 | { 132 | DataParser::parseEuler(data, vector); 133 | } 134 | 135 | void Arduino_BHY2Host::parse(SensorDataPacket& data, DataOrientation& vector, float scaleFactor) 136 | { 137 | DataParser::parseEuler(data, vector, scaleFactor); 138 | } 139 | 140 | NiclaWiring Arduino_BHY2Host::getNiclaConnection() 141 | { 142 | return _wiring; 143 | } 144 | 145 | void Arduino_BHY2Host::debug(Stream &stream) 146 | { 147 | _debug = &stream; 148 | eslovHandler.debug(stream); 149 | #ifdef __BHY2_HOST_BLE_SUPPORTED__ 150 | bleHandler.debug(stream); 151 | #endif 152 | } 153 | 154 | Arduino_BHY2Host BHY2Host; 155 | -------------------------------------------------------------------------------- /bootloader/examples/Blink_Unisense/Blink_Unisense.ino: -------------------------------------------------------------------------------- 1 | #define IS31FL3194_ADDRESS 0x53 2 | 3 | #define IS31FL3194_OP_CONFIG 0x01 4 | #define IS31FL3194_OUT_CONFIG 0x02 5 | #define IS31FL3194_CURRENT_BAND 0x03 6 | #define IS31FL3194_HOLD_FUNCTION 0x04 7 | 8 | // Current Mode 9 | #define IS31FL3194_OUT1 0x10 10 | #define IS31FL3194_OUT2 0x21 11 | #define IS31FL3194_OUT3 0x32 12 | 13 | #define IS31FL3194_COLOR_UPDATE 0x40 14 | 15 | #define IS31FL3194_RESET 0x4F 16 | 17 | // allowed colors 18 | #define red 0 19 | #define green 1 20 | #define blue 2 21 | #define yellow 3 22 | #define magenta 4 23 | #define cyan 5 24 | 25 | #include 26 | #include 27 | 28 | mbed::I2C i2c(I2C_SDA0, I2C_SCL0); 29 | 30 | uint8_t _out1 = 0; 31 | uint8_t _out2 = 0; 32 | uint8_t _out3 = 0; 33 | 34 | void setup() { 35 | // reset 36 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_RESET, 0xC5); 37 | 38 | // init 39 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OP_CONFIG, 0x01); // normal operation in current mode 40 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT_CONFIG, 0x07); // enable all three outputs 41 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_CURRENT_BAND, 0x00); // 10 mA max current 42 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_HOLD_FUNCTION, 0x00); // hold function disable 43 | 44 | // powerUp 45 | uint8_t d = readByte(IS31FL3194_ADDRESS, IS31FL3194_OP_CONFIG); 46 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OP_CONFIG, d | 0x01); //set bit 0 to enable 47 | 48 | } 49 | 50 | void loop() { 51 | //cycle through LED color configurations 52 | ledBlink(green, 1000); 53 | delay(1000); 54 | ledBlink(blue, 1000); 55 | delay(1000); 56 | ledBlink(red, 1000); 57 | delay(1000); 58 | ledBlink(cyan, 1000); 59 | delay(1000); 60 | ledBlink(magenta, 1000); 61 | delay(1000); 62 | ledBlink(yellow, 1000); 63 | delay(1000); 64 | } 65 | 66 | /** 67 | * Blink a given color for a set duration in milliseconds. 68 | */ 69 | void ledBlink(uint8_t color, uint32_t duration) 70 | /** 71 | * Intensity is defined in 256 levels according to table 7 of the IS31FL3194 datasheet: 72 | * 0x00 -> 0 73 | * 0x20 -> I_max * (2^7) / 256 = I_max * 1/2 74 | * 0xFF -> I_max * (2^7 + 2^6 + 2^5 + 2^4 + 2^3 + 2^2 + 2^1 + 2^0) / 255 ~= I_max 75 | */ 76 | { 77 | if(color == green) { 78 | _out1 = 0x00; 79 | _out2 = 0xFF; 80 | _out3 = 0x00; 81 | } 82 | 83 | if(color == blue) { 84 | _out1 = 0xFF; 85 | _out2 = 0x00; 86 | _out3 = 0x00; 87 | } 88 | 89 | if(color == red) { 90 | _out1 = 0x00; 91 | _out2 = 0x00; 92 | _out3 = 0xFF; 93 | } 94 | 95 | if(color == cyan) { 96 | _out1 = 0x20; 97 | _out2 = 0x20; 98 | _out3 = 0x00; 99 | } 100 | 101 | if(color == magenta) { 102 | _out1 = 0x20; 103 | _out2 = 0x00; 104 | _out3 = 0x20; 105 | } 106 | 107 | if(color == yellow) { 108 | _out1 = 0x00; 109 | _out2 = 0x20; 110 | _out3 = 0x20; 111 | } 112 | 113 | 114 | // set rgb led current 115 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT1, _out1); //maximum current 116 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT2, _out2); 117 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT3, _out3); 118 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_COLOR_UPDATE, 0xC5); // write to color update register for changes to take effect 119 | delay(duration); 120 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT1, 0x00); //maximum current 121 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT2, 0x00); 122 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_OUT3, 0x00); 123 | writeByte(IS31FL3194_ADDRESS, IS31FL3194_COLOR_UPDATE, 0xC5); // write to color update register for changes to take effect 124 | } 125 | 126 | void writeByte(uint8_t address, uint8_t subAddress, uint8_t data) 127 | { 128 | char command[2]; 129 | command[0] = subAddress; 130 | command[1] = data; 131 | i2c.write(address << 1, command, 2); 132 | } 133 | 134 | 135 | uint8_t readByte(uint8_t address, uint8_t subAddress) 136 | { 137 | char response = 0xFF; 138 | int ret = i2c.write(address << 1, (const char*)&subAddress, 1); 139 | ret = i2c.read(address << 1, &response, 1); 140 | return response; 141 | } 142 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/DataParser.cpp: -------------------------------------------------------------------------------- 1 | #include "sensors/DataParser.h" 2 | 3 | void DataParser::parse3DVector(SensorDataPacket& data, DataXYZ& vector) { 4 | vector.x = data.getInt16(0); 5 | vector.y = data.getInt16(2); 6 | vector.z = data.getInt16(4); 7 | } 8 | 9 | void DataParser::parseEuler(SensorDataPacket& data, DataOrientation& vector, float scaleFactor) { 10 | vector.heading = data.getInt16(0) * scaleFactor; 11 | vector.pitch = data.getInt16(2) * scaleFactor; 12 | vector.roll = data.getInt16(4) * scaleFactor; 13 | } 14 | 15 | void DataParser::parseEuler(SensorDataPacket& data, DataOrientation& vector) { 16 | parseEuler(data, vector, 1.f); 17 | } 18 | 19 | void DataParser::parseQuaternion(SensorDataPacket& data, DataQuaternion& vector, float scaleFactor) { 20 | vector.x = data.getInt16(0) * scaleFactor; 21 | vector.y = data.getInt16(2) * scaleFactor; 22 | vector.z = data.getInt16(4) * scaleFactor; 23 | vector.w = data.getInt16(6) * scaleFactor; 24 | vector.accuracy = data.getUint16(8) * scaleFactor; 25 | } 26 | 27 | void DataParser::parseBSEC(SensorLongDataPacket& data, DataBSEC& vector) { 28 | const float SCALE_BSEC_BVOC_EQ = 0.01f; 29 | const float SCALE_BSEC_COMP_T = 1.0f / 256; 30 | const float SCALE_BSEC_COMP_H = 1.0f / 500; 31 | 32 | vector.iaq = data.getUint16(0); 33 | vector.iaq_s = data.getUint16(2); 34 | vector.b_voc_eq = data.getUint16(4) * SCALE_BSEC_BVOC_EQ; //b-VOC-eq in the FIFO frame is scaled up by 100 35 | vector.co2_eq = data.getUint24(6); 36 | vector.accuracy = data.getUint8(9); 37 | vector.comp_t = data.getInt16(10) * SCALE_BSEC_COMP_T; 38 | vector.comp_h = data.getUint16(12) * SCALE_BSEC_COMP_H; 39 | vector.comp_g = (uint32_t)(data.getFloat(14)); 40 | } 41 | 42 | void DataParser::parseBSEC2(SensorDataPacket& data, DataBSEC2& vector) { 43 | vector.gas_estimates[0] = data.getUint8(0); 44 | vector.gas_estimates[1] = data.getUint8(1); 45 | vector.gas_estimates[2] = data.getUint8(2); 46 | vector.gas_estimates[3] = data.getUint8(3); 47 | vector.accuracy = data.getUint8(4); 48 | } 49 | 50 | void DataParser::parseBSEC2Collector(SensorLongDataPacket& data, DataBSEC2Collector& vector) { 51 | const float SCALE_BSEC_COMP_T = 1.0f / 256; 52 | const float SCALE_BSEC_COMP_H = 1.0f / 500; 53 | 54 | vector.timestamp = data.getUint64(0); 55 | vector.raw_temp = data.getInt16(8) * SCALE_BSEC_COMP_T; 56 | vector.raw_pressure = data.getFloat(10); 57 | vector.raw_hum = data.getUint16(14) * SCALE_BSEC_COMP_H; 58 | vector.raw_gas = data.getFloat(16); 59 | vector.gas_index = data.getUint8(20); 60 | } 61 | 62 | void DataParser::parseBSECLegacy(SensorLongDataPacket& data, DataBSEC& vector) { 63 | vector.comp_t = data.getFloat(0); 64 | vector.comp_h = data.getFloat(4); 65 | //note that: SENSOR_DATA_FIXED_LENGTH is defined as 10 by default, 66 | //so all the fields below are 0 unless it's redefined to 29 and above 67 | vector.comp_g = (uint32_t)(data.getFloat(8)); 68 | vector.iaq = (uint16_t)(data.getFloat(12)); 69 | vector.iaq_s = (uint16_t)(data.getFloat(16)); 70 | vector.co2_eq = (uint32_t)data.getFloat(20); 71 | vector.b_voc_eq = data.getFloat(24); 72 | vector.accuracy = data.getUint8(28); 73 | } 74 | 75 | void DataParser::parseData(SensorDataPacket& data, float& value, float scaleFactor, SensorPayload format) { 76 | uint8_t id = data.sensorId; 77 | (void)id; 78 | switch (format) { 79 | case P8BITSIGNED: 80 | value = data.getInt8(0) * scaleFactor; 81 | break; 82 | case P8BITUNISIGNED: 83 | value = data.getUint8(0) * scaleFactor; 84 | break; 85 | case P16BITSIGNED: 86 | value = data.getInt16(0) * scaleFactor; 87 | break; 88 | case P16BITUNSIGNED: 89 | value = data.getUint16(0) * scaleFactor; 90 | break; 91 | case P24BITUNSIGNED: 92 | value = data.getUint24(0) * scaleFactor; 93 | break; 94 | case P32BITSIGNED: 95 | value = data.getInt32(0) * scaleFactor; 96 | break; 97 | case P32BITUNSIGNED: 98 | value = data.getUint32(0) * scaleFactor; 99 | break; 100 | case PEVENT: 101 | value = 1; 102 | default: 103 | break; 104 | } 105 | 106 | } 107 | 108 | void DataParser::parseActivity(SensorDataPacket& data, uint16_t& value) { 109 | value = data.getUint16(0); 110 | } 111 | -------------------------------------------------------------------------------- /tools/bhy-controller/src/static/parse-scheme.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": 3 | [ 4 | 5 | { 6 | "id": 0, 7 | "type": "quaternion", 8 | "parse-scheme": 9 | [ 10 | {"name": "x", "type": "int16", "scale-factor": 0.000061035}, 11 | {"name": "y", "type": "int16", "scale-factor": 0.0000610351}, 12 | {"name": "z", "type": "int16", "scale-factor": 0.000061035}, 13 | {"name": "w", "type": "int16", "scale-factor": 0.000061035}, 14 | {"name": "accuracy", "type": "uint16", "scale-factor": 0.000061035} 15 | ] 16 | }, 17 | 18 | { 19 | "id": 1, 20 | "type": "xyz", 21 | "parse-scheme": 22 | [ 23 | {"name": "x", "type": "int16", "scale-factor": 1}, 24 | {"name": "y", "type": "int16", "scale-factor": 1}, 25 | {"name": "z", "type": "int16", "scale-factor": 1} 26 | ] 27 | }, 28 | 29 | { 30 | "id": 2, 31 | "type": "orientation", 32 | "parse-scheme": 33 | [ 34 | {"name": "heading", "type": "int16", "scale-factor": 0.010986}, 35 | {"name": "pitch", "type": "int16", "scale-factor": 0.010986}, 36 | {"name": "roll", "type": "int16", "scale-factor": 0.010986} 37 | ] 38 | }, 39 | 40 | { 41 | "id": 3, 42 | "type": "event", 43 | "parse-scheme": 44 | [ 45 | {"name": "Event count", "type": "none", "scale-factor": 1} 46 | ] 47 | }, 48 | 49 | { 50 | "id": 4, 51 | "type": "activity", 52 | "parse-scheme": 53 | [ 54 | {"name": "Activity status", "type": "uint16", "scale-factor": 1} 55 | ] 56 | }, 57 | 58 | { 59 | "id": 5, 60 | "type": "BSECOutput", 61 | "parse-scheme": 62 | [ 63 | {"name": "Temperature (compensated)", "type": "float", "scale-factor": 1}, 64 | {"name": "Humidity (compensated)", "type": "float", "scale-factor": 1} 65 | ] 66 | }, 67 | 68 | { 69 | "id": 6, 70 | "type": "BSECOutputV2", 71 | "parse-scheme": 72 | [ 73 | {"name": "IAQ(Mobile)", "type": "uint16", "scale-factor": 1}, 74 | {"name": "IAQ(Stationary)", "type": "uint16", "scale-factor": 1}, 75 | {"name": "bVOC-Equivalents(ppm)", "type": "uint16", "scale-factor": 0.01}, 76 | {"name": "CO2-Equivalents(ppm)", "type": "uint24", "scale-factor": 1}, 77 | {"name": "Accuracy", "type": "uint8", "scale-factor": 1} 78 | ] 79 | }, 80 | 81 | { 82 | "id": 7, 83 | "type": "BSECOutputV2Full", 84 | "parse-scheme": 85 | [ 86 | {"name": "IAQ(Mobile)", "type": "uint16", "scale-factor": 1}, 87 | {"name": "IAQ(Stationary)", "type": "uint16", "scale-factor": 1}, 88 | {"name": "b-VOC-Equivalents(ppm)", "type": "uint16", "scale-factor": 0.01}, 89 | {"name": "CO2-Equivalents(ppm)", "type": "uint24", "scale-factor": 1}, 90 | {"name": "Accuracy", "type": "uint8", "scale-factor": 1}, 91 | {"name": "Compensated-Temperature(°C)", "type": "int16", "scale-factor": 0.003906}, 92 | {"name": "Compensated-Humidity(%)", "type": "uint16", "scale-factor": 0.002}, 93 | {"name": "Compensated-Gas Resistance(Ω)", "type": "uint32", "scale-factor": 1} 94 | ] 95 | }, 96 | 97 | { 98 | "id": 8, 99 | "type": "BSEC2DataCollectorOutput", 100 | "parse-scheme": 101 | [ 102 | {"name": "Timestamp(ms)", "type": "uint64", "scale-factor": 1}, 103 | {"name": "Temperature(°C)", "type": "int16", "scale-factor": 0.003906}, 104 | {"name": "Pressure(Pa)", "type": "float", "scale-factor": 1}, 105 | {"name": "Humidity(%)", "type": "uint16", "scale-factor": 0.002}, 106 | {"name": "Gas(Ω)", "type": "float", "scale-factor": 1}, 107 | {"name": "Index", "type": "uint8", "scale-factor": 1} 108 | ] 109 | }, 110 | 111 | { 112 | "id": 9, 113 | "type": "BSEC2GasClassifierOutput", 114 | "parse-scheme": 115 | [ 116 | {"name": "Gas0(%)", "type": "uint8", "scale-factor": 1}, 117 | {"name": "Gas1(%)", "type": "uint8", "scale-factor": 1}, 118 | {"name": "Gas2(%)", "type": "uint8", "scale-factor": 1}, 119 | {"name": "Gas3(%)", "type": "uint8", "scale-factor": 1}, 120 | {"name": "Accuracy", "type": "uint8", "scale-factor": 1} 121 | ] 122 | } 123 | ] 124 | } 125 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/bosch/bhy2_parse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Bosch Sensortec GmbH. All rights reserved. 3 | * 4 | * BSD-3-Clause 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * 3. Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | * 33 | * @file bhy2_parse.h 34 | * @date 2020-03-24 35 | * @version v1.3.0 36 | * 37 | */ 38 | #ifndef __BHY2_PARSE_H__ 39 | #define __BHY2_PARSE_H__ 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif /*__cplusplus */ 44 | 45 | #include "bhy2.h" 46 | 47 | /** 48 | * @brief Function to parse FIFO frame data into temperature 49 | * @param[in] data : Reference to the data buffer storing data from the FIFO 50 | * @param[out] temperature : Reference to the data buffer to store temperature in degree C 51 | */ 52 | void bhy2_parse_temperature_celsius(const uint8_t *data, bhy2_float *temperature); 53 | 54 | /** 55 | * @brief Function to parse FIFO frame data into humidity 56 | * @param[in] data : Reference to the data buffer storing data from the FIFO 57 | * @param[out] humidity : Reference to the data buffer to store humidity in % 58 | */ 59 | void bhy2_parse_humidity(const uint8_t *data, bhy2_float *humidity); 60 | 61 | /** 62 | * @brief Function to parse FIFO frame data into barometric pressure 63 | * @param[in] data : Reference to the data buffer storing data from the FIFO 64 | * @param[out] pressure : Reference to the data buffer to store pressure in Pascals 65 | */ 66 | void bhy2_parse_pressure(const uint8_t *data, bhy2_float *pressure); 67 | 68 | /** 69 | * @brief Function to parse FIFO frame data into altitude 70 | * @param[in] data : Reference to the data buffer storing data from the FIFO 71 | * @param[out] altitude : Reference to the data buffer to store altitude 72 | */ 73 | void bhy2_parse_altitude(const uint8_t *data, bhy2_float *altitude); 74 | 75 | /** 76 | * @brief Function to parse FIFO frame data into quaternion 77 | * @param[in] data : Reference to the data buffer storing data from the FIFO 78 | * @param[out] quaternion : Reference to the data buffer to store quaternion 79 | */ 80 | void bhy2_parse_quaternion(const uint8_t *data, struct bhy2_data_quaternion *quaternion); 81 | 82 | /** 83 | * @brief Function to parse FIFO frame data into orientation 84 | * @param[in] data : Reference to the data buffer storing data from the FIFO 85 | * @param[out] orientation : Reference to the data buffer to store orientation 86 | */ 87 | void bhy2_parse_orientation(const uint8_t *data, struct bhy2_data_orientation *orientation); 88 | 89 | /** 90 | * @brief Function to parse FIFO frame data into 3 axes vector 91 | * @param[in] data : Reference to the data buffer storing data from the FIFO 92 | * @param[out] vector : Reference to the data buffer to store vector 93 | */ 94 | void bhy2_parse_xyz(const uint8_t *data, struct bhy2_data_xyz *vector); 95 | 96 | #ifdef __cplusplus 97 | } 98 | #endif /*__cplusplus */ 99 | 100 | #endif /* __BHY2_PARSE_H__ */ 101 | -------------------------------------------------------------------------------- /bootloader/BHY2-Sensor-API/bhy2_parse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Bosch Sensortec GmbH. All rights reserved. 3 | * 4 | * BSD-3-Clause 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * 3. Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | * 33 | * @file bhy2_parse.h 34 | * @date 2020-03-24 35 | * @version v1.3.0 36 | * 37 | */ 38 | #ifndef __BHY2_PARSE_H__ 39 | #define __BHY2_PARSE_H__ 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif /*__cplusplus */ 44 | 45 | #include "bhy2.h" 46 | 47 | /** 48 | * @brief Function to parse FIFO frame data into temperature 49 | * @param[in] data : Reference to the data buffer storing data from the FIFO 50 | * @param[out] temperature : Reference to the data buffer to store temperature in degree C 51 | */ 52 | void bhy2_parse_temperature_celsius(const uint8_t *data, bhy2_float *temperature); 53 | 54 | /** 55 | * @brief Function to parse FIFO frame data into humidity 56 | * @param[in] data : Reference to the data buffer storing data from the FIFO 57 | * @param[out] humidity : Reference to the data buffer to store humidity in % 58 | */ 59 | void bhy2_parse_humidity(const uint8_t *data, bhy2_float *humidity); 60 | 61 | /** 62 | * @brief Function to parse FIFO frame data into barometric pressure 63 | * @param[in] data : Reference to the data buffer storing data from the FIFO 64 | * @param[out] pressure : Reference to the data buffer to store pressure in Pascals 65 | */ 66 | void bhy2_parse_pressure(const uint8_t *data, bhy2_float *pressure); 67 | 68 | /** 69 | * @brief Function to parse FIFO frame data into altitude 70 | * @param[in] data : Reference to the data buffer storing data from the FIFO 71 | * @param[out] altitude : Reference to the data buffer to store altitude 72 | */ 73 | void bhy2_parse_altitude(const uint8_t *data, bhy2_float *altitude); 74 | 75 | /** 76 | * @brief Function to parse FIFO frame data into quaternion 77 | * @param[in] data : Reference to the data buffer storing data from the FIFO 78 | * @param[out] quaternion : Reference to the data buffer to store quaternion 79 | */ 80 | void bhy2_parse_quaternion(const uint8_t *data, struct bhy2_data_quaternion *quaternion); 81 | 82 | /** 83 | * @brief Function to parse FIFO frame data into orientation 84 | * @param[in] data : Reference to the data buffer storing data from the FIFO 85 | * @param[out] orientation : Reference to the data buffer to store orientation 86 | */ 87 | void bhy2_parse_orientation(const uint8_t *data, struct bhy2_data_orientation *orientation); 88 | 89 | /** 90 | * @brief Function to parse FIFO frame data into 3 axes vector 91 | * @param[in] data : Reference to the data buffer storing data from the FIFO 92 | * @param[out] vector : Reference to the data buffer to store vector 93 | */ 94 | void bhy2_parse_xyz(const uint8_t *data, struct bhy2_data_xyz *vector); 95 | 96 | #ifdef __cplusplus 97 | } 98 | #endif /*__cplusplus */ 99 | 100 | #endif /* __BHY2_PARSE_H__ */ 101 | -------------------------------------------------------------------------------- /Arduino_BHY2/src/sensors/DataParser.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_PARSER_H_ 2 | #define DATA_PARSER_H_ 3 | 4 | #include "Arduino.h" 5 | #include "sensors/SensorTypes.h" 6 | #include "SensorID.h" 7 | 8 | struct DataXYZ { 9 | int16_t x; 10 | int16_t y; 11 | int16_t z; 12 | 13 | String toString() { 14 | return (String)("XYZ values - X: " + String(x) 15 | + " Y: " + String(y) 16 | + " Z: " + String(z) + "\n"); 17 | } 18 | }; 19 | 20 | struct DataOrientation { 21 | float heading; 22 | float pitch; 23 | float roll; 24 | 25 | String toString() { 26 | return (String)("Orientation values - heading: " + String(heading, 3) 27 | + " pitch: " + String(pitch, 3) 28 | + " roll: " + String(roll, 3) + "\n"); 29 | } 30 | }; 31 | 32 | struct DataQuaternion { 33 | float x; 34 | float y; 35 | float z; 36 | float w; 37 | float accuracy; 38 | 39 | String toString() { 40 | return (String)("Quaternion values - X: " + String(x, 3) 41 | + " Y: " + String(y, 3) 42 | + " Z: " + String(z, 3) 43 | + " W: " + String(w, 3) 44 | + " Accuracy: " + String(accuracy, 3) 45 | + "\n"); 46 | } 47 | }; 48 | 49 | struct DataBSEC { 50 | uint16_t iaq; //iaq value for regular use case 51 | uint16_t iaq_s; //iaq value for stationary use cases 52 | float b_voc_eq; //breath VOC equivalent (ppm) 53 | uint32_t co2_eq; //CO2 equivalent (ppm) [400,] 54 | float comp_t; //compensated temperature (celsius) 55 | float comp_h; //compensated humidity 56 | uint32_t comp_g; //compensated gas resistance (Ohms) 57 | uint8_t accuracy; //accuracy level: [0-3] 58 | 59 | String toString() { 60 | return (String)("BSEC output values - iaq: " + String(iaq) 61 | + " iaq_s: " + String(iaq_s) 62 | + " b_voc_eq: " + String(b_voc_eq, 2) 63 | + " co2_eq: " + String(co2_eq) 64 | + " accuracy: " + String(accuracy) 65 | + " comp_t: " + String(comp_t, 2) 66 | + " comp_h: " + String(comp_h, 2) 67 | + " comp_g: " + String(comp_g) 68 | + "\n"); 69 | } 70 | }; 71 | 72 | struct DataBSEC2 { 73 | uint8_t gas_estimates[4]; 74 | uint8_t accuracy; /* gas estimator accuracy (0-3) */ 75 | 76 | String toString() { 77 | return (String)("BSEC2 output values (%) - gas[0]: " + String(gas_estimates[0]) 78 | + " gas[1]: " + String(gas_estimates[1]) 79 | + " gas[2]: " + String(gas_estimates[2]) 80 | + " gas[3]: " + String(gas_estimates[3]) 81 | + " accuracy: " + String(accuracy) 82 | + "\n"); 83 | } 84 | }; 85 | 86 | struct DataBSEC2Collector { 87 | uint64_t timestamp; /* Time since poweron (Milliseconds) */ 88 | float raw_temp; /* Raw temperature (deg C) */ 89 | float raw_pressure; /* Raw pressure (Hectopascals) */ 90 | float raw_hum; /* Raw humidity (%rH) */ 91 | float raw_gas; /* Raw gas resistance (ohms) */ 92 | uint8_t gas_index; /* gas index (0-9) */ 93 | 94 | String toString() { 95 | return (String)("timestamp: " 96 | + String((uint32_t)(timestamp>>32)) 97 | + String((uint32_t)(timestamp & 0xFFFFFFFF)) 98 | + " temp: " + String(raw_temp, 2) 99 | + " pressure: " + String(raw_pressure, 2) 100 | + " hum: " + String(raw_hum, 2) 101 | + " gas: " + String(raw_gas, 2) 102 | + " gas_index: " + String(gas_index) 103 | + "\n"); 104 | } 105 | }; 106 | 107 | 108 | class DataParser { 109 | public: 110 | static void parse3DVector(SensorDataPacket& data, DataXYZ& vector); 111 | static void parseEuler(SensorDataPacket& data, DataOrientation& vector); 112 | static void parseEuler(SensorDataPacket& data, DataOrientation& vector, float scaleFactor); 113 | static void parseQuaternion(SensorDataPacket& data, DataQuaternion& vector, float scaleFactor); 114 | static void parseBSEC(SensorLongDataPacket& data, DataBSEC& vector); 115 | static void parseBSEC2(SensorDataPacket& data, DataBSEC2& vector); 116 | static void parseBSEC2Collector(SensorLongDataPacket& data, DataBSEC2Collector& vector); 117 | static void parseBSECLegacy(SensorLongDataPacket& data, DataBSEC& vector); 118 | static void parseData(SensorDataPacket& data, float& value, float scaleFactor, SensorPayload format); 119 | static void parseActivity(SensorDataPacket& data, uint16_t& value); 120 | }; 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /Arduino_BHY2/examples/DataHarvester/extras/Commander/Commander.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # This scripts is the companion application for the DataHarvester sketch. 4 | # It is a very basic application and can be improved and extended. 5 | # 6 | # It waits for user input to control the data acquisition operations and the 7 | # connection to the Arduino board and to save the received data to a CSV file. 8 | # 9 | # It establish a serial communication to the board and uses COBS as encoding and 10 | # reliable transport protocol. 11 | # 12 | # The main thread will always wait for user input while a second one is created 13 | # to exchange both control and data with the Arduino board. 14 | # The two threads coordinate via a simple Queue. 15 | # 16 | # Install the required module using this command in Windows 17 | # pip -m install pyreadline3 pyserial cobs 18 | 19 | # Thread and Queue 20 | from threading import Thread 21 | import queue 22 | 23 | # Helpers 24 | from datetime import date, datetime 25 | import struct 26 | 27 | # User input 28 | from pyreadline3 import Readline # import readline 29 | readline = Readline() 30 | import argparse 31 | 32 | # Communication with the Arduino board 33 | import serial 34 | from cobs import cobs 35 | 36 | 37 | # The main serial object 38 | # initialize later 39 | ser = serial.Serial() 40 | 41 | # The function to be run in the Arduino-facing thread 42 | def receiver(cmd_q, uart, name, tag): 43 | f = None 44 | running = False 45 | cmd = 'N' 46 | 47 | while True: 48 | # Check for commands from the main thread 49 | try: 50 | cmd = cmd_q.get(block=False) 51 | except queue.Empty: 52 | pass 53 | 54 | # Start acquisition, prepare output file, etc. 55 | if cmd == 'R': 56 | running = True 57 | today = datetime.today() 58 | iso = datetime.isoformat(today) 59 | filename = f'{name}_{tag}_{iso}.csv' 60 | f = open(filename, 'wt') 61 | print(f'timestamp,accX,accY,accZ', file=f) 62 | cmd = 'N' 63 | # Stop acquisition 64 | elif cmd == 'S': 65 | running = False 66 | if f != None: 67 | f.close() 68 | cmd = 'N' 69 | # Close connection 70 | elif cmd == 'C': 71 | running = False 72 | # Quit program 73 | elif cmd == 'Q': 74 | running = False 75 | if f != None: 76 | if not f.closed: 77 | f.close() 78 | break 79 | 80 | # Receive data packet-by-packet and save to file 81 | if running: 82 | if uart.is_open: 83 | # Read full COBS packet 84 | data = uart.read_until(b'\x00') 85 | n = len(data) 86 | if n > 0: 87 | # Skip last byte 88 | decoded = cobs.decode(data[0:(n - 1)]) 89 | # Unpack the binary data 90 | ts, x, y, z = struct.unpack('ffff', decoded) 91 | # Create CSV line and print 92 | print(f'{ts},{x},{y},{z}', file=f) 93 | f.flush() 94 | 95 | 96 | # The main thread 97 | if __name__ == '__main__': 98 | 99 | # Parse arguments 100 | # - port == Arduino Serial Port 101 | # - name == Filename 102 | # - tag == Custom tag to append to filename 103 | parser = argparse.ArgumentParser() 104 | parser.add_argument('port') 105 | parser.add_argument('name') 106 | parser.add_argument('tag') 107 | args = parser.parse_args() 108 | 109 | ser.port = args.port 110 | ser.baudrate = 115200 111 | 112 | ser.close() 113 | ser.open() 114 | 115 | # Inter-thread communication queue 116 | q = queue.Queue() 117 | 118 | # Arduino-facing thread 119 | recv_thread = Thread(target=receiver, args=(q, ser, args.name, args.tag)) 120 | recv_thread.start() 121 | 122 | # Wait for user input 123 | while True: 124 | s = input("> ").upper() 125 | 126 | # Send commands to receiver thread 127 | if s == 'S': 128 | print('Stopping...') 129 | q.put('S') 130 | elif s == 'R': 131 | print('Running...') 132 | q.put('R') 133 | elif s == 'C': 134 | print('Closing...') 135 | q.put('C') 136 | if ser.is_open: 137 | ser.close() 138 | elif s == 'O': 139 | print('Opening...') 140 | if not ser.is_open: 141 | ser.open() 142 | elif s == 'Q': 143 | print('Quitting...') 144 | q.put('Q') 145 | recv_thread.join() 146 | if ser.is_open: 147 | ser.close() 148 | break 149 | 150 | # Send commands to the Arduino board 151 | if ser.is_open: 152 | enc = cobs.encode(s.encode()) 153 | ser.write(enc) 154 | ser.write(b'\x00') 155 | -------------------------------------------------------------------------------- /.github/workflows/check-license.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-license.md 2 | 3 | name: Check License 4 | 5 | env: 6 | # TODO: Define the project's license file name here: 7 | EXPECTED_LICENSE_FILENAME: LICENSE.txt 8 | # SPDX identifier: https://spdx.org/licenses/ 9 | # TODO: Define the project's license type here 10 | EXPECTED_LICENSE_TYPE: AGPL-3.0 11 | 12 | # See: https://docs.github.com/actions/using-workflows/events-that-trigger-workflows 13 | on: 14 | create: 15 | push: 16 | paths: 17 | - ".github/workflows/check-license.ya?ml" 18 | # See: https://github.com/licensee/licensee/blob/master/docs/what-we-look-at.md#detecting-the-license-file 19 | - "[cC][oO][pP][yY][iI][nN][gG]*" 20 | - "[cC][oO][pP][yY][rR][iI][gG][hH][tH]*" 21 | - "[lL][iI][cC][eE][nN][cCsS][eE]*" 22 | - "[oO][fF][lL]*" 23 | - "[pP][aA][tT][eE][nN][tT][sS]*" 24 | pull_request: 25 | paths: 26 | - ".github/workflows/check-license.ya?ml" 27 | - "[cC][oO][pP][yY][iI][nN][gG]*" 28 | - "[cC][oO][pP][yY][rR][iI][gG][hH][tH]*" 29 | - "[lL][iI][cC][eE][nN][cCsS][eE]*" 30 | - "[oO][fF][lL]*" 31 | - "[pP][aA][tT][eE][nN][tT][sS]*" 32 | schedule: 33 | # Run periodically to catch breakage caused by external changes. 34 | - cron: "0 6 * * WED" 35 | workflow_dispatch: 36 | repository_dispatch: 37 | 38 | jobs: 39 | run-determination: 40 | runs-on: ubuntu-latest 41 | permissions: {} 42 | outputs: 43 | result: ${{ steps.determination.outputs.result }} 44 | steps: 45 | - name: Determine if the rest of the workflow should run 46 | id: determination 47 | run: | 48 | RELEASE_BRANCH_REGEX="refs/heads/[0-9]+.[0-9]+.x" 49 | # The `create` event trigger doesn't support `branches` filters, so it's necessary to use Bash instead. 50 | if [[ 51 | "${{ github.event_name }}" != "create" || 52 | "${{ github.ref }}" =~ $RELEASE_BRANCH_REGEX 53 | ]]; then 54 | # Run the other jobs. 55 | RESULT="true" 56 | else 57 | # There is no need to run the other jobs. 58 | RESULT="false" 59 | fi 60 | 61 | echo "result=$RESULT" >> $GITHUB_OUTPUT 62 | 63 | check-license: 64 | name: ${{ matrix.check-license.path }} 65 | needs: run-determination 66 | if: needs.run-determination.outputs.result == 'true' 67 | runs-on: ubuntu-latest 68 | permissions: 69 | contents: read 70 | 71 | strategy: 72 | fail-fast: false 73 | 74 | matrix: 75 | check-license: 76 | # TODO: Add additional paths where license files should be 77 | - path: ./ 78 | # TODO: Define the project's license file name here: 79 | expected-filename: LICENSE.txt 80 | # SPDX identifier: https://spdx.org/licenses/ 81 | # TODO: Define the project's license type here 82 | expected-type: AGPL-3.0 83 | - path: Arduino_BHY2 84 | expected-filename: LICENSE.txt 85 | expected-type: AGPL-3.0 86 | - path: Arduino_BHY2Host 87 | expected-filename: LICENSE.txt 88 | expected-type: AGPL-3.0 89 | 90 | steps: 91 | - name: Checkout repository 92 | uses: actions/checkout@v6 93 | 94 | - name: Install Ruby 95 | uses: ruby/setup-ruby@v1 96 | with: 97 | ruby-version: ruby # Install latest version 98 | 99 | - name: Install licensee 100 | run: gem install licensee 101 | 102 | - name: Check license file for ${{ matrix.check-license.path }} 103 | run: | 104 | EXIT_STATUS=0 105 | 106 | # Go into folder path 107 | cd ./${{ matrix.check-license.path }} 108 | 109 | # See: https://github.com/licensee/licensee 110 | LICENSEE_OUTPUT="$(licensee detect --json --confidence=100)" 111 | 112 | 113 | DETECTED_LICENSE_FILE="$(echo "$LICENSEE_OUTPUT" | jq .matched_files[0].filename | tr --delete '\r')" 114 | echo "Detected license file: $DETECTED_LICENSE_FILE" 115 | if [ "$DETECTED_LICENSE_FILE" != "\"${{ matrix.check-license.expected-filename }}\"" ]; then 116 | echo "::error file=${DETECTED_LICENSE_FILE}::detected license file $DETECTED_LICENSE_FILE doesn't match expected: ${{ matrix.check-license.expected-filename }}" 117 | 118 | EXIT_STATUS=1 119 | fi 120 | 121 | DETECTED_LICENSE_TYPE="$(echo "$LICENSEE_OUTPUT" | jq .matched_files[0].matched_license | tr --delete '\r')" 122 | echo "Detected license type: $DETECTED_LICENSE_TYPE" 123 | 124 | if [ "$DETECTED_LICENSE_TYPE" != "\"${{ matrix.check-license.expected-type }}\"" ]; then 125 | echo "::error file=${DETECTED_LICENSE_FILE}::detected license type $DETECTED_LICENSE_TYPE doesn't match expected \"${{ matrix.check-license.expected-type }}\"" 126 | EXIT_STATUS=1 127 | fi 128 | 129 | exit $EXIT_STATUS 130 | -------------------------------------------------------------------------------- /bootloader/src/IS31FL3194.h: -------------------------------------------------------------------------------- 1 | #ifndef IS31FL3194_h 2 | #define IS31FL3194_h 3 | 4 | // Register Map 5 | // http://www.issi.com/WW/pdf/IS31FL3194.pdf 6 | #define IS31FL3194_PRODUCT_ID 0x00 // should return 0xCE 7 | #define IS31FL3194_OP_CONFIG 0x01 8 | #define IS31FL3194_OUT_CONFIG 0x02 9 | #define IS31FL3194_CURRENT_BAND 0x03 10 | #define IS31FL3194_HOLD_FUNCTION 0x04 11 | 12 | #define IS31FL3194_P1_STATE 0x0D 13 | #define IS31FL3194_P2_STATE 0x0E 14 | #define IS31FL3194_P3_STATE 0x0F 15 | 16 | // Current Mode 17 | #define IS31FL3194_OUT1 0x10 18 | #define IS31FL3194_OUT2 0x21 19 | #define IS31FL3194_OUT3 0x32 20 | 21 | //Pattern mode 22 | // Colors 1, 2 and 3 of pattern 1 23 | #define IS31FL3194_COL1_PATT1_R 0x10 24 | #define IS31FL3194_COL1_PATT1_G 0x11 25 | #define IS31FL3194_COL1_PATT1_B 0x12 26 | #define IS31FL3194_COL2_PATT1_R 0x13 27 | #define IS31FL3194_COL2_PATT1_G 0x14 28 | #define IS31FL3194_COL2_PATT1_B 0x15 29 | #define IS31FL3194_COL3_PATT1_R 0x16 30 | #define IS31FL3194_COL3_PATT1_G 0x17 31 | #define IS31FL3194_COL3_PATT1_B 0x18 32 | 33 | // Colors 1, 2 and 3 of pattern 2 34 | #define IS31FL3194_COL1_PATT2_R 0x20 35 | #define IS31FL3194_COL1_PATT2_G 0x21 36 | #define IS31FL3194_COL1_PATT2_B 0x22 37 | #define IS31FL3194_COL2_PATT2_R 0x23 38 | #define IS31FL3194_COL2_PATT2_G 0x24 39 | #define IS31FL3194_COL2_PATT2_B 0x25 40 | #define IS31FL3194_COL3_PATT2_R 0x26 41 | #define IS31FL3194_COL3_PATT2_G 0x27 42 | #define IS31FL3194_COL3_PATT2_B 0x28 43 | 44 | // Colors 1, 2 and 3 of pattern 3 45 | #define IS31FL3194_COL1_PATT3_R 0x30 46 | #define IS31FL3194_COL1_PATT3_G 0x31 47 | #define IS31FL3194_COL1_PATT3_B 0x32 48 | #define IS31FL3194_COL2_PATT3_R 0x33 49 | #define IS31FL3194_COL2_PATT3_G 0x34 50 | #define IS31FL3194_COL2_PATT3_B 0x35 51 | #define IS31FL3194_COL3_PATT3_R 0x36 52 | #define IS31FL3194_COL3_PATT3_G 0x37 53 | #define IS31FL3194_COL3_PATT3_B 0x38 54 | 55 | #define IS31FL3194_P1_TS_T1_Time_SET 0x19 56 | #define IS31FL3194_P1_T2_T3_Time_SET 0x1A 57 | #define IS31FL3194_P1_TP_T4_Time_SET 0x1B 58 | #define IS31FL3194_P2_TS_T1_Time_SET 0x29 59 | #define IS31FL3194_P2_T2_T3_Time_SET 0x2A 60 | #define IS31FL3194_P2_TP_T4_Time_SET 0x2B 61 | #define IS31FL3194_P3_TS_T1_Time_SET 0x39 62 | #define IS31FL3194_P3_T2_T3_Time_SET 0x3A 63 | #define IS31FL3194_P3_TP_T4_Time_SET 0x3B 64 | 65 | #define IS31FL3194_P1_COLOR_EN 0x1C 66 | #define IS31FL3194_P2_COLOR_EN 0x2C 67 | #define IS31FL3194_P3_COLOR_EN 0x3C 68 | 69 | #define IS31FL3194_P1_COLOR_CYC_TIME 0x1D 70 | #define IS31FL3194_P2_COLOR_CYC_TIME 0x2D 71 | #define IS31FL3194_P3_COLOR_CYC_TIME 0x3D 72 | 73 | #define IS31FL3194_P1_NXT 0x1E 74 | #define IS31FL3194_P2_NXT 0x2E 75 | #define IS31FL3194_P3_NXT 0x3E 76 | 77 | #define IS31FL3194_P1_LOOP_TIMES 0x1F 78 | #define IS31FL3194_P2_LOOP_TIMES 0x2F 79 | #define IS31FL3194_P3_LOOP_TIMES 0x3F 80 | 81 | #define IS31FL3194_COLOR_UPDATE 0x40 82 | 83 | #define IS31FL3194_P1_UPDATE 0x41 84 | #define IS31FL3194_P2_UPDATE 0x42 85 | #define IS31FL3194_P3_UPDATE 0x43 86 | 87 | #define IS31FL3194_RESET 0x4F 88 | 89 | #define IS31FL3194_ADDRESS 0x53 90 | 91 | #define Mode 1 92 | 93 | // define times 94 | #define t_0_03s 0x00 95 | #define t_0_13s 0x01 96 | #define t_0_26s 0x02 97 | #define t_0_38s 0x03 98 | #define t_0_51s 0x04 99 | #define t_0_77s 0x05 100 | #define t_1_04s 0x06 101 | #define t_1_60s 0x07 102 | #define t_2_10s 0x08 103 | #define t_2_60s 0x09 104 | #define t_3_10s 0x0A 105 | #define t_4_20s 0x0B 106 | #define t_5_20s 0x0C 107 | #define t_6_20s 0x0D 108 | #define t_7_30s 0x0E 109 | #define t_8_30s 0x0F 110 | 111 | // define pattern times 112 | #define TS t_2_10s // Start time 113 | #define T1 t_2_10s // Rise time 114 | #define T2 t_1_04s // Hold time 115 | #define T3 t_2_10s // Fall time 116 | #define T4 t_2_10s // Off time 117 | #define TP t_2_10s // Time between pulses 118 | 119 | // define cycle times 120 | #define endless 0x00 121 | #define once 0x15 122 | #define twice 0x2A 123 | #define thrice 0x3F 124 | 125 | // light intensity (fraction of current max) 126 | #define Imax_frac 0x80 // Imax_frac/256 * Imax = current 127 | 128 | // allowed colors 129 | #define red 0 130 | #define green 1 131 | #define blue 2 132 | #define yellow 3 133 | #define magenta 4 134 | #define cyan 5 135 | 136 | class IS31FL3194 137 | { 138 | public: 139 | IS31FL3194(); 140 | void init(); 141 | uint8_t getChipID(); 142 | void reset(); 143 | void powerDown(); 144 | void powerUp(); 145 | void ledBlink(uint8_t color, uint32_t duration); 146 | void I2Cscan(); 147 | void writeByte(uint8_t address, uint8_t subAddress, uint8_t data); 148 | uint8_t readByte(uint8_t address, uint8_t subAddress); 149 | private: 150 | uint8_t _out1; 151 | uint8_t _out2; 152 | uint8_t _out3; 153 | }; 154 | 155 | #endif 156 | -------------------------------------------------------------------------------- /Arduino_BHY2Host/src/EslovHandler.h: -------------------------------------------------------------------------------- 1 | #ifndef ESLOV_HANDLER_H_ 2 | #define ESLOV_HANDLER_H_ 3 | 4 | #include "Arduino.h" 5 | #include "sensors/SensorTypes.h" 6 | #include "DFUTypes.h" 7 | #include "Wire.h" 8 | 9 | #define ESLOV_MAX_LENGTH 255 10 | #define ESLOV_DEFAULT_ADDRESS 0x55 11 | 12 | #define ESLOV_INT_PIN (7) 13 | 14 | #define I2C_INT_PIN (0) 15 | 16 | /** 17 | * @brief Enumerator for ESLOV operational state. Defines what state the I2C protocol of the ESLOV device should be in. Used to set DFU (Device Firmware Update) mode in the ANNA-B112 and BHY260 of the Nicla Sense ME. 18 | * 19 | */ 20 | enum EslovOpcode { 21 | ESLOV_DFU_INTERNAL_OPCODE, /*!< ESLOV DFU ANNA-B112 */ 22 | ESLOV_DFU_EXTERNAL_OPCODE, /*!< ESLOV DFU BHY260 */ 23 | ESLOV_SENSOR_CONFIG_OPCODE, /*!< ESLOV Sensor Configuration */ 24 | ESLOV_SENSOR_STATE_OPCODE /*!< ESLOV Sensor State */ 25 | }; 26 | 27 | /** 28 | * @brief Enumeration for the host device operational status. 29 | * 30 | */ 31 | enum HostOpcode { 32 | HOST_DFU_INTERNAL_OPCODE = ESLOV_DFU_INTERNAL_OPCODE, 33 | HOST_DFU_EXTERNAL_OPCODE = ESLOV_DFU_EXTERNAL_OPCODE, 34 | HOST_READ_SENSOR_OPCODE, 35 | HOST_CONFIG_SENSOR_OPCODE, 36 | HOST_READ_LONG_SENSOR_OPCODE 37 | }; 38 | 39 | /** 40 | * @brief Enumeration for various states over ESLOV 41 | * 42 | */ 43 | enum EslovState { 44 | ESLOV_AVAILABLE_SENSOR_STATE = 0x00, 45 | ESLOV_READ_SENSOR_STATE = 0x01, 46 | ESLOV_DFU_ACK_STATE = 0x02, 47 | ESLOV_SENSOR_ACK_STATE = 0x03, 48 | ESLOV_AVAILABLE_LONG_SENSOR_STATE = 0x04, 49 | ESLOV_READ_LONG_SENSOR_STATE = 0x05 50 | }; 51 | 52 | /** 53 | * @brief Class to manage communication over ESLOV 54 | * 55 | */ 56 | class EslovHandler { 57 | public: 58 | EslovHandler(); 59 | virtual ~EslovHandler(); 60 | 61 | /** 62 | * @brief Start I2C communication over ESLOV between host board and Nicla 63 | * 64 | * @return true I2C communication initialized successfully. 65 | */ 66 | bool begin(bool passthrough); 67 | /** 68 | * @brief Reads incoming data to the host board based on the opcode @see EslovState. 69 | * 70 | */ 71 | void update(); 72 | /** 73 | * @brief Write a DFU (Device Firmware Update) packet to the Nicla Board over ESLOV. On the last packet, the built-in LED is pulled down. 74 | * 75 | * @param data pointer to data to be uploaded to Nicla board as a byte array 76 | * @param length amount of data to be written to the Nicla in bytes (int) 77 | */ 78 | void writeDfuPacket(uint8_t *data, uint8_t length); 79 | /** 80 | * @brief Waits for the ESLOV interrupt pin to be pulled high, then sends a packet with the @see ESLOV_SENSOR_STATE_OPCODE to over I2C 81 | * 82 | * @param state State value sent to Nicla Sense ME based on @ref EslovState enumerator. 83 | */ 84 | void writeStateChange(EslovState state); 85 | /** 86 | * @brief Write a configuration packet to the Nicla over ESLOV. First byte sets the opcode 87 | * 88 | * @param config Instance of @see SensorConfigurationPacket class, with sensorID, sampleRate and latency 89 | */ 90 | void writeConfigPacket(SensorConfigurationPacket& config); 91 | /** 92 | * @brief Requests an acknowledgment packet from the Nicla over ESLOV. 93 | * 94 | * @return uint8_t acknowledge packet Received from the Nicla over ESLOV 95 | */ 96 | uint8_t requestPacketAck(); 97 | /** 98 | * @brief Change `EslovState` of Nicla to `ESLOV_AVAILABLE_SENSOR_STATE` and wait for the interrupt pin to go high. Then read the available sensor data over I2C. 99 | * 100 | * @return uint8_t Number of available sensor data packets. 101 | */ 102 | uint8_t requestAvailableData(); 103 | /** 104 | * @brief Change `EslovState` of Nicla to `ESLOV_AVAILABLE_SENSOR_STATE` and wait for the interrupt pin to go high. Then read the available long sensor data over I2C. 105 | * 106 | * @return uint8_t Number of available long sensor data packets. 107 | */ 108 | uint8_t requestAvailableLongData(); 109 | /** 110 | * @brief Change `EslovState` of Nicla to `ESLOV_READ_SENSOR_STATE` and wait for the interrupt pin to go high. Then read the available sensor data over I2C. 111 | * 112 | * @param sData data packet containing sensorID, payload size and data payload 113 | * @return true Successful request of sensor data 114 | */ 115 | bool requestSensorData(SensorDataPacket &sData); 116 | /** 117 | * @brief Change `EslovState` of Nicla to `ESLOV_READ_SENSOR_STATE` and wait for the interrupt pin to go high. Then read the available long sensor data over I2C. 118 | * 119 | * @param sData data packet containing sensorID, payload size and data payload 120 | * @return true Successful request of sensor data 121 | */ 122 | bool requestSensorLongData(SensorLongDataPacket &sData); 123 | 124 | protected: 125 | void niclaAsShield(); 126 | 127 | private: 128 | int _rxIndex; 129 | uint8_t _rxBuffer[ESLOV_MAX_LENGTH]; 130 | 131 | EslovState _eslovState; 132 | bool _intPinAsserted; 133 | bool _intPinCleared; 134 | bool _dfuLedOn; 135 | 136 | private: 137 | friend class Arduino_BHY2Host; 138 | 139 | void flushWire(); 140 | 141 | void debug(Stream &stream); 142 | void dump(); 143 | Stream *_debug; 144 | 145 | uint8_t _eslovIntPin; 146 | }; 147 | 148 | extern EslovHandler eslovHandler; 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /tools/bhy-controller/src/bhy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "arduino/bhy/dfu" 5 | "arduino/bhy/sensor" 6 | "arduino/bhy/webserver" 7 | "flag" 8 | "fmt" 9 | "log" 10 | "os" 11 | 12 | "go.bug.st/serial/enumerator" 13 | ) 14 | 15 | func main() { 16 | // Check if a subcommand has been passed 17 | if len(os.Args) < 2 { 18 | commandError() 19 | return 20 | } 21 | // Parse the subcommand 22 | switch os.Args[1] { 23 | case "list": 24 | listCommand() 25 | case "dfu": 26 | dfuCommand() 27 | case "sensor": 28 | sensorCommand() 29 | case "webserver": 30 | webserverCommand() 31 | default: 32 | commandError() 33 | } 34 | } 35 | 36 | func dfuCommand() { 37 | // dfu command is used for uploading firmware to nicla 38 | dfuFlags := flag.NewFlagSet("dfu", flag.ExitOnError) 39 | usbPort := dfuFlags.String("p", "", "usb port") 40 | baudRate := dfuFlags.Int("baud", 115200, "baud rate") 41 | target := dfuFlags.String("t", "", "indicate a target - ( nicla | bhi ) ") 42 | binPath := dfuFlags.String("bin", "", "binary path") 43 | debug := dfuFlags.Bool("v", false, "enable verbose logging") 44 | 45 | dfuFlags.Parse(os.Args[2:]) 46 | 47 | ret := dfuCheckFlags(*baudRate, *target, *usbPort, *binPath) 48 | if !ret { 49 | dfuFlags.PrintDefaults() 50 | return 51 | } 52 | 53 | dfu.Upload(*baudRate, *target, *usbPort, *binPath, *debug) 54 | } 55 | 56 | func webserverCommand() { 57 | webserver.Execute() 58 | } 59 | 60 | func sensorCommand() { 61 | // sensor subcommand requires an additional subcommand 62 | if len(os.Args) < 3 { 63 | sensorError() 64 | return 65 | } 66 | loadJsonScheme() 67 | // parse the additional subcommand 68 | switch os.Args[2] { 69 | case "read": 70 | sensorReadCommand() 71 | case "config": 72 | sensorConfigureCommand() 73 | default: 74 | sensorError() 75 | } 76 | } 77 | 78 | func loadJsonScheme() { 79 | sensor.LoadSensors() 80 | } 81 | 82 | func sensorReadCommand() { 83 | readFlags := flag.NewFlagSet("read", flag.ExitOnError) 84 | usbPort := readFlags.String("p", "", "usb port") 85 | baudRate := readFlags.Int("baud", 115200, "baud rate") 86 | liveFlag := readFlags.Bool("live", false, "live session") 87 | 88 | readFlags.Parse(os.Args[3:]) 89 | 90 | ret := sensorCheckFlags(*usbPort) 91 | if !ret { 92 | readFlags.PrintDefaults() 93 | return 94 | } 95 | 96 | sensor.Read(*usbPort, *baudRate, *liveFlag) 97 | } 98 | 99 | func sensorConfigureCommand() { 100 | configureFlags := flag.NewFlagSet("configure", flag.ExitOnError) 101 | usbPort := configureFlags.String("p", "", "usb port") 102 | baudRate := configureFlags.Int("baud", 115200, "baud rate") 103 | sensorId := configureFlags.Int("sensor", 0, "Sensor ID to configure") 104 | sensorRate := configureFlags.Float64("rate", 0, "Rate of sample") 105 | sensorLatency := configureFlags.Int("latency", 0, "Latency") 106 | 107 | configureFlags.Parse(os.Args[3:]) 108 | 109 | ret := sensorCheckFlags(*usbPort) 110 | if !ret { 111 | configureFlags.PrintDefaults() 112 | return 113 | } 114 | 115 | sensor.Configure(*usbPort, *baudRate, *sensorId, *sensorRate, *sensorLatency) 116 | } 117 | 118 | func listCommand() { 119 | ports, err := enumerator.GetDetailedPortsList() 120 | if err != nil { 121 | log.Fatal(err) 122 | } 123 | if len(ports) == 0 { 124 | fmt.Println("No serial ports found!") 125 | return 126 | } 127 | for _, port := range ports { 128 | fmt.Printf("Found port: %s\n", port.Name) 129 | if port.IsUSB { 130 | fmt.Printf(" USB ID %s:%s\n", port.VID, port.PID) 131 | fmt.Printf(" USB serial %s\n", port.SerialNumber) 132 | } 133 | } 134 | } 135 | 136 | func dfuCheckFlags(baudRate int, target string, usbPort string, binPath string) bool { 137 | if target != "nicla" && target != "bhi" { 138 | fmt.Println("") 139 | fmt.Println(" -t target not valid, choose between 'nicla' or 'bhi'") 140 | fmt.Println("") 141 | return false 142 | } 143 | if binPath == "" { 144 | fmt.Println("") 145 | fmt.Println(" missing -bin parameter, provide a valid binary path") 146 | fmt.Println("") 147 | return false 148 | } else { 149 | if _, err := os.Stat(binPath); os.IsNotExist(err) { 150 | fmt.Println("") 151 | fmt.Println("-bin parameter is not valid, the binary provided is NOT ACCESSIBLE") 152 | fmt.Println("") 153 | return false 154 | } 155 | } 156 | if usbPort == "" { 157 | fmt.Println("") 158 | fmt.Println(" missing -p parameter, provide a valid serial port") 159 | fmt.Println("") 160 | return false 161 | } 162 | return true 163 | } 164 | 165 | func sensorCheckFlags(usbPort string) bool { 166 | if usbPort == "" { 167 | fmt.Println("") 168 | fmt.Println(" missing -p parameter, provide a valid serial port") 169 | fmt.Println("") 170 | return false 171 | } 172 | return true 173 | } 174 | 175 | func commandError() { 176 | fmt.Println(" A command is required") 177 | fmt.Println(" sensor") 178 | fmt.Println(" to control bhy sensors") 179 | fmt.Println(" dfu") 180 | fmt.Println(" to upload new firmware for nicla or bhy") 181 | fmt.Println(" list") 182 | fmt.Println(" list available serial ports") 183 | fmt.Println(" webserver") 184 | fmt.Println(" start a local webserver and open the webserver") 185 | } 186 | 187 | func sensorError() { 188 | fmt.Println(" A sensor subcommand is required") 189 | fmt.Println(" read") 190 | fmt.Println(" to read last sensor data") 191 | fmt.Println(" config") 192 | fmt.Println(" to configure a bhy sensor") 193 | } 194 | --------------------------------------------------------------------------------