{"id":20316148,"url":"https://github.com/lupyuen/bme280-nuttx","last_synced_at":"2026-02-02T23:45:35.633Z","repository":{"id":43711092,"uuid":"460289874","full_name":"lupyuen/bme280-nuttx","owner":"lupyuen","description":"Apache NuttX Driver for Bosch BME280 I2C Sensor (Temperature + Humidity + Air Pressure) ported from Zephyr OS","archived":false,"fork":false,"pushed_at":"2022-08-01T10:37:19.000Z","size":94,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-05T05:24:57.369Z","etag":null,"topics":["bl602","bl604","bme280","driver","esp32","i2c","nuttx","pinecone","pinedio","riscv32","sensor"],"latest_commit_sha":null,"homepage":"https://lupyuen.github.io/articles/bme280","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lupyuen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["lupyuen"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":["paypal.me/lupyuen"]}},"created_at":"2022-02-17T05:10:32.000Z","updated_at":"2024-10-30T22:36:21.000Z","dependencies_parsed_at":"2022-08-22T03:20:18.871Z","dependency_job_id":null,"html_url":"https://github.com/lupyuen/bme280-nuttx","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fbme280-nuttx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fbme280-nuttx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fbme280-nuttx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lupyuen%2Fbme280-nuttx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lupyuen","download_url":"https://codeload.github.com/lupyuen/bme280-nuttx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":256598284,"owners_count":22413826,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bl602","bl604","bme280","driver","esp32","i2c","nuttx","pinecone","pinedio","riscv32","sensor"],"created_at":"2024-11-14T18:24:37.820Z","updated_at":"2026-02-02T23:45:30.602Z","avatar_url":"https://github.com/lupyuen.png","language":"C","readme":"# Apache NuttX Driver for Bosch BME280 I2C Sensor (Temperature + Humidity + Air Pressure) ported from Zephyr OS\n\nRead the article...\n\n-   [\"Apache NuttX Driver for BME280 Sensor: Ported from Zephyr OS\"](https://lupyuen.github.io/articles/bme280)\n\n# Install Driver\n\nTo add this repo to your NuttX project...\n\n```bash\npushd nuttx/nuttx/drivers/sensors\ngit submodule add https://github.com/lupyuen/bme280-nuttx bme280\nln -s bme280/bundle.c bme280.c\npopd\n\npushd nuttx/nuttx/include/nuttx/sensors\nln -s ../../../drivers/sensors/bme280/bundle.h bme280.h\npopd\n```\n\nNext update the Makefile and Kconfig...\n\n-   [See the modified Makefile and Kconfig](https://github.com/lupyuen/incubator-nuttx/commit/1e0c62d409c863e866dbdea5d7e1e2d7b6d3cfc0)\n\nThen update the NuttX Build Config...\n\n```bash\n## TODO: Change this to the path of our \"incubator-nuttx\" folder\ncd nuttx/nuttx\n\n## Preserve the Build Config\ncp .config ../config\n\n## Erase the Build Config and Kconfig files\nmake distclean\n\n## For BL602: Configure the build for BL602\n./tools/configure.sh bl602evb:nsh\n\n## For ESP32: Configure the build for ESP32.\n## TODO: Change \"esp32-devkitc\" to our ESP32 board.\n./tools/configure.sh esp32-devkitc:nsh\n\n## Restore the Build Config\ncp ../config .config\n\n## Edit the Build Config\nmake menuconfig \n```\n\nIn menuconfig, enable the Bosch BME280 Sensor under \"Device Drivers → Sensor Device Support\".\n\nEdit the function `bl602_bringup` or `esp32_bringup` in this file...\n\n```text\n## For BL602:\nnuttx/boards/risc-v/bl602/bl602evb/src/bl602_bringup.c\n\n## For ESP32: Change \"esp32-devkitc\" to our ESP32 board \nnuttx/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c\n```\n\nAnd call `bme280_register` to register our BME280 Driver:\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bme280/boards/risc-v/bl602/bl602evb/src/bl602_bringup.c#L623-L640    \n\n```c\n#ifdef CONFIG_SENSORS_BME280\n#include \u003cnuttx/sensors/bme280.h\u003e\n#endif /* CONFIG_SENSORS_BME280 */\n...\nint bl602_bringup(void) {\n  ...\n#ifdef CONFIG_SENSORS_BME280\n\n  /* Init I2C bus for BME280 */\n\n  struct i2c_master_s *bme280_i2c_bus = bl602_i2cbus_initialize(0);\n  if (!bme280_i2c_bus)\n    {\n      _err(\"ERROR: Failed to get I2C%d interface\\n\", 0);\n    }\n\n  /* Register the BME280 driver */\n\n  ret = bme280_register(0, bme280_i2c_bus);\n  if (ret \u003c 0)\n    {\n      _err(\"ERROR: Failed to register BME280\\n\");\n    }\n#endif /* CONFIG_SENSORS_BME280 */\n```\n\nTo read the BME280 Sensor, enter this at the NuttX Shell...\n\n```text\nnsh\u003e ls /dev/sensor\n/dev/sensor:\n baro0\n humi0\n\nnsh\u003e sensortest -n 1 baro0\nSensorTest: Test /dev/sensor/baro0 with interval(1000000us), latency(0us)\nbaro0: timestamp:256220000 value1:981.34 value2:28.73\nSensorTest: Received message: baro0, number:1/1\n\nnsh\u003e sensortest -n 1 humi0\nSensorTest: Test /dev/sensor/humi0 with interval(1000000us), latency(0us)\nhumi0: timestamp:553560000 value:92.90\nSensorTest: Received message: humi0, number:1/1\n```\n\nThat's 981.34 millibars, 28.73 degrees Celsius, and 92.90 % relative humidity.\n\nThe rest of this doc explains how we ported the BME280 Driver from Zephyr OS to NuttX RTOS.\n\n# Test with Bus Pirate\n\n[__Bus Pirate__](http://dangerousprototypes.com/docs/Bus_Pirate) is a useful gadget for verifying whether our BME280 Sensor works OK. And for checking the I2C bytes that should be sent down the wire to BME280.\n\nHere's how we test BME280 with Bus Pirate...\n\n| Bus Pirate Pin | BME280 Pin\n|:---:|:---:\n| __`MOSI`__ | `SDA`\n| __`CLK`__ | `SCL`\n| __`3.3V`__ | `3.3V`\n| __`GND`__ | `GND`\n\nMore details: https://lupyuen.github.io/articles/i2c#appendix-test-bme280-with-bus-pirate\n\n# Connect BME280\n\nConnect BME280 to Pine64 PineCone BL602...\n\n| BL602 Pin | BME280 Pin | Wire Colour\n|:---:|:---:|:---|\n| __`GPIO 3`__ | `SDA` | Green \n| __`GPIO 4`__ | `SCL` | Blue\n| __`3V3`__ | `3.3V` | Red\n| __`GND`__ | `GND` | Black\n\nThe I2C Pins on BL602 are defined here...\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/boards/risc-v/bl602/bl602evb/include/board.h#L85-L88\n\n```c\n/* I2C Configuration */\n\n#define BOARD_I2C_SCL (GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_I2C | GPIO_PIN4)\n#define BOARD_I2C_SDA (GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_I2C | GPIO_PIN3)\n```\n\nWe disabled the UART1 Port because it uses the same pins as I2C...\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/boards/risc-v/bl602/bl602evb/include/board.h#L63-L68\n\n```c\n#ifdef TODO  /* Remember to check for duplicate pins! */\n#define BOARD_UART_1_RX_PIN (GPIO_INPUT | GPIO_PULLUP | \\\n                              GPIO_FUNC_UART | GPIO_PIN3)\n#define BOARD_UART_1_TX_PIN (GPIO_INPUT | GPIO_PULLUP | \\\n                              GPIO_FUNC_UART | GPIO_PIN4)\n#endif  /* TODO */\n```\n\nVerify with a Multimeter that BME280 is powered up with 3.3V.\n\nWe're using the SparkFun BME280 Breakout Board, which has Pull-Up Resistors (so we don't need to add our own)...\n\nhttps://learn.sparkfun.com/tutorials/sparkfun-bme280-breakout-hookup-guide/all\n\n# Configure NuttX\n\nNuttX has a driver for BMP280 (Air Pressure only), let's test it with BME280.\n\nConfigure NuttX to enable the I2C Character Driver, BMP280 Driver and Sensor Test App...\n\n- System Type → BL602 Peripheral Support → I2C0\n- Device Drivers → I2C Driver Support\n- Device Drivers → I2C Driver Support →  I2C character driver\n- Device Drivers → Sensor Device Support\n- Device Drivers → Sensor Device Support →  Bosch BMP280 Barometic Pressure Sensor\n- Application Configuration → Testing → Sensor driver test\n- Build Setup → Debug Options\n  - → Enable Informational Debug Output \n  - → I2C Debug Features\n  - → I2C Debug Features →  I2C Error Output\n  - → I2C Debug Features →  I2C Warnings Output\n  - → I2C Debug Features →  I2C Informational Output  \n  - → Sensor Debug Features\n  - → Sensor Debug Features → Sensor Error Output\n  - → Sensor Debug Features → Sensor Warnings Output  \n  - → Sensor Debug Features → Sensor Informational Output \n\n[(See the .config for BL602)](https://gist.github.com/lupyuen/9d84889f5e2415ecb0f28cea2c2a657f)\n\n# Change I2C Address and Device ID\n\nFor testing, we change the I2C Address and Device ID for BME280...\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/drivers/sensors/bmp280.c#L45-L57\n\n```c\n////  Previously: I2C Address of BMP280\n////  #define BMP280_ADDR         0x76\n\n#warning Testing: I2C Address of BME280\n#define BMP280_ADDR         0x77 //// BME280\n\n////  Previously: Device ID of BMP280\n////  #define DEVID               0x58\n\n#warning Testing: Device ID of BME280\n#define DEVID               0x60 //// BME280\n```\n\n# Register BMP280 Driver\n\nRegister BMP280 Driver at startup...\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/boards/risc-v/bl602/bl602evb/src/bl602_bringup.c#L623-L640\n\n```c\n#ifdef CONFIG_SENSORS_BMP280\n#include \u003cnuttx/sensors/bmp280.h\u003e\n#endif /* CONFIG_SENSORS_BMP280 */\n...\nint bl602_bringup(void)\n{\n...\n#ifdef CONFIG_SENSORS_BMP280\n\n  /* Init I2C bus for BMP280 */\n\n  struct i2c_master_s *bmp280_i2c_bus = bl602_i2cbus_initialize(0);\n  if (!bmp280_i2c_bus)\n    {\n      _err(\"ERROR: Failed to get I2C%d interface\\n\", 0);\n    }\n\n  /* Register the BMP280 driver */\n\n  ret = bmp280_register(0, bmp280_i2c_bus);\n  if (ret \u003c 0)\n    {\n      _err(\"ERROR: Failed to register BMP280\\n\");\n    }\n#endif /* CONFIG_SENSORS_BMP280 */\n```\n\n# Invalid Device ID\n\nBMP280 Driver fails because the detected Device ID is 0 ... Let's find out why 🤔\n\n```text\ngpio_pin_register: Registering /dev/gpio0\ngpio_pin_register: Registering /dev/gpio1\ngpint_enable: Disable the interrupt\ngpio_pin_register: Registering /dev/gpio2\nbl602_gpio_set_intmod: ****gpio_pin=115, int_ctlmod=1, int_trgmod=0\nbl602_spi_setfrequency: frequency=400000, actual=0\nbl602_spi_setbits: nbits=8\nbl602_spi_setmode: mode=0\nspi_test_driver_register: devpath=/dev/spitest0, spidev=0\nbl602_spi_select: devid: 0, CS: free\nbl602_i2c_transfer: i2c transfer success\nbl602_i2c_transfer: i2c transfer success\nbmp280_checkid: devid: 0x00\nbmp280_checkid: Wrong Device ID! 00\nbmp280_register: Failed to register driver: -19\nbl602_bringup: ERROR: Failed to register BMP280\n\nNuttShell (NSH) NuttX-10.2.0-RC0\nnsh\u003e\n```\n\n# Invalid Register ID\n\nLogic Analyser shows that BL602 sent the wrong Register ID to BME280.\n\nTo read the Device ID, the Register ID should be `0xD0`, not `0x00`.  Let's fix this 🤔\n\n```text\nWrite [0xEE]\n0x00 + ACK (Register ID is 0x00)\nRead [0xEF]\n0x00 + NAK (No Acknowledgement, because Register ID is incorrect)\n```\n\n![Invalid Register ID](https://lupyuen.github.io/images/bme280-logic.png)\n\n[(Here's why Register ID should be `0xD0`)](https://lupyuen.github.io/articles/i2c#appendix-test-bme280-with-bus-pirate)\n\n# Log I2C Transfers\n\nBL602 NuttX I2C Driver doesn't log the data transferred ... Let's log ourselves\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/arch/risc-v/src/bl602/bl602_i2c.c#L194-L197\n\n```c\nstatic void bl602_i2c_send_data(struct bl602_i2c_priv_s *priv)\n{\n  ...\n  putreg32(temp, BL602_I2C_FIFO_WDATA);\n  priv-\u003ebytes += count;\n  i2cinfo(\"count=%d, temp=0x%x\\n\", count, temp); ////\n}\n```\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/arch/risc-v/src/bl602/bl602_i2c.c#L207-L216\n\n```c\nstatic void bl602_i2c_recvdata(struct bl602_i2c_priv_s *priv)\n{\n  ...\n  count = msg-\u003elength - priv-\u003ebytes;\n  temp  = getreg32(BL602_I2C_FIFO_RDATA);\n  i2cinfo(\"count=%d, temp=0x%x\\n\", count, temp); ////\n```\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/arch/risc-v/src/bl602/bl602_i2c.c#L740-L742\n\n```c\nstatic int bl602_i2c_transfer(struct i2c_master_s *dev,\n                              struct i2c_msg_s *   msgs,\n                              int                      count)\n{\n  ...\n  for (i = 0; i \u003c count; i++)\n    {\n      ...\n      priv-\u003emsgid = i;\n      i2cinfo(\"subflag=%d, subaddr=0x%x, sublen=%d\\n\", priv-\u003esubflag, priv-\u003esubaddr, priv-\u003esublen); ////\n      bl602_i2c_start_transfer(priv);\n```\n\n# Set I2C Sub Address\n\nBL602 has a peculiar I2C Port ... We need to send the I2C Sub Address (Register ID) separately from the I2C Data ... This might cause the BMP280 Driver to fail\n\nhttps://lupyuen.github.io/articles/i2c#set-i2c-device-address-and-register-address\n\nBL602 NuttX I2C Driver needs us to provide the I2C Sub Address (Register ID)...\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/arch/risc-v/src/bl602/bl602_i2c.c#L719-L738\n\n```c\n      /* if msgs[i].flag I2C_M_NOSTOP,means start i2c with subddr */\n\n      if (msgs[i].flags \u0026 I2C_M_NOSTOP)\n        {\n          priv-\u003esubflag = 1;\n          priv-\u003esubaddr = 0;\n          for (j = 0; j \u003c msgs[i].length; j++)\n            {\n              priv-\u003esubaddr += msgs[i].buffer[j] \u003c\u003c (j * 8);\n            }\n\n          priv-\u003esublen = msgs[i].length;\n          i++;\n        }\n      else\n        {\n          priv-\u003esubflag = 0;\n          priv-\u003esubaddr = 0;\n          priv-\u003esublen  = 0;\n        }\n```\n\nHere's how we patch the NuttX BMP280 Driver to send the Register ID as I2C Sub Address (instead of I2C Data)\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/drivers/sensors/bmp280.c#L202-L217\n\n```c\nstatic uint8_t bmp280_getreg8(FAR struct bmp280_dev_s *priv, uint8_t regaddr)\n{\n  ...\n  msg[0].frequency = priv-\u003efreq;\n  msg[0].addr      = priv-\u003eaddr;\n#ifdef CONFIG_BL602_I2C0\n  //  For BL602: Register ID must be passed as I2C Sub Address\n  msg[0].flags     = I2C_M_NOSTOP;\n#else\n  //  Otherwise pass Register ID as I2C Data\n  msg[0].flags     = 0;\n#endif  //  CONFIG_BL602_I2C0\n  msg[0].buffer    = \u0026regaddr;\n  msg[0].length    = 1;\n```\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/drivers/sensors/bmp280.c#L244-L257\n\n```c\nstatic int bmp280_getregs(FAR struct bmp280_dev_s *priv, uint8_t regaddr,\n                          uint8_t *rxbuffer, uint8_t length)\n{\n  ...\n  msg[0].frequency = priv-\u003efreq;\n  msg[0].addr      = priv-\u003eaddr;\n#ifdef CONFIG_BL602_I2C0\n  //  For BL602: Register ID must be passed as I2C Sub Address\n  msg[0].flags     = I2C_M_NOSTOP;\n#else\n  //  Otherwise pass Register ID as I2C Data\n  msg[0].flags     = 0;\n#endif  //  CONFIG_BL602_I2C0\n  msg[0].buffer    = \u0026regaddr;\n  msg[0].length    = 1;\n```\n\nWe also need to set the I2C Sub Address when writing registers...\n\nhttps://github.com/lupyuen/incubator-nuttx/blob/bmp280/drivers/sensors/bmp280.c#L286-L300\n\n```c\nstatic int bmp280_putreg8(FAR struct bmp280_dev_s *priv, uint8_t regaddr,\n                          uint8_t regval)\n{\n  ...\n  msg[0].frequency = priv-\u003efreq;\n  msg[0].addr      = priv-\u003eaddr;\n#ifdef CONFIG_BL602_I2C0\n  //  For BL602: Register ID and value must be passed as I2C Sub Address\n  msg[0].flags     = I2C_M_NOSTOP;\n#else\n  //  Otherwise pass Register ID and value as I2C Data\n  msg[0].flags     = 0;\n#endif  //  CONFIG_BL602_I2C0\n  msg[0].buffer    = txbuffer;\n  msg[0].length    = 2;\n\n  //  For BL602: We read I2C Data because this forces BL602 to send the first message correctly\n  msg[1].frequency = priv-\u003efreq;\n  msg[1].addr      = priv-\u003eaddr;\n  msg[1].flags     = I2C_M_READ;\n  msg[1].buffer    = rxbuffer;\n  msg[1].length    = sizeof(rxbuffer);\n```\n\nWe must read after writing the I2C Register ID and value, so that it forces BL602 to write the data.\n\n# BMP280 Driver Loads OK\n\nNuttX BMP280 Driver loads OK on BL602 ... After setting the Register ID as I2C Sub Address! 🎉\n\nNuttX BMP280 Driver appears as \"/dev/sensor/baro0\"\n\n```text\ngpio_pin_register: Registering /dev/gpio0\ngpio_pin_register: Registering /dev/gpio1\ngpint_enable: Disable the interrupt\ngpio_pin_register: Registering /dev/gpio2\nbl602_gpio_set_intmod: ****gpio_pin=115, int_ctlmod=1, int_trgmod=0\nbl602_spi_setfrequency: frequency=400000, actual=0\nbl602_spi_setbits: nbits=8\nbl602_spi_setmode: mode=0\nspi_test_driver_register: devpath=/dev/spitest0, spidev=0\nbl602_spi_select: devid: 0, CS: free\n\nbmp280_getreg8: regaddr=0xd0\nbl602_i2c_transfer: subflag=1, subaddr=0xd0, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x60\nbl602_i2c_transfer: i2c transfer success\nbmp280_getreg8: regaddr=0xd0, regval=0x60\nbmp280_checkid: devid: 0x60\n\nbmp280_getregs: regaddr=0x88, length=24\nbl602_i2c_transfer: subflag=1, subaddr=0x88, sublen=1\nbl602_i2c_recvdata: count=24, temp=0x65e66e97\nbl602_i2c_recvdata: count=20, temp=0x8f990032\nbl602_i2c_recvdata: count=16, temp=0xbd0d581\nbl602_i2c_recvdata: count=12, temp=0xffdb1e71\nbl602_i2c_recvdata: count=8, temp=0x26acfff9\nbl602_i2c_transfer: i2c transfer success\n\nbmp280_initialize: T1 = 28311\nbmp280_initialize: T2 = 26086\nbmp280_initialize: T3 = 50\nbmp280_initialize: P1 = 36761\nbmp280_initialize: P2 = -10879\nbmp280_initialize: P3 = 3024\nbmp280_initialize: P4 = 7793\nbmp280_initialize: P5 = -37\nbmp280_initialize: P6 = -7\nbmp280_initialize: P7 = 9900\nbmp280_initialize: P8 = 15288\nbmp280_initialize: P9 = 8964\n\nbmp280_putreg8: regaddr=0xf4, regval=0x00\nbl602_i2c_transfer: subflag=1, subaddr=0xf4, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x10bdd800\nbl602_i2c_transfer: i2c transfer success\n\nbmp280_getreg8: regaddr=0xf5\nbl602_i2c_transfer: subflag=1, subaddr=0xf5, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x10bdd800\nbl602_i2c_transfer: i2c transfer success\nbmp280_getreg8: regaddr=0xf5, regval=0x00\n\nbmp280_putreg8: regaddr=0xf5, regval=0x00\nbl602_i2c_transfer: subflag=1, subaddr=0xf5, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x10bdd800\nbl602_i2c_transfer: i2c transfer success\n\nbmp280_getreg8: regaddr=0xf5\nbl602_i2c_transfer: subflag=1, subaddr=0xf5, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x10bdd800\nbl602_i2c_transfer: i2c transfer success\nbmp280_getreg8: regaddr=0xf5, regval=0x00\n\nsensor_custom_register: Registering /dev/sensor/baro0\nbmp280_register: BMP280 driver loaded successfully!\n\nNuttShell (NSH) NuttX-10.2.0-RC0\nnsh\u003e\nnsh\u003e ls /dev\n/dev:\n console\n gpio0\n gpio1\n gpio2\n i2c0\n null\n sensor/\n spi0\n spitest0\n timer0\n urandom\n zero\nnsh\u003e ls /dev/sensor\n/dev/sensor:\n baro0\nnsh\u003e\n```\n\n# Run Sensor Test App\n\nLet's run the NuttX Sensor Test App to read the sensor values from \"/dev/sensor/baro0\"...\n\nhttps://github.com/lupyuen/incubator-nuttx-apps/blob/bme280/testing/sensortest/sensortest.c\n\nConfigure NuttX to enable the Sensor Test App...\n\n- Application Configuration → Testing → Sensor driver test\n\n[(See the .config for BL602)](https://gist.github.com/lupyuen/9d84889f5e2415ecb0f28cea2c2a657f)\n\nRead 10 sensor values from \"/dev/sensor/baro0\"...\n\n```text\nnsh\u003e sensortest -n 10 baro0\nSensorTest: Test /dev/sensor/baro0 with interval(1000000us), latency(0us)\nbaro0: timestamp:30680000 value1:674.93 value2:22.18\nbaro0: timestamp:30680000 value1:674.93 value2:22.18\nbaro0: timestamp:30680000 value1:674.93 value2:22.18\nbaro0: timestamp:30680000 value1:674.93 value2:22.18\nbaro0: timestamp:30680000 value1:674.93 value2:22.18\nbaro0: timestamp:30690000 value1:674.93 value2:22.18\nbaro0: timestamp:30690000 value1:674.93 value2:22.18\nbaro0: timestamp:30690000 value1:674.93 value2:22.18\nbaro0: timestamp:30690000 value1:1006.21 value2:30.78\nbaro0: timestamp:30690000 value1:1006.21 value2:30.78\nSensorTest: Received message: baro0, number:10/10\n\nnsh\u003e sensortest -n 10 baro0\nSensorTest: Test /dev/sensor/baro0 with interval(1000000us), latency(0us)\nbaro0: timestamp:61290000 value1:1006.27 value2:30.80\nbaro0: timestamp:61300000 value1:1006.27 value2:30.80\nbaro0: timestamp:61300000 value1:1006.27 value2:30.80\nbaro0: timestamp:61300000 value1:1006.27 value2:30.80\nbaro0: timestamp:61300000 value1:1006.27 value2:30.80\nbaro0: timestamp:61300000 value1:1006.27 value2:30.80\nbaro0: timestamp:61300000 value1:1006.27 value2:30.80\nbaro0: timestamp:61300000 value1:1006.27 value2:30.80\nbaro0: timestamp:61310000 value1:1006.27 value2:30.80\nbaro0: timestamp:61310000 value1:1006.27 value2:30.80\nSensorTest: Received message: baro0, number:10/10\n\nnsh\u003e sensortest -n 10 baro0\nSensorTest: Test /dev/sensor/baro0 with interval(1000000us), latency(0us)\nbaro0: timestamp:79360000 value1:1006.22 value2:30.80\nbaro0: timestamp:79360000 value1:1006.22 value2:30.80\nbaro0: timestamp:79360000 value1:1006.22 value2:30.80\nbaro0: timestamp:79370000 value1:1006.22 value2:30.80\nbaro0: timestamp:79370000 value1:1006.22 value2:30.80\nbaro0: timestamp:79370000 value1:1006.22 value2:30.80\nbaro0: timestamp:79370000 value1:1006.22 value2:30.80\nbaro0: timestamp:79370000 value1:1006.22 value2:30.80\nbaro0: timestamp:79370000 value1:1006.22 value2:30.80\nbaro0: timestamp:79370000 value1:1006.22 value2:30.80\nSensorTest: Received message: baro0, number:10/10\n\nnsh\u003e sensortest -n 10 baro0\nSensorTest: Test /dev/sensor/baro0 with interval(1000000us), latency(0us)\nbaro0: timestamp:82370000 value1:1006.30 value2:30.81\nbaro0: timestamp:82370000 value1:1006.30 value2:30.81\nbaro0: timestamp:82380000 value1:1006.30 value2:30.81\nbaro0: timestamp:82380000 value1:1006.30 value2:30.81\nbaro0: timestamp:82380000 value1:1006.30 value2:30.81\nbaro0: timestamp:82380000 value1:1006.30 value2:30.81\nbaro0: timestamp:82380000 value1:1006.30 value2:30.81\nbaro0: timestamp:82380000 value1:1006.30 value2:30.81\nbaro0: timestamp:82380000 value1:1006.30 value2:30.81\nbaro0: timestamp:82380000 value1:1006.30 value2:30.81\nSensorTest: Received message: baro0, number:10/10\n\nnsh\u003e sensortest -n 10 baro0\nSensorTest: Test /dev/sensor/baro0 with interval(1000000us), latency(0us)\nbaro0: timestamp:83950000 value1:1006.30 value2:30.79\nbaro0: timestamp:83950000 value1:1006.30 value2:30.79\nbaro0: timestamp:83950000 value1:1006.30 value2:30.79\nbaro0: timestamp:83960000 value1:1006.30 value2:30.79\nbaro0: timestamp:83960000 value1:1006.30 value2:30.79\nbaro0: timestamp:83960000 value1:1006.30 value2:30.79\nbaro0: timestamp:83960000 value1:1006.30 value2:30.79\nbaro0: timestamp:83960000 value1:1006.30 value2:30.79\nbaro0: timestamp:83960000 value1:1006.30 value2:30.79\nbaro0: timestamp:83960000 value1:1006.30 value2:30.79\nSensorTest: Received message: baro0, number:10/10\n\nnsh\u003e sensortest -n 10 baro0\nSensorTest: Test /dev/sensor/baro0 with interval(1000000us), latency(0us)\nbaro0: timestamp:85310000 value1:1006.24 value2:30.80\nbaro0: timestamp:85310000 value1:1006.24 value2:30.80\nbaro0: timestamp:85320000 value1:1006.24 value2:30.80\nbaro0: timestamp:85320000 value1:1006.24 value2:30.80\nbaro0: timestamp:85320000 value1:1006.24 value2:30.80\nbaro0: timestamp:85320000 value1:1006.24 value2:30.80\nbaro0: timestamp:85320000 value1:1006.24 value2:30.80\nbaro0: timestamp:85320000 value1:1006.24 value2:30.80\nbaro0: timestamp:85320000 value1:1006.24 value2:30.80\nbaro0: timestamp:85320000 value1:1006.24 value2:30.80\nSensorTest: Received message: baro0, number:10/10\nnsh\u003e\n```\n\nThat's 1006.24 millibar and 30.8 °C. Yep that looks reasonable for Sunny Singapore by the Seaside 👍\n\n(Air Pressure at Sea Level is 1013.25 millibar)\n\nDetailed log...\n\n```text\nnsh\u003e sensortest -n 10 baro0\n\nsensor_ioctl: cmd=a81 arg=4201c384\nbmp280_getreg8: regaddr=0xf5\nbl602_i2c_transfer: subflag=1, subaddr=0xf5, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x10bdd800\nbl602_i2c_transfer: i2c transfer success\nbmp280_getreg8: regaddr=0xf5, regval=0x00\n\nbmp280_putreg8: regaddr=0xf5, regval=0xa0\nbl602_i2c_transfer: subflag=1, subaddr=0xa0f5, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x10bdd8a0\nbl602_i2c_transfer: i2c transfer success\n\nbmp280_getreg8: regaddr=0xf5\nbl602_i2c_transfer: subflag=1, subaddr=0xf5, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x10bdd8a0\nbl602_i2c_transfer: i2c transfer success\nbmp280_getreg8: regaddr=0xf5, regval=0xa0\n\nsensor_ioctl: cmd=a82 arg=4201c388\nsensor_ioctl: cmd=a80 arg=00000001\nbmp280_putreg8: regaddr=0xf4, regval=0x2f\nbl602_i2c_transfer: subflag=1, subaddr=0x2ff4, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x10bdd82f\nbl602_i2c_transfer: i2c transfer success\n\nSensorTest: Test /dev/sensor/baro0 with interval(1000000us), latency(0us)\nsensor_pollnotify: Report events: 01\nbmp280_getregs: regaddr=0xf7, length=6\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=6, temp=0x80000080\nbl602_i2c_recvdata: count=2, temp=0x80000000\nbl602_i2c_transfer: i2c transfer success\nbmp280_fetch: press = 524288, temp = 524288\nbaro0: timestamp:20540000 value1:714.07 value2:22.18\n\nsensor_pollnotify: Report events: 01\nbmp280_getregs: regaddr=0xf7, length=6\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=6, temp=0x80000080\nbl602_i2c_recvdata: count=2, temp=0x80000000\nbl602_i2c_transfer: i2c transfer success\nbmp280_fetch: press = 524288, temp = 524288\nbaro0: timestamp:20550000 value1:714.07 value2:22.18\n\nsensor_pollnotify: Report events: 01\nbmp280_getregs: regaddr=0xf7, length=6\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=6, temp=0x80000080\nbl602_i2c_recvdata: count=2, temp=0x80000000\nbl602_i2c_transfer: i2c transfer success\nbmp280_fetch: press = 524288, temp = 524288\nbaro0: timestamp:20550000 value1:714.07 value2:22.18\n\nsensor_pollnotify: Report events: 01\nbmp280_getregs: regaddr=0xf7, length=6\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=6, temp=0x80000080\nbl602_i2c_recvdata: count=2, temp=0x80000000\nbl602_i2c_transfer: i2c transfer success\nbmp280_fetch: press = 524288, temp = 524288\nbaro0: timestamp:20550000 value1:714.07 value2:22.18\n\nsensor_pollnotify: Report events: 01\nbmp280_getregs: regaddr=0xf7, length=6\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=6, temp=0x86401752\nbl602_i2c_recvdata: count=2, temp=0x86400035\nbl602_i2c_transfer: i2c transfer success\nbmp280_fetch: press = 336244, temp = 549712\nbaro0: timestamp:20550000 value1:1069.51 value2:30.09\n\nsensor_pollnotify: Report events: 01\nbmp280_getregs: regaddr=0xf7, length=6\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=6, temp=0x86401752\nbl602_i2c_recvdata: count=2, temp=0x86400035\nbl602_i2c_transfer: i2c transfer success\nbmp280_fetch: press = 336244, temp = 549712\nbaro0: timestamp:20560000 value1:1069.51 value2:30.09\n\nsensor_pollnotify: Report events: 01\nbmp280_getregs: regaddr=0xf7, length=6\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=6, temp=0x86401752\nbl602_i2c_recvdata: count=2, temp=0x86400035\nbl602_i2c_transfer: i2c transfer success\nbmp280_fetch: press = 336244, temp = 549712\nbaro0: timestamp:20560000 value1:1069.51 value2:30.09\n\nsensor_pollnotify: Report events: 01\nbmp280_getregs: regaddr=0xf7, length=6\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=6, temp=0x86401752\nbl602_i2c_recvdata: count=2, temp=0x86400035\nbl602_i2c_transfer: i2c transfer success\nbmp280_fetch: press = 336244, temp = 549712\nbaro0: timestamp:20560000 value1:1069.51 value2:30.09\n\nsensor_pollnotify: Report events: 01\nbmp280_getregs: regaddr=0xf7, length=6\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=6, temp=0x86401752\nbl602_i2c_recvdata: count=2, temp=0x86400035\nbl602_i2c_transfer: i2c transfer success\nbmp280_fetch: press = 336244, temp = 549712\nbaro0: timestamp:20560000 value1:1069.51 value2:30.09\n\nsensor_pollnotify: Report events: 01\nbmp280_getregs: regaddr=0xf7, length=6\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=6, temp=0x86401752\nbl602_i2c_recvdata: count=2, temp=0x86400035\nbl602_i2c_transfer: i2c transfer success\nbmp280_fetch: press = 336244, temp = 549712\nbaro0: timestamp:20570000 value1:1069.51 value2:30.09\n\nSensorTest: Received message: baro0, number:10/10\nsensor_ioctl: cmd=a80 arg=00000000\nbmp280_putreg8: regaddr=0xf4, regval=0x00\nbl602_i2c_transfer: subflag=1, subaddr=0xf4, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x80000000\nbl602_i2c_transfer: i2c transfer success\nnsh\u003e\n```\n\nThis shows that writing to I2C Registers works OK...\n\n```text\n## Register F5 has value 00\nbmp280_getreg8: regaddr=0xf5, regval=0x00\n...\n## Set Register F5 to value A0\nbmp280_putreg8: regaddr=0xf5, regval=0xa0\n...\n## Register F5 now has value A0\nbmp280_getreg8: regaddr=0xf5, regval=0xa0\n```\n\nYep the NuttX BMP280 Driver works OK! Now let's port the BME280 Driver from Zephyr OS to NuttX, so we can get the humidity.\n\n# Port BME280 Driver from Zephyr OS\n\nNuttX BMP280 Driver works OK with our BME280 Sensor ... But we're missing one thing: Humidity ... Can we port the BME280 Driver from Zephyr OS? 🤔\n\nhttps://github.com/zephyrproject-rtos/zephyr/blob/main/drivers/sensor/bme280/bme280.c\n\nZephyr BME280 Driver looks similar to [NuttX BMP280 Driver](https://github.com/apache/incubator-nuttx/blob/master/drivers/sensors/bmp280.c) ... So porting Zephyr BME280 Driver to NuttX might not be so hard 🤔\n\n`bme280_sample_fetch` and `bme280_channel_get` are explained in the Zephyr Sensor API:\n\nhttps://docs.zephyrproject.org/latest/reference/peripherals/sensor.html\n\n# Wrap Zephyr Driver as NuttX Driver\n\nZephyr BME280 Driver builds OK on #NuttX (with a few tweaks) 🎉 \n\nhttps://github.com/lupyuen/bme280-nuttx/blob/main/bme280.c\n\nNow we wrap the Zephyr Driver as a NuttX Driver...\n\nhttps://github.com/lupyuen/bme280-nuttx/blob/main/bundle.c\n\nOur NuttX Driver Wrapper wraps around the Zephyr BME280 Driver ... So it works like a NuttX Driver\n\nhttps://github.com/lupyuen/bme280-nuttx/blob/main/driver.c#L349-L424\n\n```c\nstatic int bme280_fetch(FAR struct sensor_lowerhalf_s *lower,\n                        FAR char *buffer, size_t buflen)\n{\n  sninfo(\"buflen=%d\\n\", buflen);\n  FAR struct device *priv = container_of(lower,\n                                               FAR struct device,\n                                               sensor_lower);\n\n  int ret;\n  struct timespec ts;\n  struct sensor_event_baro baro_data;\n  struct sensor_value val;\n\n  if (buflen != sizeof(baro_data))\n    {\n      return -EINVAL;\n    }\n\n  /* Zephyr BME280 Driver assumes that sensor is not in sleep mode */\n  if (!priv-\u003eactivated)\n    {\n      snerr(\"Device must be active before fetch\\n\");\n      return -EIO;\n    }\n\n  /* Fetch the sensor data (from Zephyr BME280 Driver) */\n\n  ret = bme280_sample_fetch(priv, SENSOR_CHAN_ALL);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n\n  /* Get the temperature (from Zephyr BME280 Driver) */\n\n  ret = bme280_channel_get(priv, SENSOR_CHAN_AMBIENT_TEMP, \u0026val);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n  baro_data.temperature = get_sensor_value(\u0026val);\n\n  /* Get the pressure (from Zephyr BME280 Driver) */\n\n  ret = bme280_channel_get(priv, SENSOR_CHAN_PRESS, \u0026val);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n  baro_data.pressure = get_sensor_value(\u0026val) * 10;\n\n  /* Get the humidity (from Zephyr BME280 Driver) */\n\n  ret = bme280_channel_get(priv, SENSOR_CHAN_HUMIDITY, \u0026val);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n  float humidity = get_sensor_value(\u0026val);\n\n  /* Get the timestamp */\n  \n#ifdef CONFIG_CLOCK_MONOTONIC\n  clock_gettime(CLOCK_MONOTONIC, \u0026ts);\n#else\n  clock_gettime(CLOCK_REALTIME, \u0026ts);\n#endif\n  baro_data.timestamp = 1000000ull * ts.tv_sec + ts.tv_nsec / 1000;\n\n  /* Return the sensor data */\n\n  memcpy(buffer, \u0026baro_data, sizeof(baro_data));\n  sninfo(\"temperature=%f °C, pressure=%f mbar, humidity=%f %%\\n\", baro_data.temperature, baro_data.pressure, humidity);\n\n  return buflen;\n}\n```\n\n# Read Sensor Data from Zephyr Driver\n\nOur NuttX BME280 Driver reads the Sensor Data from Zephyr Driver in two steps: 1️⃣ Fetch the sensor sample 2️⃣ Get the channel data\n\nhttps://github.com/lupyuen/bme280-nuttx/blob/main/driver.c#L374-L421\n\n```c\n  /* Fetch the sensor data (from Zephyr BME280 Driver) */\n\n  ret = bme280_sample_fetch(priv, SENSOR_CHAN_ALL);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n\n  /* Get the temperature (from Zephyr BME280 Driver) */\n\n  ret = bme280_channel_get(priv, SENSOR_CHAN_AMBIENT_TEMP, \u0026val);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n  baro_data.temperature = get_sensor_value(\u0026val);\n\n  /* Get the pressure (from Zephyr BME280 Driver) */\n\n  ret = bme280_channel_get(priv, SENSOR_CHAN_PRESS, \u0026val);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n  baro_data.pressure = get_sensor_value(\u0026val) * 10;\n\n  /* Get the humidity (from Zephyr BME280 Driver) */\n\n  ret = bme280_channel_get(priv, SENSOR_CHAN_HUMIDITY, \u0026val);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n  float humidity = get_sensor_value(\u0026val);\n\n  /* Get the timestamp */\n  \n#ifdef CONFIG_CLOCK_MONOTONIC\n  clock_gettime(CLOCK_MONOTONIC, \u0026ts);\n#else\n  clock_gettime(CLOCK_REALTIME, \u0026ts);\n#endif\n  baro_data.timestamp = 1000000ull * ts.tv_sec + ts.tv_nsec / 1000;\n\n  /* Return the sensor data */\n\n  memcpy(buffer, \u0026baro_data, sizeof(baro_data));\n  sninfo(\"temperature=%f °C, pressure=%f mbar, humidity=%f %%\\n\", baro_data.temperature, baro_data.pressure, humidity);\n```\n\n# Power Management\n\nPower Management works a little differently in NuttX vs Zephyr ... Here's how our NuttX BME280 Driver calls the Zephyr Driver to do Power Management\n\nhttps://github.com/lupyuen/bme280-nuttx/blob/main/driver.c#L315-L343\n\n```c\nstatic int bme280_activate(FAR struct sensor_lowerhalf_s *lower,\n                           bool enable)\n{\n  sninfo(\"enable=%d\\n\", enable);\n  int ret = 0;\n\n  FAR struct device *priv = container_of(lower,\n                                               FAR struct device,\n                                               sensor_lower);\n  if (enable)\n    {\n      /* Set power mode to normal */\n\n      ret = bme280_pm_action(priv, PM_DEVICE_ACTION_RESUME);\n    }\n  else\n    {\n      /* Set to sleep mode */\n\n      ret = bme280_pm_action(priv, PM_DEVICE_ACTION_SUSPEND);\n    }\n\n  if (ret \u003e= 0)\n    {\n      priv-\u003eactivated = enable;\n    }\n\n  return ret;\n}\n```\n\n# Standby Duration\n\nBME280 Standby Duration is static in Zephyr but configured at runtime in NuttX ... So we set it in our NuttX BME280 Driver\n\nhttps://github.com/lupyuen/bme280-nuttx/blob/main/driver.c#L217-L255\n\n```c\nstatic int bme280_set_standby(FAR struct device *priv, uint8_t value)\n{\n  sninfo(\"value=%d\\n\", value);\n  \n  uint8_t v_data_u8;\n  uint8_t v_sb_u8;\n  int ret;\n\n  /* Set the standby duration value */\n\n  ret = bme280_reg_read(priv, BME280_REG_CONFIG, \u0026v_data_u8, 1);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n  v_data_u8 = (v_data_u8 \u0026 ~(0x07 \u003c\u003c 5)) | (value \u003c\u003c 5);\n  ret = bme280_reg_write(priv, BME280_REG_CONFIG, v_data_u8);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n\n  /* Check the standby duration value */\n\n  ret = bme280_reg_read(priv, BME280_REG_CONFIG, \u0026v_data_u8, 1);\n  if (ret \u003c 0)\n    {\n      return ret;\n    }\n  v_sb_u8 = (v_data_u8 \u003e\u003e 5) \u0026 0x07;\n\n  if (v_sb_u8 != value)\n    {\n      snerr(\"Failed to set value for standby time.\");\n      return ERROR;\n    }\n\n  return OK;\n}\n```\n\n# Zephyr Driver Modified For NuttX\n\nHere are the minor modifications we made to the Zephyr BME280 Driver while porting to NuttX...\n\nbme280.c:\n\nhttps://github.com/lupyuen/bme280-nuttx/pull/1/files#diff-80464162211b7180f107757b7aee91398cdc088e5775ffadf7e6e1f0bbb4ad65\n\nbme280.h:\n\nhttps://github.com/lupyuen/bme280-nuttx/pull/1/files#diff-e13ff0ab44de7ead31a3dd6cbbbbf2a6fbfb2f04889300993b87ff5a31ffc233\n\nThe above files are wrapped by [bundle.c](bundle.c) and [bundle.h](bundle.h) to become a NuttX Driver.\n\n# Combined Barometer and Humidity Sensor\n\nNuttX doesn't have a Sensor Type that supports BME280 Temperature + Humidity + Pressure ... So our NuttX BME280 Driver combines 2 Sensor Types: 1️⃣ Barometer Sensor (Pressure + Temperature) 2️⃣ Humidity Sensor\n\nhttps://github.com/lupyuen/bme280-nuttx/blob/main/device.h#L36-L49\n\n```c\n/* NuttX Device for BME280 */\n\nstruct device\n{\n  FAR struct sensor_lowerhalf_s sensor_baro;  /* Barometer and Temperature Sensor */\n  FAR struct sensor_lowerhalf_s sensor_humi;  /* Humidity Sensor */\n  FAR struct i2c_master_s *i2c; /* I2C interface */\n  uint8_t addr;                 /* BME280 I2C address */\n  int freq;                     /* BME280 Frequency \u003c= 3.4MHz */\n  bool activated;               /* True if device is not in sleep mode */\n\n  char *name;                   /* Name of the device */\n  struct bme280_data *data;     /* Compensation parameters (bme280.c) */\n};\n```\n\nEach NuttX Sensor defines its operations for 1️⃣ Activating the sensor 2️⃣ Fetching sensor data 3️⃣ Setting the standby interval\n\nhttps://github.com/lupyuen/bme280-nuttx/blob/main/driver.c#L71-L87\n\n```c\n/* Operations for Barometer and Temperature Sensor */\n\nstatic const struct sensor_ops_s g_baro_ops =\n{\n  .activate      = bme280_activate_baro,\n  .fetch         = bme280_fetch_baro,\n  .set_interval  = bme280_set_interval_baro,\n};\n\n/* Operations for Humidity Sensor */\n\nstatic const struct sensor_ops_s g_humi_ops =\n{\n  .activate      = bme280_activate_humi,\n  .fetch         = bme280_fetch_humi,\n  .set_interval  = bme280_set_interval_humi,\n};\n```\n\nAt NuttX Startup we register both BME280 sensors: Barometer Sensor and Humidity Sensor\n\nhttps://github.com/lupyuen/bme280-nuttx/blob/main/driver.c#L755-L773\n\n```c\nint bme280_register(int devno, FAR struct i2c_master_s *i2c)\n{\n  ...\n  /* Register the Barometer Sensor */\n\n  ret = sensor_register(\u0026priv-\u003esensor_baro, devno);\n  if (ret \u003c 0)\n    {\n      snerr(\"Failed to register barometer sensor: %d\\n\", ret);\n      kmm_free(data);\n      kmm_free(priv);\n    }\n\n  /* Register the Humidity Sensor */\n\n  ret = sensor_register(\u0026priv-\u003esensor_humi, devno);\n  if (ret \u003c 0)\n    {\n      snerr(\"Failed to register humidity sensor: %d\\n\", ret);\n      kmm_free(data);\n      kmm_free(priv);\n    }\n```\n\nOur NuttX BME280 Driver appears as 2 sensors: 1️⃣ \"/dev/sensor/baro0\" (Barometer Sensor) 2️⃣ \"/dev/sensor/humi0\" (Humidity Sensor)\n\nThis is how we read each sensor:\n\n```text\nnsh\u003e ls /dev/sensor\n/dev/sensor:\n baro0\n humi0\n\nnsh\u003e sensortest -n 1 baro0\nSensorTest: Test /dev/sensor/baro0 with interval(1000000us), latency(0us)\nbaro0: timestamp:256220000 value1:981.34 value2:28.73\nSensorTest: Received message: baro0, number:1/1\n\nnsh\u003e sensortest -n 1 humi0\nSensorTest: Test /dev/sensor/humi0 with interval(1000000us), latency(0us)\nhumi0: timestamp:553560000 value:92.90\nSensorTest: Received message: humi0, number:1/1\n```\n\n# Output Log\n\nHere's the detailed log when BME280 Driver loads during startup...\n\n```text\nspi_test_driver_register: devpath=/dev/spitest0, spidev=0\nbme280_register: devno=0\nbme280_register: priv=0x4201b800, sensor_baro=0x4201b800, sensor_humi=0x4201b81c\nbl602_i2c_transfer: subflag=1, subaddr=0xd0, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x60\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xd0, size=1, buf[0]=0x60\nbme280_chip_init: ID OK\n\nbme280_reg_write: reg=0xe0, val=0xb6\nbl602_i2c_transfer: subflag=1, subaddr=0xb6e0, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x0\nbl602_i2c_transfer: i2c transfer success\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf3, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x0\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf3, size=1, buf[0]=0x00\n\nbl602_i2c_transfer: subflag=1, subaddr=0x88, sublen=1\nbl602_i2c_recvdata: count=24, temp=0x65e66e97\nbl602_i2c_recvdata: count=20, temp=0x8f990032\nbl602_i2c_recvdata: count=16, temp=0xbd0d581\nbl602_i2c_recvdata: count=12, temp=0xffdb1e71\nbl602_i2c_recvdata: count=8, temp=0x10bdd80a\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0x88, size=24\n\nbl602_i2c_transfer: subflag=1, subaddr=0xa1, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x10bdd84b\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xa1, size=1, buf[0]=0x4b\n\nbl602_i2c_transfer: subflag=1, subaddr=0xe1, sublen=1\nbl602_i2c_recvdata: count=7, temp=0x14000165\nbl602_i2c_recvdata: count=3, temp=0x141e000b\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xe1, size=7\n\nbme280_reg_write: reg=0xf2, val=0x05\nbl602_i2c_transfer: subflag=1, subaddr=0x5f2, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x141e0005\nbl602_i2c_transfer: i2c transfer success\n\nbme280_reg_write: reg=0xf4, val=0x57\nbl602_i2c_transfer: subflag=1,subaddr=0x57f4, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x141e0057\nbl602_i2c_transfer: i2c transfer success\n\nbme280_reg_write: reg=0xf5, val=0xa8\nbl602_i2c_transfer: subflag=1, subaddr=0xa8f5, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x141e00a8\nbl602_i2c_transfer: i2c transfer success\nbme280_chip_init: \"BME280\" OK\n\nbme280_reg_write: reg=0xf4, val=0x54\nbl602_i2c_transfer: subflag=1, subaddr=0x54f4, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x141e0054\nbl602_i2c_transfer: i2c transfer success\n\nsensor_custom_register: Registering /dev/sensor/baro0\nsensor_custom_register: Registering /dev/sensor/humi0\nbme280_register: BME280 driver loaded successfully!\n\nNuttShell (NSH) NuttX-10.2.0-RC0\nnsh\u003e\n```\n\nHere's the detailed log when we read the Barometer Sensor Data from BME280 Driver...\n\n```text\nnsh\u003e sensortest -n 1 baro0\n\nsensor_ioctl: cmd=a81 arg=4201c4b4\nbme280_set_interval_baro: period_us=1000000\nbme280_set_interval_baro: priv=0x4201b800, sensor_baro=0x4201b800\nbme280_set_standby: value=5\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf5, sublen=1\nbl602_i2c_recvdata: count=1, temp=0xcf9400a8\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf5, size=1, buf[0]=0xa8\n\nbme280_reg_write: reg=0xf5, val=0xa8\nbl602_i2c_transfer: subflag=1, subaddr=0xa8f5, sublen=2\nbl602_i2c_recvdata: count=1, temp=0xcf9400a8\nbl602_i2c_transfer: i2c transfer success\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf5, sublen=1\nbl602_i2c_recvdata: count=1, temp=0xcf9400a8\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf5, size=1, buf[0]=0xa8\n\nsensor_ioctl: cmd=a82 arg=4201c4b8\nsensor_ioctl: cmd=a80 arg=00000001\nbme280_activate_baro: enable=1\nbme280_activate_baro: priv=0x4201b800, sensor_baro=0x4201b800\n\nbl602_i2c_transfer: subflag=1, subaddr=0xd0, sublen=1\nbl602_i2c_recvdata: count=1, temp=0xcf940060\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xd0, size=1, buf[0]=0x60\nbme280_chip_init: ID OK\n\nbme280_reg_write: reg=0xe0, val=0xb6\nbl602_i2c_transfer: subflag=1, subaddr=0xb6e0, sublen=2\nbl602_i2c_recvdata: count=1, temp=0xcf940000\nbl602_i2c_transfer: i2c transfer success\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf3, sublen=1\nbl602_i2c_recvdata: count=1, temp=0xcf940000\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf3, size=1, buf[0]=0x00\n\nbl602_i2c_transfer: subflag=1, subaddr=0x88, sublen=1\nbl602_i2c_recvdata: count=24, temp=0x65e66e97\nbl602_i2c_recvdata: count=20, temp=0x8f990032\nbl602_i2c_recvdata: count=16, temp=0xbd0d581\nbl602_i2c_recvdata: count=12, temp=0xffdb1e71\nbl602_i2c_recvdata: count=8, temp=0x10bdd80a\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0x88, size=24\n\nbl602_i2c_transfer: subflag=1, subaddr=0xa1, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x10bdd84b\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xa1, size=1, buf[0]=0x4b\n\nbl602_i2c_transfer: subflag=1, subaddr=0xe1, sublen=1\nbl602_i2c_recvdata: count=7, temp=0x14000165\nbl602_i2c_recvdata: count=3, temp=0x141e000b\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xe1, size=7\n\nbme280_reg_write: reg=0xf2, val=0x05\nbl602_i2c_trasfer: subflag=1, subaddr=0x5f2, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x141e0005\nbl602_i2c_transfer: i2c transfer success\n\nbme280_reg_write: reg=0xf4, val=0x57\nbl602_i2c_transfer: subflag=1, subaddr=0x57f4, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x141e0057\nbl602_i2c_transfer: i2c transfer success\n\nbme280_reg_write: reg=0xf5, val=0xa8\nbl602_i2c_transfer: subflag=1, subaddr=0xa8f5, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x141e00a8\nbl602_i2c_transfer: i2c transfer success\nbme280_chip_init: \"BME280\" OK\n\nSensorTest: Test /dev/sensor/baro0 with interval(1000000us), latency(0us)\nsensor_pollnotify: Report events: 01\nbme280_fetch_baro: buflen=16\nbme280_fetch_baro: priv=0x4201b800, sensor_baro=0x4201b800\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf3, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x141e000c\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf3, size=1, buf[0]=0x0c\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf3, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x141e000c\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf3, size=1, buf[0]=0x0c\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf3, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x141e0004\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf3, size=1, buf[0]=0x04\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=8, temp=0x8530af51\nbl602_i2c_recvdata: count=4, temp=0x61940024\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf7, size=8\n\nbme280_fetch: temperature=28.730000 °C, pressure=981.339661 mbar, humidity=93.127930 %\nbaro0: timestamp:256220000 value1:981.34 value2:28.73\nSensorTest: Received message: baro0, number:1/1\nsensor_ioctl: cmd=a80 arg=00000000\nbme280_activate_baro: enable=0\nbme280_activate_baro: priv=0x4201b800, sensor_baro=0x4201b800\n\nbme280_reg_write: reg=0xf4, val=0x54\nbl602_i2c_transfer: subflag=1, subaddr=0x54f4, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x61940054\nbl602_i2c_transfer: i2c transfer success\n```\n\nHere's the detailed log when we read the Humidity Sensor Data from BME280 Driver...\n\n```text\nnsh\u003e sensortest -n 1 humi0\n\nsensor_ioctl: cmd=a81 arg=4201c4b4\nbme280_set_interval_humi: period_us=1000000\nbme280_set_interval_humi: priv=0x4201b800, sensor_humi=0x4201b81c\nbme280_set_standby: value=5\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf5, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x619400a8\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf5, size=1, buf[0]=0xa8\n\nbme280_reg_write: reg=0xf5, val=0xa8\nbl602_i2c_transfer: subflag=1, subaddr=0xa8f5, sublen=2\nbl602_i2c_recvdata: cunt=1, temp=0x619400a8\nbl602_i2c_transfer: i2c transfer success\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf5, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x619400a8\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf5, size=1, buf[0]=0xa8\n\nsensor_ioctl: cmd=a82 arg=4201c4b8\nsensor_ioctl: cmd=a80 arg=00000001\nbme280_activate_humi: enable=1\nbme280_activate_humi: priv=0x4201b800, sensor_humi=0x4201b81c\n\nbl602_i2c_transfer: subflag=1, subaddr=0xd0, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x61940060\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xd0, size=1, buf[0]=0x60\nbme280_chip_init: ID OK\n\nbme280_reg_write: reg=0xe0, val=0xb6\nbl602_i2c_transfer: subflag=1, subaddr=0xb6e0, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x61940000\nbl602_i2c_transfer: i2c transfer success\nbl602_i2c_transfer: subflag=1, subaddr=0xf3, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x61940000\nbl602_i2c_transfer: i2c transfer success\n\nbme280_reg_read: start=0xf3, size=1, buf[0]=0x00\nbl602_i2c_transfer: subflag=1, subaddr=0x88, sublen=1\nbl602_i2c_recvdata: count=24, temp=0x65e66e97\nbl602_i2c_recvdata: count=20, temp=0x8f990032\nbl602_i2c_recvdata: count=16, temp=0xbd0d581\nbl602_i2c_recvdata: count=12, temp=0xffdb1e71\nbl602_i2c_recvdata: count=8, temp=0x10bdd80a\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0x88, size=24\n\nbl602_i2c_transfer: subflag=1, subaddr=0xa1, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x10bdd84b\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xa1, size=1, buf[0]=0x4b\n\nbl602_i2c_transfer: subflag=1, subaddr=0xe1, sublen=1\nbl602_i2c_recvdata: count=7, temp=0x14000165\nbl602_i2c_recvdata: count=3, temp=0x141e000b\nbl02_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xe1, size=7\n\nbme280_reg_write: reg=0xf2, val=0x05\nbl602_i2c_transfer: subflag=1, subaddr=0x5f2, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x141e0005\nbl602_i2c_transfer: i2c transfer success\n\nbme280_reg_write: reg=0xf4, val=0x57\nbl602_i2c_transfer: subflag=1, subaddr=0x57f4, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x141e0057\nbl602_i2c_transfer: i2c transfer success\n\nbme280_reg_write: reg=0xf5, val=0xa8\nbl602_i2c_transfer: subflag=1, subaddr=0xa8f5, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x141e00a8\nbl602_i2c_transfer: i2c transfer success\nbme280_chip_init: \"BME280\" OK\n\nSensorTest: Test /dev/sensor/humi0 with interval(1000000us), latency(0us)\nsensor_pollnotify: Report events: 01\nbme280_fetch_humi: buflen=16\nbme280_fetch_humi: priv=0x4201b800, sensor_humi=0x4201b81c\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf3, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x141e000c\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf3, size=1, buf[0]=0x0c\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf3, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x141e000c\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf3, size=1, buf[0]=0x0c\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf3, sublen=1\nbl602_i2c_recvdata: count=1, temp=0x141e0004\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf3, size=1, buf[0]=0x04\n\nbl602_i2c_transfer: subflag=1, subaddr=0xf7, sublen=1\nbl602_i2c_recvdata: count=8, temp=0x85b0a451\nl602_i2c_recvdata: count=4, temp=0x3b94000d\nbl602_i2c_transfer: i2c transfer success\nbme280_reg_read: start=0xf7, size=8\n\nbme280_fetch: temperature=28.610001 °C, pressure=1028.345703 mbar, humidity=92.896484 %\nhumi0: timestamp:553560000 value:92.90\nSensorTest: Received message: humi0, number:1/1\nsensor_ioctl: cmd=a80 arg=00000000\nbme280_activate_humi: enable=0\nbme280_activate_humi: priv=0x4201b800, sensor_humi=0x4201b81c\n\nbme280_reg_write: reg=0xf4, val=0x54\nbl602_i2c_transfer: subflag=1, subaddr=0x54f4, sublen=2\nbl602_i2c_recvdata: count=1, temp=0x3b940054\nbl602_i2c_transfer: i2c transfer success\n```\n\n# Logic Analyser Output\n\nHere's the Logic Analyser Output when BME280 Driver loads during startup...\n\n![Top](https://lupyuen.github.io/images/bme280-boot1.png)\n\n![Bottom](https://lupyuen.github.io/images/bme280-boot2.png)\n\n[(Source)](https://github.com/lupyuen/lupyuen.github.io/blob/master/images/bme280-boot.kvdat)\n\nHere's the Logic Analyser Output when we read the Sensor Data from BME280 Driver...\n\n(`sensortest -n 1 baro0` from the previous section)\n\n![Top](https://lupyuen.github.io/images/bme280-read1.png)\n\n![Middle](https://lupyuen.github.io/images/bme280-read2.png)\n\n![Bottom](https://lupyuen.github.io/images/bme280-read3.png)\n\n[(Source)](https://github.com/lupyuen/lupyuen.github.io/blob/master/images/bme280-read.kvdat)\n","funding_links":["https://github.com/sponsors/lupyuen","paypal.me/lupyuen"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flupyuen%2Fbme280-nuttx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flupyuen%2Fbme280-nuttx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flupyuen%2Fbme280-nuttx/lists"}