Use with a different MQTT broker

I’m partial to using HiveMQ free-tier clusters as my MQTT brokers. Do you have any suggestions on how to use a broker other than the one that runs on the leader Pioreactor?

I typically use this with a Pico W microcontroller (basic example), and use paho-mqtt to send commands and receive data from the Pico W microcontroller. A full example is at 4. Hardware-Software Communication — ac-microcourses 0.0.post1.dev51+gb30633e documentation.

Hi @sgbaird,

Hm! I’ve never thought about this. We can certainly add this as a configuration. Currently we assume the MQTT broker and the webserver live at the same address, provided by leader_address in the configuration. We can introduce new configuration to split the two.

Can I ask why you’d rather use a remote MQTT broker? Just curious about use cases and features we might be missing.

1 Like

@CamDavidsonPilon, thanks for your quick reply!

Can I ask why you’d rather use a remote MQTT broker? Just curious about use cases and features we might be missing

I want to be able to access the Pioreactor from devices that aren’t on the same network as the Pioreactor (i.e., cloud access). From what I understand, this isn’t possible in the existing setup. The desire to use HiveMQ as the broker is that I can do so securely, even though it’s remotely accessible outside of the network. Another piece is that I will primarily be interacting with the Pioreactor programatically, rather than through the web interface. It seemed like if I could swap out the broker with my own cloud-hosted one, that it would make it easy to integrate with my existing workflows.

The closest conversation (sorry, meant to link this in the original post) seems to be Project cloud database - #4 by CamDavidsonPilon.

I also noticed Writing Pioreactor scripts with Python | Pioreactor Docs.

We do have some documentation on remote access: Setting up remote access | Pioreactor Docs

The above set up also allows SSH access and UI access to the Pioreactor cluster.

1 Like

Somehow missed that, thank you! I think ngrok or tailscale will work for me in the interim, but I’m still a bit hesitant for using this past our demo and prototyping settings. In general my principle has been to expose only the functionality that is necessary. The Pioreactor is meant to be a part of a fully connected and remotely accessible cloud lab. If I were to use a separate broker (i.e., one that allows communication between devices on separate networks), do you think there would be major issues from a code architecture standpoint? Also, I imagine doing so would still mean the UI is only accessible when on the same network, I’m just not sure how that would affect the ability to leverage the same functions. I will probably need to dig in a bit more, but I’m wondering, is the only communication between the web server and the Pioreactor via MQTT?

but I’m wondering, is the only communication between the web server and the Pioreactor via MQTT?

The UI uses both MQTT for real-time data (over websockets), and the http requests to the web server for historical data (charts), starting activities, and managing the cluster.

The good news is I am going to split up configuration for which MQTT broker is being used in the next release. Simply, the config.ini will have a new section:

[mqtt]
username=
password=
broker_address=
broker_ws_port=
broker_port=

Both the UI and the backend app (Python bits) will use this configuration to point to a MQTT broker. By default, we’ll provide the data for the built-in broker on the leader.

1 Like

This is great news! Thank you. I am happy to help support how I can. Additionally, one thing I have typically needed to set through the paho-mqtt client is TLS. See for example, self-driving-lab-demo/src/self_driving_lab_demo/utils/observe.py at main · sparks-baird/self-driving-lab-demo · GitHub. Do you think this would also need to be exposed?

Good point, I’ll look into that.

1 Like

Also, I remembered - you’re welcome to use the credentials from self-driving-lab-demo/src/self_driving_lab_demo/utils/observe.py at main · sparks-baird/self-driving-lab-demo · GitHub, these are public ones I use for teaching.

Hi @sgbaird,

In the 24.2.26 release, you can add your own MQTT broker for the backend. I tested it on your broker, and it connected fine after making the configuration changes. However, I couldn’t get TLS to work over websockets (I think this is a more fundamental issue), so the UI isn’t able to connect unfortunately.

1 Like

@CamDavidsonPilon thank you! I am testing it now :+1:. I came across something that might be related, though I’m out of my depth when it comes to websockets.

which links to Listeners :: HiveMQ Documentation.

On a related note, is there Python API documentation for the available functions to be used with scripting (or a subset of the core ones)? Or, would you recommend going through the developer docs and the GitHub source files to pick out what I need?

@CamDavidsonPilon
I gave it a try with the same credentials:

[mqtt]
username=sgbaird
password=***********
broker_address=***************************************.s2.eu.hivemq.cloud
broker_ws_port=9001
broker_port=8883
ws_protocol=ws
use_tls=1

and running:

pioreactor@pioreactor1:~/.pioreactor $ pios sync-configs
2023-12-10T21:38:58-0500 DEBUG  [sync_configs] Syncing configs on pioreactor1...

followed by the basic Python script for stirring locally via ssh. I can verify that the fan is activated and the stirring starts (seems much slower than 300 RPM, but I’ll look into that later). However, if I “mess up” the credentials (e.g., changing the username to something that doesn’t exist on the HiveMQ broker) and run pios sync-configs and the Python script, it executes the stirring without issue. Perhaps by having it in local access point, the configuration is being overridden, (also, it doesn’t have access to the internet anyway, so it shouldn’t be able to communicate with the HiveMQ broker to begin with), but I was wondering what tests you’d recommend for it. For me, the measure of success would be that I can go to https://www.hivemq.com/demos/websocket-client/ on a separate network, enter the broker credentials, send the appropriate MQTT message/topic, and observe the stirring initiate. This would not be my long-term intention, but this would convince me that I have a minimal working example of the IoT communication functioning to then go into my previous comment related to the Python API.

huh - if there’s no internet, then certainly that MQTT connection isn’t working. I wonder if the Pioreactor updated correctly. Can you confirm with the command pio version and check for 24.2.26?


the appropriate MQTT message/topic, and observe the stirring initiate

This is possible! Obviously you’d need the internet, but sending to the topic

pioreactor/<unit>/<experiment>/monitor/run/<job_name>

the json message:

        {
          "options": {
            "option_A": "value1",
            "option_B": "value2"
          },
          "args": ["arg1", "arg2"]
        }

would work. For example, to start stirring:

topic:

pioreactor/<unit>/<experiment>/monitor/run/stirring

message:

{"target_rpm": 500}

Confirmed that the version is 24.2.26.

On my laptop, I am connected via ethernet to the internet as well as to my IoT WiFi (https://hologram.io/ is expensive per MB, low-bandwidth, hence the ethernet connection so I can still do web browsing, etc.). I am able to successfully open the UI. I created a new experiment called test. My Pioreactor’s name is pioreactor1.

I did a fresh software install per the docs and entered my IoT WiFi credentials. I was able to SSH into the pioreactor to update the MQTT credentials (same as above). I then ran pios sync-configs again.

Finally, in HiveMQ’s interface, I noticed that the LED intensities appeared without any further action by my part (see bottom row). I tried publishing in the format you mentioned, but the fan didn’t activate.

From there, I went back to the UI and in “Pioreactors” → “Manage”, I clicked “Start” under “Stirring”. The stirring began, though interestingly after the button action completed, it didn’t seem to recognize the state as being “on” (i.e., “off” button wasn’t clickable), so I went back to “Pioreactors” and clicked “Stop all activity”, which shut off the stirring.

Detailed HiveMQ MQTT broker traffic after pressing "START" for stirring in the Pioreactor web UI
Message Topic QoS Timestamp
24.80189 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 24.80189} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: 0.0015300000000001432, derivative: 0.0, latest_input: 499.83, latest_output: 0.0015300000000001432, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:499.83,timestamp:2024-02-28T17:25:32.721407Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
24.80036 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 24.80036} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: 0.0009900000000001227, derivative: 0.0, latest_input: 499.89, latest_output: 0.0009900000000001227, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:499.89,timestamp:2024-02-28T17:25:09.721387Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
24.79937 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: -0.0003600000000001841, derivative: -0.0, latest_input: 500.04, latest_output: -0.0003600000000001841, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:500.04,timestamp:2024-02-28T17:24:46.721385Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
{17: 24.79937} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
24.79973 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 24.79973} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: 0.0006299999999999386, derivative: -0.0, latest_input: 499.93, latest_output: 0.0006299999999999386, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:499.93,timestamp:2024-02-28T17:24:23.721408Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
24.7991 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 24.7991} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: 0.0008099999999997749, derivative: 0.0, latest_input: 499.91, latest_output: 0.0008099999999997749, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:499.91,timestamp:2024-02-28T17:24:00.721390Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
24.79829 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: -0.0017099999999999793, derivative: -0.0, latest_input: 500.19, latest_output: -0.0017099999999999793, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:500.19,timestamp:2024-02-28T17:23:37.721401Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
{17: 24.79829} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
24.8 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 24.8} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: -0.0004500000000001023, derivative: 0.0, latest_input: 500.05, latest_output: -0.0004500000000001023, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:500.05,timestamp:2024-02-28T17:23:14.721404Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
24.80045 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 24.80045} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: -0.003959999999999979, derivative: -0.0, latest_input: 500.44, latest_output: -0.003959999999999979, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:500.44,timestamp:2024-02-28T17:22:51.721386Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
24.80441 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: 0.0019800000000002455, derivative: 0.0, latest_input: 499.78, latest_output: 0.0019800000000002455, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:499.78,timestamp:2024-02-28T17:22:28.721365Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
{17: 24.80441} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
24.80243 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 24.80243} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: 0.0009900000000001227, derivative: 0.0, latest_input: 499.89, latest_output: 0.0009900000000001227, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:499.89,timestamp:2024-02-28T17:22:05.721391Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
24.80144 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 24.80144} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: -0.0017099999999999793, derivative: -0.0, latest_input: 500.19, latest_output: -0.0017099999999999793, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:500.19,timestamp:2024-02-28T17:21:42.721453Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
24.80315 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: 0.26262, derivative: -0.0, latest_input: 470.82, latest_output: 0.26262, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:470.82,timestamp:2024-02-28T17:21:19.721470Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
{17: 24.80315} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
24.54053 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 24.54053} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: 0.0, proportional: 0.34578000000000014, derivative: 0.0, latest_input: 461.58, latest_output: 0.34578000000000014, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:461.58,timestamp:2024-02-28T17:20:56.721453Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
24.19475 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{source: app, message: stirring is blocking until disconnected., level: DEBUG, task: stirring, timestamp: 2024-02-28T17:20:39.207863Z} pioreactor/pioreactor1/test/logs/app 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: -0.0, proportional: -0.08351999999999975, derivative: 0.0, latest_input: 509.28, latest_output: -0.08351999999999975, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:509.28,timestamp:2024-02-28T17:20:39.182005Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
{17: 24.19475} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
24.27827 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 24.27827} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: -0.0, proportional: -0.32975999999999983, derivative: 0.0, latest_input: 536.64, latest_output: -0.32975999999999983, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:536.64,timestamp:2024-02-28T17:20:36.957757Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
24.60803 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: -0.0, proportional: -1.26774, derivative: 0.0, latest_input: 640.86, latest_output: -1.26774, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{17: 24.60803} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{measured_rpm:640.86,timestamp:2024-02-28T17:20:34.732839Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
25.87577 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 25.87577} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: -0.0, proportional: -2.6134199999999996, derivative: 0.0, latest_input: 790.38, latest_output: -2.6134199999999996, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:790.38,timestamp:2024-02-28T17:20:32.508508Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
28.48919 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 28.48919} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{setpoint: 500.0, output_limits_lb: -7.5, output_limits_ub: 7.5, Kd: 0.0, Ki: 0.0, Kp: 0.009, integral: -0.0, proportional: -3.51081, derivative: 0.0, latest_input: 890.09, latest_output: -3.51081, job_name: stirring, target_name: rpm} pioreactor/pioreactor1/test/pid_log/rpm 0 1.70914E+12
{measured_rpm:890.09,timestamp:2024-02-28T17:20:30.283997Z} pioreactor/pioreactor1/test/stirring/measured_rpm 0 1.70914E+12
{source: app, message: Stirring is blocking until RPM is near 500.0., level: DEBUG, task: stirring, timestamp: 2024-02-28T17:20:26.282238Z} pioreactor/pioreactor1/test/logs/app 0 1.70914E+12
{17: 32.0} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{source: app, message: Starting stirring with 500.0 RPM., level: DEBUG, task: stirring, timestamp: 2024-02-28T17:20:25.534140Z} pioreactor/pioreactor1/test/logs/app 0 1.70914E+12
32 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
{17: 100.0} pioreactor/pioreactor1/test/pwms/dc 0 1.70914E+12
{source: app, message: Ready., level: INFO, task: stirring, timestamp: 2024-02-28T17:20:25.531849Z} pioreactor/pioreactor1/test/logs/app 0 1.70914E+12
ready pioreactor/pioreactor1/test/stirring/$state 0 1.70914E+12
32 pioreactor/pioreactor1/test/stirring/duty_cycle 0 1.70914E+12
500 pioreactor/pioreactor1/test/stirring/target_rpm 0 1.70914E+12
{source: app, message: Operating with RPM feedback loop., level: DEBUG, task: stirring, timestamp: 2024-02-28T17:20:22.035153Z} pioreactor/pioreactor1/test/logs/app 0 1.70914E+12
string pioreactor/pioreactor1/test/stirring/state/$datatype 0 1.70914E+12
TRUE pioreactor/pioreactor1/test/stirring/duty_cycle/$settable 0 1.70914E+12
FALSE pioreactor/pioreactor1/test/stirring/measured_rpm/$settable 0 1.70914E+12
TRUE pioreactor/pioreactor1/test/stirring/target_rpm/$settable 0 1.70914E+12
TRUE pioreactor/pioreactor1/test/stirring/state/$settable 0 1.70914E+12
% pioreactor/pioreactor1/test/stirring/duty_cycle/$unit 0 1.70914E+12
float pioreactor/pioreactor1/test/stirring/duty_cycle/$datatype 0 1.70914E+12
RPM pioreactor/pioreactor1/test/stirring/measured_rpm/$unit 0 1.70914E+12
MeasuredRPM pioreactor/pioreactor1/test/stirring/measured_rpm/$datatype 0 1.70914E+12
RPM pioreactor/pioreactor1/test/stirring/target_rpm/$unit 0 1.70914E+12
float pioreactor/pioreactor1/test/stirring/target_rpm/$datatype 0 1.70914E+12
target_rpm,measured_rpm,duty_cycle,state pioreactor/pioreactor1/test/stirring/$properties 0 1.70914E+12
{source: app, message: Init., level: DEBUG, task: stirring, timestamp: 2024-02-28T17:20:21.947553Z} pioreactor/pioreactor1/test/logs/app 0 1.70914E+12
init pioreactor/pioreactor1/test/stirring/$state 0 1.70914E+12

Monitoring the broker messages (and from that initial LED status message appearing), it’s apparent that there is communication between the pioreactor and the HiveMQ broker. This is great! Such a good sign of life to see.

Aside: I’m noticing that the number of client sessions attached to the broker spiked (~10-15 or so, my limit is 100). Typically I’ve used a singleton paho client object to avoid creating repeat clients. Not super critical for me to have this reduced at this stage, but figured I’d mention it.

Any suggestions on what to try next with controlling the pioreactor from MQTT directly rather than via the UI?

Yes, this is expected since I can’t get MQTT + TLS over websockets yet! (MQTT is what controls the state of the buttons)

Aside: I’m noticing that the number of client sessions attached to the broker spiked (~10-15 or so, my limit is 100). Typically I’ve used a singleton paho client object to avoid creating repeat clients. Not super critical for me to have this reduced at this stage, but figured I’d mention it.

Right, thanks for bringing this up. I think because we had ~unlimited connections available since we are self-hosting, we liberally create clients in the software. However, in the past few months, I’ve started to cut / reuse clients for memory reasons. So expect reduce usage over time, but it will always be sorta high.

I tried publishing in the format you mentioned, but the fan didn’t activate.

whoops I got to invocation wrong:

pioreactor/<unit name>/$experiment/run/stirring  

with json message:

{"args":[],"options":{"target_rpm": 250}}

If that starts running, then you can try to change the target RPM to 300 with:

pioreactor/<unit name>/<experiment name>/stirring/target_rpm/set 

message

300

Since your last message, I’ve tried quite a few things - it’s a bit tough to describe succinctly. I’ve tried the instructions from your previous message as well as cloning the Pioreactor GitHub repository and running the Python functions locally. This is after pointing to the appropriate config file through the GLOBAL_CONFIG env variable, and I’ve tested on both Windows and WSL2. I’ve also tried mimicking the MQTT messages that I observe from the web client that I’d normally observe when running per the normal instructions (i.e., communication over same network, e.g., through running Python via ssh’ing into the Pioreactor or using the UI).

Before I dive in further, I wanted to check - were you able to verify that stirring could be started from a separate network (e.g., the MQTT web client I linked to earlier)? I’m clearly seeing traffic on the broker I’m using (see details at end), I’m just not able to observe any effect on the actual Pioreactor.

When I run the following script from a local clone in WSL2:

from pioreactor.background_jobs.stirring import Stirrer, RpmFromFrequency

unit = "pioreactor1"
experiment = "test"

st = Stirrer(
    target_rpm=300,
    unit=unit,
    experiment=experiment,
    rpm_calculator=RpmFromFrequency()
)

st.start_stirring()

st.block_until_disconnected() # pauses the execution, but stirring continues

I get:

2024-03-04T17:06:28-0500 DEBUG  [stirring] Init.
2024-03-04T17:06:28-0500 DEBUG  [stirring] Operating with RPM feedback loop.
2024-03-04T17:06:29-0500 DEBUG  [PWM@GPIO-17] Initialized GPIO-17 using software-timing, initial frequency = 75.0 hz.
2024-03-04T17:06:29-0500 INFO   [stirring] Ready.
2024-03-04T17:06:29-0500 DEBUG  [stirring] Starting stirring with 300.0 RPM.
2024-03-04T17:06:30-0500 DEBUG  [stirring] stirring is blocking until disconnected.
2024-03-04T17:06:38-0500 WARNING [stirring] Stirring RPM is 0 - attempting to restart it automatically. It may be a temporary stall, target RPM may be too low, or not reading sensor correctly.
2024-03-04T17:06:38-0500 DEBUG  [stirring] Kicking stirring

All the while, I’m getting plenty of MQTT traffic from running and stopping the Python script:

{"source": "app", "message": "Disconnected successfully from MQTT.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:09:02.529828Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590142805
{"source": "app", "message": "Disconnected.", "level": "INFO", "task": "stirring", "timestamp": "2024-03-04T22:09:02.386056Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590142786
pioreactor/pioreactor1/test/stirring/state/$unit
0	1709590142753
pioreactor/pioreactor1/test/stirring/duty_cycle/$settable
0	1709590142738
pioreactor/pioreactor1/test/stirring/measured_rpm/$unit
0	1709590142707
pioreactor/pioreactor1/test/stirring/measured_rpm/$datatype
0	1709590142690
pioreactor/pioreactor1/test/stirring/target_rpm/$datatype
0	1709590142658
pioreactor/pioreactor1/test/stirring/target_rpm/$settable
0	1709590142628
pioreactor/pioreactor1/test/stirring/target_rpm
0	1709590142617
pioreactor/pioreactor1/test/stirring/state/$datatype
0	1709590142606
pioreactor/pioreactor1/test/stirring/state/$settable
0	1709590142596
pioreactor/pioreactor1/test/stirring/duty_cycle/$unit
0	1709590142586
pioreactor/pioreactor1/test/stirring/duty_cycle/$datatype
0	1709590142574
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590142563
pioreactor/pioreactor1/test/stirring/measured_rpm/$settable
0	1709590142551
pioreactor/pioreactor1/test/stirring/measured_rpm
0	1709590142541
pioreactor/pioreactor1/test/stirring/target_rpm/$unit
0	1709590142510
pioreactor/pioreactor1/test/stirring/$properties
0	1709590142487
disconnected	
pioreactor/pioreactor1/test/stirring/$state
0	1709590141591
{"source": "app", "message": "Cleaned up GPIO-17.", "level": "DEBUG", "task": "PWM@GPIO-17", "timestamp": "2024-03-04T22:09:01.404261Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590141573
{}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590141465
{"source": "app", "message": "Exiting caused by signal Interrupt.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:09:01.323681Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590141425
{"17": 57.5}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590137227
57.5	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590137205
{"setpoint": 300.0, "output_limits_lb": -7.5, "output_limits_ub": 7.5, "Kd": 0.0, "Ki": 0.0, "Kp": 10.0, "integral": 0.0, "proportional": 3000.0, "derivative": 0.0, "latest_input": 0.0, "latest_output": 7.5, "job_name": "stirring", "target_name": "rpm"}	
pioreactor/pioreactor1/test/pid_log/rpm
0	1709590137177
{"17": 50.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590137141
50	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590137104
4	
pioreactor/pioreactor1/test/monitor/flicker_led_with_error_code
0	1709590137002
100	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590136986
{"source": "app", "message": "Kicking stirring", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:08:56.729396Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590136961
{"source": "app", "message": "Stirring RPM is 0 - attempting to restart it automatically. It may be a temporary stall, target RPM may be too low, or not reading sensor correctly.", "level": "WARNING", "task": "stirring", "timestamp": "2024-03-04T22:08:56.706303Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590136910
{"17": 100.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590136861
{"measured_rpm":0.0,"timestamp":"2024-03-04T22:08:56.704400Z"}	
pioreactor/pioreactor1/test/stirring/measured_rpm
0	1709590136813
{"17": 57.5}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590114241
57.5	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590114189
{"setpoint": 300.0, "output_limits_lb": -7.5, "output_limits_ub": 7.5, "Kd": 0.0, "Ki": 0.0, "Kp": 10.0, "integral": 0.0, "proportional": 3000.0, "derivative": 0.0, "latest_input": 0.0, "latest_output": 7.5, "job_name": "stirring", "target_name": "rpm"}	
pioreactor/pioreactor1/test/pid_log/rpm
0	1709590114137
{"17": 50.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590114114
50	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590114088
4	
pioreactor/pioreactor1/test/monitor/flicker_led_with_error_code
0	1709590113979
100	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590113951
{"source": "app", "message": "Kicking stirring", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:08:33.724260Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590113928
{"source": "app", "message": "Stirring RPM is 0 - attempting to restart it automatically. It may be a temporary stall, target RPM may be too low, or not reading sensor correctly.", "level": "WARNING", "task": "stirring", "timestamp": "2024-03-04T22:08:33.704909Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590113906
{"17": 100.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590113871
{"measured_rpm":0.0,"timestamp":"2024-03-04T22:08:33.704028Z"}	
pioreactor/pioreactor1/test/stirring/measured_rpm
0	1709590113847
{"17": 57.5}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590091261
57.5	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590091234
{"setpoint": 300.0, "output_limits_lb": -7.5, "output_limits_ub": 7.5, "Kd": 0.0, "Ki": 0.0, "Kp": 10.0, "integral": 0.0, "proportional": 3000.0, "derivative": 0.0, "latest_input": 0.0, "latest_output": 7.5, "job_name": "stirring", "target_name": "rpm"}	
pioreactor/pioreactor1/test/pid_log/rpm
0	1709590091142
{"17": 50.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590091117
50	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590091088
4	
pioreactor/pioreactor1/test/monitor/flicker_led_with_error_code
0	1709590091037
100	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590091012
{"source": "app", "message": "Kicking stirring", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:08:10.725132Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590090978
{"source": "app", "message": "Stirring RPM is 0 - attempting to restart it automatically. It may be a temporary stall, target RPM may be too low, or not reading sensor correctly.", "level": "WARNING", "task": "stirring", "timestamp": "2024-03-04T22:08:10.704999Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590090932
{"17": 100.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590090837
{"measured_rpm":0.0,"timestamp":"2024-03-04T22:08:10.704391Z"}	
pioreactor/pioreactor1/test/stirring/measured_rpm
0	1709590090808
{"17": 57.5}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590068241
57.5	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590068215
{"setpoint": 300.0, "output_limits_lb": -7.5, "output_limits_ub": 7.5, "Kd": 0.0, "Ki": 0.0, "Kp": 10.0, "integral": 0.0, "proportional": 3000.0, "derivative": 0.0, "latest_input": 0.0, "latest_output": 7.5, "job_name": "stirring", "target_name": "rpm"}	
pioreactor/pioreactor1/test/pid_log/rpm
0	1709590068180
{"17": 50.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590068159
50	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590068134
4	
pioreactor/pioreactor1/test/monitor/flicker_led_with_error_code
0	1709590067978
100	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590067948
{"source": "app", "message": "Kicking stirring", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:07:47.729647Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590067930
{"source": "app", "message": "Stirring RPM is 0 - attempting to restart it automatically. It may be a temporary stall, target RPM may be too low, or not reading sensor correctly.", "level": "WARNING", "task": "stirring", "timestamp": "2024-03-04T22:07:47.707061Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590067907
{"17": 100.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590067865
{"measured_rpm":0.0,"timestamp":"2024-03-04T22:07:47.704848Z"}	
pioreactor/pioreactor1/test/stirring/measured_rpm
0	1709590067813
{"17": 57.5}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590045229
57.5	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590045199
{"setpoint": 300.0, "output_limits_lb": -7.5, "output_limits_ub": 7.5, "Kd": 0.0, "Ki": 0.0, "Kp": 10.0, "integral": 0.0, "proportional": 3000.0, "derivative": 0.0, "latest_input": 0.0, "latest_output": 7.5, "job_name": "stirring", "target_name": "rpm"}	
pioreactor/pioreactor1/test/pid_log/rpm
0	1709590045152
{"17": 50.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590045126
50	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590045099
4	
pioreactor/pioreactor1/test/monitor/flicker_led_with_error_code
0	1709590044971
100	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590044953
{"source": "app", "message": "Kicking stirring", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:07:24.729276Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590044937
{"source": "app", "message": "Stirring RPM is 0 - attempting to restart it automatically. It may be a temporary stall, target RPM may be too low, or not reading sensor correctly.", "level": "WARNING", "task": "stirring", "timestamp": "2024-03-04T22:07:24.705525Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590044906
{"17": 100.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590044848
{"measured_rpm":0.0,"timestamp":"2024-03-04T22:07:24.704074Z"}	
pioreactor/pioreactor1/test/stirring/measured_rpm
0	1709590044806
{"17": 57.5}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590022246
57.5	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590022223
{"setpoint": 300.0, "output_limits_lb": -7.5, "output_limits_ub": 7.5, "Kd": 0.0, "Ki": 0.0, "Kp": 10.0, "integral": 0.0, "proportional": 3000.0, "derivative": 0.0, "latest_input": 0.0, "latest_output": 7.5, "job_name": "stirring", "target_name": "rpm"}	
pioreactor/pioreactor1/test/pid_log/rpm
0	1709590022172
{"17": 50.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590022146
50	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590022121
4	
pioreactor/pioreactor1/test/monitor/flicker_led_with_error_code
0	1709590021970
100	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709590021956
{"source": "app", "message": "Kicking stirring", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:07:01.735922Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590021942
{"source": "app", "message": "Stirring RPM is 0 - attempting to restart it automatically. It may be a temporary stall, target RPM may be too low, or not reading sensor correctly.", "level": "WARNING", "task": "stirring", "timestamp": "2024-03-04T22:07:01.708595Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709590021909
{"17": 100.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709590021872
{"measured_rpm":0.0,"timestamp":"2024-03-04T22:07:01.704971Z"}	
pioreactor/pioreactor1/test/stirring/measured_rpm
0	1709590021809
{"17": 57.5}	
pioreactor/pioreactor1/test/pwms/dc
0	1709589999227
57.5	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709589999207
{"setpoint": 300.0, "output_limits_lb": -7.5, "output_limits_ub": 7.5, "Kd": 0.0, "Ki": 0.0, "Kp": 10.0, "integral": 0.0, "proportional": 3000.0, "derivative": 0.0, "latest_input": 0.0, "latest_output": 7.5, "job_name": "stirring", "target_name": "rpm"}	
pioreactor/pioreactor1/test/pid_log/rpm
0	1709589999159
{"17": 50.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709589999128
50	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709589999104
4	
pioreactor/pioreactor1/test/monitor/flicker_led_with_error_code
0	1709589998946
100	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709589998936
{"source": "app", "message": "Kicking stirring", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:06:38.730002Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589998929
{"source": "app", "message": "Stirring RPM is 0 - attempting to restart it automatically. It may be a temporary stall, target RPM may be too low, or not reading sensor correctly.", "level": "WARNING", "task": "stirring", "timestamp": "2024-03-04T22:06:38.709941Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589998910
{"17": 100.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709589998853
{"measured_rpm":0.0,"timestamp":"2024-03-04T22:06:38.704485Z"}	
pioreactor/pioreactor1/test/stirring/measured_rpm
0	1709589998810
{"source": "app", "message": "stirring is blocking until disconnected.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:06:30.304400Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589990407
{"17": 60.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709589989900
60.0	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709589989875
{"source": "app", "message": "Starting stirring with 300.0 RPM.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:06:29.552764Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589989767
{"source": "app", "message": "Ready.", "level": "INFO", "task": "stirring", "timestamp": "2024-03-04T22:06:29.551499Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589989753
{"17": 100.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709589989669
ready	
pioreactor/pioreactor1/test/stirring/$state
0	1709589989652
60.0	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709589989366
300.0	
pioreactor/pioreactor1/test/stirring/target_rpm
0	1709589989267
{"source": "app", "message": "Operating with RPM feedback loop.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:06:28.380097Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589988616
True	
pioreactor/pioreactor1/test/stirring/duty_cycle/$settable
0	1709589988589
RPM	
pioreactor/pioreactor1/test/stirring/measured_rpm/$unit
0	1709589988581
MeasuredRPM	
pioreactor/pioreactor1/test/stirring/measured_rpm/$datatype
0	1709589988574
float	
pioreactor/pioreactor1/test/stirring/target_rpm/$datatype
0	1709589988566
True	
pioreactor/pioreactor1/test/stirring/target_rpm/$settable
0	1709589988560
string	
pioreactor/pioreactor1/test/stirring/state/$datatype
0	1709589988552
True	
pioreactor/pioreactor1/test/stirring/state/$settable
0	1709589988540
%	
pioreactor/pioreactor1/test/stirring/duty_cycle/$unit
0	1709589988534
float	
pioreactor/pioreactor1/test/stirring/duty_cycle/$datatype
0	1709589988522
False	
pioreactor/pioreactor1/test/stirring/measured_rpm/$settable
0	1709589988514
RPM	
pioreactor/pioreactor1/test/stirring/target_rpm/$unit
0	1709589988503
target_rpm,measured_rpm,duty_cycle,state	
pioreactor/pioreactor1/test/stirring/$properties
0	1709589988496
{"source": "app", "message": "Init.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:06:28.373431Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589988485
init	
pioreactor/pioreactor1/test/stirring/$state
0	1709589988476
{"source": "app", "message": "Disconnected successfully from MQTT.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:06:08.285138Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589968620
{"source": "app", "message": "Disconnected.", "level": "INFO", "task": "stirring", "timestamp": "2024-03-04T22:06:08.133735Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589968609
pioreactor/pioreactor1/test/stirring/state/$unit
0	1709589968600
pioreactor/pioreactor1/test/stirring/duty_cycle/$settable
0	1709589968595
pioreactor/pioreactor1/test/stirring/measured_rpm/$unit
0	1709589968576
pioreactor/pioreactor1/test/stirring/state/$datatype
0	1709589968568
pioreactor/pioreactor1/test/stirring/state/$settable
0	1709589968563
pioreactor/pioreactor1/test/stirring/duty_cycle/$unit
0	1709589968552
pioreactor/pioreactor1/test/stirring/duty_cycle/$datatype
0	1709589968545
pioreactor/pioreactor1/test/stirring/measured_rpm/$datatype
0	1709589968538
pioreactor/pioreactor1/test/stirring/target_rpm/$datatype
0	1709589968529
pioreactor/pioreactor1/test/stirring/target_rpm/$settable
0	1709589968522
pioreactor/pioreactor1/test/stirring/target_rpm
0	1709589968515
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709589968447
pioreactor/pioreactor1/test/stirring/measured_rpm/$settable
0	1709589968427
pioreactor/pioreactor1/test/stirring/measured_rpm
0	1709589968401
pioreactor/pioreactor1/test/stirring/target_rpm/$unit
0	1709589968246
pioreactor/pioreactor1/test/stirring/$properties
0	1709589968234
disconnected	
pioreactor/pioreactor1/test/stirring/$state
0	1709589967290
{"source": "app", "message": "Cleaned up GPIO-17.", "level": "DEBUG", "task": "PWM@GPIO-17", "timestamp": "2024-03-04T22:06:07.169059Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589967271
{}	
pioreactor/pioreactor1/test/pwms/dc
0	1709589967213
{"source": "app", "message": "Exiting caused by signal Interrupt.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:06:07.080500Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709589967182

EDIT: Here’s an example of traffic when running the Python script via SSH over the same network:

27.48362	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709591446519
{"17": 27.48362}	
pioreactor/pioreactor1/test/pwms/dc
0	1709591446262
{"measured_rpm":801.82,"timestamp":"2024-03-04T22:30:46.872577Z"}	
pioreactor/pioreactor1/test/stirring/measured_rpm
0	1709591446246
{"setpoint": 300.0, "output_limits_lb": -7.5, "output_limits_ub": 7.5, "Kd": 0.0, "Ki": 0.0, "Kp": 0.009, "integral": -0.0, "proportional": -4.51638, "derivative": 0.0, "latest_input": 801.82, "latest_output": -4.51638, "job_name": "stirring", "target_name": "rpm"}	
pioreactor/pioreactor1/test/pid_log/rpm
0	1709591446235
{"source": "app", "message": "stirring is blocking until disconnected.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:30:38.473562Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709591437852
{"17": 32.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709591437423
{"source": "app", "message": "Starting stirring with 300.0 RPM.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:30:37.725053Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709591437389
32.0	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709591437372
{"17": 100.0}	
pioreactor/pioreactor1/test/pwms/dc
0	1709591437106
{"source": "app", "message": "Ready.", "level": "INFO", "task": "stirring", "timestamp": "2024-03-04T22:30:37.722432Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709591437096
ready	
pioreactor/pioreactor1/test/stirring/$state
0	1709591437082
32.0	
pioreactor/pioreactor1/test/stirring/duty_cycle
0	1709591436110
300.0	
pioreactor/pioreactor1/test/stirring/target_rpm
0	1709591435839
string	
pioreactor/pioreactor1/test/stirring/state/$datatype
0	1709591433844
True	
pioreactor/pioreactor1/test/stirring/state/$settable
0	1709591433818
%	
pioreactor/pioreactor1/test/stirring/duty_cycle/$unit
0	1709591433806
float	
pioreactor/pioreactor1/test/stirring/duty_cycle/$datatype
0	1709591433795
False	
pioreactor/pioreactor1/test/stirring/measured_rpm/$settable
0	1709591433779
RPM	
pioreactor/pioreactor1/test/stirring/target_rpm/$unit
0	1709591433766
target_rpm,measured_rpm,duty_cycle,state	
pioreactor/pioreactor1/test/stirring/$properties
0	1709591433760
True	
pioreactor/pioreactor1/test/stirring/duty_cycle/$settable
0	1709591433752
RPM	
pioreactor/pioreactor1/test/stirring/measured_rpm/$unit
0	1709591433747
MeasuredRPM	
pioreactor/pioreactor1/test/stirring/measured_rpm/$datatype
0	1709591433743
float	
pioreactor/pioreactor1/test/stirring/target_rpm/$datatype
0	1709591433735
True	
pioreactor/pioreactor1/test/stirring/target_rpm/$settable
0	1709591433729
{"source": "app", "message": "Operating with RPM feedback loop.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:30:34.121096Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709591433722
{"source": "app", "message": "Init.", "level": "DEBUG", "task": "stirring", "timestamp": "2024-03-04T22:30:34.031626Z"}	
pioreactor/pioreactor1/test/logs/app
0	1709591433424
init	
pioreactor/pioreactor1/test/stirring/$state
0	1709591433401

The stirring proceeds as expected in this case.

EDIT: I noticed New Pioreactor release: 24.3.4 (hotfix patch), I will update and try again, though it would be great if you could still give it a go verifying the stirring activation on your end. Thanks again for all your help on this.

@CamDavidsonPilon, after updating to 24.3.4, I was able to trigger stirring remotely by publishing:

{"args":[],"options":{"target_rpm": 250}}

to the topic:

pioreactor/pioreactor1/test/run/stirring

where pioreactor1 is the unit name and test is the experiment name. Here is the MQTT traffic:

{“source”: “app”, “message”: “Running nohup pio run stirring --target-rpm 250 >/dev/null 2>&1 & from monitor job.”, “level”: “DEBUG”, “task”: “monitor”, “timestamp”: “2024-03-05T21:15:26.892784Z”} pioreactor/pioreactor1/$experiment/logs/app 0 1709673327641
{“args”:,“options”:{“target_rpm”: 250}} pioreactor/pioreactor1/test/run/stirring 0 1709673327373

This is great! Thank you for helping me get to this point :blush:

Regarding running it from a Python installation on a separate network, I tried again but ran into the same issue as before. Most of the MQTT traffic looks normal, but the stirring fan doesn’t activate, and I end up with the following DEBUG messages:

2024-03-05T16:46:17-0500 DEBUG  [stirring] Init.
2024-03-05T16:46:17-0500 DEBUG  [stirring] Operating with RPM feedback loop.
2024-03-05T16:46:18-0500 DEBUG  [PWM@GPIO-17] Initialized GPIO-17 using software-timing, initial frequency = 75.0 hz.
2024-03-05T16:46:19-0500 INFO   [stirring] Ready.
2024-03-05T16:46:19-0500 DEBUG  [stirring] Starting stirring with 300.0 RPM.
2024-03-05T16:46:20-0500 DEBUG  [stirring] stirring is blocking until disconnected.
2024-03-05T16:46:28-0500 WARNING [stirring] Stirring RPM is 0 - attempting to restart it automatically. It may be a temporary stall, target RPM may be too low, or not reading sensor correctly.
2024-03-05T16:46:28-0500 DEBUG  [stirring] Kicking stirring
...

Perhaps the issue is that the lgpio from my Windows Python installation is trying to control the PWM channels, even though it’s the lgpio on the Pioreactor that needs to interact directly with them instead. I’m not sure though. Thoughts?

This is great! Thank you for helping me get to this point :blush:

Great! One thing I forgot to mention: you maybe need to do a power-cycle¹ when updating MQTT config. The reason is that there is a long-running job, monitor, that listen to the topic pioreactor/+/+/run/+ and handles starting jobs over MQTT. If this job has old configuration, it’s not going to be listening to the right broker.


Most of the MQTT traffic looks normal, but the stirring fan doesn’t activate, and I end up with the following DEBUG messages:

This sounds like a hardware problem then. Have you looked at the docs here: Stirring | Pioreactor Docs?

¹ A power-cycle works, but you can also restart it with sudo systemctl restart pioreactor_startup_run@monitor.service

1 Like

Thanks for clarifying! Also, the lack of stirring fan activating is only when I’m trying to run it from a separate network using Python (i.e., cloned the Pioreactor repo and running locally). The same workflow is fine if I run the Python script while SSH’d into the Pioreactor, but doesn’t work when running Python separately.

The docs you linked helped a lot earlier during my troubleshooting in the past few days/last week, since I think one of the screw heads was interfering with the fan movement.

If your code isn’t running on a Pioreactor (IIUC), then that error is expected: there’s no RPM. If you’re just testing, you can set TESTING=1 as an env variable to “fake” RPM and other sensors.

In order to run Python remotely, and have it interact with the distant Pioreactor (over MQTT) - that’s going to require a custom API. But is that your goal?

We kinda already do have this API, in the form of experiment profiles. The YAML that defines an experiment profiles is parsed on the leader Pioreactor and turned into MQTT messages that are sent across the cluster.

1 Like