Helpers for Display, Buttons, and Setup Functions

Helpers are used to simplify the primary loop code. The helpers:

  • Calculate display coordinates for the row/column grid of blocks used for the image and text labels and values;
  • Display a status message in the center of the image area;
  • Display and refresh the sensor image;
  • Display and refresh the histogram image;
  • The parameter setup process.

element_grid() Helper

 

The element_grid() helper accepts a column/row tuple and returns a display x/y coordinate address tuple for positioning the upper left corner of each graphic box or text label.

The display is divided into 80 blocks in a matrix of 10 columns and 8 rows. The temperature value sidebar uses all rows of the two leftmost columns while the image area uses the remaining 8 columns.

The Coords assignment is a simple way to create the named tuple of x/y values for use by element_grid() or elsewhere.

### Helpers ###
def element_grid(col0, row0):  # Determine display coordinates for column, row
    x = int(ELEMENT_SIZE * col0 + 30)  # x coord + margin
    y = int(ELEMENT_SIZE * row0 + 1)   # y coord + margin
    return x, y  # Return display coordinates

flash_status() Helper

The flash_status() helper accepts a text string and displays it in the status area of the display. The text appears for as white letters for a time specified by duration then as black letters for duration length in seconds. This is very useful for flashing a message that can be seen regardless of the background colors, especially handy while displaying a sensor image.

def flash_status(text="", duration=0.05):  # Flash status message once
    status_label.color = WHITE
    status_label.text  = text
    time.sleep(duration)
    status_label.color = BLACK
    time.sleep(duration)
    return

update_image_frame() Helper

The update_image_frame() helper looks through a list of 64 sensor temperature values stored by row and column in the image list. The helper analyzes all values in degrees Celsius leaving the conversion to Fahrenheit to the primary process loop.

When invoked, the helper clears variables used to find minimum and maximum sensor values as well as a "bucket" (sum_bucket) used to sum all values to be used later to calculate the average temperature. It also makes certain that histogram legend text is not displayed when showing a sensor image.

def update_image_frame():  # Get camera data and display
    minimum = MAX_SENSOR_C  # Set minimum to sensor's maximum C value
    maximum = MIN_SENSOR_C  # Set maximum to sensor's minimum C value

    min_histo.text   = ""  # Clear histogram legend
    max_histo.text   = ""
    range_histo.text = ""

    sum_bucket = 0  # Clear bucket for building average value

Next, the image list is examined one element at a time, starting at the upper left of the display's image area array and working down to the lower right. Each sensor element's temperature value is checked against the sensor's minimum and maximum allowed values since the sensor's output value can incorrectly fall outside of that range. Based on the sensor value's relationship to the entire temperature display range, a color is assigned to the value and the corresponding array block is filled with that color and displayed.

Finally, maximum, minimum, and sum_bucket values are returned to the primary process loop to convert to Fahrenheit and display in the sidebar.

for row1 in range(0, 8):  # Parse camera data list and update display
        for col1 in range(0, 8):
            value = map_range(image[7 - row1][7 - col1],
                              MIN_SENSOR_C, MAX_SENSOR_C,
                              MIN_SENSOR_C, MAX_SENSOR_C)
            color_index = int(map_range(value, MIN_RANGE_C, MAX_RANGE_C, 0, 7))
            image_group[((row1 * 8) + col1) + 1].fill = element_color[color_index]
            sum_bucket = sum_bucket + value  # Calculate sum for average
            minimum = min(value, minimum)
            maximum = max(value, maximum)
    return minimum, maximum, sum_bucket

update_histo_frame() Helper

The update_histo_frame() helper collects a distribution of 8 temperature sub-ranges within the entire temperature display range (one for each color) and displays a histogram of relative temperature values. The helper scans all 64 sensor temperature values an counts the number of times a value falls within one of 8 sub-ranges. The helper analyzes all values in degrees Celsius leaving the conversion to Fahrenheit to the primary process loop.

When invoked, the helper clears variables used to find minimum and maximum sensor values, the average summing bucket, and the list containing the 8 "buckets" (histo_bucket) representing the distribution of values across the temperature display range. The helper displays the current temperature display range minimum and maximum values in the histogram legend area.

def update_histo_frame():
    minimum = MAX_SENSOR_C  # Set minimum to sensor's maximum C value
    maximum = MIN_SENSOR_C  # Set maximum to sensor's minimum C value

    min_histo.text   = str(MIN_RANGE_F)  # Display histogram legend
    max_histo.text   = str(MAX_RANGE_F)
    range_histo.text = "-RANGE-"

    sum_bucket = 0  # Clear bucket for building average value

Next, the 64 sensor values are examined one element at a time. The sensor element value is checked to make certain that it only falls within the allowable sensor range.

Each value is evaluated against the current display range to create the histogram distribution. An index is created (histo_index) that is used to increment the corresponding element of histo_bucket.

Minimum and maximum values are captured along with the summed values in sum_bucket as each element is evaluated.

The histo_bucket list now contains the distribution of 8 temperature sub-ranges found within the temperature display range. These values will be used to create the histogram image on the display.

histo_bucket = [0, 0, 0, 0, 0, 0, 0, 0]  # Clear histogram bucket
    for row2 in range(7, -1, -1):  # Collect camera data and calculate spectrum
        for col2 in range(0, 8):
            value = map_range(image[col2][row2],
                              MIN_SENSOR_C, MAX_SENSOR_C,
                              MIN_SENSOR_C, MAX_SENSOR_C)
            histo_index = int(map_range(value, MIN_RANGE_C, MAX_RANGE_C, 0, 7))
            histo_bucket[histo_index] = histo_bucket[histo_index] + 1
            sum_bucket = sum_bucket + value  # Calculate sum for average
            minimum = min(value, minimum)
            maximum = max(value, maximum)

The second part of the helper updates the image area to show the histogram, starting at the upper left of the display's image array area and working down to the lower right. Each box in the array is filled with a color that corresponds to the relative temperature, proportional to the count in that sub-range. The remainder of boxes in the histogram display area are colored black.

Finally, minimum, maximum, and sum_bucket values are returned to the primary process loop to convert to Fahrenheit and display in the sidebar.

for col2 in range(0, 8):  # Display histogram
        for row2 in range(0, 8):
            if histo_bucket[col2] / 8 > 7 - row2:
                image_group[((row2 * 8) + col2) + 1].fill = element_color[col2]
            else:
                image_group[((row2 * 8) + col2) + 1].fill = BLACK
    return minimum, maximum, sum_bucket

setup_mode() Helper

The setup_mode() helper pauses normal operation and collects user input to set alarm threshold and display range min/max values. During the Setup mode, the display's average value and label are blanked since it's not possible to set the computed average value.

The joystick or PyBadge D-Pad is used to select the parameter to change and to increase or decrease the parameter value. The HOLD button acts as the parameter select button. Pressing the SET button at any time during the Setup mode will exit back to the primary process loop.

The first task is to temporarily display a status message that indicates the camera is in the Setup mode. The display's average value and label are blanked and the measured maximum and minimum values are replaced with the current maximum and minimum display range values (MAX_RANGE_F and MIN_RANGE_F).

After waiting a bit for the status message to be read and prior to watching for button and joystick changes, the index pointer (param_index) is reset to point to the alarm threshold parameter.

def setup_mode():  # Set alarm threshold and minimum/maximum range values
    status_label.color = WHITE
    status_label.text  = "-SET-"

    ave_label.color = BLACK  # Turn off average label and value display
    ave_value.color = BLACK

    max_value.text = str(MAX_RANGE_F)  # Display maximum range value
    min_value.text = str(MIN_RANGE_F)  # Display minimum range value

    time.sleep(0.8)  # Show SET status text before setting parameters
    status_label.text  = ""  # Clear status text

    param_index = 0  # Reset index of parameter to set

The following is the meat of the setup process. First, the process waits until the SET button has been released before moving on to choosing which parameter to set.

As long as the HOLD (select) or the SET (setup mode exit) buttons have not been pressed, the code loops. During the loop, the joystick is watched using the move_buttons() helper. If the joystick is moved down, the parameter index is incremented, pointing to the next parameter. If moved up, the index will point to the previous parameter. The parameter label text flashes black and white, indicating which parameter is ready to be changed.

In the image_group list (that is defined later, just before the primary process loop) the three parameter text labels for alarm, maximum, and minimum are sequentially positioned in the list:

  • Alarm text label       --> image_group[66]
  • Maximum text label --> image_group[67]
  • Minimum text label  --> image_group[68]    

Using an indexed position in image_group for the parameters makes it simpler to sequentially step from one parameter to the next.

# Select parameter to set
    while not panel.button.start:
        while (not panel.button.a) and (not panel.button.start):
            up, down = move_buttons(joystick=panel.has_joystick)
            if up:
                param_index = param_index - 1
            if down:
                param_index = param_index + 1
            param_index = max(0, min(2, param_index))
            status_label.text = param_list[param_index][0]
            image_group[param_index + 66].color = BLACK
            status_label.color = BLACK
            time.sleep(0.2)
            image_group[param_index + 66].color = param_list[param_index][1]
            status_label.color = WHITE
            time.sleep(0.2)

After the HOLD button is pressed and released, the selected parameter, represented by the value of param_index, can be changed.

The selected parameter value is incrementally changed by the joystick's up and down movements as provided to this helper from the move_buttons() helper. The new value is checked against and limited to the sensor's factory min/max limits (MIN_SENSOR_F, MAX_SENSOR_F).

In the image_group list the three parameter value labels for alarm, maximum, and minimum are sequentially positioned in the list:

  • Alarm value label       --> image_group[70]
  • Maximum value label --> image_group[71]
  • Minimum value label  --> image_group[72]  

The value label for the selected parameter is changed and displayed.

Meanwhile, a flashing status message indicates which type of parameter is being changed, either the alarm or one of the range values.

When the desired value is reached and the HOLD (select) button is pressed, the Setup process continues back to the parameter select mode. If SET is pressed instead, the Setup process prepares to exit back to the primary process loop.

if panel.button.a:  # Button A pressed
            panel.play_tone(1319, 0.030)  # E6
        while panel.button.a:  # wait for button release
            pass

        # Adjust parameter value
        param_value = int(image_group[param_index + 70].text)
        while (not panel.button.a) and (not panel.button.start):
            up, down = move_buttons(joystick=panel.has_joystick)
            if up:
                param_value = param_value + 1
            if down:
                param_value = param_value - 1
            param_value = max(MIN_SENSOR_F, min(MAX_SENSOR_F, param_value))
            image_group[param_index + 70].text = str(param_value)
            image_group[param_index + 70].color = BLACK
            status_label.color = BLACK
            time.sleep(0.05)
            image_group[param_index + 70].color = param_list[param_index][1]
            status_label.color = WHITE
            time.sleep(0.2)

        if panel.button.a:  # Button A pressed
            panel.play_tone(1319, 0.030)  # E6
        while panel.button.a:  # wait for button release
            pass

    # Exit setup process
    if panel.button.start:  # Start button pressed
        panel.play_tone(784, 0.030)  # G5
    while panel.button.start:  # wait for button release
        pass

Before exiting, a resumption status message is displayed and the display of the average label and value are restored.

Finally, the text strings that may have changed during the Setup process are converted to integer numeric values and returned to the primary process loop.

status_label.text = "RESUME"
    time.sleep(0.5)
    status_label.text = ""

    # Display average label and value
    ave_label.color = YELLOW
    ave_value.color = YELLOW
    return int(alarm_value.text), int(max_value.text), int(min_value.text)

move_buttons() Helper

The move_buttons() helper first resets the variables that indicate joystick movement or D-Pad button presses. If the joystick argument is True, the joystick movements beyond set thresholds will be registered like button depressions. For example, a value for panel.joystick[1] of less than 20000 means that the joystick was moved upwards; greater than 44000 indicates downward movement.

If the joystick argument is False, instead of watching the joystick, the D-Pad buttons are checked to see if any are depressed.

Finally, the movement indicating values are returned to the calling module.

def move_buttons(joystick=False):  # Read position buttons and joystick
    move_u = move_d = False
    if joystick:  # For PyGamer: interpret joystick as buttons
        if   panel.joystick[1] < 20000:
            move_u = True
        elif panel.joystick[1] > 44000:
            move_d = True
    else:  # For PyBadge read the buttons
        if panel.button.up:
            move_u = True
        if panel.button.down:
            move_d = True
    return move_u, move_d

This guide was first published on Jan 29, 2020. It was last updated on Mar 28, 2024.

This page (Helpers) was last updated on Mar 08, 2024.

Text editor powered by tinymce.