Blue Connect pool measurements

Sorry, my comment was misleading: I am using chlorine tablet (slow chlorine) for pool disinfection and I am noticing big variations of ORP. The tablet slowly dissolve during 1 week. What I was mentioning is that I did not added or removed chlorine tablet during the time shown in the graph with ORP. I wonder of there is a problem with my Blue Connect or what I am observing is normal

FYI:

For me, the values calculated by esp match pretty close the values in the app using this formula for ORP:
float orp = (float)( (int16_t) (x[6]<< 8) + x[5]) / 4.0;

I see some of you use:
float orp = (float)( (int16_t) (x[6]<< 8) + x[5]) / 3.86 ;

Maybe this because of Blue Connect ā€œcalibrationā€?

Also: My device is ā€œnewā€ and the battery calculated by esp is ~30. Are we correctly interpreting the battery information?

I think this is normal. Iā€™m also using the same kind of chlorine tables, and my ORP levels fluctuate within about 100mV throughout the day. As far as I can see, it goes down at night and increases during the day, perhaps itā€™s activated by sunlight?

1 Like

My guess is that its normal as well.
image
same as with Nick

I calibrated my unit with a kit and then took 7 readings spread over a few days and added them to a script. The outcome was that when using the 3.86 the results were closer to the APP values.

The battery is a guess, at least at my end and what I got from the forum. MY unit is 1 .5 season in service, no idea what the real level is and if that value is actually the battery level.didnt take my battery out to check (to be honest)
When your unit is new it should be close to 3.8 - 4.1V (is that 38 - 41 in esp?). To be sure you could take it out put it on a volt meter and see the real value. Then we can modify the calculation to match

1 Like

@oxident @nheinemans

feel free to add a comment to that

1 Like

I set up MQTT Server, MQTT Broker Addon in HomeAssistant and could get some positive results with test subscriptions and publications (by command line and from within the MQTT Addon in HomeAssistant, too - no problems, communication and messaging seem to work).

Set up Proxy App of @LordMike and in my HomeAssistant Instance, there are popping up some things; my Pool is definitely found, but I do not get any sensors for PH, ORP, Temperature and so on.

All components are running in separate Docker Containers:
Mosquitto, Mikeā€™s Proxy App as well as the HomeAssistance Instance itself. All of them with docker-compose. All of them at the same physical machine (Pi 4).

From within HomeAssistant, within MQTT Integration, I set Server IP 127.0.0.1 (Mosquitto Docker is running at the same machine). I was able to successfully make a subscription which reacted on an adequate public.
As MQTT-Server Property in Mikeā€™s Proxy App I set 127.0.0.1, too, and network_mode, I set to host (same machine here, too).
In none of the logs I can see any errorā€¦

Whatā€™s going wrong? Or what has been overseen by me to get the measurings?

Strange thingā€¦ Some minutes ago, I took a look at my HomeAssistant Overview for a completely other reason and suddenly, it shows the sensors with the pool measurements :anguished:. Dunno why I didnā€™t see them after having set up Mikeā€™s proxy app, but now, theyā€™re there. So no further help needed.

Now, next step for me is to get running JosĆ©ā€™s python code for Bluetooth reading. I would like to get this connected the way that I can do on-demand bluetooth readings no matter, where I am. I still have the very very old Blue Connect Version with 2 daily automatic measurings for free without any subscription. Most time, it works well, but sometimes, I do not get any updated measurements via SigFox for some days. This very rarely occurs, but anyway, in this case, it would be helpful for me to have a possibility to enforce a ā€œremoteā€ bluetooth reading. Maybe, some time regularly, if SigFox really died.

Good you got it to work. If your device is doing readings, itā€™s entirely possible it does then 20 times a day, but BR will only ā€œreleaseā€ new readings twice a day.

I believe I made a function / command you can call to release the latest data that BR has received. Play with it and see what you can get.

Iā€™ve not done any automation here as it is in the grey area. :slight_smile:

Thank you for your reply. Two readings a day are fully enough for me.

You mean that automatically, your proxy app will fetch these 2 readings, but that I may be able to even get up to 20 by means of your tool?

Just to make sure that I correctly understand youā€¦

Yes.

As Iā€™ve understood it, BR have a setup where data may come in at any time from automated means, like wifi (Bluetooth gateway) or sigfox. But depending on your license, they will not store that data for you unless you request it.

As an example. With the wifi gateway. If you do a new reading of the sensor, the data may be up to an hour old. This, and given the name of the api called when you ā€œdo a readingā€, I imagine the sensor will send out readings often - but BR wonā€™t store them.

What you can do then is automatically call ā€œget a new readingā€ to ensure they store the new resdings. Itā€™s entirely possible that the older sensors also push out readings 20 times a day, but BR wonā€™t store 18 of them. Itā€™s also possible they do 2 - who knows :slight_smile:

Mike.

Hi all, first of all, many thanks to the people who worked on the YAML script from @nheinemans.

I configured my ESP and mainly only at the MAC address from my BR, and it immediately started to retrieve my values. Iā€™m really happy with it so far! However, I noticed some differences in the ORP values between the App (sigfox) and the BT measurements. Could this be caused by the ā€œdelayā€ in the measurements? The strange thing is that over BT, it indicates the highest measurement from last night as 798mV, but in the app, it shows the highest point at 772mV.


HA-blueRiiot-meeting

Unfortunately, as I have seen so far, all the advices for Bluetooth readings are very ESP-specific. If thereā€™s only a Raspberry Pi 4 with integrated Bluetooth module (and no extra ESP Module) - is there already any way to make HA trigger bluetooth readings?

What Iā€™ve already done so far, is to set up MQTT Server and Broker as well as Mikeā€™s proxy app what finally led me to have integrated all the information to HA. Whatā€™s missing to me, is being able to sync via Bluetooth.

I have been using the BT via ESPHome for almost a week now, and what I did notice is that the value I am getting via the BT proxy is about +/- 20mV higher than what the app is showing.

I did several manual measurements, first over the ESP BTproxy and directly after that over the BT in the BlueRiiot app. I took the average difference between them and put it in the float, in my case it whas minus 21.57826

float orp = (float)((int16_t)(x[6]<< 8) + x[5]) / 3.86 - 21.57826;

First of all, thanks to anyone that has contributed. AS @robertjan, I too have to subtract the same amount to get a correct measurement for ORP.

Plus, Iā€™ve transformed the battery voltage to a percentage (the new one is 3.6v):

float bat = (float)( (int16_t) x[11]) / 36 * 100;

and added a restart switch.

My complete setup is here: esphome-contrib/blueconnect.yaml at main Ā· dbochicchio/esphome-contrib (github.com)

Iā€™ve also added the secrets in the readme, for anyone wanting a smoother start.

3 Likes

From now, it looks really good :blush: I am happy and starting to use my chlorinator based on this.

Just moved my config over to this, thank you!

One question, my battery is reporting as 0. Logs below. However Iā€™m guessing itā€™s not 0 otherwise I wouldnā€™t be getting a response. Any suggestions?

[20:59:12][V][sensor:043]: 'pooltest Battery': Received new state 0.000000
[20:59:12][D][sensor:094]: 'pooltest Battery': Sending state 0.00000 % with 0 decimals of accuracy

Remove the / 36 * 100 part and try to post the value. It could be decoded different, idk. From this thread, my impression is that different firmware may have different results.

Iā€™ve edited the yaml so the battery looks like this:

float bat = (float)( (int16_t) x[11]);
ESP_LOGD("bat", "%f", bat);
id(sensor_${blueriiot_id_prefix}_bat).publish_state(bat);

Unfortunately, the battery is still reporting 0%, logs are:

[14:58:33][D][bat:171]: 0.000000
[14:58:33][V][sensor:043]: 'pooltest Battery': Received new state 0.000000
[14:58:33][D][sensor:094]: 'pooltest Battery': Sending state 0.00000 % with 0 decimals of accuracy

With my very limited knowledge it looks like the device is reporting 0%, so a device issue rather than esphome issue?

Try to post the raw values, so other may look for it and give further advices.

I have installed the esphome based solution from jose, but the esp is not connecting, i am not getting any data. Is there a good idea, how to debug it, in the logs of the esp i see not a single debug info? Any tip is welcome.

This is my code (also parts for my apple watch inside)


substitutions:
  name: atom-lite-1
#  name: atom-lite-1-de0ce8
  roomname: Wohnzimmer
  staticip: 192.168.1.145
  yourname: Lukas
  rssi_present: id(harssi_present).state
  rssi_not_present: id(harssi_not_present).state
  rssi_timeout: id(harssi_timeout).state

  # erster Blueconnect
  blueriiot1_mac: '00:A0:50:C7:46:XX'
  blueriiot1_name_prefix: 'whirlpool'
  blueriiot1_id_prefix: 'pl'

  # send true 0x01 to this service ID
  blueriiot_send_service_uuid: 'F3300001-F0A2-9B06-0C59-1BC4763B5C00'
  blueriiot_send_characteristic_uuid: 'F3300002-F0A2-9B06-0C59-1BC4763B5C00'
  
  # notification is recieved on this Service ID                  
  blueriiot_recieve_service_uuid: 'F3300001-F0A2-9B06-0C59-1BC4763B5C00'
  blueriiot_recieve_characteristic_uuid: 'F3300003-F0A2-9B06-0C59-1BC4763B5C00'


esphome:
  name: ${name}
  name_add_mac_suffix: True
  platformio_options:
    build_flags: 
      - -Wno-maybe-uninitialized
packages:
  esphome.bluetooth-proxy: github://esphome/bluetooth-proxies/m5stack-atom-lite.yaml@main

esp32:
  board: m5stack-atom
  framework:
#    type: arduino
    type: esp-idf    

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: ""

ota:
  password: ""

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    # Set this to the IP of the ESP
    static_ip: $staticip
    # Set this to the IP address of the router. Often ends with .1
    gateway: 192.168.1.3
    # The subnet of the network. 255.255.255.0 works for most home networks.
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Atom-Lite-1 Fallback Hotspot"
    password: "xx"

#captive_portal:

bluetooth_proxy:
  active: true

#web_server:
#  port: 80
#  auth:
#    username: admin
#    password: Quatschkopf01

esp32_ble_tracker:
  scan_parameters:
    interval: 300ms
    window: 50ms
    active: true
  on_ble_advertise:
    - then:
      # Look for manufacturer data of form: 4c00 10 05 YY 98 XXXXXX
      # Where YY can be 01..0F or 20..2F; and XXXXXX is ignored
      - lambda: |-
          optional<int16_t> best_rssi = nullopt;
          for (auto data : x.get_manufacturer_datas()) {
            const int16_t device_rssi = x.get_rssi();
            // ESP_LOGD("ble_adv", "Found Device (mac %s) rssi %i", x.address_str().c_str(), device_rssi);
            // Guard against non-Apple datagrams, or those that are too small.
            if (data.data.size() < 4 || data.uuid.to_string() != "0x004C" || data.data[0] != 0x10 || data.data[1] < 5) {
              // ESP_LOGD("ble_adv", "Non Apple datagrams found, skipping, uuid=%s, data.data.size()=%i, data.data[0]=%i, data.data[1]=%i",data.uuid.to_string().c_str(),data.data.size(),data.data[0],data.data[1]);
              continue;
            }
            const int16_t rssi = x.get_rssi();
            const uint8_t status_flags = data.data[2] >> 4;  // High nibble
            const uint8_t data_flags = data.data[3];
            // ESP_LOGD("ble_adv", "status_flags=%i, data_flags=%i",status_flags,data_flags);            

            if (data_flags == 0x98 || data_flags == 0x18) {  // Match unlocked Apple Watch. To also match locked watch use: if (data_flags == 0x98 || data_flags == 0x18) {
              if (status_flags == 0 || status_flags == 2) {
                best_rssi = max(rssi, best_rssi.value_or(rssi));
                ESP_LOGD("ble_adv", "Found Apple Watch (mac %s) rssi %i", x.address_str().c_str(), rssi);
              } else {
                ESP_LOGD("ble_adv", "Possible Apple Watch? (mac %s) rssi %i, unrecognised status/action flags %#04x", x.address_str().c_str(), rssi, data.data[2]);
              }
            }
          }
          if (best_rssi) {
            ESP_LOGD("ble_adv", "Publish State of Apple Watch (mac %s)", x.address_str().c_str());
            id(apple_watch_rssi).publish_state(*best_rssi);
          } 
          //else {
          //  ESP_LOGD("ble_adv", "Not publishing State of Apple Watch (mac %s)", x.address_str().c_str());
          //}

ble_client:
  # erster Sensor
  - mac_address: ${blueriiot1_mac}
    id: ble_client_${blueriiot1_id_prefix}
    on_connect: 
      then:
        - lambda: "id(binary_sensor_${blueriiot1_id_prefix}_connected).publish_state(true);"
        - delay: 2s
        - button.press: button_${blueriiot1_id_prefix}_doreading
    on_disconnect: [lambda: "id(binary_sensor_${blueriiot1_id_prefix}_connected).publish_state(false);"]

switch:
  # erster Sensor
  - platform: ble_client
    ble_client_id: ble_client_${blueriiot1_id_prefix}
    name: "${blueriiot1_name_prefix} Enable"
    id: switch_${blueriiot1_id_prefix}_enable
    restore_mode: ALWAYS_OFF

globals:
  - id: timeout
    type: int
    restore_value: no
    initial_value: '30000'

sensor:
  # erster Sensor
  - platform: template 
    id: sensor_${blueriiot1_id_prefix}_temperature
    name: ${blueriiot1_name_prefix} Temperature
    # Optional variables:
    unit_of_measurement: "Ā°C"
    icon: "mdi:water-percent"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1

  - platform: template 
    id: sensor_${blueriiot1_id_prefix}_ph
    name: ${blueriiot1_name_prefix} PH
    # Optional variables:
    #unit_of_measurement: "Ā°C"
    #icon: "mdi:water-percent"
    #device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
 
  - platform: template 
    id: sensor_${blueriiot1_id_prefix}_orp
    name: ${blueriiot1_name_prefix} Chlor
    # Optional variables:
    #unit_of_measurement: "Ā°C"
    #icon: "mdi:water-percent"
    device_class: "voltage"
    state_class: "measurement"
    accuracy_decimals: 1
    
  - platform: template 
    id: sensor_${blueriiot1_id_prefix}_bat
    name: ${blueriiot1_name_prefix} Battery
    # Optional variables:
    #unit_of_measurement: "Ā°C"
    #icon: "mdi:water-percent"
    device_class: "battery"
    state_class: "measurement"
    accuracy_decimals: 1

  - platform: template
    id: apple_watch_rssi
    name: "$yourname Apple Watch $roomname RSSI"
    device_class: signal_strength
    unit_of_measurement: dBm
    accuracy_decimals: 0
    filters:
      - exponential_moving_average:
          alpha: 0.3
          send_every: 1
    on_value:
      then:
        - lambda: |-
            if (id(apple_watch_rssi).state > $rssi_present) {
              id(room_presence_debounce).publish_state(1);
              ESP_LOGD("sensor_value", "Detected Apple Watch (rssi %i)", (int)id(apple_watch_rssi).state);
            } else if (id(apple_watch_rssi).state < $rssi_not_present) {
              id(room_presence_debounce).publish_state(0);
              ESP_LOGD("sensor_value", "Lost Apple Watch (rssi %i)", (int)id(apple_watch_rssi).state);
            }
        - script.execute: presence_timeout  # Publish 0 if no rssi received
  
  - platform: template
    id: room_presence_debounce
    filters:
      - sliding_window_moving_average:
          window_size: 3
          send_every: 1
          
  - platform: homeassistant
    name: HA RSSI Present Value
    entity_id: input_number.rssi_present_$roomname
    id: harssi_present
    internal: true
  - platform: homeassistant
    name: HA RSSI Not Present Value
    entity_id: input_number.rssi_not_present_$roomname
    id: harssi_not_present
    internal: true
  - platform: homeassistant
    name: HA RSSI Timeout Value
    entity_id: input_number.rssi_timeout_$roomname
    id: harssi_timeout
    internal: true
    accuracy_decimals: 2
    on_value:
      - globals.set:
          id: timeout
          value: !lambda 'return id(harssi_timeout).state * 1000;'
    
binary_sensor:
# erster Sensor  
  - platform: template
    id: binary_sensor_${blueriiot1_id_prefix}_connected
    name: ${blueriiot1_name_prefix} Status
    device_class: connectivity
    entity_category: diagnostic

  - platform: template
    id: room_presence
    name: "$yourname $roomname presence"
    device_class: occupancy
    lambda: |-
      if (id(room_presence_debounce).state > 0.99) {
        return true;
      } else if (id(room_presence_debounce).state < 0.01) {
        return false;
      } else {
        return id(room_presence).state;
      }
      
script:
  # Publish event every 30 seconds when no rssi received
  id: presence_timeout
  mode: restart
  then:
    - delay: !lambda 'return id(timeout);'
    - lambda: |-
        id(room_presence_debounce).publish_state(0);
    - script.execute: presence_timeout

button:
  - platform: template
    id: button_${blueriiot1_id_prefix}_doreading
    name: ${blueriiot1_name_prefix} do reading
    internal: true
    on_press:
      then:
        - ble_client.ble_write:
            id: ble_client_${blueriiot1_id_prefix}
            service_uuid: ${blueriiot_send_service_uuid}
            characteristic_uuid: ${blueriiot_send_characteristic_uuid}
            # A lambda returning an std::vector<uint8_t>.
            value: !lambda |-
              return {0x01};

  - platform: restart
    id: room_restart
    name: "$yourname $roomname restart"

text_sensor:
  # erster Sensor
  - platform: ble_client
    id: ${blueriiot1_id_prefix}_reading_data
    name: ${blueriiot1_name_prefix} reading data
    internal: true
    ble_client_id: ble_client_${blueriiot1_id_prefix}
    service_uuid: ${blueriiot_recieve_service_uuid}
    characteristic_uuid: ${blueriiot_recieve_characteristic_uuid}
    notify: true
    update_interval: never
    on_notify:
      then:
       lambda: |-
          std::string rawhex = format_hex_pretty((uint8_t *) x.c_str(), x.size()).c_str();
          ESP_LOGD("raw_hex", "%s", rawhex.c_str());
 
          float temperature = (float)((int16_t)(x[2]<< 8) + x[1])/100;
          ESP_LOGD("temp", "%f", temperature);
          id(sensor_${blueriiot1_id_prefix}_temperature).publish_state(temperature);
              
          float raw_ph = (float)( (int16_t) (x[4]<< 8) + x[3]) ;
          float ph = (float)( (int16_t) (2048 - raw_ph)) / 232  + 7 ; 
          ESP_LOGD("ph", "%f", ph);
          id(sensor_${blueriiot1_id_prefix}_ph).publish_state(ph);
          
          float orp = (float)( (int16_t) (x[6]<< 8) + x[5]) / 3.86 ;
          ESP_LOGD("orp", "%f", orp);
          id(sensor_${blueriiot1_id_prefix}_orp).publish_state(orp);
          
          float bat = (float)( (int16_t) x[11]) ;
          ESP_LOGD("bat", "%f", bat);
          id(sensor_${blueriiot1_id_prefix}_bat).publish_state(bat);
          
          id(switch_${blueriiot1_id_prefix}_enable).turn_off();
    # ENDE erster Sensor
 

Lukas