This repository has been archived on 2024-07-04. You can view files and clone it, but cannot push or open issues or pull requests.
serial_debugger/hardware/_controller/_controller.ino

601 lines
20 KiB
Arduino
Raw Permalink Normal View History

//<App !Start!>
// FILE: [_controller.ino]
// Created by GUIslice Builder version: [0.15.b004]
//
// GUIslice Builder Generated File
//
// For the latest guides, updates and support view:
// https://github.com/ImpulseAdventure/GUIslice
//
//<App !End!>
// ------------------------------------------------
// Headers to include
// ------------------------------------------------
//<Includes !Start!>
2020-09-03 23:24:06 +00:00
// Varous system includes
#include <Arduino.h>
// Various library includes
#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <BBQ10Keyboard.h>
#include <CircularBuffer.h>
2020-09-03 23:24:06 +00:00
// Various local includes
#include "_controller_GSLC.h"
// SD Card stuff
// The adagfx driver was updated to be able to retun a reference to the sd card
// This will NOT work w/o the driver tweak
// The implementation of gslc_GetSDCard only lives in the ada cpp driver so we need to extern it here
#define MAX_FILE_NAME_CHARS 21 // Add 1 to the max limit for null termination of filenames (SdFat API need)
#define MAX_PINOUTS 24
#define DIRECTORY_PINOUTS "/pinouts"
#include "src/SdFat/SdFat.h"
extern SdFat SD;
SdFile dirFile;
SdFile file;
char fileName[MAX_FILE_NAME_CHARS]; // Account for null termination
//<Includes !End!>
// ------------------------------------------------
// Program Globals
// ------------------------------------------------
2020-09-03 23:24:06 +00:00
// Battery level measurement
#define VBATPIN A6
float measuredVBat;
int batteryPercent;
2020-09-03 23:24:06 +00:00
// TFT Setup
#define TFT_CS 9
#define TFT_DC 10
Adafruit_ILI9341 tft(TFT_CS, TFT_DC);
#define CHARS_HORIZONTAL 29
#define CHARS_ROWS 11
#define CHARS_TOTAL CHARS_HORIZONTAL * CHARS_ROWS
CircularBuffer<char,CHARS_TOTAL> textBuffer;
// Keyboard
BBQ10Keyboard keyboard;
#define KBD_BTN_1 0x06
#define KBD_BTN_2 0x11
#define KBD_BTN_3 0x07
#define KBD_BTN_4 0x12
#define KBD_SW_UP 0x01
#define KBD_SW_DN 0x02
#define KBD_SW_LF 0x03
#define KBD_SW_RT 0x04
#define KBD_SW_OK 0x05
CircularBuffer<int16_t,8> uiKeyBuffer;
2020-09-09 02:14:28 +00:00
// Upstream keyboard definitions that will be really important later
#define _REG_CFG 2
#define CFG_OVERFLOW_INT (1 << 1)
#define CFG_KEY_INT (1 << 4)
#define CFG_USE_MODS (1 << 7) // Should Alt, Sym and Shifts modify the keys reported
#define CFG_REPORT_MODS (1 << 6) // Should Alt, Sym and Shifts be reported as well
2020-09-03 23:24:06 +00:00
// NeoPixels
#define PIXELS_NUM_BOARD 1
#define PIXELS_NUM_WING 1
#define PIXELS_WING_PIN 11
Adafruit_NeoPixel pixels_board(PIXELS_NUM_BOARD, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixels_wing(PIXELS_NUM_WING, PIXELS_WING_PIN, NEO_GRB + NEO_KHZ800);
// GUI state globals
bool textChanged = false;
bool popupOnScreen = false;
2020-09-13 07:41:21 +00:00
int listDiagramsSize = -1;
int listDiagramSelection = -1;
int listDiagramsRowsShown = 0;
// Various loop handlers
void handlerKeyboard();
void handlerBatteryLevel();
void processRingBuffer();
void sdCardInfo();
2020-09-13 07:41:21 +00:00
void listNext(gslc_tsElemRef*);
void listPrevious(gslc_tsElemRef*);
// Save some element references for direct access
//<Save_References !Start!>
2020-09-13 07:41:21 +00:00
gslc_tsElemRef* m_SliderDiagrams = NULL;
gslc_tsElemRef* m_pElemBatteryLevel= NULL;
2020-09-13 07:41:21 +00:00
gslc_tsElemRef* m_pElemLsDiagrams = NULL;
gslc_tsElemRef* m_pElemRd1152 = NULL;
gslc_tsElemRef* m_pElemRd96 = NULL;
gslc_tsElemRef* m_pElemRdUART0 = NULL;
gslc_tsElemRef* m_pElemRdUART5 = NULL;
2020-09-21 03:08:09 +00:00
gslc_tsElemRef* m_pElemRdUARTA = NULL;
gslc_tsElemRef* m_pElemSDInfo = NULL;
gslc_tsElemRef* m_pElemStatusText = NULL;
gslc_tsElemRef* m_pElemText = NULL;
gslc_tsElemRef* pImgFthM0Bsc = NULL;
gslc_tsElemRef* pImgKbdFw = NULL;
gslc_tsElemRef* pImgPi4Overview = NULL;
gslc_tsElemRef* pImgPi4Pins = NULL;
gslc_tsElemRef* pImgPiOrientation = NULL;
gslc_tsElemRef* pImgRpi = NULL;
//<Save_References !End!>
// Keyboard map related
#define MAX_INPUT_MAP 5
gslc_tsInputMap m_asInputMap[MAX_INPUT_MAP];
// Define debug message function
static int16_t DebugOut(char ch) { if (ch == (char)'\n') Serial.println(""); else Serial.write(ch); return 0; }
// ------------------------------------------------
// Callback Methods
// ------------------------------------------------
// Common Button callback
bool CbBtnCommon(void* pvGui,void *pvElemRef,gslc_teTouch eTouch,int16_t nX,int16_t nY)
{
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
if ( eTouch == GSLC_TOUCH_UP_IN ) {
// From the element's ID we can determine which button was pressed.
switch (pElem->nId) {
//<Button Enums !Start!>
//<Button Enums !End!>
default:
break;
}
}
return true;
}
//<Checkbox Callback !Start!>
//<Checkbox Callback !End!>
//<Keypad Callback !Start!>
//<Keypad Callback !End!>
//<Spinner Callback !Start!>
//<Spinner Callback !End!>
2020-09-13 07:41:21 +00:00
bool CbListbox(void* pvGui, void* pvElemRef, int16_t nSelId)
{
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui, pElemRef);
char acTxt[MAX_STR + 1];
if (pElemRef == NULL) {
return false;
}
// From the element's ID we can determine which listbox was active.
switch (pElem->nId) {
//<Listbox Enums !Start!>
case E_ELEM_LS_DIA:
if (nSelId != XLISTBOX_SEL_NONE) {
gslc_ElemXListboxGetItem(&m_gui, pElemRef, nSelId, acTxt, MAX_STR);
}
break;
//<Listbox Enums !End!>
default:
break;
}
return true;
}
//<Draw Callback !Start!>
//<Draw Callback !End!>
//<Slider Enums !Start!>
2020-09-13 07:41:21 +00:00
bool CbSlidePos(void* pvGui,void* pvElemRef,int16_t nPos)
{
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
int16_t nVal;
// From the element's ID we can determine which slider was updated.
switch (pElem->nId) {
//<Slider Enums !Start!>
case E_SLR_DIAGRAMS:
// Fetch the slider position
nVal = gslc_ElemXSliderGetPos(pGui,m_SliderDiagrams);
break;
//<Slider Enums !End!>
default:
break;
}
return true;
}
//<Slider Enums !End!>
//<Tick Callback !Start!>
//<Tick Callback !End!>
2020-09-03 23:24:06 +00:00
// Keyboard Input polling callback function
bool CbKbdPoll(void* pvGui, int16_t* pnPinInd, int16_t* pnPinVal) {
// Check for new keyboard events just in case
handlerKeyboard();
// If the key buffer is empty no events to process
if (uiKeyBuffer.isEmpty()) {
return false;
}
// Process a key that's on the key buffer for the UI event(s)
*pnPinInd = uiKeyBuffer.pop();
*pnPinVal = 1;
return true;
}
2020-09-13 06:25:03 +00:00
// ------------------------------------------------
// Setup
// ------------------------------------------------
void setup() {
Serial.begin(115200);
// If GUI Slice debug of drivers is turned on, wait for serial so we get proper debug output
#if defined(DBG_LOG) || defined(DBG_TOUCH) || defined(DBG_FRAME_RATE) || defined(DBG_DRAW_IMM) || defined(DBG_DRIVER)
while (!Serial) {
;
}
#endif
// GUI Slice debugging
gslc_InitDebug(&DebugOut);
2020-09-03 23:24:06 +00:00
// Setup red LED to indicate device is on (in case we disable NeoPixel battery level later)
pinMode(3, OUTPUT);
digitalWrite(3, HIGH);
// Setup NeoPixels
pixels_board.begin();
pixels_wing.begin();
pixels_board.clear();
pixels_wing.clear();
pixels_board.setBrightness(10);
pixels_wing.setBrightness(5);
// Setup neopixels to indicate we are initializing gui slice
// This can serve as an indication something has gone wrong like the sd card is missing
pixels_wing.setPixelColor(0, pixels_board.Color(255, 0, 0));
pixels_board.setPixelColor(0, pixels_board.Color(255, 0, 0));
pixels_wing.show();
pixels_board.show();
// Setup BBQ10Keyboard
Wire.begin();
keyboard.begin();
2020-09-09 02:14:28 +00:00
keyboard.writeRegister(_REG_CFG, CFG_OVERFLOW_INT | CFG_KEY_INT | CFG_USE_MODS | CFG_REPORT_MODS);
keyboard.setBacklight(0.5f);
// Init GUI Slicle
InitGUIslice_gen();
// Set the pin poll callback function
gslc_SetPinPollFunc(&m_gui, CbKbdPoll);
// Create the GUI input mapping (pin event to GUI action)
gslc_InitInputMap(&m_gui, m_asInputMap, MAX_INPUT_MAP);
gslc_InputMapAdd(&m_gui, GSLC_INPUT_PIN_ASSERT, KBD_SW_UP, GSLC_ACTION_FOCUS_NEXT, 0);
2020-09-13 07:41:21 +00:00
gslc_InputMapAdd(&m_gui, GSLC_INPUT_PIN_ASSERT, KBD_SW_DN, GSLC_ACTION_FOCUS_PREV, 0);
gslc_InputMapAdd(&m_gui, GSLC_INPUT_PIN_ASSERT, KBD_SW_LF, GSLC_ACTION_FOCUS_NEXT, 0);
gslc_InputMapAdd(&m_gui, GSLC_INPUT_PIN_ASSERT, KBD_SW_RT, GSLC_ACTION_FOCUS_PREV, 0);
gslc_InputMapAdd(&m_gui, GSLC_INPUT_PIN_ASSERT, KBD_SW_OK, GSLC_ACTION_SELECT, 0);
2020-09-13 07:41:21 +00:00
// Setup list of diagrams
// List files in pinouts directory and add to main list of available pinout files
if (!dirFile.open(DIRECTORY_PINOUTS, O_RDONLY)) {
SD.errorHalt("open root failed");
}
int n = 0;
while (n < MAX_PINOUTS && file.openNext(&dirFile, O_RDONLY)) {
n++;
// Skip directories and hidden files.
if (!file.isSubDir() && !file.isHidden()) {
// Save dirIndex of file in directory.
file.getName(fileName, MAX_FILE_NAME_CHARS * sizeof(char));
#if defined(DBG_LOG) || defined(DBG_TOUCH) || defined(DBG_FRAME_RATE) || defined(DBG_DRAW_IMM) || defined(DBG_DRIVER)
Serial.println(fileName);
#endif
gslc_ElemXListboxAddItem(&m_gui, m_pElemLsDiagrams, fileName);
}
file.close();
}
2020-09-13 07:41:21 +00:00
// Set diagrams slider max equal to the number of elements
listDiagramsSize = gslc_ElemXListboxGetItemCnt(&m_gui, m_pElemLsDiagrams);
gslc_tsXSlider* pSlider = (gslc_tsXSlider*)gslc_GetXDataFromRef(&m_gui,m_SliderDiagrams,GSLC_TYPEX_SLIDER,__LINE__);
pSlider->nPosMax = listDiagramsSize;
// Figure out how many rows are shown for diagrams list and cache (needed for scrolling)
gslc_tsXListbox* pListBox = (gslc_tsXListbox*)gslc_GetXDataFromRef(&m_gui,m_pElemLsDiagrams,GSLC_TYPEX_LISTBOX,__LINE__);
listDiagramsRowsShown = pListBox->nRows;
// Set neopixels to standard start state
pixels_wing.clear();
pixels_wing.show();
pixels_board.setPixelColor(0, pixels_board.Color(0, 0, 255));
pixels_board.show();
}
// -----------------------------------
// Main event loop
// -----------------------------------
void loop() {
// Reset text changed state (processing methods will update if needed)
textChanged = false;
// Handle keyboard events
handlerKeyboard();
// Update battery level as appropriate
handlerBatteryLevel();
// Update UI
if (textChanged) {
processRingBuffer();
}
gslc_Update(&m_gui);
}
// Move text from buffer -> text box on screen
void processRingBuffer() {
// Add keycodes to text box
char* textForDisplay = (char*) calloc(textBuffer.size(), sizeof(char));
for (int i=0; i<textBuffer.size(); i++) {
textForDisplay[i] = textBuffer[i];
}
gslc_ElemXTextboxAdd(&m_gui, m_pElemText, textForDisplay);
}
2020-09-13 06:25:03 +00:00
// ------------------------------------------------
// Keyboard Handler
// ------------------------------------------------
void handlerKeyboard() {
2020-09-09 02:14:28 +00:00
// Don't do anything if there are no key presses to process
if (keyboard.keyCount() == 0) {
return;
}
// Check if keys were pressed and drain queue of pressed keys
while (keyboard.keyCount() > 0) {
// Get keyboard event
const BBQ10Keyboard::KeyEvent key = keyboard.keyEvent();
// Process key press events
if (key.state == BBQ10Keyboard::StatePress) {
// Override ALL keyboard handling and hide popup if it's on-screen
if (popupOnScreen) {
gslc_PopupHide(&m_gui);
popupOnScreen = false;
}
// Move right through screens
else if (key.key == KBD_BTN_4) {
if (popupOnScreen) {
// Do nothing -- need to clear popup first
}
// Main Screen Flip
else if (gslc_GetPageCur(&m_gui) == E_PG_MAIN) {
gslc_ElemSetTxtStr(&m_gui, m_pElemStatusText, "Raspberry Pi");
gslc_SetPageCur(&m_gui, E_CONF_RPI);
}
// Raspberry Pi Screen Flip
else if (gslc_GetPageCur(&m_gui) == E_CONF_RPI) {
2020-09-13 07:41:21 +00:00
sdCardInfo();
gslc_ElemSetTxtStr(&m_gui, m_pElemStatusText, "Wiring Diagrams");
gslc_SetPageCur(&m_gui, E_WIRING);
}
// Wiring Diagram Screen Flip
else if (gslc_GetPageCur(&m_gui) == E_WIRING) {
sdCardInfo();
gslc_ElemSetTxtStr(&m_gui, m_pElemStatusText, "SD Card");
gslc_SetPageCur(&m_gui, E_SD_CARD);
}
// SD Card Info screen Flip
else if (gslc_GetPageCur(&m_gui) == E_SD_CARD) {
gslc_ElemSetTxtStr(&m_gui, m_pElemStatusText, "Serial Console");
gslc_SetPageCur(&m_gui, E_PG_MAIN);
}
}
// Add keys to the key buffer that are mapped to UI functions (only for config screens)
else if (key.key == KBD_SW_UP
2020-09-13 07:41:21 +00:00
|| key.key == KBD_SW_LF) {
if (gslc_GetPageCur(&m_gui) == E_WIRING) {
listPreviousDiagrams();
}
else {
uiKeyBuffer.push(key.key);
}
}
else if (key.key == KBD_SW_DN
|| key.key == KBD_SW_RT) {
if (gslc_GetPageCur(&m_gui) == E_WIRING) {
listNextDiagrams();
}
else {
uiKeyBuffer.push(key.key);
}
}
else if (key.key == KBD_SW_OK) {
if (gslc_GetPageCur(&m_gui) == E_WIRING) {
int16_t selection = gslc_ElemXListboxGetSel(&m_gui, m_pElemLsDiagrams);
char selectionText[MAX_FILE_NAME_CHARS];
gslc_ElemXListboxGetItem(&m_gui, m_pElemLsDiagrams, selection, selectionText, MAX_FILE_NAME_CHARS);
#if defined(DBG_LOG) || defined(DBG_TOUCH) || defined(DBG_FRAME_RATE) || defined(DBG_DRAW_IMM) || defined(DBG_DRIVER)
Serial.println(selectionText);
#endif
char fullPath[sizeof(DIRECTORY_PINOUTS) + 1 + MAX_FILE_NAME_CHARS];
strncat(fullPath, DIRECTORY_PINOUTS, sizeof(DIRECTORY_PINOUTS));
strncat(fullPath, "/", sizeof(char));
strncat(fullPath, selectionText, MAX_FILE_NAME_CHARS);
#if defined(DBG_LOG) || defined(DBG_TOUCH) || defined(DBG_FRAME_RATE) || defined(DBG_DRAW_IMM) || defined(DBG_DRIVER)
Serial.println(fullPath);
#endif
// gslc_tsImgRef imageRef = gslc_GetImageFromSD(fullPath, GSLC_IMGREF_FMT_BMP24);
// gslc_ElemSetImage(&m_gui, m_pElemImgPin, imageRef, imageRef);
gslc_PopupShow(&m_gui, E_PG_DIAGRAM, true);
popupOnScreen = true;
}
else {
uiKeyBuffer.push(key.key);
}
}
// Process keys 'normally'
else {
char output[28];
snprintf(output, 28, "key: '%c' (dec %d, hex %02x)", key.key, key.key, key.key);
textChanged = true;
for (int i=0; i<sizeof(output); i++) {
char toPush = output[i];
if (toPush != '\0') {
textBuffer.push(toPush);
}
else if (toPush == '\0') {
textBuffer.push('\n');
}
}
}
}
// Pick color for signaling a key was pressed (short or long press)
uint32_t keyboard_pixel_color = pixels_wing.Color(0, 0, 255);
if (key.state == BBQ10Keyboard::StateLongPress) {
keyboard_pixel_color = pixels_wing.Color(0, 255, 255);
}
// Display indicator accordingly
if (key.state == BBQ10Keyboard::StatePress || key.state == BBQ10Keyboard::StateLongPress) {
pixels_wing.setPixelColor(0, keyboard_pixel_color);
pixels_wing.show();
}
else {
pixels_wing.clear();
pixels_wing.show();
}
}
2020-09-03 23:24:06 +00:00
}
2020-09-13 06:25:03 +00:00
// ------------------------------------------------
// Battery level handler
// ------------------------------------------------
void handlerBatteryLevel() {
2020-09-03 23:24:06 +00:00
measuredVBat = analogRead(VBATPIN);
measuredVBat *= 2; // we divided by 2, so multiply back
measuredVBat *= 3.3; // Multiply by 3.3V, our reference voltage
measuredVBat /= 1024; // convert to voltage
batteryPercent = (measuredVBat / 3.3) * 100;
gslc_tsColor colorForHeaderElements;
if (batteryPercent >= 75) {
pixels_board.setPixelColor(0, pixels_board.Color(0, 255, 0));
colorForHeaderElements = GSLC_COL_GREEN;
2020-09-03 23:24:06 +00:00
}
else if (batteryPercent >= 50) {
colorForHeaderElements = GSLC_COL_YELLOW;
pixels_board.setPixelColor(0, pixels_board.Color(255, 255, 0));
2020-09-03 23:24:06 +00:00
}
else if (batteryPercent >= 25) {
colorForHeaderElements = GSLC_COL_ORANGE;
pixels_board.setPixelColor(0, pixels_board.Color(255, 128, 0));
2020-09-03 23:24:06 +00:00
}
else {
colorForHeaderElements = GSLC_COL_RED;
pixels_board.setPixelColor(0, pixels_board.Color(255, 0, 0));
2020-09-03 23:24:06 +00:00
}
gslc_ElemXProgressSetVal(&m_gui, m_pElemBatteryLevel, batteryPercent);
gslc_tsXProgress* pGauge = (gslc_tsXProgress*)gslc_GetXDataFromRef(&m_gui,m_pElemBatteryLevel,GSLC_TYPEX_PROGRESS,__LINE__);
pGauge->colGauge = colorForHeaderElements;
gslc_ElemSetTxtCol(&m_gui, m_pElemStatusText, colorForHeaderElements);
// Update GUI and NeoPixel with battery status information
pixels_board.show();
}
2020-09-13 07:41:21 +00:00
// ------------------------------------------------
// List next/previous handlers (no way to do this with the std kbd stuff)
// ------------------------------------------------
void diagramsScrollUpdate() {
gslc_ElemXListboxSetSel(&m_gui, m_pElemLsDiagrams, listDiagramSelection);
gslc_ElemXSliderSetPos(&m_gui, m_SliderDiagrams, listDiagramSelection);
gslc_ElemXListboxSetScrollPos(&m_gui, m_pElemLsDiagrams, listDiagramSelection);
}
void listNextDiagrams() {
listDiagramSelection++;
if (listDiagramSelection >= listDiagramsSize) {
listDiagramSelection = -1;
}
diagramsScrollUpdate();
}
void listPreviousDiagrams() {
listDiagramSelection--;
if (listDiagramSelection < -1) {
listDiagramSelection = listDiagramsSize-1;
}
diagramsScrollUpdate();
}
2020-09-13 06:25:03 +00:00
// ------------------------------------------------
// SD Card Info
// ------------------------------------------------
void sdCardInfo() {
// Clear text box
gslc_ElemXTextboxReset(&m_gui, m_pElemSDInfo);
// Reasonably small buffer for filling text box
char textForDisplay[64];
// FAT Type
snprintf(textForDisplay, 64, "Volume is FAT%d\n", int(SD.vol()->fatType()));
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, textForDisplay);
// Free Space
snprintf(textForDisplay, 64, "Free Space (MB): N/A\n (Performance Issues)\n");
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, textForDisplay);
2020-09-13 06:25:03 +00:00
uint32_t cardSizeBlocks = SD.card()->cardSize();
uint32_t cardCapacityMB = (cardSizeBlocks + 2047)/2048;
snprintf(textForDisplay, 64, "Card Size: %.0fMb\n", 1.048576*cardCapacityMB);
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, textForDisplay);
2020-09-13 06:25:03 +00:00
char* type;
switch (SD.card()->type()) {
case SD_CARD_TYPE_SD1:
type = "SD1";
break;
case SD_CARD_TYPE_SD2:
type = "SD2";
break;
case SD_CARD_TYPE_SDHC:
if (cardSizeBlocks < 70000000) {
type = "SDHC";
} else {
type = "SDXC";
}
break;
default:
type = "Unknown";
}
snprintf(textForDisplay, 64, "Type: %s\n", type);
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, textForDisplay);
2020-09-13 06:25:03 +00:00
cid_t cid;
SD.card()->readCID(&cid);
snprintf(textForDisplay, 64, "Serial Number: %x\n", cid.psn);
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, textForDisplay);
snprintf(textForDisplay, 64, "Manufacturing date: %04d-%02d\n", (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high), int(cid.mdt_month));
gslc_ElemXTextboxAdd(&m_gui, m_pElemSDInfo, textForDisplay);
2020-09-13 06:25:03 +00:00
gslc_Update(&m_gui);
}