Non-invasive optical-based glucose sensor for ENGI 301. *** Note: Work-in-progress. Not complete yet. ***
Things used in this project
Hardware components
Story
About
Diabetes is a common illness impacting the lives of hundreds of millions of people around the world. 1 out of every 11 people have diabetes, meaning there are over 420 million diabetics worldwide. 3.7 million diabetics die from diabetes and hyperglycemia (high blood glucose levels).
To effectively treat diabetes, patients must monitor glucose levels using current methods:
Invasive procedures that require implantation of a device Painful finger prick tests produce a lot of waste and are not reusable
These methods require patients to actively monitor their glucose, which is inconvenient and reduces patient compliance, ultimately leading to worse outcomes. Furthermore, the invasive nature of these methods increases the risk of infection. To combat this, I aim to develop a non-invasive glucometer to enable diabetics to monitor their glucose levels with ease and comfort.
Working Principle
To attack this challenge, I propose an optical-based glucose sensor that can calculate blood glucose concentrations. A near-infrared LED transmits light through the finger which is measured by a spectroscopy sensor. The light interacts with the various constituents in the blood, resulting in granular changes in the absorbance reading of the spectroscopy sensor. This is strongly correlated with blood glucose concentration, so a regression model can be fit to the sensor data to calibrate the sensor to be patient-specific. I was inspired to pursue this project by the paper "Non-Invasive Glucose Monitoring Using NIR Spectroscopy" by Reddy et al. Since the paper is interesting to me and the results are promising, I wanted to test if the results are replicable using the PocketBeagle microcomputer.
Hardware Setup
System block diagram of the NIR glucometer.
Power block diagram of the NIR glucometer.
Software Setup
Software flow diagram for NIR glucometer program.
I learned that temperature impacts the IR spectroscopy reading quite significantly. To account for this, the sensor module includes a calibration circuit which automatically corrects the analog voltage reading of the IR sensor. Since glucose concentration can be measured in mg/dL or in mmol/L, I am including a feature that enables the user to toggle between the different units. Furthermore, a patient might not be aware of what the reading means, so I use color coded LEDs which classify the glucose reading into severe/moderate dysglycemia, mild dysglycemia, or healthy. I also made the skeleton of the program in MATLAB, so when the parts arrive I can get started with rapid prototyping.
MATLAB code for software implementation of embedded system.
Unfortunately, the parts never arrived so this is still a work in progress. In the meantime, however, I worked on creating the initial structure of the Python file that the PocketBeagle would run. I made some progress, but many of the functions are still missing, since I want to test my functions with the hardware. Porting the MATLAB code to Python was straightforward, but the biggest hurdle was writing the functions for the glucose sensor. Since the sensor was created by SparkFun targeting the Arduino line of microprocessors, I need to read through the Arduino libraries which are written in C and C++, and convert them to Python. Also, since the Arduino NIR spectroscopy library depends on another Arduino library facilitating the I2C communication with the chip, I need to find how to implement this in the PocketBeagle which runs on Linux. I think I am on the right track - it involves using the i2cget and i2cset commands and finding the virtual addresses from the documentation. Still more work is needed, which would be easier to complete once the materials arrive.
Concluding Remarks
Although the parts did not arrive on time, I was able to complete the software implementation in MATLAB and was able to port most of the code to Python. Further work is needed to interface with the sensor via I2C communication and to test the other hardware components. Down the line, I hope to shrink the form factor of the device to become wearable, so it is more convenient for patients to use. I also plan to incorporate a Bluetooth module and replace the hardware UI with a smartphone application. This will make the product more polished and patients will have the potential to better interpret the data with a software assistant. Including features such as push notifications to alert patients of dysglycemia or rewards to incentivize healthy glucose levels, I aim to help improve patients' quality of life and help them better manage their glucose levels with ease. I want to thank Professor Welsh for his help and support with this project. I learned a lot of new skills that will allow me to solve more problems in the future!
Schematics
Circuit Schematics
These are the schematics for the proposed circuit. Includes a system block diagram and power block diagram.
Code
glucometer.py
Python
This is the main file that the PocketBeagle would run to initiate the program.
# -*- coding: utf-8 -*-
"""
--------------------------------------------------------------------------
Glucometer Code
--------------------------------------------------------------------------
License:
Copyright 2022 Ibrahim Al-Akash
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
Use the following hardware components to make a noninvasive NIR-based glucometer:
- HT16K33 Display
- Button
- Red LED
- Yellow LED
- Green LED
- White Indicator LEDs (x2)
Requirements:
- Hardware:
- Button
- Waiting for button press to initiate sensor function
- Upon completion of glucose measurement, wait for button input
- Record press time and return
- Display shows the status of the glucometer and glucose concentration after measurement
- User Interaction (UI):
- Needs to be able to initiate glucose reading
- Can change the units of the glucose concentration displayed from mg/dL to mmol/L if button is pressed
- Clear the measurement if the button is held for a certain interval
- Easily interpret the glucose reading as healthy, moderate dysglycemia, or severe dysglycemia
Uses:
- HT16K33 display library developed in class
"""
# ------------------------------------------------------------------------
# Constants
# ------------------------------------------------------------------------
# None
# ------------------------------------------------------------------------
# Global variables
# ------------------------------------------------------------------------
# None
# ------------------------------------------------------------------------
# Functions / Classes
# ------------------------------------------------------------------------
import Adafruit_BBIO.GPIO as GPIO
import Adafruit_BBIO.ADC as ADC
import ht16k33 as HT16K33
from AS726X import IR_SENSOR
class Sensor():
""" Sensor """
clear_time = None
red_led = None
yellow_led = None
green_led = None
mmol_led = None
mgdl_led = None
display = None
button = None
ir_sensor = None
sensor_reading = None
def __init__(self, clear_time=3.0, button="P2_2",
red_led="P2_6", yellow_led="P2_4",
green_led="P2_8", mmol_led="P2_10",
mgdl_led="P1_6", ir_sensor=0x49,
ir_bus=2, display_address=0x70,
display_bus=1):
""" Initialize variables and set up display """
self.clear_time = clear_time
self.button = button
self.red_led = red_led
self.yellow_led = yellow_led
self.green_led = green_led
self.mmol_led = mmol_led
self.mgdl_led = mgdl_led
self.ir_sensor = IR_SENSOR(ir_bus, ir_sensor)
self.display = HT16K33.HT16K33(display_bus, display_address)
self._setup()
# End def
def _setup(self):
""" Setup the hardware components """
# Initialize Display
self.set_display_dash()
# Initialize Button
GPIO.setup(self.button, GPIO.IN)
# Initialize LEDs
GPIO.setup(self.red_led, GPIO.OUT)
GPIO.setup(self.yellow_led, GPIO.OUT)
GPIO.setup(self.green_led, GPIO.OUT)
GPIO.setup(self.mmol_led, GPIO.OUT)
GPIO.setup(self.mgdl_led, GPIO.OUT)
# IR Sensor is initialized when object is created
# End def
def set_display_dash(self):
"""Set display to word "----" """
self.display.text("----")
# End def
def glucose_concentration(self):
"""Calculate glucose concentration from sensor NIR spectroscopy measurement"""
pass
# End def
def read_sensor(self):
"""Read sensor value and save to object"""
value = self.ir_sensor.take_measurements()
self.sensor_reading = value
# End def
AS726X.py
Python
This is the driver file for the SparkFun NIR spectroscopy sensor.
# -*- coding: utf-8 -*-
"""
--------------------------------------------------------------------------
AS7263 Driver
--------------------------------------------------------------------------
License:
Copyright 2022 Ibrahim Al-Akash
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
Driver for the AS7263 NIR Spectroscopy Sensor
"""
# ------------------------------------------------------------------------
# Constants
# ------------------------------------------------------------------------
from sympy import integer_log
import os,time
import subprocess
AS726X_ADDR = 0x49 #7-bit unshifted default I2C Address
#AS7263 Registers
AS726x_DEVICE_TYPE = 0x00
AS726x_HW_VERSION = 0x01
AS726x_CONTROL_SETUP = 0x04
AS726x_INT_T = 0x05
AS726x_DEVICE_TEMP = 0x06
AS726x_LED_CONTROL = 0x07
AS72XX_SLAVE_STATUS_REG = 0x00
AS7263_R = 0x08
AS7263_S = 0x0A
AS7263_T = 0x0C
AS7263_U = 0x0E
AS7263_V = 0x10
AS7263_W = 0x12
AS7263_R_CAL = 0x14
AS7263_S_CAL = 0x18
AS7263_T_CAL = 0x1C
AS7263_U_CAL = 0x20
AS7263_V_CAL = 0x24
AS7263_W_CA =0x28
AS72XX_SLAVE_TX_VALID = 0x02
AS72XX_SLAVE_RX_VALID = 0x01
SENSORTYPE_AS726 = 0x3F
POLLING_DELAY = 5 #Amount of ms to wait between checking for virtual register changes
# ------------------------------------------------------------------------
# Global variables
# ------------------------------------------------------------------------
# None
# ------------------------------------------------------------------------
# Functions / Classes
# ------------------------------------------------------------------------
class IR_SENSOR():
""" IR Sensor """
i2c_address = None
mode = None
gain = None
integration_time = None
def __init__(self, bus=2, address=AS726X_ADDR, mode=3, gain=3,
integration_time=50):
if mode not in [0, 1, 2, 3]:
raise ValueError('Invalid Mode')
self.mode = mode
self.gain = gain
self.integration_time = integration_time
self.write_i2c_command = "i2cset -y {0} {1}".format(bus, address)
self.read_i2c_command = "i2cget -y {0} {1}".format(bus, address)
# End def
def read_byte(self, data_address):
""" Reads byte at specified data address of IR sensor """
command = self.read_i2c_command + " {0}".format(data_address)
proc=subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, )
output=proc.communicate()[0]
return output
# End def
def write_byte(self, data_address, value):
""" Write byte to specified data address of IR sensor """
command = self.write_i2c_command + " {0} {1}".format(data_address, value)
proc=subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, )
output=proc.communicate()[0]
return output
# End def
#### WIP ####
def init_device(self):
self._sensor_version = self.virtual_read_register(AS726x_HW_VERSION) # How to read virtual register in Linux?
if (self._sensor_version != 0x3E) & (self._sensor_version != 0x3F):
raise ValueError("Wrong sensor version {}. Should be 0x3E or 0x3F".format(self._sensor_version))
self.set_bulb_current(0)
self.disable_bulb()
self.set_indicator_current(0b11)
self.disable_indicator_led()
self.set_integration_time(self._integration_time)
self.set_gain(self._gain)
self.set_measurement_mode(self._mode)
# End def
#### WIP ####
def set_measurement_mode(self, mode):
# Sets the measurement mode
# Mode 0: Continuous reading of VBGY (7262) / STUV (7263)
# Mode 1: Continuous reading of GYOR (7262) / RTUX (7263)
# Mode 2: Continuous reading of all channels (power-on default)
# Mode 3: One-shot reading of all channels
if (mode > 0b11):
mode = 0b11
value = self.virtual_read_register(AS726x_CONTROL_SETUP)
value = value & 0b11110011
value = value | (mode << 2) #Set BANK bits with user's choice
self.virtual_write_register(AS726x_CONTROL_SETUP, value) # How to write virtual register in Linux
self._mode = mode
# End def
#### WIP ####
def take_measurements(self):
# Clear DATA_RDY flag when using Mode 3
self.clear_data_available()
# Goto mode 3 for one shot measurement of all channels
self.set_measurement_mode(3);
#Wait for data to be ready
while self.data_available() == False:
time.sleep_ms(POLLING_DELAY)
r_val = self.read_byte(AS7263_R)
s_val = self.read_byte(AS7263_S)
t_val = self.read_byte(AS7263_T)
u_val = self.read_byte(AS7263_U)
v_val = self.read_byte(AS7263_V)
w_val = self.read_byte(AS7263_W)
return [r_val, s_val, t_val, u_val, v_val, w_val]
# End def
skeleton.m
MATLAB
This is the software implementation of the glucose sensor device. It simulates the general structure of the program and generates sample data.
function skeleton
% This is the skeleton outline of the glucose sensor program
clear = "";
button_state = 0;
% If button is pressed, initiate sensing procedure
while button_state == 0
button_state = read_button;
end
ir_data = initiate_sensor();
[mgdl, mmol] = analyze(ir_data);
glucose = [mgdl, mmol];
unit_toggle = 1;
output_screen(glucose(unit_toggle));
button_state = 0;
while button_state == 0
button_state = read_button;
end
output_screen(clear);
if unit_toggle == 1
output_screen(glucose(2));
unit_toggle = 2;
else
output_screen(glucose(1));
unit_toggle = 1;
end
end
function button_value = read_button
% Reads button input
button_value = 1;
end
function data = initiate_sensor
% Reads spectroscopy data
data = 499; % Analog voltage of sensor
end
function [mgdl,mmol] = analyze(data)
% Performs regression on calibrate sensor readings and converts to glucose
% concentration levels
severe_hypo = 53; % Severe Hypoglycemia is <53 mg/dL
hypo = 70; % Hypoglycemia is <70 mg/dL
hyper = 125; % Hyperglycemia is >125 mg/dL
severe_hyper = 200; % Sever Hyperglycemia is >200 mg/dL
red = 3;
yellow = 2;
green = 1;
mgdl = (3*10^-5) *data^2 + 0.2903*data - 4.798; % Glucose concentration in mg/dL
mmol = mgdl/18; % Glucose concentration in mmol/L
led = green;
if mgdl > severe_hyper || mgdl < severe_hypo
led = red;
elseif mgdl > hyper || mgdl < hypo
led = yellow;
end
activate_led(led, 1);
end
function activate_led(led, state)
% Toggles LED given the index of the color LED and whether HIGH or LOW
% state is requested
leds = ["green", "yellow", "red"];
fprintf("%s LED is toggled %d\n", leds(led), state);
end
function output_screen(output)
% This function outputs result to display
disp(output);
end
ht16k33.py
Python
This is the HT16K33 7-SEGMENT Display driver file I created in class.
# -*- coding: utf-8 -*-
"""
--------------------------------------------------------------------------
HT16K33 I2C Library
--------------------------------------------------------------------------
License:
Copyright 2018-2022 Ibrahim Al-Akash
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
Software API:
HT16K33(bus, address=0x70)
- Provide i2c bus that display is on
- Provide i2c address for the display
clear()
- Sets value of display to "0000"
blank()
- Turns off all LEDs on display
set_colon(enable)
- Turns on / off the colon on the display. Enable must be True/False.
update(value)
- Update the value on the display. Value must be between 0 and 9999.
text(value)
- Update the value on the display with text.
The following characters are supported:
"abcdefghijlnopqrstuyABCDEFGHIJLNOPQRSTUY? -"
--------------------------------------------------------------------------
Background Information:
* Using seven-segment digit LED display for Adafruit's HT16K33 I2C backpack:
* http://adafruit.com/products/878
* https://learn.adafruit.com/assets/36420
* https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf
* Base code (adapted below):
* https://github.com/emcconville/HT16K33/blob/master/FourDigit.py
* https://github.com/emcconville/HT16K33/blob/master/_HT16K33.py
* https://github.com/adafruit/Adafruit_Python_LED_Backpack/blob/master/Adafruit_LED_Backpack/HT16K33.py
* https://github.com/adafruit/Adafruit_Python_LED_Backpack/blob/master/Adafruit_LED_Backpack/SevenSegment.py
* https://github.com/adafruit/Adafruit_Python_LED_Backpack/blob/master/examples/sevensegment_test.py
* Letters Supported from:
* https://en.wikichip.org/wiki/seven-segment_display/representing_letters
"""
import os
# ------------------------------------------------------------------------
# Constants
# ------------------------------------------------------------------------
# See https://en.wikipedia.org/wiki/Seven-segment_display for reference
HEX_DIGITS = [0x3f, 0x06, 0x5b, 0x4f, # 0, 1, 2, 3
0x66, 0x6d, 0x7d, 0x07, # 4, 5, 6, 7
0x7f, 0x6f, 0x77, 0x7c, # 8, 9, A, b
0x39, 0x5e, 0x79, 0x71] # C, d, E, F
LETTERS = { "a" : 0x77, "A" : 0x77, # "A"
"b" : 0x7c, "B" : 0x7c, # "b"
"c" : 0x58, "C" : 0x39, # "c", "C"
"d" : 0x5e, "D" : 0x5e, # "d"
"e" : 0x79, "E" : 0x79, # "E"
"f" : 0x71, "F" : 0x71, # "F"
"g" : 0x6F, "G" : 0x6F, # "g"
"h" : 0x74, "H" : 0x76, # "h", "H"
"i" : 0x04, "I" : 0x30, # "i", "I"
"j" : 0x0e, "J" : 0x0e, # "J"
# Cannot be implemented "k" : None, "K" : None,
"l" : 0x38, "L" : 0x38, # "L"
# Cannot be implemented "m" : None, "M" : None,
"n" : 0x54, "N" : 0x54, # "n"
"o" : 0x5c, "O" : 0x3f, # "o", "O"
"p" : 0x73, "P" : 0x73, # "P"
"q" : 0x67, "Q" : 0x67, # "q"
"r" : 0x50, "R" : 0x50, # "r"
"s" : 0x6D, "S" : 0x6D, # "S"
"t" : 0x78, "T" : 0x78, # "t"
"u" : 0x1c, "U" : 0x3e, # "u", "U"
# Cannot be implemented "v" : None, "V" : None,
# Cannot be implemented "w" : None, "W" : None,
# Cannot be implemented "x" : None, "X" : None,
"y" : 0x6e, "Y" : 0x6e, # "y"
# Cannot be implemented "z" : None, "Z" : None,
" " : 0x00, # " "
"-" : 0x40, # "-"
"0" : 0x3f, # "0"
"1" : 0x06, # "1"
"2" : 0x5b, # "2"
"3" : 0x4f, # "3"
"4" : 0x66, # "4"
"5" : 0x6d, # "5"
"6" : 0x7d, # "6"
"7" : 0x07, # "7"
"8" : 0x7f, # "8"
"9" : 0x6f, # "9"
"?" : 0x53 # "?"
}
CLEAR_DIGIT = 0x7F
POINT_VALUE = 0x80
DIGIT_ADDR = [0x00, 0x02, 0x06, 0x08]
COLON_ADDR = 0x04
HT16K33_BLINK_CMD = 0x80
HT16K33_BLINK_DISPLAYON = 0x01
HT16K33_BLINK_OFF = 0x00
HT16K33_BLINK_2HZ = 0x02
HT16K33_BLINK_1HZ = 0x04
HT16K33_BLINK_HALFHZ = 0x06
HT16K33_SYSTEM_SETUP = 0x20
HT16K33_OSCILLATOR = 0x01
HT16K33_BRIGHTNESS_CMD = 0xE0
HT16K33_BRIGHTNESS_HIGHEST = 0x0F
HT16K33_BRIGHTNESS_DARKEST = 0x00
# ------------------------------------------------------------------------
# Functions / Classes
# ------------------------------------------------------------------------
class HT16K33():
""" Class to manage a HT16K33 I2C display """
# Class variables
bus = None
address = None
command = None
def __init__(self, bus, address=0x70, blink=HT16K33_BLINK_OFF, brightness=HT16K33_BRIGHTNESS_HIGHEST):
""" Initialize class variables; Set up display; Set display to blank """
# Initialize class variables
self.bus = bus
self.address = address
self.command = "i2cset -y {0} {1}".format(bus, address)
# Set up display
self.setup(blink, brightness)
# Set display to blank
self.blank()
# End def
def setup(self, blink, brightness):
"""Initialize the display itself"""
# i2cset -y 1 0x70 0x21
os.system("{0} {1}".format(self.command, (HT16K33_SYSTEM_SETUP | HT16K33_OSCILLATOR)))
# i2cset -y 1 0x70 0x81
os.system("{0} {1}".format(self.command, (HT16K33_BLINK_CMD | blink | HT16K33_BLINK_DISPLAYON)))
# i2cset -y 1 0x70 0xEF
os.system("{0} {1}".format(self.command, (HT16K33_BRIGHTNESS_CMD | brightness)))
# End def
def encode(self, data, double_point=False):
"""Encode data to TM1637 format.
This function will convert the data from decimal to the TM1637 data fromt
:param value: Value must be between 0 and 15
Will throw a ValueError if number is not between 0 and 15.
"""
ret_val = 0
try:
if (data != CLEAR_DIGIT):
if double_point:
ret_val = HEX_DIGITS[data] + POINT_VALUE
else:
ret_val = HEX_DIGITS[data]
except:
raise ValueError("Digit value must be between 0 and 15.")
return ret_val
# End def
def set_digit(self, digit_number, data, double_point=False):
"""Update the given digit of the display."""
os.system("{0} {1} {2}".format(self.command, DIGIT_ADDR[digit_number], self.encode(data, double_point)))
# End def
def set_digit_raw(self, digit_number, data, double_point=False):
"""Update the given digit of the display using raw data value"""
os.system("{0} {1} {2}".format(self.command, DIGIT_ADDR[digit_number], data))
# End def
def set_colon(self, enable):
"""Set the colon on the display."""
if enable:
os.system("{0} {1} {2}".format(self.command, COLON_ADDR, 0x02))
else:
os.system("{0} {1} {2}".format(self.command, COLON_ADDR, 0x00))
# End def
def blank(self):
"""Clear the display to read nothing"""
self.set_colon(False)
self.set_digit_raw(3, 0x00)
self.set_digit_raw(2, 0x00)
self.set_digit_raw(1, 0x00)
self.set_digit_raw(0, 0x00)
# End def
def clear(self):
"""Clear the display to read '0000'"""
self.set_colon(False)
self.set_digit(3, 0)
self.set_digit(2, 0)
self.set_digit(1, 0)
self.set_digit(0, 0)
# End def
def update(self, value):
"""Update the value on the display.
This function will clear the display and then set the appropriate digits
:param value: Value must be between 0 and 9999.
Will throw a ValueError if number is not between 0 and 9999.
"""
if value < 0 or value > 9999:
raise ValueError("Value is not a number not between 0 and 9999!")
self.set_digit(0, value // 1000)
self.set_digit(1, (value % 1000) // 100)
self.set_digit(2, (value % 100) // 10)
self.set_digit(3, (value % 10))
# End def
def text(self, value):
""" Update the value on the display with text
:param value: Value must have between 1 and 4 characters
Will throw a ValueError if there are not the appropriate number of
characters or if characters are used that are not supported.
"""
if ((len(value) < 1) or (len(value) > 4)):
raise ValueError("Must have between 1 and 4 characters")
# Clear the display
self.blank()
# Set the display to the correct characters
for i, char in enumerate(value):
try:
# Translate the character into the value needed for hex display
self.set_digit_raw(i, LETTERS[char])
except:
raise ValueError("Character {0} not supported".format(char))
# End class
# ------------------------------------------------------------------------
# Main script
# ------------------------------------------------------------------------
if __name__ == '__main__':
import time
delay = 0.1
print("Test HT16K33 Display:")
display = HT16K33(1, 0x70)
for i in range(0, 10):
display.update(i)
time.sleep(delay)
for i in range(0, 100, 10):
display.update(i)
time.sleep(delay)
for i in range(0, 1000, 100):
display.update(i)
time.sleep(delay)
for i in range(0, 10000, 1000):
display.update(i)
time.sleep(delay)
for value in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00]:
display.set_digit_raw(0, value)
time.sleep(delay)
# Test letters
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?"
for char in letters:
try:
display.text(char)
time.sleep(delay)
except:
print("Character not supported: {0}".format(char))
display.text("done")
time.sleep(1)
display.set_colon(True)
time.sleep(1)
display.clear()
print("Test Finished.")
The article was first published in hackster, October 24, 2022
cr: https://www.hackster.io/isa1/nir-glucometer-engi-301-3c6461
author: Ibrahim Al-Akash