This repository contains the electronics design and control software for a portable, low-cost luminescence reader developed by the BioE team at Chan Zuckerberg Biohub San Francisco (CZ Biohub SF). This instrument was developed in response to the need for a device to read out a split-luciferase luminescence assay for a sars-cov-2 antibody test compatible with remote, low-resource settings. The assay itself was developed by Susanna Elledge et al. in Jim Wells' lab at the University of California San Francisco (UCSF), in collaboration with Cristina Tato (CZ Biohub SF).
Link to preprint: https://www.medrxiv.org/content/10.1101/2023.05.18.23290120v1
Like many assays, the split-luciferase assay was developed on a high-performance benchtop luminometer with sub-attomole limit of detection for luciferase. Unfortunately, there is an orders-of-magnitude performance gap between these devices and available handheld luminometers. The need for the equivalent performance level but in a robust, handheld format was identified by Cristina Tato, and together with Senjuti Saha at CHRF Dhaka, is leading an effort to map coronavirus immunity across rural regions in Bangladesh.
In response to this need, the BioE team developed a portable Silicon Photomultipler (SiPM) - based luminometer. The device accepts 1-2 PCR tubes, and reports on luminescence levels using sensors that are read out by a 24-bit analog-to-digital converter. The device includes a shutter system that repeatedly blocks and unblocks the signal from reaching the sensor, thereby continuously performing dark measurements in order to stabilize the baseline of the measurement against drift, achieving sub-attomole performance on par with high-end benchtop instruments costing an order of magnitude more.
The software was written to run on the Raspberry Zero W inside the device, which interfaces with custom PCBs that perform the physical measurements. The user interacts with the device with side-mounted buttons, and results are displayed on a low-power, e-ink screen.
The structure of this repo is illustrated below. Note: the 'Electronics design' and 'Early calculations' directories were truncated for brevity.
├── CHANGES.md
├── Early calculations
├── Electronics design
├── LICENSE
├── README.md
├── RPi Config
│ └── config.txt
├── gpclk_config
│ ├── README.md
│ └── dt-blob.dts
├── luminometer
│ ├── __init__.py
│ ├── adc_constants.py
│ ├── adc_phat_example.py
│ ├── adc_reader.py
│ ├── ads131m08_reader.py
│ ├── crc.py
│ ├── luminometer.py
│ ├── luminometer_constants.py
│ ├── lumiscreen.py
│ ├── menu.py
│ ├── no_sensor_validation.py
│ ├── pwm_test.py
│ ├── screen_test.py
│ └── temp_coeffs.json
└── setup.py
- Flash our pre-existing 16gb SD card onto a new card. The latest image can be downloaded from Google Drive here. After downloading the image, use the recommended Raspberry Pi Imager, select the "Use Custom" option, and select the downloaded image to flash it onto the 16gb SD card.
- Install the latest Raspbian OS on a micro-SD card by following the instructions at: https://www.raspberrypi.org/software/
- The
config.txt
has been validated with RPi OS (full w/ recommended software) version 5.10.17+- We may want to switch to RPi Lite (smaller size, stripped away of superfluous services which may help w/ more "real-time" timing), but we have not validated it yet.
- The
- After the OS is loaded on the micro-SD card and while still loaded in your mac/PC/linux machine, replace
/boot/config.txt
with the version found in this repo. This configures the SPI bus on the device, turns off audio processing and the HDMI driver (to save power), and turns off the on-board activity light (to reduce stray light inside the device). - Follow the instructions to set up the RPi 'headless' (no keyboard or monitor), using WiFi.
- ssh into the device
- Remember to change the hostname after ssh'ing in:
sudo raspi-config
(see here for more, note that the raspi-config menu may look slightly different than what the documentation shows, but the option to configure the hostname will still be there somewhere in the menu)
- Remember to change the hostname after ssh'ing in:
- Create and activate a virtual environment with Python3:
- TODO: convert the following three commands into a single command once the repo has been made public
cd /home/pi/Documents/
python3 -m venv lumi
source lumi/bin/activate
- Install the display driver from Pimoroni, answering 'y' when prompted.
curl https://get.pimoroni.com/inky | bash
- Clone this repository and navigate to the base:
git clone https://github.com/czbiohub/ulc-tube-reader/
cd ulc-tube-reader
- Install setuptools:
pip install setuptools
- Install wheel
pip install wheel
- Install module
pip install .
- Follow the instructions in
gpclk_configi/README.md
in order to configure the general purpose clock on-board the RPi, which the ADC requires as input. - Configure the RPi to run the luminometer software after boot is completed. After these commands are entered, the RPi will automatically start executing the luminometer software upon startup, but it will still be possible to ssh into the device at the same time.
sudo nano /lib/systemd/system/lumiboot.service
In the newly-opened text file, paste the following text:
- TODO: add in a command to activate the virtualenv on boot as well
[Unit]
Description=Luminometer boot command
After=multi-user.target
[Service]
Type=idle
ExecStart=/usr/bin/python3 /home/pi/Documents/ulc-tube-reader/luminometer/luminometer.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Now enter the following commands in the terminal to activate the boot service:
ExecStart=/usr/bin/python3 /home/pi/Documents/ulc-tube-reader/luminometer/luminometer.py > /home/pi/lumilog.log 2>&1
sudo chmod 644 /lib/systemd/system/lumiboot.service
sudo systemctl daemon-reload
sudo systemctl enable lumiboot.service
- Run the following command to start the pigpiod daemon on startup:
sudo systemctl enable pigpiod
- Power down the RPi:
sudo poweroff
- Attach the Inky pHat to the GPIO header, then securely plug the RPi into the socket header on the luminometer board.
- Power on the device and make measurements.
gpclk_config/
- contains the dt-blob.dts
file required to run the ADC. See the README for instructions on setting up the Raspberry Pi.
adc_reader.py
- defines the ADCReader
abstract base class, includes the basic operations for ADC drivers for this project
ads131m08_reader.py
- defines the ADS131M08Reader
driver for the ADS131M08. The if __name__ == '__main__'
block at the bottom of the file has an example of running the software.
consts.py
- defines specific constants for the ADS131M08Reader
class, such as register addresses, commands, e.t.c.
crc.py
- defines the CRC functions for communications with the ADC.
- Pull changes from remote repository
- Activate virtual environment with previous install
- Navigate to the module directory
- Update module (pip install . --upgrade)
Two different licenses are used in this repo. The BSD 3-Clause License is applied to the entire repo except for the hardware design files contained in the Electronics design
directory, which are separately licensed by the CERN Open Hardware Licence Version 2 - Weakly Reciprocal
, contained in that directory.