BLE raw frames seems to be common on differents Yieryi devices. https://community.home-assistant.io/t/blue-connect-pool-measurements/118901/170
the app still the same with all the connected device they made.
Hi Jimenbar, I use the NRF52840 USB dongle with the associated WireShark profile as seen on this site : Overview | BLE Sniffer with nRF52840 | Adafruit Learning System on my Windows PC. Nothing else required
I tried to decompress the android app to understand the exchange but, seriously, I’m totally lost in the code…
i also tried to reverse engineer the android app, but it was created with flutter, so the actual app code is inside the libapp.so (resources/libs/…/libapp.so)
This seems to be very hard to decompile though
Hello, I am not from HA, but OpenHAB and I have a lot of Esp32 devices using EspHome to integrate with OH using MQTT. I found this topic searching for the BLE-YC01 device, that I just have bought some days ago (I still no have it physically), but very interested to integrate with OH.
Well, the case is that I ask directly to some of the brands of this device, and send me some information, that I would share with all of you, as could be better to develop between more than one!. I think that the best could be integrate with EspHome or C++ in Esp32 devices, and send the data to MQTT.
I don’t know how to share the information, as can’t attach the excel with the information. Probably better to send to interested people? The protocol doesn’t seems very complicated, but I am newbie with BT communications.
Ruben
PD: I post it in the next posts, is not so much, but probably enough
Bluetooth communication protocol
Basic Information
1 Bluetooth name: BEL-9100 i.e. product name(BLE-9100,BLE-Ph01,BLE-9909,…)
2 Service UUID FF01
3 Feature UUID FF02 read and write
read data
1 After the Bluetooth connection is successful, subscribe to the characteristic UUID FF02,
2 Read the characteristic UUID FF02 to return the data
3 The data format is as follows: (The data is a byte array, a total of 24) (requires decryption with deCode(), please refer to the encrpt data form)
serial number data significance
0 1 data
2 Calibration data
1 fixed at 2
2 0 BLE-None, Product name code
12 BLE-9100,
BLE-9100
3 DO(mg/L)Value high 8 bits
4 DO(mg/L)lower 8 bits of value
5 DO(%)Value high 8 bits
6 DO(%)lower 8 bits of value
7
8
9
10
11
12
13 8-bit high temperature value
14 Temperature value lower 8 bits
15 Battery voltage value high 8 bits
16 Battery voltage value lower 8 bits
17 flag bit 0b 0000 0000 7 6 5 4 3 2 1 0
hold reading Backlight status
18
19
20
21
22
23 Check Digit Please refer to the CheckSum form
data input
Write the characteristic UUID FF02 in the format (write does not require encryption)
byte array Control the backlight
0 0x01
1 0x00
2 check code
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"void enCode(uint8 *pValue,uint8 len){
uint8 tmp=0;
uint8 hibit=0;
uint8 lobit=0;
uint8 hibit1=0;
uint8 lobit1=0;
//uint8 bit=0;
//uint8 ecode[8]={1,3,4,5,2,6,3,4};
for(int i=0;i<len-1;i++)
{
tmp=~pValue[i];
hibit=(tmp&0xAA)>>1;
lobit=(tmp&0x55)<<1;
tmp=~pValue[i+1];
hibit1=(tmp&0xAA)>>1;
lobit1=(tmp&0x55)<<1;
pValue[i]=hibit|lobit1;
pValue[i+1]=hibit1|lobit;
}
}"
"void deCode(uint8 *pValue,uint8 len){
uint8 tmp=0;
uint8 hibit=0;
uint8 lobit=0;
uint8 hibit1=0;
uint8 lobit1=0;
//uint8 bit=0;
//uint8 ecode[8]={1,3,4,5,2,6,3,4};
for(int i=len-1;i>0;i--)
{
tmp=pValue[i];
hibit1=(tmp&0x55)<<1;
lobit1=(tmp&0xAA)>>1;
tmp=pValue[i-1];
hibit=(tmp&0x55)<<1;
lobit=(tmp&0xAA)>>1;
pValue[i]=~(hibit1|lobit);
pValue[i-1]=~(hibit|lobit1);
}
}"
uint8 checksum(const uint8* data, uint8 len)
{
uint8 i = 0;
uint8 chksum = 0;
for(i = 0; i < len; i ++)
chksum = chksum^data[i];
return chksum;
}
Hi @RubenKona ,
Suggestion. In order to share the excel you could probalby add an hyperlink in here to your public are of any public cloud you use where you could upload excel and from there any interested folk could download it.
I honestly do not have time (unfortunately) to dig on this and i am not a BLE expert although i am very interested, but i believe that with the above info plus ESPHome BLE functionality plus the lambda funtionality it could be done.
Is not necessary finally to post the Excel, as the above posts with code has all the same information that has the Excel.
I am also not expert in BLE, and with EspHOME lambda, and also don’t have yet the device, but I try to do it when my unit arrive. In any case, maybe some of the above, could dig a little more and find useful this information
Ruben
Hi @RubenKona
Was you able to do some tests ?
I have a similar sensor, the BLE-C600 with more inputs (EC / TDS / SALT / PH / ORP and temp).
I did convert your c++ code to python one and for now i’m only able to find the ORP value on the data received.
Anas
Hi @anasm2010 , can you share your python code? Maybe I can test it with my two floating devices.
Thanks
Here is a script example that can decode the full packet.
For now i’m locking for a solution to disable the auto-turn off function … Any idea?
import asyncio
from bleak import BleakClient
import time
from decodeInt import Messure
address = "C0:00:00:00:8A:D1"
# MODEL_NBR_UUID = "00002a24-0000-1000-8000-00805f9b34fb"
MODEL_NBR_UUID = "0000180D-0000-1000-8000-00805F9B34FB"
Battery_Service = "00002a19-0000-1000-8000-00805f9b34fb"
Vendor_specific = "0000ff10-0000-1000-8000-00805f9b34fb"
"""
00001800-0000-1000-8000-00805f9b34fb (Handle: 1): Generic Access Profile
00001801-0000-1000-8000-00805f9b34fb (Handle: 8): Generic Attribute Profile
0000180a-0000-1000-8000-00805f9b34fb (Handle: 12): Device Information
0000180f-0000-1000-8000-00805f9b34fb (Handle: 31): Battery Service
0000ff01-0000-1000-8000-00805f9b34fb (Handle: 36): Vendor specific
"""
class Messure(object):
def __init__(self, frame : str) -> None:
self.frame = frame
self.packet = self.decode(frame)
self.data = self.packet[0]
self.constant = self.packet[1]
self.product_name_code = self.packet[2]
self.hold_reading = self.packet[17] >> 4
self.backlight_status = (self.packet[17] & 0x0F) >> 3
self.battery = self.decode_position(15)
self.ec = self.decode_position(5)
self.tds = self.decode_position(7)
self.salt_tds = self.decode_position(9)
self.salt_sg = self.decode_position(11) ##TO DO
self.ph = self.decode_position(3)/100
self.orp = self.decode_position(20)
self.temperature = self.decode_position(13)/10
def decode(self, byte_frame : bytes ):
frame_array = [int(x) for x in byte_frame]
size = len(frame_array)
for i in range(size-1, 0 , -1):
tmp=frame_array[i]
hibit1=(tmp&0x55)<<1
lobit1=(tmp&0xAA)>>1
tmp=frame_array[i-1]
hibit=(tmp&0x55)<<1
lobit=(tmp&0xAA)>>1
frame_array[i]=0xff -(hibit1|lobit)
frame_array[i-1]= 0xff -(hibit|lobit1)
return frame_array
def reverse_bytes(self, bytes : list):
return (bytes[0] << 8) + bytes[1]
def decode_position(self,idx):
return self.reverse_bytes(self.packet[idx:idx+2])
def show_values(self):
return_string = f"h=[{self.hold_reading}/bl={self.backlight_status}/B={self.battery}] EC={self.ec:4} TDS={self.tds:4} SALT(TDS)={self.salt_tds:4} SALT(S.G.)={self.salt_sg:4} pH={self.ph:4} ORP(mV)={self.orp:4} Temperature(C)={self.temperature:4} "
return return_string
async def main(address):
async with BleakClient(address) as client:
print(f"Connected: {client.is_connected}")
paired = await client.pair(protection_level=2)
print(f"Paired: {paired}")
old_time = time.time()
print(time.time() - old_time)
while True:
model_number = await client.read_gatt_char(Vendor_specific)
print(Messure(model_number).show_values())
time.sleep(1)
asyncio.run(main(address))
Try to check the saved data buttom and assign an interval, it may keep it alive.
Yes thank you, it seems to work on the app. However, once I switch to the python script it goes off again after 5min. I assume the app is sending a request that force it to stay on … I need to find what.
Hi,
Here is a functional code for an ESP32 with Esphome associated with Home Assistant. Every 30 minutes, the BLE device is polled for new datas.
time:
- platform: homeassistant
id: homeassistant_time
on_time:
# Turn on BLE client every 30 minutes for 2 minutes
- seconds: 0
minutes: /30
then:
- switch.turn_on: ble_switch
- delay: 2min
- switch.turn_off: ble_switch
esp32_ble_tracker: #### Stop the active scan ####
scan_parameters:
active: false
######################################################
## ##
## To initiate to connection with the BLE device ##
## ##
######################################################
ble_client:
- mac_address: YO:UR:_A:DD:RE:SS #Use the MAC address of your BLE device
id: ble_yc01
on_connect: #### Actions to perform when connecting to the BLE device ####
then:
#### Wait until characteristic is discovered ####
- wait_until:
lambda: |-
esphome::ble_client::BLEClient* client = id(ble_yc01);
auto service_uuid = 0xFF01;
auto char_uuid = 0xFF02;
//#### When waiting for connection, we extract the available characteristics ####
esphome::ble_client::BLECharacteristic* chr = client->get_characteristic(service_uuid, char_uuid);
return chr != nullptr;
#### Official connection to the BLE device with the desired characteristic ####
- lambda: |-
ESP_LOGD("ble_client_lambda", "Connected to BLE-YC01");
esphome::ble_client::BLEClient* client = id(ble_yc01);
auto service_uuid = 0xFF01;
auto char_uuid = 0xFF02;
esphome::ble_client::BLECharacteristic* chr = client->get_characteristic(service_uuid, char_uuid);
if (chr == nullptr) {
ESP_LOGW("ble_client", "[0xFF01] Characteristic not found. State update can not be written.");
}
on_disconnect:
then:
- lambda: |-
ESP_LOGD("ble_client", "Disconnected from BLE-YC01");
######################################################
## ##
## Sensors associated with the BLE device ##
## ##
######################################################
sensor: #### Template sensor as their values are publish from a lambda or the BLE client ####
- platform: template
name: "BLE-YC01 EC"
id: ble_yc01_ec_sensor
unit_of_measurement: "µS/cm"
accuracy_decimals: 0
state_class: measurement
icon: mdi:water-opacity
- platform: template
name: "BLE-YC01 TDS"
id: ble_yc01_tds_sensor
unit_of_measurement: "ppm"
accuracy_decimals: 0
state_class: measurement
icon: mdi:water-opacity
- platform: template
name: "BLE-YC01 Temperature"
id: ble_yc01_temperature_sensor
unit_of_measurement: "F"
accuracy_decimals: 1
state_class: measurement
device_class: temperature
- platform: template
name: "BLE-YC01 ORP"
id: ble_yc01_orp_sensor
unit_of_measurement: "mV"
accuracy_decimals: 0
state_class: measurement
device_class: voltage
- platform: template
name: "BLE-YC01 pH"
id: ble_yc01_ph_sensor
unit_of_measurement: "pH"
accuracy_decimals: 2
state_class: measurement
icon: mdi:ph
- platform: template
name: "BLE-YC01 batterie"
id: ble_yc01_battery
unit_of_measurement: "%"
accuracy_decimals: 0
state_class: measurement
device_class: battery
icon: mdi:battery
- platform: ble_client #### Sensor required to manage values coming from the BLE device ####
ble_client_id: ble_yc01
id: ble_yc01_sensor
internal: true
service_uuid: FF01
characteristic_uuid: FF02
notify: true
#### Lambda to decode values and push to the associated sensors ####
lambda: |-
if (x.size() == 0) return NAN;
//ESP_LOGD("ble_client.receive", "value received with %d bytes: [%.*s]", x.size(), x.size(), &x[0]); // #### Useful for debugging ####
// ### DECODING ###
uint8_t tmp = 0;
uint8_t hibit = 0;
uint8_t lobit = 0;
uint8_t hibit1 = 0;
uint8_t lobit1 = 0;
auto message = x;
for (int i = x.size() -1 ; i > 0; i--) {
tmp=message[i];
hibit1=(tmp&0x55)<<1;
lobit1=(tmp&0xAA)>>1;
tmp=message[i-1];
hibit=(tmp&0x55)<<1;
lobit=(tmp&0xAA)>>1;
message[i]=~(hibit1|lobit);
message[i-1]=~(hibit|lobit1);
}
//ESP_LOGD("ble_client.receive", "value received with %d bytes: [%.*s]", message.size(), message.size(), &message[0]); // #### For debug ####
// #### Extraction of individual values ####
auto temp = ((message[13]<<8) + message[14]);
auto ph = ((message[3]<<8) + message[4]);
auto orp = ((message[20]<<8) + message[21]);
auto battery = ((message[15]<<8) + message[16]);
auto ec = ((message[5]<<8) + message[6]);
auto tds = ((message[7]<<8) + message[8]);
// #### Sensors updated with new values
id(ble_yc01_temperature_sensor).publish_state(((temp/10.0) * (9.0/5.0)) + 32.0);
id(ble_yc01_ph_sensor).publish_state(ph/100.0);
id(ble_yc01_orp_sensor).publish_state(orp);
id(ble_yc01_battery).publish_state(battery/45);
id(ble_yc01_ec_sensor).publish_state(ec);
id(ble_yc01_tds_sensor).publish_state(tds);
return 0.0; // this sensor isn't actually used other than to hook into raw value and publish to template sensors
switch: #### To switch on and off the communication with the BLE device ####
- platform: ble_client
id: ble_switch
ble_client_id: ble_yc01
name: "Enable BLE-YC01"
Here is a way to calculate free chlorine in HA. For now, I can’t figure out how to integrate it directly in Esphome.
The formula comes from raspipool github: https://github.com/segalion/raspipool
sensors:
- platform: template
sensors:
chlore_libre_spa:
value_template: "{{ ( 0.23 * (1 + 0 / 100 ) * ( 14 - states('sensor.ble_yc01_ph')|float(0.0)) ** ( 1 / (400 - states('sensor.ble_yc01_orp')|float(0.0)) ) * ( states('sensor.ble_yc01_ph')|float(0.0) -4.1) ** ( ( states('sensor.ble_yc01_orp')|float(0.0) - 516)/145) + 10.0 ** ( (states('sensor.ble_yc01_orp')|float(0.0) + states('sensor.ble_yc01_ph')|float(0.0) * 70 -1282) / 40 ) ) |round(1) }}"
unit_of_measurement: ppm
icon_template: mdi:react
friendly_name: "Chlore libre estimé spa"
Be careful to switch off the comm with the BLE device before uploading in the ESP32. The BLE device seems to freeze sometimes if the connection isn’t properly disconnected. You will have to remove the batteries to restart the device… A little bit annoying.
Great!!!
I’m looking for a orp and ph sensor for my swimming pool, I’m automating everything. do you recomend it? It looks so good but in amazon there are terrible descriptions.
Please let me know.
Thanks
I would personally take a look to the Blueriiot products. I bought the BLE-YC01 just for testing given its price and can see massive differences in the measures even enough I made the calibration (which I am pending of repeating just in case). The Blueriiiot is perfectly integrated with HA too although it is far more expensive. Take a look to the integration and forum and make your own decision. I have been running the Blueriiot for +2y and my pool have never been better. I have a full automation of the pumps for filtering and chemical’s (bleach, etc).
Thanks, I’ve built an automated system with injectors of ph and chrorine,pumps with vvd and also ph sensor but with orp sensor I have some problems and after checking 2 orp electrodes nothing works, i used drobot kit but after several hours working, values get crazy.
This is why i’m looking for something different without invest too much money.
I’ve taked a look blueriot devices but they are quite expensive and the probe need to be changed every 2 years with more than 100€ for this part.
If the values of ble-yc01 are correct and it works at least 1 year it deserves it.
Thanks