This tour demonstrates how to use SlowDash with SQLite as the data backend, which requires no server setup. All files created during this tour are contained within a single project directory and can be completely removed by simply deleting that directory.
First, create and navigate to a new project directory:
$ mkdir QuickTour
$ cd QuickTour
If you’re using Docker, the directory you just created will be
mounted as a volume in the container. You can work either inside the
container (using docker exec ... /bin/bash
) or outside. In
the beginning, we recommend working outside the container.
We’ll use the SlowPy Python library, included with the SlowDash
package, to generate test data. Create a file named
generate-testdata.py
in your project directory with the
following code:
from slowpy.control import ControlSystem, RandomWalkDevice
from slowpy.store import DataStore_SQLite, LongTableFormat
class TestDataFormat(LongTableFormat):
= '(datetime DATETIME, timestamp INTEGER, channel VARCHAR(100), value REAL, PRIMARY KEY(timestamp, channel))'
schema_numeric def insert_numeric_data(self, cur, timestamp, channel, value):
f'INSERT INTO {self.table} VALUES(CURRENT_TIMESTAMP,%d,?,%f)' % (timestamp, value), (channel,))
cur.execute(
= ControlSystem()
ctrl = RandomWalkDevice(n=4)
device = DataStore_SQLite('sqlite:///QuickTourTestData.db', table="testdata", table_format=TestDataFormat())
datastore
def _loop():
for ch in range(4):
= device.read(ch)
data ="ch%02d"%ch)
datastore.append(data, tag1)
ctrl.sleep(
def _finalize():
datastore.close()
if __name__ == '__main__':
ctrl.stop_by_signal()while not ctrl.is_stop_requested():
_loop() _finalize()
Details of the script are described in the Controls section. For now, just copy and paste the script and use it to generate some test data.
If you installed SlowPy in a virtual environment (the standard installation method), activate it using either:
$ slowdash-activate-venv
or (if slowdash-bashrc
hasn’t been sourced):
$ source PATH/TO/SLOWDASH/venv/bin/activate
Running this script will create a SQLite database file and populate it with simulated time-series data every second:
$ python3 generate-testdata.py
After letting it run for about a minute, stop the script using
Ctrl
-c
and examine the created files:
$ ls -l
-rw-r--r-- 1 sanshiro sanshiro 24576 Apr 11 16:52 QuickTourTestData.db -rwxr-xr-x 1 sanshiro sanshiro 3562 Apr 11 16:51 generate-testdata.py
You can inspect the database contents using the SQLite command-line
program, sqlite3
. If this program isn’t available on your
system, you can skip this step and view the data through SlowDash in the
next section.
$ sqlite3 QuickTourTestData.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .table
testdata
sqlite> .schema testdata
CREATE TABLE testdata(datetime DATETIME, timestamp INTEGER, channel VARCHAR(100), value REAL, PRIMARY KEY(timestamp, channel));
sqlite> select * from testdata limit 10;
2023-04-11 23:52:13|1681257133|ch00|0.187859
2023-04-11 23:52:13|1681257133|ch01|-0.418021
2023-04-11 23:52:13|1681257133|ch02|0.482607
2023-04-11 23:52:13|1681257133|ch03|1.733749 ...
As shown above, the schema of the data table is:
testdata(datetime DATETIME, timestamp INTEGER, channel VARCHAR(100), value REAL, PRIMARY KEY(timestamp, channel))
and the table contents are:
datetime (DATETIME/TEXT) | timestamp (INTEGER) | channel (VARCHAR(100)) | value (REAL) |
---|---|---|---|
2023-04-11 23:52:13 | 1681257133 | ch00 | 0.187859 |
2023-04-11 23:52:13 | 1681257133 | ch01 | -0.418021 |
2023-04-11 23:52:13 | 1681257133 | ch02 | 0.482607 |
2023-04-11 23:52:13 | 1681257133 | ch03 | 1.733749 |
… |
(Note: In SQLite, DATETIME is stored as TEXT. Times are in UTC, though not explicitly specified.)
For demonstration purposes, this table includes two timestamp columns: one for (emulated) hardware data time in UNIX timestamp format, and another for database writing time in datetime format. In a real system, you might use just one of these formats.
For information about other supported data table formats, please refer to the Data Binding section.
Each SlowDash project requires a configuration file named
SlowdashProject.yaml
in the project directory. This file
specifies which database to read, which columns contain timestamps and
data values, and other essential settings.
Create SlowdashProject.yaml
with the following
content:
slowdash_project:
name: QuickTour
title: SlowDash Quick Tour
data_source:
url: sqlite:///QuickTourTestData.db
time_series:
schema: testdata [channel] @timestamp(unix) = value
To use the datetime
column for timestamps instead,
modify the schema section as follows:
time_series:
schema: testdata[channel]@datetime(unspecified utc)=value
The timestamp type is specified after the time column name. Common
timestamp types include: - aware
(or
with time zone
): for time data with explicit time zones -
naive
(or without time zone
or
local
): for implied “local” time zone (generally not
recommended) - unspecified utc
: for time data without
explicit time zones but known to be in UTC
(Docker users should first enter the container using
docker exec -it CONTAINER_ID /bin/bash
.)
Test your configuration using the slowdash config
command in the project directory:
$ slowdash config
{
"project": {
"name": "QuickTour",
"title": "SlowDash Quick Tour",
"error_message": ""
},
"data_source": {
"type": "SQLite",
"parameters": {
"file": "QuickTourTestData.db",
"time_series": {
"schema": "testdata[channel]@timestamp(unix)=value"
}
}
},
"style": null,
"contents": {
"slowdash": [],
"slowplot": []
} }
The channels in the data-store can be listed with the
slowdash channels
command:
$ slowdash channels
[
{"name": "ch00"}, {"name": "ch01"}, {"name": "ch02"}, ... ]
The data values can be displayed with the slowdash data
command:
$ slowdash "data/ch00?length=10"
{
"ch00": {
"start": 1680223465, "length": 10,
"t": [0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
"x": [5.180761, 5.92074, 5.515459, 4.883299, 5.650556, 4.284527, 3.884656, 3.223627, 2.06343]
} }
This step starts a SlowDash server on port 18881. To stop the server,
press Ctrl
-c
.
$ slowdash --port=18881
Image from DockerHub
$ docker run --rm -p 18881:18881 -v $(pwd):/project slowproj/slowdash
or locally created image:
$ docker run --rm -p 18881:18881 -v $(pwd):/project slowdash
Create a docker-compose.yaml
file in the project
directory
version: '3'
services:
slowdash:
image: slowproj/slowdash
volumes:
- .:/project
ports:
- "18881:18881"
Then start docker compose
$ docker compose up
Launch a web browser and access
http://localhost:18881
.
$ firefox http://localhost:18881
The browser should show the home page of the project:
In order to continuously fill the data while plotting, run the test-data generator in parallel (maybe in another terminal window):
$ python3 generate-testdata.py
The data file size is roughly 5 MB per hour. The test data file,
QuickTourTestData.db
, can be deleted safely when SlowDash
is not running. Once the file is deleted, run
generate-testdata.py
again before starting SlowDash next
time.
The easiest way to get started is to explore the GUI:
Currently, only time-series plots are available since our test database contains only time-series data.
You can save and share your plot layouts (called SlowPlot Layouts) by clicking the 💾 (save) button in the top-right corner. Saved layouts appear on the SlowDash home page.
Open a saved layout with a specific time range using a URL:
http://localhost:18881/slowplot.html?config=slowplot-QuickTour.json&time=2023-03-30T18:00:00&reload=0
Create a new layout directly through a URL by specifying channels and plot types:
http://localhost:18881/slowplot.html?channel=ch00;ch00/ts-histogram&length=360&reload=60&grid=2x1
SlowDash consists of two parts, a web application (web server + browser UI) and a Python library used in user scripts. In the previous example with dummy data, we used this library, called SlowPy. The application and library work seamlessly together, but each can also operate independently. In this section, we’ll use the SlowPy library alone to read real data from a device, replacing the dummy data generator used in the previous section.
SlowPy is installed automatically along with SlowDash. In the standard installation, it resides inside a virtual environment (venv). Please activate this venv before starting:
$ slowdash-activate-venv
You need to run this command every time you open a new terminal. If
you’re using a dedicated SlowDash machine and no other Python
environments, you can add the following line to your
.bashrc
to avoid doing it manually:
source $SLOWDASH_DIR/venv/bin/activate
Here, we’ll show an example that reads DC voltage from a network-controllable digital multimeter (DMM). Many DMMs share common command sets, and we have confirmed the following models (verified by ChatGPT, August 2025):
Manufacturer | Model |
---|---|
Keysight / Agilent | 34460A DMM |
Tektronix / Keithley | DMM6500 |
Rigol | DM3058 |
BK Precision | 5492B DMM |
Similarly, many DC power supplies use the same command for output voltage readout. You can therefore use the same example code. The following models are confirmed (ChatGPT, August 2025):
Manufacturer | Model |
---|---|
Keysight / Agilent | E363x / E364x |
Tektronix / Keithley | 2230G / 2231A |
Rohde & Schwarz | NGA100 series |
Rigol | DP800 / DP2000 |
BK Precision | 9180 / 9190 |
All of these devices are controllable via Ethernet. Before proceeding, make sure your device is powered on, connected to the network, and that you know its IP address (and port number, to be sure - see the manual).
According to ChatGPT, most DMMs and power supplies share a compatible command set. If you have any network-controllable device that can report voltage, you might be able to use it here.
If you don’t have access to any physical device, SlowDash provides a built-in simulator. You can launch it as follows:
$ slowdash-activate-venv
$ cd PATH/TO/SLOWDASH/utils
$ python ./dummy-scpi.py
listening at 172.26.0.1:5025
line terminator is: x0d type Ctrl-c to stop
This behaves like a real device on your local network (running on
localhost
). Press Ctrl-C
to stop it.
All of the above devices use the SCPI (Standard Commands for Programmable Instruments) text-based protocol. The commands we’ll use here are as follows:
Action | Command | Example Response |
---|---|---|
Get device ID | *IDN? |
Keysight Technologies,34460A... |
Reset settings | *RST |
(no response) |
Read DC voltage | MEAS:VOLT:DC? |
3.24 |
In SlowPy, device control is represented as a logical control tree,
where each node of the tree has set(value)
and/or
get()
. For this SCPI example, the hierarchy looks like:
[Measurement System] → [Ethernet] → [SCPI Control] → [Command Nodes]
A complete SlowPy script to retrieve and print the device ID is:
from slowpy.control import control_system as ctrl
print(ctrl.ethernet('172.26.0.1', 5025).scpi().command('*IDN?').get())
Adjust the IP address and port number as needed. With this two line code, you can verify the connection:
$ slowdash-activate-venv
$ python read-my.py
Keysight Technologies,34460A...
Multiple calls to the connection node (.ethernet()
) may
or may not create a new connection every time, depending on the node
specification and optional parameters. A common practice is to keep the
device-level node in a variable.
Example: continuously read DC voltage once per second after resetting the device.
from slowpy.control import control_system as ctrl
= ctrl.ethernet('172.26.0.1', 5025).scpi()
device
'*RST').set()
device.command(
while True:
= device.command('MEAS:VOLT:DC?').get()
volt print(volt)
1) ctrl.sleep(
(ctrl.sleep()
behaves like time.sleep()
,
but works better with SlowDash’s signal handling.)
SlowPy also provides database-write capabilities. The following code stores the measurements into a local SQLite database instead of printing them.
from slowpy.control import control_system as ctrl
= ctrl.ethernet('172.26.0.1', 5025).scpi()
device
from slowpy.store import DataStore_SQLite
= DataStore_SQLite('sqlite:///TestData.db', table="slowdata")
datastore
'*RST').set()
device.command(
while True:
= device.command('MEAS:VOLT:DC?').get()
volt 'volt': volt})
datastore.append({1) ctrl.sleep(
Running this script instead of the dummy-data generator enables
SlowDash to visualize real measurements. Stop it with
Ctrl-C
or Ctrl-\
. (You might see messy output,
but it’s harmless.)
Any Python script (not necessarily using SlowPy) placed in your
project’s config
directory as slowtask-XXX.py
will automatically appear in the “SlowTask” section of the SlowDash home
screen, where it can be controlled via the web interface.
You can also configure it to auto-start or edit directly from the
browser via entries in SlowdashProject.yaml
(see the
official documentation).
However, the previous script cannot be gracefully stopped yet. To
allow start/stop control from the app, implement the callback functions
defined by SlowDash, such as _loop()
and
_run()
:
from slowpy.control import control_system as ctrl
= ctrl.ethernet('172.26.0.1', 5025).scpi()
device
from slowpy.store import DataStore_SQLite
= DataStore_SQLite('sqlite:///QuickTourTestData.db', table="testdata")
datastore
'*RST').set()
device.command(
def _loop():
= device.command('MEAS:VOLT:DC?').get()
volt 'volt': volt})
datastore.append({1) ctrl.sleep(
Here, while True
is replaced by
def _loop()
. When executed by SlowDash,
_loop()
will be repeatedly called in a managed thread.
See the “Control Script” section of the documentation for additional
callbacks such as _initialize()
and details about threads
and asynchronous execution.
To make the script runnable standalone, add:
if __name__ == '__main__':
while True:
_loop()
Or, for graceful termination with Ctrl-C
:
if __name__ == '__main__':
ctrl.stop_by_signal()while not ctrl.is_stop_requested():
_loop()
A full working example is provided in
ExampleProjects/QuickTour/RealDevice
.
$ cd PATH/TO/SLOWDASH/ExampleProjects/QuickTour/RealDevice
$ slowdash --port=18881
Open your browser at http://localhost:18881 — you’ll find “read-my” under SlowTask, with [start] and [stop] buttons.
You can still run the script without the SlowDash app as before:
$ slowdash-activate-venv
$ python config/slowtask-read-my.py
For power supply devices that need voltage control:
from slowpy.control import control_system as ctrl
= ctrl.ethernet('172.26.0.1', 5025).scpi(append_opc=True)
device
'VOLT').set(3.0) # sends "VOLT 3.0; *OPC?"
device.command('OUTP').set('ON') # sends "OUTP ON; *OPC?" device.command(
SlowPy expects every command to return a response. If the device
doesn’t normally return one, append *OPC?
to make it
respond when complete. You can apply this globally (as above) or
per-command by:
'OUTP ON; *OPC?').set() device.command(
For USB or RS-232 devices, replace the Ethernet part of the control tree. For example, using a VISA interface:
from slowpy.control import control_system as ctrl
'VISA') # Load VISA plugin
ctrl.load_control_module(= ctrl.visa('USB00::0x2A8D::0x201:MY54700218::00::INSTR').scpi()
device
# (rest is the same)
For Ethernet devices using HiSLIP, also use VISA with an address
like: TCPIP0::<IP address>::hislip0
.
task
entry in SlowdashProject.yaml
to auto-launch the script.See the “Project Configuration” and “Controls Script” chapters in the official documentation for details.
The SlowPy library also includes a server-side SCPI interface, allowing any Python program to act as an SCPI-controllable device, making it fully compatible with SlowDash monitoring, control, and data storage.
from slowpy.control import ScpiServer, ScpiAdapter
class MyDevice(ScpiAdapter):
def __init__(self):
super().__init__(idn='MyDevice')
def do_command(self, cmd_path, params):
# cmd_path: list of strings, uppercase SCPI path parts (split by :)
# params: list of strings, uppercase SCPI parameters (split by ,)
if cmd_path[0].startswith('DATA'):
return <data_value>
elif ...:
...return None # Unknown command
= MyDevice()
device = ScpiServer(device, port=5025)
server server.start()
In do_command()
, simply read the command and return a
string value. Return an empty string ""
for commands with
no response, or None
for invalid commands. Standard
commands like *IDN?
and *OPC?
are already
implemented in the base class, and command concatenation
(;
) is automatically handled.
If you add this script to /etc/rc.local
or a similar
startup mechanism, your Raspberry Pi can act as a real SCPI device
accessible over the network. This is convenient not only for using the
attached hardware through GPIB/I2C/SPI, but also for integrating USB
devices (even with a vendor-provided library) as Ethernet-SCPI
devices.