Nanodrop (nanodrop v0.3.1)
Copy MarkdownElixir library for interfacing with NanoDrop 1000 spectrophotometers.
The NanoDrop 1000 internally uses an Ocean Optics USB2000 spectrometer and communicates via the OOI (Ocean Optics Interface) protocol over USB.
Quick Start
# Start the server (connects to first available device)
{:ok, pid} = Nanodrop.start_link()
# Calibrate with water/buffer on pedestal (takes dark + blank in one shot)
:ok = Nanodrop.calibrate(pid)
# Measure a sample (includes full spectrum in result)
{:ok, result} = Nanodrop.measure_nucleic_acid(pid)
# => %{a260: 1.5, a280: 0.75, a260_a280: 2.0, concentration_ng_ul: 75.0, spectrum: ...}
# Or get the spectrum and analyze it
{:ok, spectrum} = Nanodrop.get_spectrum(pid)
abs_280 = Nanodrop.absorbance_at(spectrum, 280.0)Device Identification
- USB Vendor ID:
0x2457(Ocean Optics) - USB Product ID:
0x1002(USB2000)
Calibration
For accurate absorbance measurements, you need two reference spectra:
- Dark - Detector baseline with no light (close pedestal arm, no sample)
- Blank - 100% transmission through solvent (water/buffer on pedestal)
Absorbance is then calculated as: A = -log10((sample - dark) / (blank - dark))
Distributed Operation
This module is designed to work over Erlang distribution. The NanoDrop server can run on a dedicated node (e.g., a Nerves device with USB access) while being controlled from a remote node.
Direct node reference
# On the device node (e.g., nanodrop@device.local)
{:ok, pid} = Nanodrop.start_link(name: Nanodrop)
# From a remote node
Nanodrop.set_dark({Nanodrop, :"nanodrop@device.local"})
Nanodrop.set_blank({Nanodrop, :"nanodrop@device.local"})
{:ok, result} = Nanodrop.measure_nucleic_acid({Nanodrop, :"nanodrop@device.local"})Global registration
# On the device node
{:ok, pid} = Nanodrop.start_link(name: {:global, :nanodrop})
# From any connected node
Nanodrop.set_dark({:global, :nanodrop})
{:ok, result} = Nanodrop.measure_nucleic_acid({:global, :nanodrop})Process groups (pg)
# On the device node
{:ok, pid} = Nanodrop.start_link()
:pg.join(:spectrophotometers, pid)
# From any connected node
[pid | _] = :pg.get_members(:spectrophotometers)
{:ok, result} = Nanodrop.measure_nucleic_acid(pid)All API functions accept any valid GenServer.server() reference.
Summary
Functions
Returns the absorbance at a specific wavelength from a spectrum.
Performs full calibration in one shot (dark + blank).
Returns whether the device is calibrated (has both dark and blank spectra).
Returns a specification to start this module under a supervisor.
Acquires a raw spectrum from the device.
Acquires a spectrum and calculates absorbance values.
Returns device information.
Lists all connected NanoDrop devices.
Measures nucleic acid concentration.
Measures protein concentration using A280.
Returns the device serial number.
Measures and stores the blank/reference spectrum.
Measures and stores the dark spectrum.
Sets the integration time in microseconds.
Starts the NanoDrop server and connects to a device.
Returns the wavelength calibration coefficients.
Types
Functions
@spec absorbance_at(absorbance_spectrum(), float()) :: float()
Returns the absorbance at a specific wavelength from a spectrum.
Wavelength is in nanometers. This is a pure function that operates
on a spectrum returned by get_spectrum/1.
@spec calibrate(GenServer.server()) :: :ok | {:error, term()}
Performs full calibration in one shot (dark + blank).
Call this with your reference solvent (water, buffer) on the pedestal and the arm closed. This will:
- Take a dark measurement (no lamp flash) - detector baseline
- Take a blank measurement (lamp flashes) - reference through solvent
This is more efficient than calling set_dark/1 and set_blank/1 separately
since both measurements are taken with the same sample in place.
@spec calibrated?(GenServer.server()) :: boolean()
Returns whether the device is calibrated (has both dark and blank spectra).
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec get_raw_spectrum(GenServer.server()) :: {:ok, Nanodrop.Spectrum.t()} | {:error, term()}
Acquires a raw spectrum from the device.
Returns pixel intensity values without any processing.
@spec get_spectrum(GenServer.server()) :: {:ok, absorbance_spectrum()} | {:error, term()}
Acquires a spectrum and calculates absorbance values.
Requires calibration (dark and blank spectra). Returns a spectrum with absorbance and wavelength values for each pixel.
This is the single GenServer call for spectrum acquisition. Use this
with absorbance_at/2 or the measurement functions for analysis.
@spec info(GenServer.server()) :: map()
Returns device information.
@spec list_devices() :: [Nanodrop.Device.device_info()]
Lists all connected NanoDrop devices.
Returns a list of device info maps that can be used with start_link/1.
Example
Nanodrop.list_devices()
#=> [%{vendor_id: 9303, product_id: 4098, bus: 1, address: 19, device_ref: #Reference<...>}]
@spec measure_nucleic_acid( GenServer.server(), keyword() ) :: {:ok, map()} | {:error, term()}
Measures nucleic acid concentration.
Returns A260, A280, A260/A280 ratio, estimated concentration, and the full spectrum. Uses the approximation: 1 A260 = 50 ng/µL for dsDNA (1mm path).
Options
:factor- Conversion factor (default: 50.0 for dsDNA, use 33.0 for ssDNA, 40.0 for RNA)
Requires calibration.
@spec measure_protein( GenServer.server(), keyword() ) :: {:ok, map()} | {:error, term()}
Measures protein concentration using A280.
Returns A280, estimated concentration, and the full spectrum.
Options
:extinction_coefficient- Extinction coefficient (default: 1.0, meaning 1 A280 = 1 mg/mL)
Requires calibration.
@spec serial_number(GenServer.server()) :: String.t()
Returns the device serial number.
@spec set_blank(GenServer.server()) :: :ok | {:error, term()}
Measures and stores the blank/reference spectrum.
Call this with your reference solvent (water, buffer) on the pedestal.
@spec set_dark(GenServer.server()) :: :ok | {:error, term()}
Measures and stores the dark spectrum.
Call this with the light path blocked (pedestal arm closed, nothing on pedestal).
@spec set_integration_time(GenServer.server(), pos_integer()) :: :ok | {:error, term()}
Sets the integration time in microseconds.
Valid range: 3,000 - 655,350,000 µs. Default is 100,000 µs (100ms).
@spec start_link(keyword()) :: GenServer.on_start()
Starts the NanoDrop server and connects to a device.
Returns :ignore if running in network-only mode or if the USB library
is not available. This allows the application to start on nodes that
don't have USB access (e.g., remote control nodes in a distributed setup).
Options
:device- A device info map fromNanodrop.list_devices/0. If not provided, connects to the first available device.:name- Optional name for the GenServer.
Configuration
:network_only- When set totruein application config, the server will return:ignoreinstead of connecting to USB. This is useful for nodes that only need to call a remote NanoDrop server over distribution.config :nanodrop, network_only: true
@spec wavelength_calibration(GenServer.server()) :: map()
Returns the wavelength calibration coefficients.