MCH2022 badge?
go to mch2022.badge.team

Git revision: 16f78c88795f96fcb08854282f265189548651b5 sensible_card10 rev. 3 (by scy)

sensible card10

Broadcast BME680 sensor readings of your card10 badge as BLE advertisements.

on Codeberg | in the badge hatchery

What is it?

This is an application designed for the card10 badge that will broadcast the readings of its on-board BME680 sensor using Bluetooth Low Energy (BLE) advertisement packets. These readings consist of

  • Temperature
  • Relative humidity
  • Atmospheric pressure
  • IAQ, a value measuring the indoor air quality
  • IAQ accuracy (i.e. how well the sensor thinks it’s calibrated)
  • Estimated CO₂ in the air (derived from the IAQ; the BME680 sensor cannot measure CO₂ directly)
  • Charge left in your card10’s battery

Installation

Create a folder inside of apps on your card10’s USB drive, place the contents of this repository (or at least __init.py__ and metadata.json) there. For details, see the card10 docs section on app installation.

Usage

Start the application. Your display will turn off to conserve power (and because it’s an open task for me to display something useful). You’ll notice that the blue rocket (left of the display) will blink faintly about every 3 seconds to let you know that the card10 is broadcasting.

If your battery is at 20 % or less, red LEDs will blink every 3 seconds to make you aware of this. The top row of LEDs will show a red “charge left” bar slowly depleting.

You can press the menu button as usual to exit the app.

Status

It’s working, but there’s no UI yet. I’d like to display the values on the screen, with a button to turn the screen off.

Data structure

The advertisement packet will contain two things:

  • The current sensor readings, as an element of type 0x16 (“service data”) with a service UUID of 0x181a (“environmental sensing”).
  • A name element (type 0x09) containing the string card10 to help you identify the card10 in scan results.

Note that Bluetooth values, UUIDs, etc. are transmitted in little-endian byte order. Also note (if your library doesn’t help you with it) that advertisement data elements are prefixed by their size in bytes.

The actual sensor readings in the service data element are all integers, encoded like this:

  • Magic number 0xccc1. The 1 may change in the future, should there be a new version of the data structure.
  • Temperature (16-bit signed) in centidegrees Celsius (123 meaning 1.23 °C).
  • Humidity (16-bit unsigned) in centi-percent (123 meaning 1.23 %).
  • Pressure (32-bit unsigned) in deci-Pascal (123 meaning 12.3 Pa).
  • IAQ accuracy (8-bit unsigned, see also the API docs), with the values meaning
    • 0: sensor warm-up (takes about 5 minutes)
    • 1: sensor not calibrated, low accuracy (takes about 10 hours initially)
    • 2: sensor re-calibrating, medium accuracy (happens occasionally, takes tens of minutes)
    • 3: sensor calibrated, high accuracy
    • 255: proprietary BSEC library disabled (see card10.cfg); IAQ and eCO₂ will be unavailable and set to zero.
  • IAQ (16-bit unsigned) from 0 (best) to 500 (worst), see page 10 of the BME680 datasheet for a description of these values.
  • Estimated CO₂ equivalents (16-bit unsigned) in parts per million.
  • Battery charge left (8-bit unsigned) in percent. Values greater than 100 are reserved for future use (e.g. to signal “plugged in”).

I’ve tried to keep the type of each of the values identical or at least similar to the ESS.

Integration into ESPHome

This is an example of how to access these values from ESPHome’s BLE tracker. Note that my C++ skills are rudimentary at best, which is why even the unsigned values will be interpreted as being signed here. Please let me know how to fix this. 🙈

esp32_ble_tracker:
  scan_parameters:
    active: false  # Only listen for advertisements, no active scans.
    interval: 42ms
    window: 23ms

  on_ble_service_data_advertise:
    - mac_address: CA:4D:10:11:22:33  # Replace with your card10’s Bluetooth MAC address.
      service_uuid: 181A
      then:
        - lambda: |-
            // TODO: cast uint values to be actually unsigned
            id(card10_temp).publish_state((x[2] + (x[3]<<8)) / 100.0);
            id(card10_hum).publish_state((x[4] + (x[5]<<8)) / 100.0);
            id(card10_press).publish_state((x[6] + (x[7]<<8) + (x[8]<<16) + (x[9]<<24)) / 1000.0);
            if (x[10] != 0xff) {  // BSEC data available
              id(card10_iaq_acc).publish_state(x[10]);
              id(card10_iaq).publish_state(x[11] + (x[12]<<8));
              id(card10_eco2).publish_state(x[13] + (x[14]<<8));
            }
            id(card10_bat).publish_state(x[15]);

sensor:
  - id: card10_temp
    name: card10 Temperature
    platform: template
    unit_of_measurement: "°C"
    accuracy_decimals: 2
    filters:
      - throttle_average: 10s
      - filter_out: nan
    state_topic: card10/temperature_c

  - id: card10_hum
    name: card10 Humidity
    platform: template
    unit_of_measurement: "%"
    accuracy_decimals: 2
    filters:
      - throttle_average: 10s
      - filter_out: nan
    state_topic: card10/humidity_pct

  - id: card10_press
    name: card10 Pressure
    platform: template
    unit_of_measurement: "hPa"
    accuracy_decimals: 2
    filters:
      - throttle_average: 10s
      - filter_out: nan
    state_topic: card10/pressure_hpa

  - id: card10_iaq_acc
    name: card10 IAQ Accuracy
    platform: template
    unit_of_measurement: ""
    accuracy_decimals: 0
    filters:
      - throttle: 10s
      - filter_out: nan
    state_topic: card10/iaq_accuracy

  - id: card10_iaq
    name: card10 IAQ
    platform: template
    unit_of_measurement: ""
    accuracy_decimals: 0
    filters:
      - throttle_average: 10s
      - filter_out: nan
    state_topic: card10/iaq

  - id: card10_eco2
    name: card10 eCO₂
    platform: template
    unit_of_measurement: "ppm"
    accuracy_decimals: 0
    filters:
      - throttle_average: 10s
      - filter_out: nan
    state_topic: card10/eco2_ppm

  - id: card10_bat
    name: card10 Battery
    platform: template
    unit_of_measurement: "%"
    accuracy_decimals: 0
    filters:
      - throttle: 10s
      - filter_out: nan
    state_topic: card10/sensor_battery_pct

Q & A

How is this different from the Environmental Sensing Service?

The ESS (available on firmware 1.17 and newer) basically exposes the same values. The difference is:

  • With the ESS, you have to create a bi-directional connection to the card10 in order to retrieve the values. This consumes more battery power on both the card10 and the device you’re connecting from.
  • You can only use the ESS after pairing.
  • The ESS is encrypted, as far as I can tell.
  • Reading the advertisement data provided by sensible card10 might be easier to do in the Bluetooth library you’re using than to go through the pairing process and opening a connection.

If you’re not too concerned about privacy implications of broadcasting the sensor values, sensible card10 might make it easier for you to access the sensor data.

Why the name?

Because SENSors via BLE. So clever of me.

I have another question or suggestion!

Feel free to open an issue on Codeberg.

Category: hardware
Status: unknown
Vote Comment Date
No votes yet :(
Compatibility
  • card10: unknown
Dependencies
  • No dependencies found
Dependants
  • No dependants found
File Last edited Size
LICENSE.txt 2022-01-24 11:55:34 1.23 KiB
README.md 2022-01-24 11:55:34 7.92 KiB
__init__.py 2022-01-24 11:55:34 2.95 KiB
metadata.json 2022-01-24 11:55:34 201 B