Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

naneos-devices

GitHub Issues GitHub Pull Requests Ruff License

Projektlogo

Python package for the naneos particle solutions measurement devices (Partector 1, Partector 2, Partector 2 Pro). It connects to the devices over USB and Bluetooth Low Energy, delivers the measurements as pandas DataFrames, and can upload them to the naneos IoT service.

Installation

Python 3.11 to 3.14 is supported.

pip install naneos-devices

The device manager

NaneosDeviceManager is all most applications need. It runs as a background thread, finds and connects every Partector on USB and Bluetooth, gathers their data in snapshots and, if you want, uploads them to the naneos IoT service.

  • 🔌 USB and BLE, each can be switched on and off, also while running
  • 🎯 Optional BLE allow-list (ble_serial_numbers) and link limit (ble_max_links, default 7)
  • ⏱️ Gathering interval of 10 to 600 s
  • 📤 Optional upload to the naneos IoT service (always at 1 Hz)
  • 📦 Snapshots as dict[int, pandas.DataFrame] on a queue, for your own processing
  • ⚡ Live data: every data point on a queue the moment it arrives
  • 💬 Send commands to a device, read its answers and set its data rate, the same way on USB and BLE

Quick start: upload everything in reach

Example: examples/quick_start.py

import time

from naneos import NaneosDeviceManager, enable_console_logging
from naneos.logger import LEVEL_INFO

enable_console_logging(LEVEL_INFO)  # the library is silent by default

manager = NaneosDeviceManager(
    use_serial=True,
    use_ble=True,
    upload_active=True,
    gathering_interval_seconds=30,  # clamped to [10, 600]
    ble_serial_numbers=None,  # or e.g. [8617, 8764] to link only to your own devices
)
manager.start()

try:
    while True:
        time.sleep(manager.seconds_until_next_snapshot + 1)
        for device in manager.get_devices():
            print(device)  # e.g. <Partector2 SN8617 P2 serial>
except KeyboardInterrupt:
    pass

manager.stop()
manager.join()

Process the data yourself

Example: examples/queue_handoff.py

Register a queue and every snapshot is put on it, with or without the upload:

import queue
import time

from naneos import NaneosDeviceManager

snapshots: queue.Queue = queue.Queue()

manager = NaneosDeviceManager(upload_active=False, gathering_interval_seconds=15)
manager.register_output_queue(snapshots)
manager.start()

try:
    while True:
        time.sleep(manager.seconds_until_next_snapshot + 1)
        while not snapshots.empty():
            snapshot = snapshots.get()  # dict[int, pandas.DataFrame], keyed by serial number
            for serial_number, df in snapshot.items():
                print(f"SN{serial_number}: {len(df)} rows, mean LDSA {df['ldsa'].mean():.1f}")
except KeyboardInterrupt:
    pass

manager.stop()
manager.join()

The frames are indexed by the unix timestamp in milliseconds; the columns are the fields of NaneosDeviceDataPoint (ldsa, particle_number_concentration, average_particle_diameter, device_status, ...).

Live data

Example: examples/live_data.py

Snapshots arrive every 10 s at best. For a live view, register a live queue: it receives every data point the moment it arrives, as a NaneosDeviceDataPoint, next to the snapshots and the upload.

import queue

from naneos import NaneosDeviceManager

live: queue.Queue = queue.Queue(maxsize=10_000)  # bounded: a full queue drops its oldest point

manager = NaneosDeviceManager(upload_active=False)
manager.register_live_queue(live)
manager.start()

while True:
    point = live.get()
    print(point.serial_number, point.connection_type, point.unix_timestamp, point.ldsa)

examples/live_plot.py uses this to plot the diffusion current of a device on USB.

The points come at the rate of the device (1 Hz, or what you set over USB). A device that is connected over USB and BLE delivers its USB points only.

Change it while it runs

Example: examples/runtime_controls.py

manager.use_serial = False  # USB devices off / on
manager.use_ble = True  # BLE devices off / on
manager.upload_active = False  # keep gathering, stop uploading
manager.gathering_interval_seconds = 45  # 10 to 600 s

print(manager.seconds_until_next_snapshot, manager.pending_upload_count)

Talk to a device: commands and data rate

Example: examples/device_commands.py

Every connected device is available as a handle with the same API over USB and BLE. A device that is reachable both ways is handed out with its USB connection.

from naneos import NotSupportedError

for device in manager.get_devices():
    print(device.serial_number, device.device_type, device.connection_type)

    print(device.query("f?"))  # a command with an answer -> ["422"]
    device.write("A0002!")  # a command without an answer

    try:
        device.set_sample_rate(10)  # 0 (off), 1, 10 or 100 Hz, None for the device default
    except NotSupportedError:
        pass  # over BLE the rate is fixed at 1 Hz, it can only be changed over USB

# or address a device by its serial number
manager.query(8617, "name?")
manager.write(8617, "A0002!")
manager.set_sample_rate(8617, 100)

# or set the rate of every USB device, now and for the ones plugged in later
manager.sample_rate_hz = 10  # also NaneosDeviceManager(sample_rate_hz=10)
  • An unknown serial number raises KeyError, a lost device ConnectionError, a missing answer TimeoutError. Calls are safe from any thread.
  • Your queue receives the data at the rate you set. The upload to naneos is always limited to 1 Hz. 100 Hz is meant for tests.
  • A rate set on one device is not remembered: a device that reconnects gets manager.sample_rate_hz.
  • A Partector 2 Pro on USB starts in size distribution mode, where it sets its own pace (sample_rate_hz is None); a rate switches it to the plain P2 line. See the documentation for the details.

Logging

The library logs to loggers below naneos and prints nothing by default:

from naneos.logger import LEVEL_INFO, enable_console_logging, enable_file_logging

enable_console_logging(LEVEL_INFO)  # coloured output on stderr
enable_file_logging("logs/", LEVEL_INFO)  # appends to logs/naneos-devices.log

More examples

Example What it shows
quick_start.py upload everything in reach
queue_handoff.py process the snapshots yourself
live_data.py every data point the moment it arrives
live_multi_device.py several USB devices at 10 Hz on the live queue
live_plot.py live plot of the diffusion current of a device on USB (needs pip install matplotlib)
runtime_controls.py switch transports, upload and interval while running
device_commands.py commands, answers and the data rate
send_commands.py send a file of commands to one device
serial_device.py one USB device without the manager
download_iotweb.py read your data back from the naneos IoT service (needs pip install "naneos-devices[download]")

Documentation

The documentation covers the rest:

License

This repository is licensed under the MIT License.

Contact

Release files for naneos-devices 2.0.4rc3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for naneos-devices 2.0.4rc3
File Size Uploaded
naneos_devices-2.0.4rc3.tar.gz 248.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for naneos-devices 2.0.4rc3
File Interpreter ABI Platform
naneos_devices-2.0.4rc3-py3-none-any.whl Python 3 none any Details

Total release size: 325.9 kB

Release files / naneos_devices-2.0.4rc3.tar.gz

Download URL naneos_devices-2.0.4rc3.tar.gz
Size 248.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1d585d551a011b02cd12ff1b31dd91c15ff6385a19f10e8d00f4a35c64af043a
BLAKE2b-256 checksum
How to use checksums
4a8908119c2e82d41405301b940cb495d50b3ce08d070c658a16f843ecada4f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / naneos_devices-2.0.4rc3-py3-none-any.whl

Download URL naneos_devices-2.0.4rc3-py3-none-any.whl
Size 77.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3b3983de0a680223f4466ccbecbc8d88f6f7840de9096a074d37028f05f60a0a
BLAKE2b-256 checksum
How to use checksums
b85f31d0fba1d2375f1cffa2a65f9ca07faed8acb537d2f5482c04f15603958c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

2.0.11

2 release files

2.0.10

2 release files

2.0.9

2 release files

2.0.8

2 release files

2.0.7

2 release files

2.0.6

2 release files

2.0.5

2 release files

2.0.4

2 release files

This release

2.0.4rc3 This release

2 release files

2.0.3

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.2.0

2 release files

1.1.22

2 release files

1.1.21

2 release files

1.1.20

2 release files

1.1.19

2 release files

1.1.18

2 release files

1.1.17

2 release files

1.1.12

2 release files

1.1.11

2 release files

1.1.10

2 release files

1.1.9

2 release files

1.1.8

2 release files

1.1.7

2 release files

1.1.6

2 release files

1.1.5

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.61

2 release files

1.0.60

2 release files

1.0.59

2 release files

1.0.57

2 release files

1.0.56

2 release files

1.0.55

2 release files

1.0.54

2 release files

1.0.53

2 release files

1.0.51

2 release files

1.0.50

2 release files

1.0.49

2 release files

1.0.48

2 release files

1.0.47

2 release files

1.0.46

2 release files

1.0.45

2 release files

1.0.44

2 release files

1.0.43

2 release files

1.0.42

2 release files

1.0.41

2 release files

1.0.40

2 release files

1.0.39

2 release files

1.0.35

2 release files

1.0.34

2 release files

1.0.33

2 release files

1.0.32

2 release files

1.0.31

2 release files

1.0.30

2 release files

1.0.29

2 release files

1.0.28

2 release files

1.0.20

2 release files

1.0.19

2 release files

1.0.18

2 release files

1.0.17

2 release files

1.0.16

2 release files

1.0.15

2 release files

1.0.14

2 release files

1.0.13

2 release files

1.0.12

2 release files

1.0.11

2 release files

1.0.10

2 release files

1.0.9

2 release files

1.0.8

2 release files

1.0.4

2 release files

1.0.3

2 release files

0.7.9

2 release files

0.7.8

2 release files

0.7.7

2 release files

0.7.6

2 release files

0.7.5

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.5.1

2 release files

0.5

2 release files

0.4

1 release file

0.3

2 release files

0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page