Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
90d3d53
Update GitHub Action Versions
github-actions[bot] Jan 5, 2026
3527c7d
feat : add telemetrix-esp32 dependency in pyproject.toml
0x697A756B69 Mar 24, 2026
4158c06
add ESP32 WiFi hardware wrapper
0x697A756B69 Mar 24, 2026
9252050
add Fan, Heater, Address IP and Port config
0x697A756B69 Mar 24, 2026
72d8d5b
edit config
0x697A756B69 Mar 24, 2026
e274a02
chore: remove files for clean reimplementation
0x697A756B69 May 12, 2026
a48adf0
resolve merge conflicts
0x697A756B69 May 12, 2026
7ab2efd
add ArduinoWifi class base
0x697A756B69 May 12, 2026
0154476
feat : add asyncio event loop in dedicated thread
0x697A756B69 May 12, 2026
e771dc4
feat : add board initialization via asyncio
0x697A756B69 May 12, 2026
c58f4a2
fix: disable autostart to avoid event loop conflict
0x697A756B69 May 12, 2026
75254e6
feat: add _run helper + round_value utility
0x697A756B69 May 12, 2026
fec161a
feat: add set_pin_mode_analog_output
0x697A756B69 May 12, 2026
3a86296
fix: correct channel mapping for set_pin_mode_analog_output
0x697A756B69 May 12, 2026
9730059
feat: add analog_write
0x697A756B69 May 12, 2026
ea88ccc
fix: use pin directly for ledcWrite on ESP32
0x697A756B69 May 12, 2026
501f9d2
feat: add analog_write_and_memorize, set_pins_output_to, get_output_p…
0x697A756B69 May 12, 2026
d4e64a8
fix: improve board initialization and add debug logs
0x697A756B69 May 12, 2026
f572955
add : DAQ_Move_FanHeater class base
0x697A756B69 May 12, 2026
20ab984
add : params and ini_attributes
0x697A756B69 May 12, 2026
18df882
add : ini_stage and set_pins
0x697A756B69 May 12, 2026
7ceac82
feat : add get_actuator_value, close, commit_settings
0x697A756B69 May 12, 2026
133a73a
feat : add move_abs, move_rel, move_home, stop_motion
0x697A756B69 May 12, 2026
6b367e4
feat : add get_actuator_value, close, commit_settings
0x697A756B69 May 12, 2026
fced776
fix: remove debug prints and clean up code
0x697A756B69 May 12, 2026
f530fa2
fix: remove files
0x697A756B69 May 28, 2026
1cc90d5
Merge branch 'feature/daq-move-fanheater' into '5.0.x'
0x697A756B69 May 28, 2026
6cb2a5f
resolve merge conflicts
0x697A756B69 May 28, 2026
9d0a1d9
add MAX31865 class
0x697A756B69 May 28, 2026
fc6d95e
add MAX31865 class pattern injection
0x697A756B69 May 28, 2026
70fc8a2
Add SPI initialization and MAX31865 config
0x697A756B69 May 28, 2026
1072c02
Add read_rtd_resistance method via SPI
0x697A756B69 May 28, 2026
d5104bd
Add temperature conversion and get_temperature method
0x697A756B69 May 28, 2026
cb25e87
Add DAQ_0DViewer_PT100 class and delete useless files and folders
0x697A756B69 May 28, 2026
30e4f76
Add params and attribute
0x697A756B69 Jun 1, 2026
80bd5fe
Add ini_detector and close methods
0x697A756B69 Jun 1, 2026
5b3d0b8
Add grab_data method and temperature acquisition
0x697A756B69 Jun 1, 2026
8db22c6
Add stop method
0x697A756B69 Jun 1, 2026
8302ea1
Fix stop and main
0x697A756B69 Jun 1, 2026
1cbf707
Fix change name file
0x697A756B69 Jun 1, 2026
9d75aa8
Fix change name file
0x697A756B69 Jun 1, 2026
ddb422b
fix: rename max31965 to max31865 rename file
0x697A756B69 Jun 1, 2026
79660ad
Fix update import path to spi_max31865
0x697A756B69 Jun 1, 2026
da8a67d
Fix use method for wait SPI callback reponse
0x697A756B69 Jun 1, 2026
f2692e6
Feat SPI pins configurable from config_template.toml
0x697A756B69 Jun 2, 2026
606e3b4
Add max31865 pins config
0x697A756B69 Jun 2, 2026
7a7780f
Feat SPI pins in Pymodaq dashboard
0x697A756B69 Jun 2, 2026
fbc0756
Feat Custom SPI pins via SPI_INIT Command
0x697A756B69 Jun 2, 2026
5e44f2e
Fix SPI protocol
0x697A756B69 Jun 2, 2026
29bdf0c
Fix all structures and edit comments
0x697A756B69 Jun 4, 2026
7e9fe22
Add instance on gitignore
0x697A756B69 Jun 6, 2026
f1b4567
Add env with Pixi
0x697A756B69 Jun 6, 2026
f6ef10c
Edit README
0x697A756B69 Jun 8, 2026
ae0d72a
Fix GPIO pins for sensors
0x697A756B69 Jun 9, 2026
30ac7e3
Merge branch 'feature/sensor-pt100-max31865' into '5.0.x'
0x697A756B69 Jun 9, 2026
feadd09
Add Feature ADS1115.py
0x697A756B69 Jun 9, 2026
b924448
Merge branch 'feature/sensor-GY-ADS1115' into '5.0.x'
0x697A756B69 Jun 10, 2026
2b5fca2
Fix improve SPI
0x697A756B69 Jun 11, 2026
81cf13d
Fix name MOVE
0x697A756B69 Jun 11, 2026
eb0e24f
Fix I2C and SPI protocol communication
0x697A756B69 Jun 11, 2026
8a92673
Fix name of DAQ_VIEWER
0x697A756B69 Jun 11, 2026
181f537
Fix SPI
0x697A756B69 Jun 11, 2026
6907198
Merge branch 'I2C-FIX' into '5.0.x'
0x697A756B69 Jun 11, 2026
8b54113
chore: prepare branch for upstream PR — remove local/dev artifacts
VIL-CIEL Jun 26, 2026
89156a2
fix: make ESP32 plugin import-safe without telemetrix at load time
VIL-CIEL Jun 26, 2026
8531074
fix: pin bleak<1.0 so telemetrix-esp32 stays importable
VIL-CIEL Jun 26, 2026
3a18a9c
Fix readme
0x697A756B69 Jun 27, 2026
b406db1
Fix review feedback
0x697A756B69 Jun 29, 2026
1af638d
Fix commentary Units
0x697A756B69 Jun 30, 2026
502c00b
Fix drop old bleak
0x697A756B69 Jun 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 63 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@ pymodaq_plugins_arduino
:target: https://github.com/PyMoDAQ/pymodaq_plugins_arduino/actions/workflows/Test.yml


This package regroups a list of instrument created around an arduino board. Some instruments use the
Telemetrix library to use python together with the arduino board.

This package regroups a list of instruments created around an Arduino or ESP32 board. Some
instruments use the Telemetrix library to use Python together with the Arduino board. Others use the
Telemetrix AIO ESP32 library to communicate with an ESP32 board over WiFi.

Authors
=======

* Sebastien J. Weber (sebastien.weber@cemes.fr)
* Jérémie Margueritat

* Mohamed El Mokhtari (mohamed.elmokhtari26@gmail.com)
* Fabien Villedieu (fabien.villedieu.pro@gmail.com)

Instruments
===========
Expand All @@ -40,24 +41,41 @@ Actuators

* **LED**: control of a multicolor LED using three PWM digital outputs and the Telemetrix library.
Allows the control of the three color channel independently

* **LEDwithLCD**: same as **LED** actuator but displaying the red, green, blue values on a standard 16x2 liquid crystal
display
* **Analog**: data acquisition from analog inputs

* **Servo**: control of a servo motor position (in degrees) on a digital pin using the Telemetrix
library

* **Analog**: data acquisition from analog inputs

* **FanHeater**: control of a heater and a fan using two PWM outputs and the Telemetrix AIO ESP32
library. Allows the control of the heater and fan independently over WiFi. The PWM duty cycle
ranges from 0 to 255 (8-bit resolution)

Extensions
==========

* **ColorSynthesizer**: DashBoard extension using RBG LED actuators. Allows to quicly select a RGB value and apply those
* **ColorSynthesizer**: DashBoard extension using RBG LED actuators. Allows to quickly select a RGB value and apply those
to the actuators

Viewers
=======

* **Voltage_ADS1115**: reads up to four single-ended analog voltages (AIN0–AIN3) from an ADS1115
(16-bit) or ADS1015 (12-bit) I2C ADC. The PGA gain, data rate and number of active channels are
configurable. Communication is performed over I2C by an ESP32 running the Telemetrix AIO WiFi
firmware.

* **Temperature MAX31865**: reads temperature from a PT100 resistance temperature detector (RTD) wired to a
MAX31865 amplifier/ADC board. Communication with the MAX31865 is performed over bit-banged SPI
by an ESP32 running the Telemetrix AIO WiFi firmware.

Installation instructions
=========================

* PyMoDAQ version > 4.1.0

* PyMoDAQ version > 5.0.0

LED actuator
++++++++++++
Expand All @@ -75,12 +93,49 @@ I2C backpack. The functionalities used to drive the LCD are adapted from a micro
(https://github.com/brainelectronics/micropython-i2c-lcd) itself adapted from
https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library

Servo actuator
++++++++++++++

The **Servo** actuator uses the telemetrix library. The corresponding sketch should therefore be
uploaded on the arduino board. This allows to drive a servo motor connected to a digital pin of the
Arduino board, the position being commanded in degrees. See https://mryslab.github.io/telemetrix/

Analog 0D viewer
++++++++++++++++

The **Analog** 0D viewer uses the telemetrix library. The corresponding sketch should therefore be uploaded
on the arduino board. This allows to acquire data from the analog inputs on an Arduino board from python objects on
the connected computer. See https://mryslab.github.io/telemetrix/

FanHeater actuator
++++++++++++++++++

The **FanHeater** actuator uses the telemetrix-aio-esp32 library. The corresponding firmware should
therefore be uploaded on the ESP32 board. This allows to control a heater and a fan connected to
the ESP32 over WiFi from python objects on the connected computer.
See https://mryslab.github.io/telemetrix-esp32/

Voltage_ADS1115 0D viewer
+++++++++++++++++++++++++

The **Voltage_ADS1115** 0D viewer uses the telemetrix-aio-esp32 library. The corresponding firmware
should therefore be uploaded on the ESP32 board. This allows to acquire analog voltages from an
ADS1115/ADS1015 I2C ADC connected to the ESP32 over WiFi.
See https://mryslab.github.io/telemetrix-esp32/

Temperature MAX31865 0D viewer
+++++++++++++++

The **Temperature MAX31865** 0D viewer uses the telemetrix-aio-esp32 library. The corresponding firmware should
therefore be uploaded on the ESP32 board. This allows to acquire temperature data from a PT100
sensor wired to a MAX31865 board connected to the ESP32 over WiFi.

Here are `detailed installation instructions <https://pymodaq.cnrs.fr/en/latest/lab_story_folder/arduino_ubuntu.html#>`_.

Wiki
====

Additional documentation for this fork is available on the dedicated Wiki:
https://wiki-plugins-dap-pymodaq.github.io/


1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ description = 'Set of instrument plugins implemented using an Arduino Board'
dependencies = [
"pymodaq>=5.0.0",
'telemetrix',
'telemetrix-esp32>=2.1.1',
'pyvisa',
'pyvisa-py',

Expand Down
149 changes: 149 additions & 0 deletions src/pymodaq_plugins_arduino/daq_move_plugins/daq_move_FanHeater.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
from typing import Optional

from pymodaq.control_modules.move_utility_classes import (DAQ_Move_base, comon_parameters_fun, main,
DataActuatorType, DataActuator)
from pymodaq_utils.utils import ThreadCommand
from pymodaq_gui.parameter import Parameter

from pymodaq_plugins_arduino.hardware.esp32_telemetrix import ArduinoWifi
from pymodaq_plugins_arduino.utils import Config

config = Config()


class DAQ_Move_FanHeater(DAQ_Move_base):
"""Instrument plugin class for the heater and fan actuators.

This object inherits all functionalities to communicate with PyMoDAQ's DAQ_Move module through
inheritance via DAQ_Move_base. It makes a bridge between the DAQ_Move module and the Python
wrapper of a particular instrument.

Both actuators are driven by a XY-MOS PWM board connected to an ESP32 over WiFi using the
Telemetrix AIO protocol. The PWM duty cycle ranges from 0 to 255 (8-bit resolution).

Heater
Fan

Attributes:
-----------
controller: object
The particular object that allows communication with the hardware, in general a Python
wrapper around the hardware library.
"""

_controller_units = '' # raw PWM level (0-255) unit depends on wired device
is_multiaxes = True
_axis_names = {
'Heater': config('esp32', 'pins', 'heater_pin'),
'Fan': config('esp32', 'pins', 'fan_pin'),
}
_epsilon = 0.1
data_actuator_type = DataActuatorType['DataActuator']

params = [
{'title': 'IP Address:', 'name': 'ip_address', 'type': 'str',
'value': config('esp32', 'ip_address')},
] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilon)

def ini_attributes(self):
self.controller: Optional[ArduinoWifi] = None

def get_actuator_value(self):
"""Get the current value from the hardware with scaling conversion.

Returns
-------
float: The position obtained after scaling conversion.
"""
pos = DataActuator(data=self.controller.get_output_pin_value(self.axis_value), units=self.axis_unit)
pos = self.get_position_with_scaling(pos)
return pos

def close(self):
"""Terminate the communication protocol"""
if self.is_master:
self.controller.set_pins_output_to(0)
self.controller.shutdown()

def commit_settings(self, param: Parameter):
"""Apply the consequences of a change of value in the detector settings

Parameters
----------
param: Parameter
A given parameter (within detector_settings) whose value has been changed by the user
"""
pass

def ini_stage(self, controller=None):
"""Actuator communication initialization

Parameters
----------
controller: (object)
custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller
(Master case)

Returns
-------
info: str
initialized: bool
False if initialization failed otherwise True
"""

if self.is_master:
self.controller = ArduinoWifi(
ip_address=self.settings['ip_address']
)
self.set_pins()
else:
self.controller = controller

info = "Heater and Fan ready"
initialized = True
return info, initialized

def set_pins(self):
"""Configure every axis pin as a PWM output and reset it to 0."""
for pin in self._axis_names.values():
self.controller.set_pin_mode_analog_output(pin)
self.controller.analog_write_and_memorize(pin, 0)

def move_abs(self, value: DataActuator):
"""Move the actuator to the absolute target defined by value

Parameters
----------
value: (float) value of the absolute target positioning
"""
value = self.check_bound(value) # apply user-defined bounds
self.target_value = value
value = self.set_position_with_scaling(value) # apply scaling if the user specified one

self.controller.analog_write_and_memorize(self.axis_value, int(value.value(self.axis_unit)))

def move_rel(self, value: DataActuator):
"""Move the actuator to the relative target actuator value defined by value

Parameters
----------
value: (float) value of the relative target positioning
"""
value = self.check_bound(self.current_position + value) - self.current_position
self.target_value = value + self.current_position
value = self.set_position_relative_with_scaling(value)

# PWM value is set in duty-cycle counts (0–255)
self.controller.analog_write_and_memorize(self.axis_value, int(self.target_value.value(self.axis_unit)))

def move_home(self):
"""Call the reference method of the controller"""
self.controller.analog_write_and_memorize(self.axis_value, 0)

def stop_motion(self):
"""Stop the actuator and emits move_done signal"""
pass


if __name__ == '__main__':
main(__file__)
Loading