Work in progress - NP 2023-06-14
It is sometimes advantageous for fly-on-ball behavior to have the behavior enclosure warmer and more humid than room temp. It is also useful to be able to log ambient temperature and humidity in the enclosure as an experimental variable.
There are a lot of way to achieve control and measurement, but here is a system that has worked for me:
Heaters and humidifiers#
Get a heater and a humidifier. These would ideally be fairly bare-bones as we’re going to switch them on and off based on the measured temp/humidity. Built in thermostats may be factory tuned with some fixed hysteresis that might be larger than the narrow range of temp that we want to keep the enclosure at.
Heaters#
Heaters with fans will heat the enclosure much faster, but also cause faster and larger temperature swings than passive radiant heaters. There are cheaper heaters on amazon, but the enclosure heaters from mcmaster have some built in safety features i.e. will turn off if they catch fire. This is the one I use:
DIN-Rail Enclosure Heater, with Adjustable. Thermostat, 120V AC, 1850 Btu/hr. | McMaster-Carr
Other options: https://www.mcmaster.com/19095K21/ https://www.amazon.com/AmazonBasics-500-Watt-Ceramic-Personal-Heater/dp/B074MX8VN5/ref=sr_1_25?crid=29DHEJA8X2EJJ&keywords=enclosure%2Bheater&qid=1683733340&sprefix=enclosure%2Bheater%2Caps%2C94&sr=8-25&th=1 Something simple like this could also work (has the advantage that heater and fan are controlled separately, so you can run the fan constantly: https://www.amazon.com/Constant-Temperature-Electric-Heating-Incubator/dp/B07BJWJQLK/ref=psdc_3206324011_t3_B07NYX5DKD
Note: heaters with fans on them can blow in ways that move the antenna. If you need to avoid that then a “pump-house heater” is a good option. They tend to be powerful but are very compact.
Humidifers#
Control#
Option 1: specialized commercial plug controllers#
Temp: https://www.amazon.com/gp/product/B094QC2R72/ref=ewc_pr_img_3?smid=A3306XZQF174G6&th=1 Humidity: Amazon.com: DIGITEN Digital Humidity Controller Humidistat Pre-Wired Plug Simple-Stage AC 110V -240V with High Accuracy Humidity Sensor for Dehumidifier Humidifier Reptiles Greenhouse Exhaust Fan Fermentation : Appliances
Option 2: Phidget control#
Have not tried this, but using a phidget you can control an outlet directly, so a more elegant solution may be to both monitor and control using the phidget.
2 x https://www.phidgets.com/?tier=3&catid=46&pcid=39&prodid=1142
2x https://www.phidgets.com/?tier=3&catid=30&pcid=26&prodid=1153
#
Monitoring temperature and humidity
Using a phidget#
#
Parts
- Phidget hub: https://www.phidgets.com/?tier=3&catid=2&pcid=1&prodid=1202
- Phidget ambient temperature and humidity tracker https://www.phidgets.com/?tier=3&catid=14&pcid=12&prodid=1179
- Optional (thermocouple phidget) https://www.phidgets.com/?tier=3&catid=14&pcid=12&prodid=1215 and K-type thermocouple: https://www.phidgets.com/?tier=3&catid=14&pcid=12&prodid=727
Software setup#
Phidget control software and packages
- Windows: https://www.phidgets.com/docs/OS_-_Windows
- Install a python distribution
- pip install Phidget22
Example code#
The following python code (attached as phidget_temp_humidity_udp.py) logs temperature and humidity readings every 1 second to a file and also writes them to a UDP port so that you can read them in in another program (for example matlab).
import socket
import time
from datetime import datetime
import os
from Phidget22.Phidget import *
from Phidget22.Devices.TemperatureSensor import *
from Phidget22.Devices.HumiditySensor import *
# Configuration
UDP_IP = "127.0.0.1"
UDP_PORT = 49160
INTERVAL = 1 # Time interval in seconds
LOG_FILE = f"temp_humidity_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
# Set up UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Set up temperature sensor
temperature_sensor = TemperatureSensor()
temperature_sensor.openWaitForAttachment(5000) # Adjust timeout as needed
# Set up humidity sensor
humidity_sensor = HumiditySensor()
humidity_sensor.openWaitForAttachment(5000) # Adjust timeout as needed
def log_data(temperature, humidity):
with open(LOG_FILE, "a") as log_file:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
log_line = f"{timestamp}, {temperature}, {humidity}\n"
log_file.write(log_line)
# print(f"Logged data: {log_line.strip()}")
try:
print("Press Ctrl+C to exit...")
while True:
temperature = temperature_sensor.getTemperature()
humidity = humidity_sensor.getHumidity()
# send the data
message = f"{temperature},{humidity}\n"
sock.sendto(message.encode(), (UDP_IP, UDP_PORT))
print(f"Sent {message[:-1]} to {UDP_IP}:{UDP_PORT}")
log_data(temperature, humidity)
# print temperature and humidity on the same line
# print(f"Temperature: {temperature}C, Humidity {humidity}%")
time.sleep(INTERVAL)
except KeyboardInterrupt:
pass
finally:
temperature_sensor.close()
humidity_sensor.close()
sock.close()📎 phidget_temp_humidity_udp.py
Interfacing with matlab#
Matlab code for reading in temp and humidity values from the UDP port (assumes python code above is running!)
% Configuration
%udpPort = 5005; % Set to the same port number as used in the Python script
bufferSize = 1024;
% Create and configure UDP object
udpObj = udpport("byte", "IPV4", "LocalPort", 49160, "LocalHost", '127.0.0.1', ...
"Timeout", 5);
fopen(udpObj);
% flush the port
flush(udpObj)
%%
try
while true
% Check if there is data on the port
if udpObj.BytesAvailable > 1
while udpObj.BytesAvailable > 1
% Read data and parse the string into numeric temperature and humidity values
dataString = fscanf(udpObj);
dataValues = sscanf(dataString, "%f,%f");
temperature = dataValues(1);
humidity = dataValues(2);
% Display temperature and humidity values
end
fprintf("Temperature: %.2f °C, Humidity: %.2f %%\n", temperature, humidity);
end
% Wait a moment before checking for data again
pause(0.1);
end
catch ME
if strcmp(ME.identifier, 'MATLAB:dispatcher:InputArgOutOfRange')
disp("Exiting...");
else
rethrow(ME);
end
end
% Close and delete UDP object
fclose(udpObj);
delete(udpObj);
clear udpObj;📎 phidget_read_temp_and_hum_test_script.m
In theory, you could run the python code and have it continuously writing to UDP port and just read in what you need when you run your experiment in matlab. In practice though, for reasons I have not yet figured out, I have encountered issues in matlab connecting to the same UDP port repeatedly (for example on different trial runs) without restarting the python script. Thus, my current workflow is to run the python script from within matlab whenever I run a virmen experiment, and then connect to it. This is pretty much the same as triggering fictrac at the start of a matlab/virmen/flyg experiment. Here are the steps to do this (modify your paths and environent names appropriately):
- Create a windows batch file to run the python code:
@echo OFF
rem code modified from : https://gist.github.com/maximlt/531419545b039fa33f8845e5bc92edd6
rem How to run a Python script in a given conda environment from a batch file.
rem It doesn't require:
rem - conda to be in the PATH
rem - cmd.exe to be initialized with conda init
rem Define here the path to your conda installation
set CONDAPATH=C:\Users\Noah\miniconda3
rem Define here the name of the environment
set ENVNAME=phidget
rem The following command activates the base environment.
rem call C:\ProgramData\Miniconda3\Scripts\activate.bat C:\ProgramData\Miniconda3
if %ENVNAME%==base (set ENVPATH=%CONDAPATH%) else (set ENVPATH=%CONDAPATH%\envs\%ENVNAME%)
rem Activate the conda environment
rem Using call is required here, see: https://stackoverflow.com/questions/24678144/conda-environments-and-bat-files
call %CONDAPATH%\Scripts\activate.bat %ENVPATH%
rem Run a python script in that environment
python phidget_temp_humidity_udp.py
rem Deactivate the environment
call conda deactivate
rem If conda is directly available from the command line then the following code works.
rem call activate someenv
rem python script.py
rem conda deactivate
rem One could also use the conda run command
rem conda run -n someenv python script.py- In matlab: CD into the directory where you want the log file stored, and run the batch file
[~,~] = system('cd C:\Users\Noah\Documents\GitHub\virmNP_fly\phidget_control\ & run_temp_hum_phidget.bat &');- You’re good to go. You should see a command window appear and start to print the temp and humidity. Data will also become available on the UDP port to read.
Example of virmen integration#
Here’s how you could integrate it with virmen
%% example of one way you can integrate phidget readings into virmen
%% IN THE INITIALIZATION FUNCTION
% in the init function
% phidget input settings, matching python code
vr.ops.phidget_host = '127.0.0.1';
vr.ops.phidget_port = 49160;
vr.ops.phidget_read_function = @read_phidget_bcave2;
vr.ops.phidget_timeout = 60;
vr = read_phidget_bcave2(vr);
disp('waiting for phidget data');
pause(2)
if vr.phUDP.NumBytesAvailable==0
% phidget likely not running, try starting it up
disp('no data on port, starting up phidget');
[~,~] = system('cd C:\Users\Noah\Documents\GitHub\virmNP_fly\phidget_control\ & run_temp_hum_phidget.bat &');
pause(1)
vr = read_phidget_bcave2(vr);
end
%% IN THE RUNTIME FUNCTION
vr = read_phidget_bcave2(vr);
% this adds / sets the variables
% vr.last_phidget_read
% vr.phgt_enclosure_temp (-1 at start if no data present)
% vr.phgt_enclosure_hum (-1 at start if no data present)
% vr.phgt_data_age: age of data in seconds (time since last phidget read)
% vr.phgt_new_read: whether it is a new reading or not
%% You might need to terminate the phidget window before the next trial
function vr = read_phidget_bcave2(vr)
% check to see if phidget udp port is initialized
if ~isfield(vr,'phUDP')
disp('CONNECTING TO PHIDGET UDP PORT')
% set up code for fictrac
vr.phUDP = udpport("byte", "IPV4", "LocalPort", vr.ops.phidget_port, "LocalHost", vr.ops.phidget_host, ...
"Timeout", vr.ops.phidget_timeout, "EnablePortSharing", true);
%flush(vr.phUDP);
% initialize variables
vr.last_phidget_read = tic;
vr.phgt_enclosure_temp = -1;
vr.phgt_enclosure_hum = -1;
end
vr.phgt_data_age = toc(vr.last_phidget_read);
vr.phgt_new_read = 0;
% check to see if there are bytes on the port
if vr.phUDP.NumBytesAvailable == 0
if toc(vr.last_phidget_read)>5
warning('phidget data not present for over 5 seconds');
end
return
end
% if data on port, read it in
while vr.phUDP.NumBytesAvailable > 0
vr.phidget_raw_data = readline(vr.phUDP);
vr.last_phidget_read = tic;
vr.phgt_new_read = 1;
end
% parse the raw data
data_values = sscanf(vr.phidget_raw_data, "%f,%f");
vr.phgt_enclosure_temp = data_values(1);
vr.phgt_enclosure_hum = data_values(2);
end📎 phidget_virmen_integration_example.m
Expected results#
Using the above system, you can get relatively stable temperature and humidity

Note the actual temperature swings are likely a bit bigger than the phidget readings, I think it is buffered a bit by the sensor enclosure. Using a thermocouple would give faster and more accurate temperature readings and I think would be necessary if you want to use the phidget to directly control the heater.