Apogee USB Sensors and Linux
Apogee USB sensors can be connected directly to a computer (Windows or macOS X) for taking spot measurements or graphing and datalogging real-time PPFD using the included software. Apogee currently does not have software for our sensors with Linux systems; however, while we do not design custom programs, we can offer a set of commands, sample code, and comments from the developer of ApogeeConnect that should help provide a good foundation for getting your own program started using Apogee USB sensors (SP-420, SQ-420, SQ-520, etc.). This information is copied below:
Command |
Transmitted Bytes |
Returns |
---|---|---|
GET_VOLT | 55 21 | 5 [cmd + four byte floating point number] |
READ_CALIBRATION | 83 21 |
9 [cmd + four byte float multiplier + four byte float offset] |
SET_CALIBRATION | 84 XX XX XX XX YY YY YY YY 21 (X = 4 byte floating point multiplier, Y = 4 byte floating point offset) | 9 [same as above] |
READ_SERIAL_NUM | 87 21 |
5 [cmd + four byte floating point number] |
GET_LOGGING_INTERVAL | f1 21 |
9 [cmd + logging interval + sample interval] all floats |
GET_LOGGING_COUNT | f3 21 |
5 [cmd + number of entries] four byte integer |
GET_LOGGED_ENTRY | f2 XX XX XX XX 21 (X = 4 byte long integer) |
5 [cmd + four byte floating point number] |
SET_LOGGING_INTERVAL | f0 XX XX XX XX YY YY YY YY 21 (X = 4 byte floating point logging interval, Y = 4 byte floating point sampling interval |
8 [cmd + received logging interval + 3 dummy bytes] |
ERASE_LOGGED_DATA | f4 21 | 4 [cmd + 3 dummy bytes] |
There are many online resources on how to convert hexadecimal to floating point numbers. Here's a link with a few suggestions from other programmers using c# https://protect-eu.mimecast.com/s/pokJCZzGOF7xBKtNSk8F?domain=url9639.waterbearenergy.com.
The response from Apogee USB sensors is always sent in little endian format meaning the least significant byte is sent first.
Below is a super basic python class similar to the one used in the ApogeeConnect software. In the code, you can see examples of how to read in calibration data and how to use it to convert the voltage to usable data. This code is written for quantum sensors but will work the same way for pyranometers and UV USB sensors. The developer of ApogeeConnect has found python to be very easy to use with these sensors. If using python, you will need to install the Pyserial library if you do not already have it. This code is meant to work on Windows using python 2.7. Some modifications may be necessary for other operating systems and/or python 3.
*Please note: This code was thrown together without testing, so it may or may not need some debugging.
from serial import Serial
from time import sleep
import struct
GET_VOLT = '\x55!'
READ_CALIBRATION = '\x83!'
SET_CALIBRATION = '\x84%s%s!'
READ_SERIAL_NUM = '\x87!'
GET_LOGGING_COUNT = '\xf3!'
GET_LOGGED_ENTRY = '\xf2%s!'
ERASE_LOGGED_DATA = '\xf4!'
class Quantum(object):
def __init__(self):
"""Initializes class variables, and attempts to connect to device"""
self.quantum = None
self.offset = 0.0
self.multiplier = 0.0
self.connect_to_device()
def connect_to_device(self):
"""This function creates a Serial connection with the defined comport
and attempts to read the calibration values"""
port = 'COM1' # you'll have to check your device manager and put the actual com port here
self.quantum = Serial(port, 115200, timeout=0.5)
try:
self.quantum.write(READ_CALIBRATION)
multiplier = self.quantum.read(5)[1:]
offset = self.quantum.read(4)
self.multiplier = struct.unpack('<f', multiplier)[0]
self.offset = struct.unpack('<f', offset)[0]
except (IOError, struct.Error), data:
print data
self.quantum = None
def get_micromoles(self):
"""This function converts the voltage to micromoles"""
voltage = self.read_voltage()
if voltage == 9999:
# you could raise some sort of Exception here if you wanted to
return
# this next line converts volts to micromoles
micromoles = (voltage - self.offset) * self.multiplier * 1000
if micromoles < 0:
micromoles = 0
return micromoles
def read_voltage(self):
"""This function averages 5 readings over 1 second and returns
the result."""
if self.quantum == None:
try:
self.connect_to_device()
except IOError:
# you can raise some sort of exception here if you need to
return
# store the responses to average
response_list = []
# change to average more or less samples over the given time period
number_to_average = 5
# change to shorten or extend the time duration for each measurement
# be sure to leave as floating point to avoid truncation
number_of_seconds = 1.0
for i in range(number_to_average):
try:
self.quantum.write(GET_VOLT)
response = self.quantum.read(5)[1:]
except IOError, data:
print data
# dummy value to know something went wrong. could raise an
# exception here alternatively
return 9999
else:
if not response:
continue
# if the response is not 4 bytes long, this line will raise
# an exception
voltage = struct.unpack('<f', response)[0]
response_list.append(voltage)
sleep(number_of_seconds/number_to_average)
if response_list:
return sum(response_list)/len(response_list)
return 0.0
**Please note: Apogee does not provide technical support for custom programs.