Have you made it available on Github?
It is not public (yet).
Hi, you have a Python script to read the log data?
This is the code i am currently using, give it a try. I would be happy to know if it works for you:
"""
Connect to RuuviTag using bluetooth and read sensor log data
# Code stack
* python (version 3.x, tested on python 3.10.7)
* bleak (python bluetooth library)
https://bleak.readthedocs.io/en/latest/usage.html
Tested on Linux (64-Bit) with kernel 5.19.7 and bluez 5.65-3
# Setup
Some python libs (bleak) are required, install them using your package manager
OR
create a virtual environment:
python -m venv env
source env/bin/activate
pip install bleak
# Run
python ./ruuvitag_read_log_data.py
# What is this all about
The RuuviTag is a Wireless Temperature, Humidity, Air Pressure and Motion Sensor.
* https://ruuvi.com/ruuvitag/
It stores measurement data (temperature, humidity and air pressure) to internal storage.
The default log interval is 5 minutes.
The longlife/longmem firmware for the RuuviTag increases the log interval to 15 minutes.
To read the history log from the RuuviTag a bluetooth connection has to be established first.
The RuuviTag exposes the NUS (Nordic UART Service) to read/write data from/to the device.
* https://docs.ruuvi.com/communication/bluetooth-connection/nordic-uart-service-nus
By sending the read log data command the tag sends back the logged data.
* https://docs.ruuvi.com/communication/bluetooth-connection/nordic-uart-service-nus/log-read
The data is _not_ deleted from the RuuviTag when reading the log. It is automatically
overwritten when the storage space is used up (should be 10 days for the default firmware).
"""
## The sensor measurements which can be requested from the RuuviTag (used by DATATYPE_LOG below)
TEMPERATURE = 0x30 # \x30 Temperature [0,01°C]
HUMIDITY = 0x31 # \x31 Humidity [0,01%rH]
AIR_PRESSURE = 0x32 # \x32 Air pressure [1Pa]
ALL_SENSORS = 0x3A # \x3A All of the above
## Nordic UART Service UUIDs, do _not_ change these
UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
UART_RX_CHAR_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
UART_TX_CHAR_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
## Header datatype for advertisement data, do _not_ change
DATATYPE_ADV = 0x05
### SETTINGS AREA BEGIN ###
## Enter the MAC address of your RuuviTag
DEVICE_MAC = "00:00:00:00:00:00"
## Number of seconds to get log data
LOG_SECONDS = 7200 # 7200 = last 2 hours
## Choose the log data to read from the RuuviTag (TEMPERATURE or HUMIDITY or AIR_PRESSURE or ALL_SENSORS)
DATATYPE_LOG = ALL_SENSORS
### SETTINGS AREA END ###
import asyncio
import sys
import calendar
import time
import struct
import bleak
from datetime import datetime, timezone
log_data_end_of_data = False
async def ruuvi_read_log_data():
global log_data_end_of_data
def handle_disconnect(_: bleak.BleakClient):
print("Device was disconnected", flush=True)
# cancelling all tasks effectively ends the program
for task in asyncio.all_tasks():
task.cancel()
# data received from bluetooth client, requested by start_notify()
def handle_rx(sender_handle: int, data: bytearray):
global log_data_end_of_data
if len(data) <= 0:
print(f"no data (size == {len(data)})")
else:
if (data[0] == DATATYPE_LOG):
if len(data) != 11:
print("Header = log data, but wrong data size", flush=True)
else:
# unpack binary data
dat = struct.unpack('>BBBII', data)
if (dat[3] == 0xFFFFFFFF and dat[4] == 0xFFFFFFFF):
print(" Log: end of output received", flush=True)
log_data_end_of_data = True
else:
if (dat[1] == TEMPERATURE):
print(f" Log data: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(dat[3]))} {dat[4]/100.0:.2f}°C", flush=True)
elif (dat[1] == HUMIDITY):
print(f" Log data: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(dat[3]))} {dat[4]/100.0}%rH", flush=True)
elif (dat[1] == AIR_PRESSURE):
print(f" Log data: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(dat[3]))} {dat[4]/100.0}mBar", flush=True)
elif data[0] == DATATYPE_ADV:
if len(data) != 18:
print("Header = advertisement data, but wrong size", flush=True)
else:
dat = struct.unpack('>BhHHhhhHBH', data)
print(f" Advertisement data received: "
f"T={dat[1]/200.0:.2f}°C "
f"H={dat[2]/400.0:.1f}%rH "
f"P={(dat[3] + 50000)/100.0:.2f}mBar "
f"[x={dat[4]:+5d} y={dat[5]:+5d} z={dat[6]:+5d}] "
f"U={((dat[7]/32)+1600)/1000.0:.2f}V "
f"Tx_strength={(dat[7]%32)*2 - 40:+d}dBm "
f"movement_counter={dat[8]:3d} "
f"sequence_counter={dat[9]:5d}", flush=True)
else:
print(f"unknown first byte: {hex(data[0])}")
# This code will run when calling ruuvi_read_log_data()
print(f"Searching for RuuviTag {DEVICE_MAC}", flush=True)
async with bleak.BleakClient(DEVICE_MAC, disconnected_callback=handle_disconnect, timeout=30) as client:
print(f"Connected to Ruuvi, starting notify on UART channel", flush=True)
await client.start_notify(UART_TX_CHAR_UUID, handle_rx)
# Build request data header
data_tx = b''
data_tx += bytes([DATATYPE_LOG])
data_tx += bytes([DATATYPE_LOG])
data_tx += b'\x11'
# timestampts
timenow = int(time.time()) # get current system time
timeprv = timenow - LOG_SECONDS
data_tx += struct.pack('>I', timenow)
data_tx += struct.pack('>I', timeprv)
print(f"Requesting log data for the last {LOG_SECONDS} seconds")
await client.write_gatt_char(UART_RX_CHAR_UUID, data_tx)
# wait for data
while True:
if log_data_end_of_data:
print(f"All log data received, closing connection")
client.close()
break
await asyncio.sleep(1)
if __name__ == "__main__":
try:
asyncio.run(ruuvi_read_log_data())
except asyncio.CancelledError:
# task is cancelled on disconnect, so we ignore this error
pass
except bleak.exc.BleakDBusError as error:
## bleak.exc.BleakDBusError: [org.bluez.Error.NotReady] Resource Not Ready
## bluetoothctl power on
## bleak.exc.BleakDBusError: [org.freedesktop.systemd1.NoSuchUnit] Unit dbus-org.bluez.service not found.
## systemctl start bluetooth.service
print("bleak.exc.BleakDBusError")
print(error)
except bleak.exc.BleakError as error:
## bleak.exc.BleakError: Not connected
print("bleak.exc.BleakError")
print(error)
except:
print("unknown exception")
raise # re-raise exception for the user to debug