Ibrahim Al-Akash
Published © CC BY

NIR Glucometer ENGI 301

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

IntermediateWork in progressOver 1 day773
NIR Glucometer ENGI 301

Things used in this project

Hardware components

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

Story

Read more

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.")

Credits

Ibrahim Al-Akash

Ibrahim Al-Akash

1 project • 3 followers
Thanks to Professor Welsh.

Comments