NIR Glucometer ENGI 301

0 36172 Medium

Non-invasive optical-based glucose sensor for ENGI 301. *** Note: Work-in-progress. Not complete yet. ***

 

NIR Glucometer ENGI 301

Things used in this project

 

Hardware components

HARDWARE LIST
1 DFRobot Gravity:Digital Push Button (Yellow)
1 5V LDO (Mouser Part #511-LD29150PT50R)
4 Through Hole Resistor, 330 ohm
1 5 mm LED: Red
1 5 mm LED: Yellow
1 5 mm LED: Green
1 NIR LED 950nm (Mouser Part #755-SIR-34ST3F)
1 BeagleBoard.org PocketBeagle
1 SparkFun Spectral Sensor Breakout - AS7263 NIR (Qwiic)
1 SparkFun 7-Segment Serial Display - Red

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.

 

System block diagram of the NIR glucometer.

 

Power block diagram of the NIR glucometer.

 

Power block diagram of the NIR glucometer.

 

Software Setup

 

Software flow diagram for NIR glucometer program.

 

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.

 

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.

 

icon system_block_diagram_LHJsYa32Vh.zip 614KB Download(17)

Code

 

glucometer.py

Python

This is the main file that the PocketBeagle would run to initiate the program.

CODE
# -*- 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.

CODE
# -*- 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.

CODE
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.

CODE
# -*- 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

License
All Rights
Reserved
licensBg
0