diff --git a/README.md b/README.md index ebe982db8752e40381844efb4b3e83e615d6cb92..84fe85a93b7937cec09e15f0a7618e75d7ef2303 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,9 @@ * [Contributing to labRemote](#contributing-to-labremote) * [Creating New labRemote Based Projects](#creating-new-projects) * [Python Bindings](#python-bindings) + * [Requirements for Python Bindings](#requirements-for-python-bindings) + * [Installation with pip](#installation-with-pip) + * [How to use the Python bindings](#how-to-use-the-python-bindings) <!----------------------------------------------------------------------------> <!----------------------------------------------------------------------------> @@ -126,27 +129,60 @@ An example `labRemote-app` is provided for by [ExampleLabRemoteProject](https:// <!----------------------------------------------------------------------------> # Python Bindings -`python` bindings to the `labRemote` target can be enabled by setting a flag during -the `cmake` compilation steps. +Python bindings to the `labRemote` target can be enabled either by setting a flag +during the [cmake compilation step](#compiling-labremote) or via `pip`. -Note that `python` version 3 is required for these bindings, and you are required -to have the `python` development packages installed on your system. +## Requirements for Python Bindings -To install `labRemote` with the `python` bindings enabled follow these steps: +A `python` version `>=3.6` is required in order to build the `python` bindings for `labRemote`. Ensure that +you have the needed `python` source files and development packages installed on your system. +On `CentOS` this can be achieved with: ```shell -yum install python3-devel # if needed on your system -cd /path/to/labRemote -mkdir build -cd build -cmake3 -DUSE_PYTHON=on .. -make -j4 -export PYTHONPATH=/path/to/labRemote/build/lib:$PYTHONPATH -python3 -c "import _labRemote as labRemote" # this should succeed if everything compiled OK +yum install python3-devel +``` +More information on how to install `python` can be found [here](https://github.com/dantrim/danny_installs_python). + +## Installation with `pip` + +You can build the `labRemote` `python` bindings using `pip`. There are two ways you can do this: + - [Method Number One](#method-number-one): requires that you first [clone the `labRemote` repository as usual](#getting-the-code) + - [Method Number Two](#method-number-two): does not require that you manually clone `labRemote` + +The two methods are outlined below. + +### Method Number One +This method assumes that you have [cloned the `labRemote` repository](#getting-the-code) and is the method +to use if you need to have the `labRemote` `C++` source code and/or utilities available in addition to using +the `python` bindings. For example, if you are a developer you should follow this method. + +The method is outlined here: +```shell +cd /path/to/labRemote +python3 -m venv myenv +source myenv/bin/activate # start the virtual environment +{myenv} python -m pip install -U pip +{myenv} python -m pip install . +{myenv} python -c "import labRemote" ``` +Note that you are not absolutely required to follow the [usual CMake build procedure](#compiling-labremote) in order +for the steps above to succeed: the necessary build for the `labRemote` module occurs during the `pip install .` step. +Of course, if you wish to use the `C++` utilities you will still need to [compile labRemote as usual](#compiling-labremote) +even after following the above steps. + -Alternatively, one can choose to install the `labRemote` `python` bindings via `pip`: +### Method Number Two + +This method does not require that you have cloned the `labRemote` repository beforehand, and is as follows: ```shell -yum install python3-devel # if needed on your system -python3 -m pip install git+https://gitlab.cern.ch/berkeleylab/labRemote.git@devel +python3 -m venv myenv +source myenv/bin/activate # start the virtual environment +{myenv} python -m pip install -U pip +{myenv} python -m pip install git+https://gitlab.cern.ch/berkeleylab/labRemote.git@devel +{myenv} python -c "import labRemote" ``` + +## How to use the `python` bindings + +Examples of how to use the `labRemote` `python` bindings are provided in the [examples](src/examples) directory, +where you will find `python` versions of existing `C++` `labRemote` examples. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0bdc44bfa3f7b57ba39ec1e83b5b5935b482a8da..e700dbbe48e2be247da5dc56ed15960bd0295594 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,7 +33,7 @@ find_package( libusb-1.0 ) if ( (${LIBFTDI1_FOUND}) AND (${LIBMPSSE_FOUND}) ) message(STATUS "Enabling FTDI and MPSSE support") set(ENABLE_FTDI 1) - add_definitions(-DFTDI_ENABLED) + add_definitions(-DENABLE_FTDI) else() message(STATUS "Disabling FTDI code due to missing libftdi1 or libmpsse:") message(STATUS " LIBFTDI1_FOUND = ${LIBFTDI1_FOUND}") diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index eb23d778ba13b4157eedbe9f5b1dfd49c34e7ba3..2571aa7be8253d6394a9831e18f390b13178e09d 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -1,19 +1,77 @@ -# add executables +# setup the output directory for the executables set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples) -file(GLOB examples [a-zA-Z]*.cpp) +# control powersupply +add_executable(control_powersupply_example) +target_sources(control_powersupply_example PRIVATE control_powersupply_example.cpp) +target_link_libraries(control_powersupply_example PRIVATE Utils PS EquipConf) -foreach(target ${examples}) - get_filename_component(execname ${target} NAME_WE) - get_filename_component(srcfile ${target} NAME) +# fluke +add_executable(fluke_example) +target_sources(fluke_example PRIVATE fluke_example.cpp) +target_link_libraries(fluke_example PRIVATE Utils Meter Com) - add_executable(${execname}) - target_sources(${execname} PRIVATE ${srcfile}) - target_link_libraries(${execname} PRIVATE Com PS Meter Load DevCom Utils EquipConf Chiller) +# scope +if ( ${libScope_FOUND} ) + add_executable(scope_example) + target_sources(scope_example PRIVATE scope_example.cpp) + target_link_libraries(scope_example PRIVATE Utils Scope) + target_compile_definitions(scope_example PRIVATE SCOPE=1) +endif() - if( ${libScope_FOUND} ) - target_link_libraries(${execname} PRIVATE Scope) - target_compile_definitions(${execname} PRIVATE SCOPE=1) - endif() +# chiller +add_executable(chiller_example) +target_sources(chiller_example PRIVATE chiller_example.cpp) +target_link_libraries(chiller_example PRIVATE Utils Chiller Com) -endforeach() +# datasink +add_executable(datasink_example) +target_sources(datasink_example PRIVATE datasink_example.cpp) +target_link_libraries(datasink_example PRIVATE Utils EquipConf) + +# devcomuino +add_executable(devcomuino_example) +target_sources(devcomuino_example PRIVATE devcomuino_example) +target_link_libraries(devcomuino_example PRIVATE Com Utils) + +# ntc +add_executable(ntc_example) +target_sources(ntc_example PRIVATE ntc_example.cpp) +target_link_libraries(ntc_example PRIVATE Com DevCom Utils) + +# sht85 +if ( ${ENABLE_FTDI} ) + add_executable(sht85_example) + target_sources(sht85_example PRIVATE sht85_example.cpp) + target_link_libraries(sht85_example PRIVATE DevCom Utils) +endif() + +# ftdi adc +if ( ${ENABLE_FTDI} ) + add_executable(ft232h_adc_example) + target_sources(ft232h_adc_example PRIVATE ft232h_adc_example.cpp) + target_link_libraries(ft232h_adc_example PRIVATE Utils DevCom) + #target_compile_definitions(ft232h_adc_example PUBLIC FTDI=1) +endif() + +# uio +add_executable(uio_example) +target_sources(uio_example PRIVATE uio_example.cpp) +target_link_libraries(uio_example PRIVATE DevCom Com Utils) + +# Si7021 example +add_executable(Si7021_example) +target_sources(Si7021_example PRIVATE Si7021_example.cpp) +target_link_libraries(Si7021_example PRIVATE Utils DevCom) + +# temp hum monitor +add_executable(temp_hum_monitor_example) +target_sources(temp_hum_monitor_example PRIVATE temp_hum_monitor_example.cpp) +target_link_libraries(temp_hum_monitor_example PRIVATE Com DevCom EquipConf) + +# tempsensor monitor +if ( ${ENABLE_FTDI} ) + add_executable(tempsensor_monitor_example) + target_sources(tempsensor_monitor_example PRIVATE tempsensor_monitor_example.cpp) + target_link_libraries(tempsensor_monitor_example PRIVATE DevCom) +endif() diff --git a/src/examples/README.md b/src/examples/README.md index 997eceb4ec84bc011afeea2757ea379a1ee63dc3..bf1dfba8be686d24eda959b9531a68eec15a2057 100644 --- a/src/examples/README.md +++ b/src/examples/README.md @@ -1,13 +1,55 @@ # Examples -## Power supply: control_powersupply_example +Tabulated below are the `labRemote` examples, with some additional details related to them. + +| Example Name | Has Python Version? | `labRemote` libraries Used | Devices Used | Communication Used | +| -- | -- | -- | -- | -- | +| datasink_example | [Yes](src/examples/datasink_example.py) | `libDataSink` | | | +| [control_powersupply_example](#power-supply-control_powersupply_example) | [Yes](src/examples/control_powersupply_example.py) | `libDevCom`, `libPS`, `libEquipConf` | | +| ntc_example | [Yes](src/examples/ntc_example.py) | `libCom`, `libDevCom` | [NTCSensor](src/libDevCom/NTCSensor.h) | [TextSerialCom](src/libCom/TextSerialCom.h) | +| sht85_example | [Yes](src/examples/sht85_example.py) | `libDevCom`, `libCom` | [FT232H](src/libDevCom/FT232H.h), [SHT85](src/libDevCom/SHT85.h) | [I2CDevComuino](src/libCom/I2CDevComuino.h) | +| ft232h_adc_example | [Yes](src/examples/ft232h_adc_example.py) | `libDevCom` | [AD7998](src/libDevCom/AD799X.h), [FT232H](src/libDevCom/FT232H.h), [I2CFTDICom](src/libDevCom/I2CFTDICom) | +| tempsensor_monitor_example | [Yes](src/examples/tempsensor_monitor_example.py) | `libDevCom` | [FT232H](src/libDevCom/FT232H.h), [HIH61300](src/libDevCom/HIH6130.h), [I2CFTDICom](src/libDevCom/I2CFTDICom.h) | | +| temp_hum_monitor_example | [Yes](src/examples/temp_hum_monitor_example.py) | `libDevCom`, `libDataSink`, `libCom` | [NTCSensor](src/libDevCom/NTCSensor.h), [PtSensor](src/libDevCom/PtSensor.h), [HIH4000](src/libDevCom/HIH4000.h), [ADCDevComuino](src/libDevCom/ADCDevComuino.h) | [TextSerialCom](src/libCom/TextSerialCom.h)| +| [devcomuino_example](#arduino-communication) | [Yes](src/examples/devcomuino_example.py) | `libCom` | | [TextSerialCom](src/libCom/TextSerialCom) | +| chiller_example | [Yes](src/examples/chiller_example.py) | `libChiller` | [PolySciLM](src/libChiller/PolySciLM.h) | [TextSerialCom](src/libCom/TextSerialCom) | +| Si7021_example | [Yes](src/examples/Si7021_example.py) | `libDevCom` | [Si7021](src/libDevCom/Si7021.h), [I2CDevComuino](src/libDevCom/I2CDevComuino.h) | | +| fluke_example | No | `libMeter` | [Fluke8842](src/libMeter/Fluke8842.h) | | +| scope_example | No | `libScope` | [PS6000](src/libScope/PS6000.h) | | +| pixel_scope_run | No | `libScope` | [PS6000](src/libScope/PS6000.h) | | + + + + +## Arduino Communication +Communication with an an Arduino-compatible micro-controller is enabled by flashing the [devcomuino.ino](arduino/devcomuino/devcomuino.ino) +firmware onto the micro-controller, after which it will respond to specific commands sent to it over its serial communication (established +via the USB serial connection with the device). + +The [devcomuino_example](src/examples/devcomuino_example.py) illustrates how to send commands and receive responses from +a micro-controller running the [devcomuino.ino](arduino/devcomuino/devcomuino.ino) firmware using the [TextSerialCom](src/libCom/TextSerialCom.h) class. + +## Power supply: `control_powersupply_example` To run it, use: -./build/bin/control_powersupply_example ./src/configs/input-hw.json low-voltage +```shell +$ ./examples/control_powersupply_example ./src/configs/input-hw.json low-voltage +``` + +Further information about its use: +```shell +$ ./examples/control_powersupply_example +[ERROR] : Missing positional arguments. +Usage: ./examples/control_powersupply_example configfile.json channelname + +List of options: + -d, --debug Enable more verbose printout, use multiple for increased debug level + +``` This simple script shows how to create the hardware database and how to configure one of the channels listed (low-voltage). Then it programs the channel using the settings in the program block of the json file (this is optional), it powers the channel on, returns the measured voltage and current and powers it off. To access functions that are only defined for a specific power supply, one needs to cast the pointer to the IPowerSupply object back to -the specific class. For example, beefOff() is a function that is implemented only for AgilentPS. +the specific class. For example, beepOff() is a function that is implemented only for AgilentPS. The advantage of using the logical channel (PowerSupplyChannel) is that the same executable will work regardless of the power supply being multi- or single-channel. The only change must be in the json file. diff --git a/src/libDevCom/examples/Si7021_example.cpp b/src/examples/Si7021_example.cpp similarity index 100% rename from src/libDevCom/examples/Si7021_example.cpp rename to src/examples/Si7021_example.cpp diff --git a/src/examples/Si7021_example.py b/src/examples/Si7021_example.py new file mode 100644 index 0000000000000000000000000000000000000000..90d7999ffb096459f5c6865b4a0a56f1be32310a --- /dev/null +++ b/src/examples/Si7021_example.py @@ -0,0 +1,61 @@ +from labRemote import devcom, com + +from argparse import ArgumentParser +import sys +from datetime import datetime + +import logging +logging.basicConfig() +logger = logging.getLogger("Si7021_example") + +def si7021_example(device, baud, address) : + + ## + ## initialize the communication line + ## + try : + serial = com.TextSerialCom(device, baud) + except : + logger.error(f"Unable to initialize serial communication with device at port \"{device}\"") + sys.exit(1) + if serial is None : + logger.error(f"Returned communication device is None") + sys.exit(1) + serial.setTermination("\r\n") + serial.init() + + ## + ## setup the I2C bus line + ## + i2c = devcom.I2CDevComuino(address, serial) + + ## + ## initialize the sensor + ## + sensor = devcom.Si7021(i2c) + sensor.init() + + ## + ## perform continuous measurements + ## + while True : + time.sleep(1) + sensor.read() + now = datetime.now() + logger.info(f"Measurement[{now}]: humidity = {sensor.humidity()}, temperature = {sensor.temperature()}") + +if __name__ == "__main__" : + + parser = ArgumentParser(description = "Example of how to use the Si7021 device") + parser.add_argument("-p", "--port", required = True, type = str, + help = "Port at which the device is connected (e.g. /dev/ttyAMC0)" + ) + parser.add_argument("-b", "--baud", default = 9600, type = int, + help = "Baud rate for serial communication with the device" + ) + parser.add_argument("-a", "--address", default = 0x40, type = int, + help = "I2C bus address for the Si7021 device" + ) + args = parser.parse_args() + + si7021_example(args.port, args.baud, args.address) diff --git a/src/examples/chiller_example.py b/src/examples/chiller_example.py index 34ff6c53f038ff20549244cf0cdd637680275a29..9c0a3a3aa4d68babc44b63e6828524d9084ef161 100644 --- a/src/examples/chiller_example.py +++ b/src/examples/chiller_example.py @@ -1,22 +1,56 @@ from labRemote import chiller, com + import time +from argparse import ArgumentParser + +import logging +logging.basicConfig() +logger = logging.getLogger("chiller_example") + +def chiller_example(device, baud, temperature) : + + ## + ## setup the communication line + ## + try : + serial = com.TextSerialCom(device, baud) + except : + logger.error(f"Unable to initialize serial communication with device at port \"{device}\"") + sys.exit(1) + if serial is None : + logger.error("Returned communication device is None") + sys.exit(1) + serial.setTermination("\r") + + ## + ## setup the chiller device + ## + chiller = chiller.PolySciLM() + chiller.setCom(serial) + chiller.init() + chiller.turnOn() + chiller.setTargetTemperature(temperature) + + ## + ## continous monitoring + ## + while True : + temp = chiller.measureTemperature() + set_temp = chiller.getTargetTemperature() + logger.info(f"Set temperature: {set_temp:0.4f}, Measured temperature: {temp:0.4f}") + time.sleep(1) # 1 second between measurements + +if __name__ == "__main__" : + parser = ArgumentParser(description = "labRemote libChiller example") + parser.add_argument("-p", "--port", required = True, type = str, + help = "Set the address of the port connected to the chiller (e.g. /dev/ttyUSB1)" + ) + parser.add_argument("-b", "--baud", default = 9600, type = int, + help = "Set the baud rate of the chiller" + ) + parser.add_argument("-t", "--temperature", default = 15, type = float, + help = "Set the target temperature of the chiller (degrees Celsius)" + ) + args = parser.parse_args() -device="/dev/ttyUSB1" -baud=9600 -target_temp=15 - -com_obj = com.TextSerialCom(device,baud) -com_obj.setTermination("\r") - -chiller_obj = chiller.PolySciLM() -chiller_obj.setCom(com_obj) -chiller_obj.init() -chiller_obj.turnOn() -chiller_obj.setTargetTemperature(target_temp) - -while True: - temp=chiller_obj.measureTemperature() - print("Temperature: "+str(temp)) - stemp=chiller_obj.getTargetTemperature() - print("Set Temperature: "+str(stemp)) - time.sleep(1) + chiller_example(args.port, args.baud, args.temperature) diff --git a/src/examples/control_powersupply_example.py b/src/examples/control_powersupply_example.py index 9d0cebf054dfeb5162a6e13dca89094c7c64b83e..53c4ebad80deca9b45bca2d656087879bca012f5 100644 --- a/src/examples/control_powersupply_example.py +++ b/src/examples/control_powersupply_example.py @@ -1,50 +1,80 @@ -import labRemote -import argparse -import logging +from labRemote import ps, ec +from argparse import ArgumentParser +from pathlib import Path +import sys +import logging logging.basicConfig() -logger = logging.getLogger(__name__) +logger = logging.getLogger("control_powersupply_example") + +def control_powersupply_example(config_file, channel_name) : -def main(configFile, channelName): - logger.debug(f"Settings:\nConfig file: {configFile}\nPS channel: {channelName}") - hw = labRemote.ec.EquipConf() - hw.setHardwareConfig(configFile) + ## + ## print the user input + ## + logger.info("Settings:") + logger.info(f" Configuration file : {config_file}") + logger.info(f" Channel name : {channel_name}") - logger.debug("Configuring power-supply.") - PS = hw.getPowerSupplyChannel(channelName) + ## + ## setup the hw + ## + equip_config = ec.EquipConf() + try : + equip_config.setHardwareConfig(config_file) + except : + logger.error("Unable to load hardware configuration") + sys.exit(1) - logger.debug("Program power-supply.") - PS.program() + ## + ## get the requested power supply channel + ## + ps_channel = hw.getPowerSupplyChannel(channel_name) + if ps_channel is None : + logger.error(f"Unable to get power supply channel \"{channel_name}\"") + sys.exit(1) - logger.debug("Sending command to power-supply.") - logger.info("Program") - PS.program() - logger.info("Turn ON") - PS.turnOn() - logger.info(f"Voltage: {PS.measureVoltage()}\nCurrent: {PS.measureCurrent()}") + ## + ## do some programming control and measurement actions + ## + logger.info(f"Program channel \"{channel_name}\"") + ps_channel.program() - Agilent = PS.getPowerSupply() - if not isinstance(Agilent, labRemote.ps.AgilentPs): - logger.warning("The power supply is not an Agilent, can't execute the required action") - else: - logger.info("Switch off beeping for Agilent PS") - Agilent.beepOff() + logger.info(f"Turn on channel \"{channel_name}\"") + ps_channel.turnOn() - logger.info("Turn OFF") - PS.turnOff() + logger.info(f"Channel \"{channel_name}\" measurements:") + logger.info(f" Voltage: {ps_channel.measureVoltage()}") + logger.info(f" Current: {ps_channel.measureCurrent()}") - logger.info("All done") - return 0 + ## + ## Agilent-specific stuff + ## + agilent = ps_channel.getPowerSupply() + if not isinstance(agilent, ps.AgilentPs) : + logger.warning("The power supply is not an Agilent power supply") + else : + logger.info("Switch off beeping for Agilent powersupply") + agilent.beepOff() + + logger.info(f"Turning of channel \"{channel_name}\"") + ps_channel.turnOff() + + logger.info("Done!") if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('configfile', type=str) - parser.add_argument('channelname', type=str) - parser.add_argument('-d','--debug', help='Enable more verbose printout, use multiple for increased debug level', action='store_true') + parser = ArgumentParser(description = "Powersupply control example") + parser.add_argument("config-file", type = str, + help = "labRemote JSON configuration file for the power supply device" + ) + parser.add_argument("channel-name", type = str, + help = "Powersupply channel to control" + ) args = parser.parse_args() - if args.debug: - logger.setLevel(logging.DEBUG) - - main(args.configfile, args.channelname) + config = Path(args.config_file) + if not config.exists() or not config.is_file() : + logger.error(f"Provided configuration file \"{args.config_file}\" could not be found") + sys.exit(1) + control_powersupply_example(args.config_file, args.channel_name) diff --git a/src/examples/datasink_example.py b/src/examples/datasink_example.py index 42e5e2f496ecc475b81ac0e67f1ccc3aad05702d..3b285c5ca85c125b58faef97372693cc594bdd58 100644 --- a/src/examples/datasink_example.py +++ b/src/examples/datasink_example.py @@ -1,27 +1,62 @@ from labRemote import ec -import datetime, argparse -def main(configFile, sinkName): - #Create sink - ds=ec.DataSinkConf() - ds.setHardwareConfig(configFile) - sink = ds.getDataSink(sinkName); +from datetime import datetime +from argparse import ArgumentParser +import sys +from pathlib import Path + +import logging +logging.basicConfig() +logger = logging.getLogger("datasink_example") + +def datasink_example(config_file, stream_name) : + + ## + ## Create the datastream + ## + ds = ec.DataSinkConf() + try : + ds.setHardwareConfig(config_file) + except : + logger.error("Failed to load datastream configuration") + sys.exit(1) + if ds is None : + logger.error("Failed to load datastream configuration (it is None)") + sys.exit(1) + + stream = ds.getDataStream(stream_name) + if stream is None : + logger.error(f"Requested stream \"{stream_name}\" not found") + sys.exit(1) - sink.startMeasurement("climate", datetime.datetime.now()) - sink.setField("temperature", 48623) - sink.recordPoint() - sink.endMeasurement() + ## + ## record some measurements + ## + stream.startMeasurement("climate", datetime.now()) + stream.setField("temperature", 48623) + stream.recordPoint() + stream.endMeasurement() - sink.setTag("location","fridge") - sink.startMeasurement("climate", datetime.datetime.now()) - sink.setField("temperature", -23453.1) - sink.recordPoint() - sink.endMeasurement() + stream.setTag("location","fridge") + stream.startMeasurement("climate", datetime.now()) + stream.setField("temperature", -23453.1) + stream.recordPoint() + stream.endMeasurement() if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('configFile', type=str) - parser.add_argument('sinkName', type=str) + + parser = ArgumentParser(description = "Example of how to use DataSinks and DataStreams") + parser.add_argument("config-file", type = str, + help = "labRemote JSON configuration file for datasink and datastream object(s)" + ) + parser.add_argument("stream-name", type = str, + help = "Name of the datastream to use" + ) args = parser.parse_args() - - main(args.configFile, args.sinkName) + + config_path = Path(args.config_file) + if not config_path.exists() or not config_path.is_file() : + logger.error(f"Provided configuration file \"{args.config_file}\" could not be found") + sys.exit(1) + + datasink_example(args.config_file, args.stream_name) diff --git a/src/examples/devcomuino_example.py b/src/examples/devcomuino_example.py new file mode 100644 index 0000000000000000000000000000000000000000..bf1812a123661a996a28d77788ef1789ce6c83d6 --- /dev/null +++ b/src/examples/devcomuino_example.py @@ -0,0 +1,92 @@ +from labRemote import com + +from argparse import ArgumentParser +import sys, time + +import logging +logging.basicConfig() +logger = logging.getLogger("devcomuino_example") + +def decode_sensor_data(response, humidity = True) : + + raw = int(response, 16) + measurement = (raw >> 8) & 0xffff + checksum = raw & 0x0000ff + if humidity : + calibrated = (125 * measurement/65536.) - 6 + else : + calibrated = (175.75 * measurement/65536.) - 46.85 + return measurement, checksum, calibrated + +def devcomuino_example(device, baud) : + + ## + ## initialize the communication line + ## + try : + serial = com.TextSerialCom(device, baud) + except : + logger.error(f"Unable to initialize serial communication with device at port \"{device}\"") + sys.exit(1) + if serial is None : + logger.error(f"Returned communication device is None") + sys.exit(1) + serial.setTermination("\r\n") + serial.init() + + ## + ## wait for Arduino device to sync + ## + time.sleep(2) + + ## + ## a couple of random test commands + ## + serial.send("I2C WRITE 40 84B8") + serial.receive() + + serial.send("I2C READ 40 1") + logger.info(f"Response: {serial.receive()}") + + serial.send("I2C WRITE 40 0xE7") + serial.receive() + + serial.send("I2C READ 40 1") + logger.info(f"Response: {serial.receive()}") + + while True : + serial.send("I2C WRITE 40 F5") + serial.receive() + + serial.send("I2C READ 40 3") + reponse = serial.receive() + logger.info(f"Response: {response}") + + humidity, checksum, calibrated = decode_sensor_data(response, humidity = True) + logger.info(f"Raw humidity data from sensor : {response}, raw humidity: {hex(humidity)}, checksum: {hex(checksum)}") + logger.info(f"Calibrated humidity : {calibrated}") + + serial.send("I2C WRITE 40 E0") + serial.receive() + + serial.send("I2C READ 40 3") + response = serial.receive() + + temp, checksum, calibrated = decode_sensor_data(response, humidity = False) + logger.info(f"Raw temperature data from sensor : {response}, raw temperature: {hex(temp)}, checksum: {hex(checksum)}") + logger.info(f"Calibrated temperature : {calibrated}") + + time.sleep(2) + +if __name__ == "__main__" : + + parser = ArgumentParser(description = "Example usage of devcomuino") + parser.add_argument("-p", "--port", required = True, type = str, + help = "Port at which Arduino is connected (e.g. /dev/ttyACM1)" + ) + parser.add_argument("-b", "--baud", default = 9600, type = int, + help = "Baud rate for USB communication with the connected Arduino device" + ) + args = parser.parse_args() + + devcomuino_example(args.port, args.baud) diff --git a/src/examples/fluke_test.cpp b/src/examples/fluke_example.cpp similarity index 100% rename from src/examples/fluke_test.cpp rename to src/examples/fluke_example.cpp diff --git a/src/libDevCom/examples/ftdi_adc_example.cpp b/src/examples/ft232h_adc_example.cpp similarity index 72% rename from src/libDevCom/examples/ftdi_adc_example.cpp rename to src/examples/ft232h_adc_example.cpp index 488b8267fe5d17a1ae145b43f79b8bc60c7a5ea5..c0d97ba9b4b10a0b9834307d1a9fb92ccbbe08e5 100644 --- a/src/libDevCom/examples/ftdi_adc_example.cpp +++ b/src/examples/ft232h_adc_example.cpp @@ -1,19 +1,19 @@ //std/stl -#include <iostream> #include <memory> // shared_ptr, unique_ptr #include <stdint.h> //labremote +#include "Logger.h" #include "AD799X.h" -#ifdef FTDI +#ifdef ENABLE_FTDI #include "FT232H.h" #include "I2CFTDICom.h" #endif int main(int argc, char* argv[]) { -#ifndef FTDI_ENABLED - std::cerr << "FTDI support is not enabled." << std::endl; +#ifndef ENABLE_FTDI + logger(logERROR) << "FTDI support is not enabled."; return 1; #else @@ -25,11 +25,11 @@ int main(int argc, char* argv[]) ft232 = std::make_shared<FT232H>(MPSSEChip::Protocol::I2C ,MPSSEChip::Speed::FOUR_HUNDRED_KHZ ,MPSSEChip::Endianness::MSBFirst); - std::cout << "Iniialized MPSSE: " << ft232->to_string() << std::endl; + logger(logINFO) << "Initialized MPSSE: " << ft232->to_string(); } catch(std::exception& e) { - std::cout << "Failed to initialize MPSSE: " << e.what() << std::endl; + logger(logERROR) << "Failed to initialized MPSSE: " << e.what(); return 1; } @@ -44,12 +44,12 @@ int main(int argc, char* argv[]) uint8_t vddd_chan = 0; // Channel 0 on ADC0 samples VDDD uint8_t ntc_chan = 6; // Channel 6 on ADC1 amples the NTC - std::cout << "------------------" << std::endl; + logger(logINFO) << "------------------"; for(size_t i = 0; i < 10; i++) { - std::cout << "[" << i << "] VDD = " << adc0->read(vddd_chan) << ", NTC = " << adc1->read(ntc_chan) << std::endl; + logger(logINFO) << "[" << i << "] VDD = " << adc0->read(vddd_chan) << ", NTC = " << adc1->read(ntc_chan); } - std::cout << "------------------" << std::endl; + logger(logINFO) << "------------------"; return 0; #endif // FTDI diff --git a/src/examples/ft232h_adc_example.py b/src/examples/ft232h_adc_example.py new file mode 100644 index 0000000000000000000000000000000000000000..f8829d4579ceb5bf32009236d52bf27195a8f45c --- /dev/null +++ b/src/examples/ft232h_adc_example.py @@ -0,0 +1,45 @@ +from labRemote import devcom + +from argparse import ArgumentParser +import sys, inspect +import logging +logging.basicConfig() +logger = logging.getLogger("ft232h_adc_example") + +def ft232h_adc_example() : + + ## + ## Create and initialize the FT232H device + ## + mpsse = devcom.MPSSEChip + protocol = mpsse.Protocol.I2C + speed = mpsse.Speed.FOUR_HUNDRED_KHZ + endianness = mpsse.Endianness.MSBFirst + ft232 = devcom.FT232H(protocol, speed, endianness, "", "") + + ## + ## create two ADCs that are on the I2C bus, each with their specific FTDI I2C bus line + ## + adc0 = devcom.AD799X(2.5, devcom.AD799X.Model.AD7998, devcom.I2CFTDICom(ft232, 0x21)) + adc1 = devcom.AD799X(2.5, devcom.AD799X.Model.AD7998, devcom.I2CFTDICom(ft232, 0x22)) + + vddd_chan = 0 + ntc_chan = 6 + print(31 * "-") + for i in range(10) : + print(f"[{i}] VDDD = {adc0.read(vddd_chan):.4f}, NTC = {adc1.read(ntc_chan):.4f}") + print(31 * "-") + +if __name__ == "__main__" : + + parser = ArgumentParser(description = "Example of communicating with an I2C-based ADC through the FTDI FT232H chip") + parser.parse_args() + + ## + ## first check that the FTDI support has been enabled in the current labRemote build + ## + if not inspect.isclass(devcom.I2CFTDICom) : + logger.error("FTDI support has not been enabled") + sys.exit(1) + + ft232h_adc_example() diff --git a/src/examples/ntc_example.py b/src/examples/ntc_example.py index a31a6bf59fd78f9fd30c66d796453977fb6cce46..9195349bfe70f63361c1ed5cefa6e0a6bb411088 100644 --- a/src/examples/ntc_example.py +++ b/src/examples/ntc_example.py @@ -1,55 +1,75 @@ -from _labRemote import devcom, com -import time, argparse +from labRemote import devcom, com + +import time +from argparse import ArgumentParser +import sys + +import logging +logging.basicConfig() +logger = logging.getLogger("ntc_example") # Voltage powering the NTC -voltage=5.0 +NTC_VOLTAGE = 5.0 # Resistance of resistor in voltage divider -Rvdiv=10000.0 +R_VOLTAGE_DIVIDER = 10000.0 # ohms +# The total voltage across the voltage divider +VOLTAGE_DIVIDER_REF = 5.0 # volts # Information specific to the NTC # Part number: 103KT1608 # See https://www.datasheets360.com/pdf/6686957959228959166 # --------------------------------------------------------- # Reference temperature in Kelvin -refTemp=298.15 +NTC_REF_TEMP = 298.15 # Resistance at reference temperature in ohms -Rref=10000.0 +NTC_R_REF = 10000.0 # ohms # B value for NTC in Kelvin -Bntc=3435 - -# TDKNTCG103JF103FTDS Steinhart-Hart coefficients -A = 0.8676453371787721e-3 -B = 2.541035850140508e-4 -C = 1.868520310774293e-7 - -def main(port, pin): - c = com.TextSerialCom(port,9600) - c.setTermination("\r\n") - c.setTimeout(5) - c.init() - time.sleep(1) # need to nap, otherwise communication error - - arduino = devcom.ADCDevComuino(voltage, c) - - sensor = devcom.NTCSensor(pin, arduino, False, refTemp, Rref, Bntc, False, Rvdiv, voltage) # read voltage drop over Rdiv, using beta formula for temperature conversion - # sensor = devcom.NTCSensor(pin, arduino, True, A, B, C, True, Rvdiv, voltage) # read voltage drop over NTC, using Steinhart-Hart for temperature conversion +NTC_BETA = 3435 + +def ntc_example(device, pin) : + + ## + ## initialize the communication line + ## + try : + serial = com.TextSerialCom(device, 9600) + except : + logger.error(f"Unable to initialize serial communication with device at port \"{device}\"") + sys.exit(1) + if serial is None : + logger.error(f"Returned communication device is None") + sys.exit(1) + serial.setTermination("\r\n") + serial.setTimeout(5) + serial.init() + + ## + ## initialize the NTCSensor object + ## + sensor = devcom.NTCSensor(pin, serial, NTC_REF_TEMP, NTC_R_REF, NTC_BETA, R_VOLTAGE_DIVIDER, VOLTAGE_DIVIDER_REF) sensor.init() + ## + ## perform measurements continuously + ## while True: + time.sleep(1) try: sensor.read(); - except RuntimeError as e: - print("Error: "+str(e)+". Continuing") - time.sleep(1) + except RuntimeError as err: + logger.error(f"Sensor read error: {err}, continuing...") continue - temp=sensor.temperature() - print("Temperature: "+str(temp)+" C") - time.sleep(1) + temp = sensor.temperature() + logger.info(f"Temperature measurement: {temp} C") if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('deviceport', type=str) - parser.add_argument('pin', type=int) - args = parser.parse_args() - main(args.deviceport, args.pin) + parser = ArgumentParser(description = "Example for how to use the NTCSensor class") + parser.add_argument("device-port", type = str, + help = "Port at which the device is connected (e.g. /dev/ttyAMC0)" + ) + parser.add_argument("pin", type = str, + help = "Pin on the sensor to sample from" + ) + args = parser.parse_args() + ntc_example(args.device_port, args.pin) diff --git a/src/examples/scope_test.cpp b/src/examples/scope_example.cpp similarity index 100% rename from src/examples/scope_test.cpp rename to src/examples/scope_example.cpp diff --git a/src/examples/sht85_test.cpp b/src/examples/sht85_example.cpp similarity index 100% rename from src/examples/sht85_test.cpp rename to src/examples/sht85_example.cpp diff --git a/src/examples/sht85_example.py b/src/examples/sht85_example.py new file mode 100644 index 0000000000000000000000000000000000000000..36e87323b28cc38802e50608e2615108c4834b3f --- /dev/null +++ b/src/examples/sht85_example.py @@ -0,0 +1,90 @@ +from labRemote import devcom, com + +from argparse import ArgumentParser +import sys, inspect +from datetime import datetime +import logging +logging.basicConfig() +logger = logging.getLogger("sht85_example") + +def sht85_example(use_ftdi, device, baud) : + + if use_ftdi : + ## + ## configure and initialize the FT232H device + mpsse = devcom.MPSSEChip + protocol = mpsse.Protocol.I2C + speed = mpsse.Speed.ONE_HUNDRED_KHZ + endianness = mpsse.Endianness.MSBFirst + ft232 = devcom.FT232H(protocol, speed, endianness, "", "") + + ## + ## i2c line and address for the sensor + ## + i2c = devcom.I2CFTDICom(ft232, 0x44) + else : + ## + ## initialize the communication line + ## + try : + serial = com.TextSerialCom(device, baud) + except : + logger.error(f"Unable to initialize serial communication with the device at port \"{device}\"") + sys.exit(1) + if serial is None : + logger.error("Returned communication device is None") + sys.exit(1) + serial.setTermination("\r\n") + serial.init() + + ## + ## initialize the I2C bus line + ## + i2c = devcom.I2CDevComuino(0x44, serial) + + ## + ## initialize the sensor on the I2C bus + ## + sensor = devcom.SHT85(i2c) + + if sensor is None : + logger.error("Returned sensor device is None") + sys.exit(1) + + ## + ## perform continuous measurements + ## + while True : + time.sleep(1) + try : + sensor.read() + except RuntimeError as err : + logger.error(f"Sensor read error: {err}, continuing...") + continue + now = datetime.now() + logger.info(f"Measurement[{now}]: temperature = {sensor.temperature()}, humidity = {sensor.humidity()}") + +if __name__ == "__main__" : + + parser = ArgumentParser(description = "Example of communicating with an I2C-based SHT85 device") + parser.add_argument("--ftdi", action = "store_true", default = False, + help = "Use FTDI-based FT232H device" + ) + parser.add_argument("-p", "--port", default = "", type = str, + help = "Port at which device is connected (e.g. /dev/ttyACM0) (leave blank if you wish to use FTDI-based FT232H device)" + ) + parser.add_argument("-b", "--baud", default = 9600, type = int, + help = "Baud rate for serial communication with device (ignore if you wish to use FTDI-based FT232H device)" + ) + parser.parse_args() + + ## + ## first check that FTDI support has been enabled in the current labRemote build + ## + if args.ftdi and not inspect.isclas(devcom.I2CFTDICom) : + logger.error("Python support for FTDI devices has not been added!") + sys.exit() + elif not args.ftdi and args.port == "" : + logger.error("You must provide a device port if not using FTDI-based device") + sys.exit(1) + sht85_example(args.ftdi, args.port, args.baud) diff --git a/src/libDevCom/examples/temp_hum_monitor.cpp b/src/examples/temp_hum_monitor_example.cpp similarity index 100% rename from src/libDevCom/examples/temp_hum_monitor.cpp rename to src/examples/temp_hum_monitor_example.cpp diff --git a/src/examples/temp_hum_monitor_example.py b/src/examples/temp_hum_monitor_example.py new file mode 100644 index 0000000000000000000000000000000000000000..339adefa86b06a0d70c41c300239983f9a0851c7 --- /dev/null +++ b/src/examples/temp_hum_monitor_example.py @@ -0,0 +1,25 @@ +from labRemote import devcom + +from argparse import ArgumentParser +import sys, inspect +import logging +logging.basicConfig() +logger = logging.getLogger("temp_hum_monitor_example") + +def temp_hum_monitor_example() : + + logger.warning("Example not implemented!") + +if __name__ == "__main__" : + + parser = ArgumentParser(description = "Example of performing continous monitoring of several climate sensors, using data streams") + parser.parse_args() + + ## + ## first check that the FTDI support has been enabled in the current labRemote build + ## + if not inspect.isclass(devcom.I2CFTDICom) : + logger.error("FTDI support has not been enabled") + sys.exit(1) + + temp_hum_monitor() diff --git a/src/libDevCom/examples/tempsensor_monitor.cpp b/src/examples/tempsensor_monitor_example.cpp similarity index 81% rename from src/libDevCom/examples/tempsensor_monitor.cpp rename to src/examples/tempsensor_monitor_example.cpp index a7c954baea963210be56e2425aa0b6d5f5a7b813..6b0e393edeb9d3ad426c5d503a4b37ba5ec1d6df 100644 --- a/src/libDevCom/examples/tempsensor_monitor.cpp +++ b/src/examples/tempsensor_monitor_example.cpp @@ -11,6 +11,7 @@ #include "FT232H.h" #include "I2CFTDICom.h" #endif +#include "Logger.h" #include "HIH6130.h" @@ -18,7 +19,7 @@ int main(int argc, char* argv[]) { #ifndef FTDI - std::cerr << "FTDI support not enabled." << std::endl; + logger(logERROR) << "FTDI support not enabled."; return -1; #else // Initialize I2C (for DAC comm) @@ -38,7 +39,7 @@ int main(int argc, char* argv[]) { tempsensor.read(); // Save the data - std::cout << time << "\t" << tempsensor.status() << "\t" << tempsensor.humidity() << "\t" << tempsensor.temperature() << std::endl; + logger(logINFO) << time << "\t" << tempsensor.status() << "\t" << tempsensor.humidity() << "\t" << tempsensor.temperature(); } #endif diff --git a/src/examples/tempsensor_monitor_example.py b/src/examples/tempsensor_monitor_example.py new file mode 100644 index 0000000000000000000000000000000000000000..d78ddbdc083b0a9298b973e1a6a3cc3072b1774f --- /dev/null +++ b/src/examples/tempsensor_monitor_example.py @@ -0,0 +1,38 @@ +from labRemote import devcom + +from argparse import ArgumentParser +import sys, inspect +from datetime import datetime +import time +import logging +logging.basicConfig() +logger = logging.getLogger("tempsensor_monitor_example") + +def tempsensor_monitor_example() : + + ## + ## configure and initialize the FT232H device + ## + mpsse = devcom.MPSSEChip + ft232 = devcom.FT232H(mpsse.Protocol.I2C, mpsse.Speed.ONE_HUNDRED_KHZ, mpsse.Endianness.MSBFirst, "", "") + i2c = devcom.I2CFTDICom(ft232, 0x27) + + sensor = devcom.HIH6130(i2c) + + while True : + time.sleep(1) + sensor.read() + logger.info(f"[{datetime.now()}] Sensor status: {sensor.status()}, humidity: {sensor.humidity()}, temperature: {sensor.temperature()}") + +if __name__ == "__main__" : + + parser = ArgumentParser(description = "Example of I2C-based temperature sensor monitoring through the FTDI-based FT232H chip") + parser.parse_args() + + ## + ## first check that hte FTDI support as been enabled in the current labRemote build + ## + if not inspect.isclass(devcom.I2CFTDICom) : + logger.error("FTDI support has not been enabled") + sys.exit(1) + tempsensor_monitor_example() diff --git a/src/libDevCom/examples/uio.cpp b/src/examples/uio_example.cpp similarity index 91% rename from src/libDevCom/examples/uio.cpp rename to src/examples/uio_example.cpp index f151611b2842555b5f93d982aff0cb37115a2feb..0866ce4e9d8090fbdfefc23064f70505102517ff 100644 --- a/src/libDevCom/examples/uio.cpp +++ b/src/examples/uio_example.cpp @@ -6,6 +6,7 @@ #include <memory> #include "UIOCom.h" +#include "Logger.h" #include "ComIOException.h" void print_help() @@ -81,12 +82,12 @@ int main(int argc, char* argv[]) unsigned int address=std::stoul(argv[optind+1], nullptr, 0); unsigned int read=uio->read_reg32(address); - std::cout << "0x" << std::hex << std::setw(8) << std::setfill('0') << read << std::endl; + logger(logINFO) << "0x" << std::hex << std::setw(8) << std::setfill('0') << read; } } catch(const ComIOException& e) { - std::cout << "ERROR: " << e.what() << std::endl; + logger(logERROR) << "ComIOException: " << e.what(); } return 0; } diff --git a/src/labRemote/CMakeLists.txt b/src/labRemote/CMakeLists.txt index 8863482ae418c6ec2d166f1f2764c11ebb178882..be02947409d691f1e856ff4dbf6ea65ce2c8e90e 100644 --- a/src/labRemote/CMakeLists.txt +++ b/src/labRemote/CMakeLists.txt @@ -23,4 +23,3 @@ endif() set(PYLABREMOTE_INSTALL_PATH ${CMAKE_INSTALL_LIBDIR}/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages/labRemote) install(TARGETS ${python_module_name} LIBRARY DESTINATION ${PYLABREMOTE_INSTALL_PATH} ) install(FILES __init__.py com.py ps.py ec.py DESTINATION ${PYLABREMOTE_INSTALL_PATH} ) - diff --git a/src/libDevCom/CMakeLists.txt b/src/libDevCom/CMakeLists.txt index f3b74e2d8c91e347fd8645074372ea5e49619f41..8e5b9aa3b98b70d00004325c7893866a2916e7ef 100644 --- a/src/libDevCom/CMakeLists.txt +++ b/src/libDevCom/CMakeLists.txt @@ -101,5 +101,3 @@ endif() set_target_properties(DevCom PROPERTIES VERSION ${labRemote_VERSION_MAJOR}.${labRemote_VERSION_MINOR}) install(TARGETS DevCom) - -add_subdirectory(examples) diff --git a/src/libDevCom/examples/CMakeLists.txt b/src/libDevCom/examples/CMakeLists.txt deleted file mode 100644 index e9ce6946b3eb612b7cf3dd3a69d8a089eeebb750..0000000000000000000000000000000000000000 --- a/src/libDevCom/examples/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ - - -# add executables -file(GLOB examples [a-zA-Z]*.cpp) - -foreach(target ${examples}) - get_filename_component(execname ${target} NAME_WE) - get_filename_component(srcfile ${target} NAME) - - add_executable(${execname}) - target_sources(${execname} PRIVATE ${srcfile}) - target_link_libraries(${execname} DevCom Utils PS DataSink EquipConf) - - if ( ${ENABLE_FTDI} ) - target_compile_definitions(${execname} PUBLIC FTDI=1) - endif() - -endforeach() diff --git a/src/libDevCom/python.cpp b/src/libDevCom/python.cpp index 62528f06e72a56db454d512f64b6ae86ca262bab..3840b1efa3f05d5c473e321d694a1a621a65eb44 100644 --- a/src/libDevCom/python.cpp +++ b/src/libDevCom/python.cpp @@ -19,14 +19,35 @@ #include "ADCDevComuino.h" #include "PCA9548ACom.h" -#ifdef FTDI +#ifdef ENABLE_FTDI #include "I2CFTDICom.h" + #include "MPSSEChip.h" + #include "FT232H.h" #endif +#include "DeviceCalibration.h" +#include "DummyCalibration.h" +#include "LinearCalibration.h" +#include "FileCalibration.h" + +#include "ADCDevice.h" +#include "AD799X.h" +#include "LTC2451.h" +#include "MAX11619.h" +#include "MCP3425.h" +#include "MCP3428.h" + +#include "DACDevice.h" +#include "AD56X9.h" +#include "DAC5571.h" +#include "DAC5574.h" +#include "MCP4801.h" + #include "TextSerialCom.h" namespace py = pybind11; + class PyClimateSensor : public ClimateSensor { public: using ClimateSensor::ClimateSensor; @@ -637,6 +658,486 @@ public: } }; +#ifdef ENABLE_FTDI +class PyMPSSEChip : public MPSSEChip { +public: + using MPSSEChip::MPSSEChip; + + bool start() override { + PYBIND11_OVERLOAD_PURE( + bool, + MPSSEChip, + start + ); + } + + bool stop() override { + PYBIND11_OVERLOAD_PURE( + bool, + MPSSEChip, + stop + ); + } + + bool write(char* data, int size) override { + PYBIND11_OVERLOAD_PURE( + bool, + MPSSEChip, + write, + data, + size + ); + } + + char* read(int size) override { + PYBIND11_OVERLOAD_PURE( + char*, + MPSSEChip, + read, + size + ); + } + + void gpio_write(int pin, int state) override { + PYBIND11_OVERLOAD_PURE( + void, + MPSSEChip, + gpio_write, + pin, + state + ); + } + + bool get_ack() override { + PYBIND11_OVERLOAD_PURE( + bool, + MPSSEChip, + get_ack + ); + } + + void set_ack() override { + PYBIND11_OVERLOAD_PURE( + void, + MPSSEChip, + set_ack + ); + } + + void set_nack() override { + PYBIND11_OVERLOAD_PURE( + void, + MPSSEChip, + set_nack + ); + } +}; + +template <class FTDIDevice = FT232H> class PyFTDIDevice : public FTDIDevice { +public: + using FTDIDevice::FTDIDevice; + + bool start() override { + PYBIND11_OVERLOAD( + bool, + FTDIDevice, + start + ); + } + + bool stop() override { + PYBIND11_OVERLOAD( + bool, + FTDIDevice, + stop + ); + } + + bool write(char* data, int size) override { + PYBIND11_OVERLOAD( + bool, + FTDIDevice, + write, + data, + size + ); + } + + char* read(int size) override { + PYBIND11_OVERLOAD( + char*, + FTDIDevice, + read, + size + ); + } + + void gpio_write(int pin, int state) override { + PYBIND11_OVERLOAD( + void, + FTDIDevice, + gpio_write, + pin, + state + ); + } + + bool get_ack() override { + PYBIND11_OVERLOAD( + bool, + FTDIDevice, + get_ack + ); + } + + void set_ack() override { + PYBIND11_OVERLOAD( + void, + FTDIDevice, + set_ack + ); + } + + void set_nack() override { + PYBIND11_OVERLOAD( + void, + FTDIDevice, + set_nack + ); + } +}; +#endif + +template <class DeviceCalibrationBase = DeviceCalibration> class PyDeviceCalibrationBase : public DeviceCalibrationBase +{ +public: + using DeviceCalibrationBase::DeviceCalibrationBase; + double calibrate(int32_t counts) override { PYBIND11_OVERLOAD_PURE(double, DeviceCalibrationBase, calibrate, counts); } + int32_t uncalibrate(double value) override { PYBIND11_OVERLOAD_PURE(int32_t, DeviceCalibrationBase, uncalibrate, value); } +}; + +template <class DevCal = DummyCalibration> class PyDevCal : public DevCal //public PyDeviceCalibration<DevCal> +{ +public: + using DevCal::DevCal; + double calibrate(int32_t counts) override { PYBIND11_OVERLOAD(double, DevCal, calibrate, counts); } + int32_t uncalibrate(double value) override { PYBIND11_OVERLOAD(int32_t, DevCal, uncalibrate, value); } +}; + +template <class ADCDeviceBase = ADCDevice> class PyADCDeviceBase : public ADCDeviceBase +{ +public: + using ADCDeviceBase::ADCDeviceBase; + + void setCalibration(std::shared_ptr<DeviceCalibration> calibration) { + PYBIND11_OVERLOAD( + void, + ADCDeviceBase, + setCalibration, + calibration + ); + } + + void setCalibration(uint8_t ch, std::shared_ptr<DeviceCalibration> calibration) { + PYBIND11_OVERLOAD( + void, + ADCDeviceBase, + setCalibration, + ch, + calibration + ); + } + + double read() { + PYBIND11_OVERLOAD( + double, + ADCDeviceBase, + read + ); + } + + double read(uint8_t ch) { + PYBIND11_OVERLOAD( + double, + ADCDeviceBase, + read, + ch + ); + } + + void read(const std::vector<uint8_t>& chs, std::vector<double>& data) { + PYBIND11_OVERLOAD( + void, + ADCDeviceBase, + read, + chs, + data + ); + } + + int32_t readCount() override { + PYBIND11_OVERLOAD_PURE( + int32_t, + ADCDeviceBase, + readCount + ); + } + + int32_t readCount(uint8_t channel) override { + PYBIND11_OVERLOAD_PURE( + int32_t, + ADCDeviceBase, + readCount, + channel + ); + } + + void readCount(const std::vector<uint8_t>& channels, std::vector<int32_t>& data) override { + PYBIND11_OVERLOAD_PURE( + void, + ADCDeviceBase, + readCount, + channels, + data + ); + } + +}; // class PyADCDeviceBase + +template <class ADCDev = ADCDevice> class PyADCDev : public ADCDev +{ +public: + using ADCDev::ADCDev; + + int32_t readCount() override { + PYBIND11_OVERLOAD( + int32_t, + ADCDev, + readCount + ); + } + + int32_t readCount(uint8_t channel) override { + PYBIND11_OVERLOAD( + int32_t, + ADCDev, + readCount, + channel + ); + } + + void readCount(const std::vector<uint8_t>& channels, std::vector<int32_t>& data) override { + PYBIND11_OVERLOAD( + void, + ADCDev, + readCount, + channels, + data + ); + } + +}; // class PyADCDev + +template <class DACDeviceBase = DACDevice> class PyDACDeviceBase : public DACDeviceBase +{ +public: + using DACDeviceBase::DACDeviceBase; + + void setCalibration(std::shared_ptr<DeviceCalibration> calibration) { + PYBIND11_OVERLOAD( + void, + DACDeviceBase, + setCalibration, + calibration + ); + } + + void setCalibration(uint8_t ch, std::shared_ptr<DeviceCalibration> calibration) { + PYBIND11_OVERLOAD( + void, + DACDeviceBase, + setCalibration, + ch, + calibration + ); + } + + void setCount(int32_t counts) override { + PYBIND11_OVERLOAD_PURE( + void, + DACDeviceBase, + setCount, + counts + ); + } + + void setCount(uint8_t ch, int32_t counts) override { + PYBIND11_OVERLOAD_PURE( + void, + DACDeviceBase, + setCount, + ch, + counts + ); + } + + void setCount(const std::vector<uint8_t>& chs, const std::vector<int32_t>& counts) override { + PYBIND11_OVERLOAD_PURE( + void, + DACDeviceBase, + setCount, + chs, + counts + ); + } + + int32_t readCount() override { + PYBIND11_OVERLOAD_PURE( + int32_t, + DACDeviceBase, + readCount + ); + } + + int32_t readCount(uint8_t ch) override { + PYBIND11_OVERLOAD_PURE( + int32_t, + DACDeviceBase, + readCount, + ch + ); + } + + void readCount(const std::vector<uint8_t>& chs, std::vector<int32_t>& data) override { + PYBIND11_OVERLOAD_PURE( + void, + DACDeviceBase, + readCount, + chs, + data + ); + } + + double set(double value) { + PYBIND11_OVERLOAD( + double, + DACDeviceBase, + set, + value + ); + } + + double set(uint8_t ch, double value) { + PYBIND11_OVERLOAD( + double, + DACDeviceBase, + set, + ch, + value + ); + } + + void set(const std::vector<uint8_t>& chs, const std::vector<double>& values) { + PYBIND11_OVERLOAD( + void, + DACDeviceBase, + set, + chs, + values + ); + } + + double read() { + PYBIND11_OVERLOAD( + double, + DACDeviceBase, + read + ); + } + + double read(uint8_t ch) { + PYBIND11_OVERLOAD( + double, + DACDeviceBase, + read, + ch + ); + } + + void read(const std::vector<uint8_t>& chs, std::vector<double>& data) { + PYBIND11_OVERLOAD( + void, + DACDeviceBase, + read, + chs, + data + ); + } + +}; // class PyDACDeviceBase + +template <class DACDev = DACDevice> class PyDACDev : public DACDev +{ +public: + using DACDev::DACDev; + + void setCount(int32_t counts) override { + PYBIND11_OVERLOAD( + void, + DACDev, + setCount, + counts + ); + } + + void setCount(uint8_t ch, int32_t counts) override { + PYBIND11_OVERLOAD( + void, + DACDev, + setCount, + ch, + counts + ); + } + + void setCount(const std::vector<uint8_t>& chs, const std::vector<int32_t>& counts) override { + PYBIND11_OVERLOAD( + void, + DACDev, + setCount, + chs, + counts + ); + } + + int32_t readCount() override { + PYBIND11_OVERLOAD( + int32_t, + DACDev, + readCount + ); + } + + int32_t readCount(uint8_t ch) override { + PYBIND11_OVERLOAD( + int32_t, + DACDev, + readCount, + ch + ); + } + + void readCount(const std::vector<uint8_t>& chs, std::vector<int32_t>& data) override { + PYBIND11_OVERLOAD( + void, + DACDev, + readCount, + chs, + data + ); + } + +}; // class PyDACDev void register_devcom(py::module& m) { py::class_<ClimateSensor, PyClimateSensor, std::shared_ptr<ClimateSensor>>(m, "ClimateSensor") @@ -670,7 +1171,7 @@ void register_devcom(py::module& m) { py::class_<HIH6130, PySensor_w_status<HIH6130>, ClimateSensor, std::shared_ptr<HIH6130>>(m, "HIH6130") .def(py::init<std::shared_ptr<I2CCom>>()) .def("status", &HIH6130::status); - + py::class_<DeviceCom, PyPureVirtualCom<DeviceCom>, std::shared_ptr<DeviceCom>>(m, "DeviceCom") .def(py::init<>()) .def("write_reg32", (void (DeviceCom::*)(uint32_t address, uint32_t data)) &DeviceCom::write_reg32) @@ -702,34 +1203,177 @@ void register_devcom(py::module& m) { py::class_<I2CDevComuino, PyCom<I2CDevComuino>, I2CCom, std::shared_ptr<I2CDevComuino>>(m, "I2CDevComuino") .def(py::init<uint8_t, std::shared_ptr<TextSerialCom>>()); - - py::class_<DeviceCalibration, PyDeviceCalibration, std::shared_ptr<DeviceCalibration>>(m, "DeviceCalibration") - .def(py::init<>()) - .def("calibrate", &DeviceCalibration::calibrate) - .def("uncalibrate", &DeviceCalibration::uncalibrate); - - py::class_<ADCDevice, PyADC<ADCDevice>, std::shared_ptr<ADCDevice>>(m, "ADCDevice") - .def(py::init<std::shared_ptr<DeviceCalibration>>()) - .def("setCalibration", (void (ADCDevice::*)(std::shared_ptr<DeviceCalibration> calibration)) &ADCDevice::setCalibration) - .def("setCalibration", (void (ADCDevice::*)(uint8_t ch, std::shared_ptr<DeviceCalibration> calibration)) &ADCDevice::setCalibration) - .def("read", (double (ADCDevice::*)()) &ADCDevice::read) - .def("read", (double (ADCDevice::*)(uint8_t ch)) &ADCDevice::read) - .def("read", (void (ADCDevice::*)(const std::vector<uint8_t>& chs, std::vector<double>& data)) &ADCDevice::read) - .def("readCount", (int32_t (ADCDevice::*)()) &ADCDevice::readCount) - .def("readCount", (int32_t (ADCDevice::*)(uint8_t ch)) &ADCDevice::readCount) - .def("readCount", (void (ADCDevice::*)(const std::vector<uint8_t>& chs, std::vector<int32_t>& data)) &ADCDevice::readCount); - py::class_<ADCDevComuino, PyADC<ADCDevComuino>, ADCDevice, std::shared_ptr<ADCDevComuino>>(m, "ADCDevComuino") - .def(py::init<double, std::shared_ptr<TextSerialCom>>()) - .def("readCount", (int32_t (ADCDevComuino::*)()) &ADCDevComuino::readCount) - .def("readCount", (int32_t (ADCDevComuino::*)(uint8_t ch)) &ADCDevComuino::readCount) - .def("readCount", (void (ADCDevComuino::*)(const std::vector<uint8_t>& chs, std::vector<int32_t>& data)) &ADCDevComuino::readCount); - -#ifdef FTDI +#ifdef ENABLE_FTDI py::class_<I2CFTDICom, PyCom<I2CFTDICom>, I2CCom, std::shared_ptr<I2CFTDICom>>(m, "I2CFTDICom") - .def(py::init<uint8_t>()); + .def(py::init<std::shared_ptr<FT232H>, uint8_t>()); + + py::class_<MPSSEChip, PyMPSSEChip, std::shared_ptr<MPSSEChip>> mpssechip_class(m, "MPSSEChip"); + mpssechip_class.def(py::init<MPSSEChip::Protocol, MPSSEChip::Speed, MPSSEChip::Endianness, int, int, const std::string&, const std::string&>()) + .def("start", &MPSSEChip::start) + .def("stop", &MPSSEChip::stop) + .def("write", &MPSSEChip::write) + .def("read", &MPSSEChip::read) + .def("gpio_write", &MPSSEChip::gpio_write) + .def("get_ack", &MPSSEChip::get_ack) + .def("set_ack", &MPSSEChip::set_ack) + .def("set_nack", &MPSSEChip::set_nack) + .def("to_string", &MPSSEChip::to_string) + .def("protocol2str", &MPSSEChip::protocol2str) + .def("speed2str", &MPSSEChip::speed2str) + .def("endianness2str", &MPSSEChip::endianness2str) + .def("protocol", &MPSSEChip::protocol) + .def("speed", &MPSSEChip::speed) + .def("endianness", &MPSSEChip::endianness); + + py::enum_<MPSSEChip::Protocol>(mpssechip_class, "Protocol") + .value("I2C", MPSSEChip::Protocol::I2C) + .value("SPI0", MPSSEChip::Protocol::SPI0) + .value("SPI1", MPSSEChip::Protocol::SPI1) + .value("SPI2", MPSSEChip::Protocol::SPI2) + .value("SPI3", MPSSEChip::Protocol::SPI3); + + py::enum_<MPSSEChip::Speed>(mpssechip_class, "Speed") + .value("ONE_HUNDRED_KHZ", MPSSEChip::Speed::ONE_HUNDRED_KHZ) + .value("FOUR_HUNDRED_KHZ", MPSSEChip::Speed::FOUR_HUNDRED_KHZ) + .value("ONE_MHZ", MPSSEChip::Speed::ONE_MHZ) + .value("TWO_MHZ", MPSSEChip::Speed::TWO_MHZ) + .value("FIVE_MHZ", MPSSEChip::Speed::FIVE_MHZ) + .value("SIX_MHZ", MPSSEChip::Speed::SIX_MHZ) + .value("TEN_MHZ", MPSSEChip::Speed::TEN_MHZ) + .value("TWELVE_MHZ", MPSSEChip::Speed::TWELVE_MHZ) + .value("FIFTEEN_MHZ", MPSSEChip::Speed::FIFTEEN_MHZ) + .value("THIRTY_MHZ", MPSSEChip::Speed::THIRTY_MHZ) + .value("SIXTY_MHZ", MPSSEChip::Speed::SIXTY_MHZ); + + py::enum_<MPSSEChip::Endianness>(mpssechip_class, "Endianness") + .value("MSBFirst", MPSSEChip::Endianness::MSBFirst) + .value("LSBFirst", MPSSEChip::Endianness::LSBFirst); + + py::class_<FT232H, PyFTDIDevice<FT232H>, MPSSEChip, std::shared_ptr<FT232H>>(m, "FT232H") + .def(py::init<MPSSEChip::Protocol, MPSSEChip::Speed, MPSSEChip::Endianness, const std::string&, const std::string&>()); #endif py::class_<PCA9548ACom, PyCom<PCA9548ACom>, I2CCom, std::shared_ptr<PCA9548ACom>>(m, "PCA9548ACom") .def(py::init<uint8_t, uint8_t, std::shared_ptr<I2CCom>>()); + + // Converters (ADC/DAC) and calibrated devices + py::class_<DeviceCalibration, PyDeviceCalibrationBase<>>(m, "DeviceCalibration"); + + py::class_<DummyCalibration, DeviceCalibration, PyDevCal<>>(m, "DummyCalibration") + .def(py::init<>()) + .def("calibrate", &DummyCalibration::calibrate) + .def("uncalibrate", &DummyCalibration::uncalibrate); + + py::class_<LinearCalibration, DeviceCalibration, PyDevCal<LinearCalibration>>(m, "LinearCalibration") + .def(py::init<double, uint32_t>()) + .def("calibrate", &LinearCalibration::calibrate) + .def("uncalibrate", &LinearCalibration::uncalibrate); + + py::class_<FileCalibration, DeviceCalibration, PyDevCal<FileCalibration>>(m, "FileCalibration") + .def(py::init<std::string>()) + .def("calibrate", &FileCalibration::calibrate) + .def("uncalibrate", &FileCalibration::uncalibrate); + + // ADCDevice + py::class_<ADCDevice, PyADCDeviceBase<>>(m, "ADCDevice") + .def(py::init<std::shared_ptr<DeviceCalibration>>()) + .def("setCalibration", static_cast<void (ADCDevice::*)(std::shared_ptr<DeviceCalibration>)>(&ADCDevice::setCalibration)) + .def("setCalibration", static_cast<void (ADCDevice::*)(uint8_t, std::shared_ptr<DeviceCalibration>)>(&ADCDevice::setCalibration)) + .def("read", static_cast<double (ADCDevice::*)()>(&ADCDevice::read)) + .def("read", static_cast<double (ADCDevice::*)(uint8_t)>(&ADCDevice::read)) + .def("read", static_cast<void (ADCDevice::*)(const std::vector<uint8_t>&, std::vector<double>&)>(&ADCDevice::read)) + .def("readCount", static_cast<int32_t (ADCDevice::*)()>(&ADCDevice::readCount)) + .def("readCount", static_cast<int32_t (ADCDevice::*)(uint8_t)>(&ADCDevice::readCount)) + .def("readCount", static_cast<void (ADCDevice::*)(const std::vector<uint8_t>&, std::vector<int32_t>&)>(&ADCDevice::readCount)); + + + py::class_<AD799X, ADCDevice, PyADCDev<AD799X>> ad799x_class(m, "AD799X"); + py::enum_<AD799X::Model>(ad799x_class, "Model") + .value("AD7993", AD799X::Model::AD7993) + .value("AD7994", AD799X::Model::AD7994) + .value("AD7997", AD799X::Model::AD7997) + .value("AD7998", AD799X::Model::AD7998); + ad799x_class.def(py::init<double, AD799X::Model, std::shared_ptr<I2CCom>>()); + + py::class_<ADCDevComuino, ADCDevice, PyADCDev<ADCDevComuino>>(m, "ADCDevComuino") + .def(py::init<double, std::shared_ptr<TextSerialCom>>()); + + py::class_<LTC2451, ADCDevice, PyADCDev<LTC2451>> ltc2451_class(m, "LTC2451"); + py::enum_<LTC2451::Speed>(ltc2451_class, "Speed") + .value("Speed60Hz", LTC2451::Speed::Speed60Hz) + .value("Speed30Hz", LTC2451::Speed::Speed30Hz); + ltc2451_class.def(py::init<double, std::shared_ptr<I2CCom>>()) + .def("setSpeed", <C2451::setSpeed); + + py::class_<MAX11619, ADCDevice, PyADCDev<MAX11619>>(m, "MAX11619") + .def(py::init<double, std::shared_ptr<SPICom>>()); + + py::class_<MCP3425, ADCDevice, PyADCDev<MCP3425>> mcp3425_class(m, "MCP3425"); + py::enum_<MCP3425::Resolution>(mcp3425_class, "Resolution") + .value("bit12", MCP3425::Resolution::bit12) + .value("bit14", MCP3425::Resolution::bit14) + .value("bit16", MCP3425::Resolution::bit16); + py::enum_<MCP3425::ConversionMode>(mcp3425_class, "ConversionMode") + .value("Shot", MCP3425::ConversionMode::Shot) + .value("Cont", MCP3425::ConversionMode::Cont); + py::enum_<MCP3425::Gain>(mcp3425_class, "Gain") + .value("x1", MCP3425::Gain::x1) + .value("x2", MCP3425::Gain::x2) + .value("x4", MCP3425::Gain::x4) + .value("x8", MCP3425::Gain::x8); + mcp3425_class.def(py::init<MCP3425::Resolution, MCP3425::ConversionMode, MCP3425::Gain, std::shared_ptr<I2CCom>>()) + .def(py::init<std::shared_ptr<I2CCom>>()); + + py::class_<MCP3428, ADCDevice, PyADCDev<MCP3428>> mcp3428_class(m, "MCP3428"); + py::enum_<MCP3428::Resolution>(mcp3428_class, "Resolution") + .value("bit12", MCP3428::Resolution::bit12) + .value("bit14", MCP3428::Resolution::bit14) + .value("bit16", MCP3428::Resolution::bit16); + py::enum_<MCP3428::ConversionMode>(mcp3428_class, "ConversionMode") + .value("Shot", MCP3428::ConversionMode::Shot) + .value("Cont", MCP3428::ConversionMode::Cont); + py::enum_<MCP3428::Gain>(mcp3428_class, "Gain") + .value("x1", MCP3428::Gain::x1) + .value("x2", MCP3428::Gain::x2) + .value("x4", MCP3428::Gain::x4) + .value("x8", MCP3428::Gain::x8); + mcp3428_class.def(py::init<MCP3428::Resolution, MCP3428::ConversionMode, MCP3428::Gain, std::shared_ptr<I2CCom>>()) + .def(py::init<std::shared_ptr<I2CCom>>()) + .def("setGain", &MCP3428::setGain); + + // DACDevice + py::class_<DACDevice, PyDACDeviceBase<>>(m, "DACDevice") + .def(py::init<std::shared_ptr<DeviceCalibration>>()) + .def("setCalibration", static_cast<void (DACDevice::*)(std::shared_ptr<DeviceCalibration>)>(&DACDevice::setCalibration)) + .def("setCalibration", static_cast<void (DACDevice::*)(uint8_t, std::shared_ptr<DeviceCalibration>)>(&DACDevice::setCalibration)) + .def("set", static_cast<double (DACDevice::*)(double)>(&DACDevice::set)) + .def("set", static_cast<double (DACDevice::*)(uint8_t, double)>(&DACDevice::set)) + .def("set", static_cast<void (DACDevice::*)(const std::vector<uint8_t>&, const std::vector<double>&)>(&DACDevice::set)) + .def("read", static_cast<double (DACDevice::*)()>(&DACDevice::read)) + .def("read", static_cast<double (DACDevice::*)(uint8_t)>(&DACDevice::read)) + .def("read", static_cast<void (DACDevice::*)(const std::vector<uint8_t>&, std::vector<double>&)>(&DACDevice::read)) + .def("setCount", static_cast<void (DACDevice::*)(int32_t)>(&DACDevice::setCount)) + .def("setCount", static_cast<void (DACDevice::*)(uint8_t, int32_t)>(&DACDevice::setCount)) + .def("setCount", static_cast<void (DACDevice::*)(const std::vector<uint8_t>&, const std::vector<int32_t>&)>(&DACDevice::setCount)) + .def("readCount", static_cast<int32_t (DACDevice::*)()>(&DACDevice::readCount)) + .def("readCount", static_cast<int32_t (DACDevice::*)(uint8_t)>(&DACDevice::readCount)) + .def("readCount", static_cast<void (DACDevice::*)(const std::vector<uint8_t>&, std::vector<int32_t>&)>(&DACDevice::readCount)); + + py::class_<AD56X9, DACDevice, PyDACDev<AD56X9>> ad56x9_class(m, "AD56X9"); + py::enum_<AD56X9::Model>(ad56x9_class, "Model") + .value("AD5629", AD56X9::AD5629) + .value("AD5669", AD56X9::AD5669); + ad56x9_class.def(py::init<double, AD56X9::Model, std::shared_ptr<I2CCom>>()); + + py::class_<DAC5571, DACDevice, PyDACDev<DAC5571>>(m, "DAC5571") + .def(py::init<float, std::shared_ptr<I2CCom>>()); + + py::class_<DAC5574, DACDevice, PyDACDev<DAC5574>>(m, "DAC5574") + .def(py::init<float, std::shared_ptr<I2CCom>>()); + + py::class_<MCP4801, DACDevice, PyDACDev<MCP4801>>(m, "MCP4801") + .def(py::init<std::shared_ptr<SPICom>>()); + + }