Quick Start, Set up a LabRat and Logging Inator#
This is a guide to get start logging with LabRat and a Inator logging device. Currently LabRat is at an early stage of development with lots likely to change.
Requirements
A computer with Python installed
A microprocessor (we are using a Pico W which has built in Wifi)
A DHT11 temperature and humidity sensor (we used the version from Seeed Studios)
The first step is to set up the Inator to log data from. We will make a DHT_Inator in this example where we connect a DHT11 Temperature and Humidity Sensor to our Pico. But everything here can be applied to any sensor you would like to use.
Inator set up#
To set up the pico as a logger follow the instructions in the tutorial to install the CircuitPython firmware
The next step is to connect the DHT sensor.
Make the following connections:
DHT11 Pin |
Pico Pin |
|---|---|
GND |
GND |
VCC |
3.3V out |
NC |
Not connected |
SIG |
GP0 |
Next we will set some test code on the Pico to connect to the DHT11.
A library is required, it is not included in the core CircuitPython package you uploaded to the Pico but can be very easily added using the instructions here.
import time
import board
import adafruit_dht
dht = adafruit_dht.DHT11(board.GP0)
while True:
try:
temp = dht.temperature
humidity = dht.humidity
print(f"Temperature: {temp} C \t Humidity: {humidity}%")
except RuntimeError as e:
print("Reading from DHT failure: ", e.args)
time.sleep(5)
This should print the temperature and humidity recorded by the DHT11 to your serial connection. For more info on how to connect the Pico to serial and upload code the instructions are here.
LabRat set up#
THe next step is to install the LabRat control and logging software. The set up can be done in two ways. First Go to the repo[inator-project/LabRat] and download the repo contents, either use Git or just download the zip. Or get it from (PyPI) [https://pypi.org/project/labrat_project/], though this is not as up to date as the repo.
The first step is to install the necessary Python libraries. Files for installation from pip or conda (or mamba) are in the installation folder
conda env create -f environment.yml
pip install -r /path/to/requirements.txt
If you now run
python labrat_log.py -h
You should get something like the following
Readying Logging at:2026-02-21T16:17:47+00:00
usage: labrat_log.py [-h] [-sql SQL] [-secrets SECRETS] [-setupsql] [--createdb] [--dev_file DEVFILE] [-file FILE]
[--setfile] [-ser_port SER_PORT] [-baud BAUD]
CONN
Purpose: Connect sensors and log to DB
positional arguments:
CONN Which connection will be monitored. Can be mqtt for a MQTT connectionserial for a wired Serial
Connection or flask for a Flaks based API
options:
-h, --help show this help message and exit
-sql SQL The SQL database path. Should be absolute
-secrets SECRETS The path to the toml fil with secrets, such as log ins are kept
-setupsql If added this flag will run the create Inator DB function.This will then require extra flags
such as the device info file location
--createdb If setup flag added this will create a new DB if the given db doesn't exist
--dev_file DEVFILE If the setup flag is added this will load the device informationThis is the path to the JSON
file that contains the deviceinformation to be loaded into the SQL
-file FILE If set this is the file path to a text file where data can be logged.Should be an absolute path
--setfile If set this will run a set up file function, for headers etc...
-ser_port SER_PORT The COM port to connect to for serial
-baud BAUD The Baud rate for the serial connection
Connecting it together#
Now we will combine the logging Pico with the LabRat programme to save data from a sensor into a sqlite database. There are several ways to make this connection. Choose which ever you prefer/need.
If we are connecting over serial we will use the following code on the pico
import os
import microcontroller
import time
import json
import board
import adafruit_dht
dht = adafruit_dht.DHT11(board.GP0)
#Retrieve variables from settings.toml
inatorname = os.getenv("INATORNAME")
accTime = os.getenv('ACQUIRETIME') #how often to acquire and transmit data
while True:
temp = dht.temperature
humidity = dht.humidity
#Create dictionary
json_dict = {"inator":inatorname,"temperature":temp,"humidity":humidity} # Send data with JSON syntax
json_data = json.dumps(json_dict)
print(f"{json_data}")
time.sleep(accTime)
Where we would have the following settings.toml file saved on the pico. We shall call the Inator teminator_1 and acquire for 30 seconds
INATORNAME = "teminator_1" # Name of the device, will be used when sending data (put name between the quote marks)
ACQUIRETIME = 30 # The value (in seconds) of how often to acquire data and log for
To add the inator to the database we need a device_info.json file which can be saved onto the device
{
"devices": [
{
"device_name": "teminator_1",
"device_guid": "",
"num_sensors": 2,
"device_info": "Has DHT11 sensor",
"device_type": "pico",
"device_location": "TBD",
"device_active": 1,
"connection": "Serial",
"sensors": [
{
"sens_name": "temperature",
"measures": "Temperature",
"returns": "Celsius",
"calib": "1:1",
"range": "0-50",
"info": "Seeed DHT11 on GP0",
"comments": "None"
},
{
"sens_name": "humidity",
"measures": "Humidity",
"returns": "%",
"calib": "1:1",
"range": "0-100",
"info": "Seeed DHT11 on GP0",
"comments": "None"
}
]
}
]
}
The most important thing here is that the device_name and sens_name must match the names sent from the pico, otherwise the logging will fail It is also necessary to have A GUID set for the device. To generate this run
python -c "import uuid
print(uuid.uuid4())"
The next step is to find out which COM port the Pico is on.
On Windows go to the device manager and look under ports to see which COM port is connected
On linux
ls /dev/tty*
Will display a list of all tty devices. The pico will probably be either ttyAM0 or ttyAM1 if in doubt plug and unplug it. If you get permission errors run
sudo adduser <red-user> dialout
We can now (finally) set up the LabRat,
If we are connecting over MQTT we will use the following code on the pico
import os
import time
import json
import supervisor
import microcontroller
import adafruit_connection_manager
import wifi
import adafruit_requests
import adafruit_minimqtt.adafruit_minimqtt as MQTT
import adafruit_logging as logging
import adafruit_dht
#set up sensor
dht = adafruit_dht.DHT11(board.GP0)
#Function for Wifi Connection
def conn_wifi(wifi_ssid, wifi_password):
print(f"\nConnecting to {wifi_ssid} network...")
try:
wifi.radio.connect(wifi_ssid, wifi_password)
wifi_con = True
except OSError as e:
print(f"❌ OSError: {e}")
wifi_con = False
return wifi_con
# Retrieve variables from settings.toml
inatorname = os.getenv("INATORNAME")
ssid = os.getenv("WIFI_SSID")
password = os.getenv("WIFI_PASSWORD")
# MQTT Topic to publish data from Pico to MQTT Cloud Broker
mqtt_topic = os.getenv("INATORTOPIC")
accTime = os.getenv(
"ACQUIRETIME"
) # how often to acquire and transmit data - should be number in settings
recTime = os.getenv(
"RECONTIME"
) # how oftern to reconnect in seconds - should be number in settings
wifi_con = False
# Initalize Wifi, Socket Pool, Request Session
pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)
requests = adafruit_requests.Session(pool, ssl_context)
# Connect to the Wi-Fi network
wifi_con = conn_wifi(ssid, password)
if wifi_con:
print("✅ Wifi!\n")
print("IP-Adress is", wifi.radio.ipv4_address)
# MQTT Topic to publish data from Pico to MQTT Cloud broker
mqtt_topic = f"{mqtt_topic}/{inatorname}"
# Define callback methods which are called when events occur
def connect(mqtt_client, userdata, flags, rc):
# This function will be called when the mqtt_client is connected
# successfully to the broker.
print("Connected to MQTT Broker!")
print(f"Flags: {flags}\n RC: {rc}")
def disconnect(mqtt_client, userdata, rc):
# This method is called when the mqtt_client disconnects
# from the broker.
print("Disconnected from MQTT Broker!")
def subscribe(mqtt_client, userdata, topic, granted_qos):
# This method is called when the mqtt_client subscribes to a new feed.
print(f"Subscribed to {topic} with QOS level {granted_qos}")
def unsubscribe(mqtt_client, userdata, topic, pid):
# This method is called when the mqtt_client unsubscribes from a feed.
print(f"Unsubscribed from {topic} with PID {pid}")
def publish(mqtt_client, userdata, topic, pid):
# This method is called when the mqtt_client publishes data to a feed.
print(f"Published to {topic} with PID {pid}")
def message(client, topic, message):
print(f"New message on topic {topic}: {message}")
# Set up the MQTT client
mqtt_client = MQTT.MQTT(
broker=os.getenv("BROKER"),
port=os.getenv("PORT"),
username=os.getenv("MQTT_USERNAME"),
password=os.getenv("MQTT_KEY"),
socket_pool=pool,
is_ssl=True,
keep_alive=3600,
ssl_context=ssl_context,
)
# Set up logging if having connection difficulties
# mqtt_client.logger = logging.getLogger('test')
# mqtt_client.logger.setLevel(logging.DEBUG)
# Connect callback handlers to mqtt_client
mqtt_client.on_connect = connect
mqtt_client.on_disconnect = disconnect
mqtt_client.on_subscribe = subscribe
mqtt_client.on_unsubscribe = unsubscribe
mqtt_client.on_publish = publish
mqtt_client.on_message = message
print(f"Attempting to connect to {mqtt_client.broker}:{mqtt_client.port}")
mqtt_client.connect()
while True:
if wifi.radio.connected:
try:
temp = dht.temperature
humidity = dht.humidity
# Create dictionary
json_dict = {
"inator": inatorname,
"temperature": temp,
"humidity":humidity,
} # Send data with JSON syntax
json_data = json.dumps(json_dict)
print(f" | ✅ Sending JSON ('key':'value'): {json_data}")
mqtt_client.publish(mqtt_topic, str(json_data))
time.sleep(accTime)
except (ValueError, RuntimeError) as e:
# this is a broad error, would be better to catch specifics
# maybe RuntimeError
print(f"Error {e}")
# Attemp reconnect (for example if e is EBDAF as MQTT connection is down)
mqtt_client.reconnect()
except Exception as e:
print(f"Error {e}")
supervisor.reload()
else:
print(f"Wifi Disconnected - will attempt to reconnect every {recTime} mins")
wifi_con = conn_wifi(ssid, password)
if not wifi_con:
# wait recTime until reattempt connection
time.sleep(recTime)
else:
# Attemp reconnect (for example if e is EBDAF as MQTT connection is down)
mqtt_client.reconnect()
else:
print("❌ Wifi!\n")
print("Connection attempt failed")
while True:
time.sleep(accTime)
Where we would have the following settings.toml file saved on the pico. We shall call the Inator teminator_1 and acquire for 30 seconds. The topic we will publish to is going to be /data/teminator_1 which is contsructed in the code from INATORTOPIC and the INATORNAME
INATORNAME = "teminator_1" # Name of the device, will be used when sending data (put name between the quote marks)
INATORTOPIC = "/data" # The topic to subscribe to on the MQTT broker
ACQUIRETIME = 30 # The value (in seconds) of how often to acquire data and log for
RECONTIME = 3600 # The value (in seconds) of how often to attempt to reconnect to the wifi network if lost
WIFI_SSID = "" # Fill in with your WiFi network's name
WIFI_PASSWORD = "" # Fill in with your WiFi network's password
PORT = # Fill in the port you are connecting to the MQTT Broker
MQTT_USERNAME = "" #The user name on the MQTT broker that can publish
MQTT_KEY = "" #The password for the MQTT User
BROKER = "" #The address for the MQTT broker
To add the inator to the database we need a device_info.json file which can be saved onto the device
{
"devices": [
{
"device_name": "teminator_1",
"device_guid": "",
"num_sensors": 2,
"device_info": "Has DHT11 sensor",
"device_type": "pico",
"device_location": "TBD",
"device_active": 1,
"connection": "MQTT",
"sensors": [
{
"sens_name": "temperature",
"measures": "Temperature",
"returns": "Celsius",
"calib": "1:1",
"range": "0-50",
"info": "Seeed DHT11 on GP0",
"comments": "None"
},
{
"sens_name": "humidity",
"measures": "Humidity",
"returns": "%",
"calib": "1:1",
"range": "0-100",
"info": "Seeed DHT11 on GP0",
"comments": "None"
}
]
}
]
}
The most important thing here is that the device_name and sens_name must match the names sent from the pico, otherwise the logging will fail It is also necessary to have A GUID set for the device. To generate this run
python -c "import uuid
print(uuid.uuid4())"
Once installed create a secrets.toml file that contains this line:
"FLASK_KEY" = ""
You need to fill in a security key between the quote marks. This can be anything for the first quick run. But long term it is a good idea to generate a secure key
python3 -c "import secrets; print(secrets.token_hex(24))"
Now run the quick start guide
python labrat_log.py -secrets "mypath/secrets.toml" quick_start
Then go to the local IP address given in the terminal (normally http://127.0.0.1:5000) and then add /setup at the end (so http://127.0.0.1:5000/setup) and fill in the form.
Starting with creating the local logging database. Then add the device so it matches this.
{
"devices": [
{
"device_name": "teminator_1",
"device_guid": "",
"num_sensors": 2,
"device_info": "Has DHT11 sensor",
"device_type": "pico",
"device_location": "TBD",
"device_active": 1,
"connection": "Serial",
"sensors": [
{
"sens_name": "temperature",
"measures": "Temperature",
"returns": "Celsius",
"calib": "1:1",
"range": "0-50",
"info": "Seeed DHT11 on GP0",
"comments": "None"
},
{
"sens_name": "humidity",
"measures": "Humidity",
"returns": "%",
"calib": "1:1",
"range": "0-100",
"info": "Seeed DHT11 on GP0",
"comments": "None"
}
]
}
]
}
Warning
The quick start wizard will generate a GUID for you automatically. In which case you need to edit the device_json on the Pico after setting this up
From the command line run the following
python labrat_log.py -sql ./datalog.sql -setupsql --createdb --dev_file ./device_info.json
This should start the programme, create the sqlite database, and add tables for the teminator_1 device.
Just make sure the device_info.json file is correct
When you need to run it again the command can be simplifed to:
python labrat_log.py -sql ./datalog.sql -ser_port <PORT> -baud 11520 serial
as the database exists and has the correct tables.
We are now set up for logging
python labrat_log.py -sql ./datalog.sql -secrets path/to/secrets.toml mqtt
as the database exists and has the correct tables.