[{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/832/original/IMG_1988.jpeg?1713203554","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n        \u003cp\u003eThe CG-35 is a \u003cem\u003e\u003cstrong\u003eCircuitPython\u003c/strong\u003e\u003c/em\u003e emulation of the Hewlett Packard HP-35 Scientific Reverse-Polish Notation (RPN) calculator designed for the Adafruit ESP32-S3 Feather and 3.5-inch TFT FeatherWing capacitive touch display. The calculator consists of a 10-digit LED-like display backed-up with 20-digit internal calculation precision.\u003c/p\u003e\n\u003cp\u003eThis emulation reproduces the HP-35 calculator's v2.0 firmware where the change sign (CHS) key is active only after digit entry has begun. And because of the \u003ccode\u003eudecimal\u003c/code\u003e and \u003ccode\u003eutrig\u003c/code\u003e classes, calculation accuracy of monadic, dyadic, and trigonometric functions was improved. As an added bonus not present on the original calculator, a status message will appear just below the primary display when a calculation error is encountered.\u003c/p\u003e\n\u003cp\u003eThe calculator's graphical layout was designed to mimic the aspect ratio of the original calculator -- that's why the left and right sides of the display screen were left empty. However, to provide a more reliable touch screen experience, the keys are somewhat proportionally larger than the original.\u003c/p\u003e\n\u003cp\u003eThis project was inspired by Jeff Epler's \u003ca href=\"https://learn.adafruit.com/diy-rpn-desktop-calculator-with-circuitpython\" target=\"_blank\"\u003eDIY Desktop Calculator with CircuitPython\u003c/a\u003e project and Jeff's work to create CircuitPython versions of \u003ccode\u003eudecimal\u003c/code\u003e and \u003ccode\u003eutrig\u003c/code\u003e. Thank you Jeff!\u003c/p\u003e\n\u003cp\u003eGitHub Repository: \u003ca href=\"https://github.com/CedarGroveStudios/CG-35_Calculator\" target=\"_blank\"\u003ehttps://github.com/CedarGroveStudios/CG-35_Calculator\u003c/a\u003e\u003c/p\u003e\n      \n\n\n","metadata":{}},{"element_type":"alert","content":"\u003cp\u003eReverse Polish Notation is a postfix scheme where operator (function) selection follows the input of the operands (values). During the age of mechanical calculator engines, postfix notation was an efficient way of entering values and functions for computing a result. Early electronic calculators were designed to operate with the familiar mechanical notation method, eventually to be mostly replaced by devices using infix (\"algebraic\" or \"arithmetical\") notation.\u003c/p\u003e","metadata":{"class":"element alert-element build-alert alert-info","markdown":"Reverse Polish Notation is a postfix scheme where operator (function) selection follows the input of the operands (values). During the age of mechanical calculator engines, postfix notation was an efficient way of entering values and functions for computing a result. Early electronic calculators were designed to operate with the familiar mechanical notation method, eventually to be mostly replaced by devices using infix (\"algebraic\" or \"arithmetical\") notation.","alert_type":"info"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/913/original/IMG_1990.jpeg?1717475786","metadata":{"caption":""}},{"element_type":"embed","content":"https://youtu.be/bBqApLaqzmY"},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/504/original/IMG_2555.jpeg?1736791955","metadata":{"caption":""}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/505/original/IMG_2557.jpeg?1736791979","metadata":{"caption":""}},{"element_type":"text","content":"\n        \u003ch2\u003eImplementation Notes\u003c/h2\u003e\n\u003cp\u003eHardware:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eAdafruit ESP32-S3 4Mb/2Mb Feather or UnexpectedMaker FeatherS2\u003c/li\u003e\n\u003cli\u003eAdafruit TFT Display FeatherWing - 3.5\" 480x320 Capacitive Touchscreen\u003c/li\u003e\n\u003cli\u003ePiezo Speaker (optional)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eSoftware:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eJeff Epler's adaptation of MicroPython udecimal and utrig\u003c/li\u003e\n\u003cli\u003eCedarGrove SevenSeg-12.bdf font\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n      ","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/5477","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/5872","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/160","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eFuture Feature Wish List\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eImplement display brightness control.\u003c/li\u003e\n\u003cli\u003eAdjust for automatic scientific notation conversion for entered values less than |1| with a negative exponent greater than -6. This is an inherent behavior of the Decimal class in MicroPython, CircuitPython, and CPython.\u003c/li\u003e\n\u003cli\u003ePurge the T-register when calculating trigonometric functions to fully emulate the HP-35 calculation process.\u003c/li\u003e\n\u003cli\u003eIncorporate a selectable degrees/radians mode with display indicator. (Enhancement)\u003c/li\u003e\n\u003cli\u003eIncorporate a selectable scientific/engineering/fixed decimal point mode. (Enhancement)\u003c/li\u003e\n\u003cli\u003eAdd a setup button for setting initial parameters. (Enhancement)\u003c/li\u003e\n\u003c/ul\u003e\n      \n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eCircuitPython Code\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eThe Bundle\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe bundle folder of the CG-35_Calculator repository contains all the necessary code, libraries, and fonts needed for the calculator.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/836/original/cg_35_repo.jpeg?1713284743","metadata":{"caption":"The repository's bundle folder contents"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eDownload the contents of the bundle folder and place the files into the root directory of the ESP32-S3 Feather.\u003c/p\u003e\n\u003cp\u003eThe ESP32-S3 Feather's root directory should now contain the following folders and files:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cem\u003ecg_35_calculator.py\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003ecode.py\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003esettings.toml\u003c/em\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003efonts folder:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cem\u003eOpenSans-9.bdf\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eSevenSeg-12.bdf\u003c/em\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003elib (library) folder containing these required libraries:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cem\u003eadafruit_bitmap_font\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eadafruit_bus_device\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eadafruit_button\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eadafruit_display_shapes\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eadafruit_display_text\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eadafruit_ft5336\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eadafruit_hx835\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eadafruit_register\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003ejepler_udecimal\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003esimpleio\u003c/em\u003e\u003c/li\u003e\n\u003c/ul\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/837/original/cg_35_circuitpy_drive.jpeg?1713284823","metadata":{"caption":"Contents of the ESP32-S3 Feather root directory"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003e\u003cstrong\u003ePrimary Code Module\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe primary code module \u003ccode\u003ecg_35_calculator.py\u003c/code\u003e, imported by \u003ccode\u003ecode.py\u003c/code\u003e, instantiates the display, plots the calculator case and buttons, and implements all calculator operational processes. This module uses a state machine design with the following named states:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cspan\u003eIDLE -- Display results or wait for input\u003c/span\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cspan\u003eC_ENTRY -- Coefficient entry (keys\u003c/span\u003e\u003cspan class=\"hljs-comment\"\u003e: \u003ccode\u003e0\u003c/code\u003e-\u003ccode\u003e9\u003c/code\u003e, \u003ccode\u003e.\u003c/code\u003e, \u003ccode\u003eCHS\u003c/code\u003e, \u003c/span\u003e\u003cspan class=\"hljs-comment\"\u003e\u003ccode\u003eEEX\u003c/code\u003e)\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cspan\u003eE_ENTRY -- Exponent entry (keys:\u003c/span\u003e\u003cspan class=\"hljs-comment\"\u003e\u0026nbsp;\u003ccode\u003e0\u003c/code\u003e-\u003ccode\u003e9\u003c/code\u003e, \u003ccode\u003e.\u003c/code\u003e, \u003c/span\u003e\u003cspan class=\"hljs-comment\"\u003e\u003ccode\u003eCHS\u003c/code\u003e)\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cspan\u003eSTACK -- Stack management (keys:\u003c/span\u003e\u003cspan class=\"hljs-comment\"\u003e \u003ccode\u003eENTER\u003c/code\u003e, \u003ccode\u003eCLR\u003c/code\u003e, \u003ccode\u003eCLX\u003c/code\u003e, \u003ccode\u003eSTO\u003c/code\u003e, \u003ccode\u003eRCL\u003c/code\u003e, \u003ccode\u003eR\u003c/code\u003e, \u003ccode\u003ex\u0026lt;\u0026gt;y\u003c/code\u003e, \u003ccode\u003eπ\u003c/code\u003e)\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cspan\u003eMONADIC -- \u003c/span\u003e\u003cspan class=\"hljs-comment\"\u003eMonadic calculator functions (keys: \u003ccode\u003eLOG\u003c/code\u003e, \u003ccode\u003eLN,\u003c/code\u003e \u003ccode\u003ee^x\u003c/code\u003e, \u003ccode\u003e√x\u003c/code\u003e, \u003ccode\u003eARC\u003c/code\u003e, \u003ccode\u003eSIN\u003c/code\u003e, \u003ccode\u003eCOS\u003c/code\u003e,\u003ccode\u003eTAN\u003c/code\u003e, \u003ccode\u003e1/x\u003c/code\u003e)\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cspan\u003eDYADIC --\u003c/span\u003e\u003cspan class=\"hljs-comment\"\u003e Dyadic calculator functions (keys: \u003ccode\u003ex^y\u003c/code\u003e, \u003ccode\u003e-\u003c/code\u003e, \u003ccode\u003e+\u003c/code\u003e, \u003ccode\u003e*\u003c/code\u003e, \u003ccode\u003e÷\u003c/code\u003e)\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cspan\u003eERROR -- Calculation error\u003c/span\u003e\u003cspan\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cspan\u003eThe calculator's display precision and internal calculation precision are specified using the variables \u003ccode\u003eDISPLAY_PRECISION\u003c/code\u003e and \u003ccode\u003eINTERNAL_PRECISION\u003c/code\u003e. Although the internal precision exceeds that of the original HP-35 calculator, it is recommended to keep the existing default settings of 10 digits and 20 digits, respectively, to avoid rounding errors.\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan\u003eThe variable \u003ccode\u003eDEBUG\u003c/code\u003e can be use to provide additional internal register status via the REPL. This boolean variable defaults to \u003ccode\u003eFalse\u003c/code\u003e (no additional register status).\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"# SPDX-FileCopyrightText: 2022, 2024 Cedar Grove Maker Studios\n# SPDX-License-Identifier: MIT\n\n\"\"\"\ncg_35_calculator.py  2024-04-16 v2.2\nFor the ESP32-S3 4Mb/2Mb Feather and 3.5-inch TFT Capacitive FeatherWing\n====================================\n\nAn HP-35-like RPN calculator application for the Adafruit ESP32-S3 Feather\nand 3.5-inch TFT FeatherWing display with capacitive touch. The calculator\nconsists of a 10-digit LED display with 20-digit internal calculation\nprecision.\n\nThis application emulates the HP-35 calculator's v2.0 firmware where the change\nsign (CHS) key is active only after digit entry has begun. Calculation accuracy\nof monadic, dyadic, and trigonometric functions was improved. An error descriptor\nmessage area just below the primary display was added.\n\nThe calculator's graphical layout was designed to mimic the aspect ratio of the\noriginal calculator. Because of the relative small size of the buttons,\ntouchscreen accuracy is important, requiring the use of a capacitive touch\nscreen rather than a resistive touch screen.\n\nFor audible key press and status feedback, connect a piezo speaker from\npin A0 to ground.\n\n* Author(s): JG for Cedar Grove Maker Studios\n* GitHub: \u003chttps://github.com/CedarGroveStudios/CG-35_Calculator\u003e\n\nImplementation Notes\n--------------------\n**Hardware:**\n* Adafruit 'ESP32-S3 4Mb/2Mb FeatherWing\n  \u003chttps://www.adafruit.com/product/5477\u003e\n* Adafruit 'Adafruit TFT FeatherWing - 3.5\" 480x320 Capacitive Touchscreen\n  \u003chttps://www.adafruit.com/product/5872\u003e\n\n**Software and Dependencies:**\n* Adafruit CircuitPython firmware for the ESP32-S3 FeatherWing:\n  \u003chttps://circuitpython.org/downloads\u003e\n* Jeff Epler's adaptation of micropython `udecimal` and `utrig`:\n  \u003chttps://github.com/jepler/Jepler_CircuitPython_udecimal\u003e\n* CedarGrove's `SevenSeg-12.bdf` font:\n  \u003chttps://github.com/CedarGroveStudios/SevenSeg_font\u003e\n\nOptional/future features:\n    - Implement display brightness control.\n    - Adjust for automatic scientific notation conversion for entered\n      values \u003c |1| with a negative exponent \u003e -6. This is an inherent behavior\n      of the Decimal class in micropython, CircuitPython, and CPython.\n    - Purge the T register when calculating trigonometric functions to fully\n      emulate the HP-35 process.\n    - Incorporate a selectable degrees/radians mode with indicator\n      (enhancement).\n    - Incorporate a selectable scientific/engineering/fixed decimal point mode\n      (enhancement).\n    - Add a setup button for setting initial parameters (enhancement).\n\"\"\"\n\nimport board\nimport displayio\nimport time\nimport gc\nimport adafruit_ft5336\nimport adafruit_hx8357\nfrom simpleio import tone\n# import digitalio\n\nfrom cedargrove_calculator.buttons import CalculatorButtons\nfrom cedargrove_calculator.case import CalculatorCase, LEDDisplay, Colors\nfrom jepler_udecimal import Decimal, getcontext, setcontext, localcontext, ROUND_HALF_UP\nimport jepler_udecimal.utrig  # Needed for trig functions in Decimal\n\n# User-modifiable parameters\nDISPLAY_PRECISION = 10\nINTERNAL_PRECISION = 20\n\nDEBUG = False  # Turns on debug print ('printd()')function\n\n# Calculator states\nIDLE = \"IDLE\"  # Waiting for input or displaying results\nC_ENTRY = \"C_ENTRY\"  # Coefficient entry: 0-9, ., CHS, EEX\nE_ENTRY = \"E_ENTRY\"  # Exponent entry: 0-9, ., CHS\nSTACK = \"STACK\"  # Stack management: ENTER, CLR, CLX, STO, RCL, R, x\u003c\u003ey, π\nMONADIC = \"MONADIC\"  # Monadic operation: LOG, LN, e^x, √x, ARC, SIN, COS, ,TAN, 1/x\nDYADIC = \"DYADIC\"  # Dyadic operation: x^y, -, +, *, ÷\nERROR = \"ERROR\"  # Calculation error\n\nSTATE = IDLE  # Set initial state to IDLE\n\n# Constants\nPI = Decimal(1).atan() * 4  # Alternative: PI = Decimal(\"3.141592654\")\n\nt0 = time.monotonic()  # Reset start-up time counter\ngc.collect()  # Clean-up memory heap space\n\n# Create the primary displayio.Group layer\ncalculator = displayio.Group()\n\n# 'TFT FeatherWing - 3.5\" 480x320 Touchscreen'; portrait orientation\ndisplayio.release_displays()  # Release display resources\ntft_bus = displayio.FourWire(\n    board.SPI(), command=board.D10, chip_select=board.D9, reset=None\n)\ntft = adafruit_hx8357.HX8357(tft_bus, width=480, height=320)\ntft.rotation = 270\n\n# Capacitive touch panel\ncts = adafruit_ft5336.Adafruit_FT5336(board.I2C())\n\n# tft.brightness = 1  # 0.55 for camera image\n\n# Instantiate case group and buttons class\ncase_group = CalculatorCase(display=tft)\nbuttons = CalculatorButtons(\n    l_margin=case_group.l_margin, timeout=10, click=True, display=tft, touch=cts\n)\nled_display = LEDDisplay(scale=1, display=tft)\n\ngc.collect()  # Clean-up memory heap space\n\n# Set the default internal precision and exponent range\ngetcontext().prec = INTERNAL_PRECISION\ngetcontext().Emax = 99\ngetcontext().Emin = -99\n# getcontext().rounding = ROUND_HALF_UP\n\n# Initiate the display, memory, and stack\nDISPLAY_C = \" 0.\"\nDISPLAY_E = \" 00\"\nX_REG = Y_REG = Z_REG = T_REG = MEM = Decimal(\"0\")\n\n# Add the case, bubble display, and button displayio layers\ncalculator.append(case_group)\ncalculator.append(led_display)\ncalculator.append(buttons)\n\n\ndef printd(line):\n    \"\"\"Debug print function. Use formatted print statements.\"\"\"\n    if DEBUG:\n        print(line)\n    return\n\n\ndef clr(register=None):\n    \"\"\"Clear the display and all or just X_REG; None for all registers; \"x\" for X_REG.\"\"\"\n    global DISPLAY_C, DISPLAY_E, X_REG, Y_REG, Z_REG, T_REG, MEM, ARC_FLAG\n    if not register:\n        # Clear display, stack registers, memory, and reset ARC flag\n        DISPLAY_C = \" 0.\"\n        DISPLAY_E = \" 00\"\n        X_REG = Y_REG = Z_REG = T_REG = MEM = Decimal(\"0\")\n        ARC_FLAG = False\n    elif register == \"x\":\n        # Clear display and X_REG\n        DISPLAY_C = \" 0.\"\n        DISPLAY_E = \" 00\"\n        X_REG = Decimal(\"0\")\n    else:\n        return False  # No register specified\n    return True\n\n\ndef get_key():\n    \"\"\"Get pressed key name. This is a blocking method (for now).\"\"\"\n    key_name = None\n    while not key_name:\n        key_name, _, hold_time = buttons.read_buttons()\n    printd(f\"get_key: name:{key_name:5s} hold_time:{hold_time:5.3f}s\")\n    return key_name\n\n\ndef push_stack():\n    \"\"\"Push stack values; T_REG is lost.\"\"\"\n    global X_REG, Y_REG, Z_REG, T_REG\n    T_REG = Z_REG\n    Z_REG = Y_REG\n    Y_REG = X_REG\n    return\n\n\ndef pull_stack():\n    \"\"\"Pull (drop) stack values; place \"0\" into T_REG.\"\"\"\n    global X_REG, Y_REG, Z_REG, T_REG\n    X_REG = Y_REG\n    Y_REG = Z_REG\n    Z_REG = T_REG\n    T_REG = Decimal(\"0\")\n    return\n\n\ndef roll_stack():\n    \"\"\"Roll stack values; place X_REG into T_REG.\"\"\"\n    global X_REG, Y_REG, Z_REG, T_REG\n    temp = X_REG\n    X_REG = Y_REG\n    Y_REG = Z_REG\n    Z_REG = T_REG\n    T_REG = temp\n    return\n\n\ndef convert_display_to_decimal(coefficient=\" 0.\", exponent=\"   \"):\n    \"\"\"Convert display text to an equivalent Decimal value.\"\"\"\n    printd(f\"display_to_decimal input: '{coefficient}'  '{exponent}'\")\n    getcontext().prec = DISPLAY_PRECISION\n    sign = coefficient[0]\n    coefficient = coefficient[1:12]\n    exponent = exponent[0:3]\n\n    # Reformat sign for decimal value conversion\n    if sign == \" \":\n        sign = \"+\"\n\n    # Clean up and reformat coefficient; remove spaces, put zero before dp if dp is in first position\n    while coefficient.find(\" \") \u003e= 0:\n        coefficient = coefficient.split(\" \", 1)[0] + coefficient.split(\" \", 1)[1]\n    if coefficient[0] == \".\":\n        coefficient = \"0\" + coefficient\n    # Pad coefficient with trailing zeros for values with positive exponent\n    for i in range(len(coefficient), getcontext().prec + 1):\n        coefficient = coefficient + \"0\"\n    # Clean up and reformat exponent; remove spaces, set to zero if blank, add separator E character\n    while exponent.find(\" \") \u003e= 0:\n        exponent = exponent.split(\" \", 1)[0] + exponent.split(\" \", 1)[1]\n    if exponent == \"\":\n        exponent = \"0\"\n    exponent = \"E\" + str(int(exponent))\n    # Reconstruct the value as Decimal type\n    new_value = Decimal(sign + coefficient + exponent)\n\n    # Set display precision and return value\n    getcontext().prec = INTERNAL_PRECISION\n    new_value = new_value / 1\n    printd(f\"display_to_decimal: new_value out: '{new_value}'\")\n    return new_value\n\n\ndef convert_decimal_to_display(value=Decimal(\"0\")):\n    \"\"\"Convert a Decimal value into the equivalent display text.\"\"\"\n    # Round to display precision and convert to string\n    printd(f\"decimal_to_display value input: '{value}'  type: {type(value)}\")\n    if value.is_finite():\n        value = value / 1\n        getcontext().prec = DISPLAY_PRECISION\n        value = value / 1\n    else:\n        value = Decimal(0)\n    decimal_text = str(value)\n\n    # Separate coefficient from exponent\n    if decimal_text.find(\"E\") \u003e= 0:\n        coefficient, exponent = decimal_text.split(\"E\", 1)\n    else:\n        coefficient = decimal_text\n        exponent = \" 00\"\n\n    # Coefficient: Retain \"-\" as sign; add \" \" for positive value\n    if coefficient[0] != \"-\":\n        coefficient = \" \" + coefficient\n\n    # Exponent: Replace + with space; retain minus sign\n    if exponent[0] == \"+\":  # if plus sign, replace with space\n        exponent = (\" \" * (4 - len(exponent))) + exponent[1:]\n    if exponent[0] == \"-\" and len(exponent) == 2:\n        exponent = \"- \" + exponent[-1]\n\n    # If no decimal point in coefficient, add one to the end\n    if coefficient.find(\".\") \u003c 0 and len(coefficient) \u003c 12:\n        coefficient = coefficient + \".\"\n\n    # Remove trailing zeros from coefficient\n    while coefficient.find(\".\") \u003c= 12 and coefficient[-1] == \"0\":\n        coefficient = coefficient[0:-1]\n\n    # Remove leading zeros except at start of digit entry\n    if coefficient[1:] != \"0.\":\n        while coefficient.find(\".\") \u003e 1 and coefficient[1] == \"0\":\n            coefficient = coefficient[0] + coefficient[2:]\n\n    # Don't display a minus zero coefficient\n    if coefficient == \"-0.\":\n        coefficient = \" 0.\"\n\n    # If no exponent separator or coefficient is zero, blank the exponent value\n    if decimal_text.find(\"E\") \u003c 0 or coefficient[1:] == \"0.\":\n        exponent = \"   \"\n\n    getcontext().prec = INTERNAL_PRECISION\n    printd(f\"**** coefficient '{coefficient}', exponent '{exponent}'\")\n    printd(f\"     len(coefficient):{len(coefficient)}, len(exponent):{len(exponent)}\")\n    return coefficient, exponent\n\n\ndef print_stack():\n    \"\"\"Print stack registers and auxiliary memory in REPL.\"\"\"\n    print(f\"-\" * 48)\n    print(\n        f\"D.X_REG:  {X_REG}  DISPLAY: '{convert_decimal_to_display(X_REG)[0] + convert_decimal_to_display(X_REG)[1]}'\"\n    )\n    print(f\"D.Y_REG:  {Y_REG}\")\n    print(f\"D.Z_REG:  {Z_REG}\")\n    print(f\"D.T_REG:  {T_REG}\")\n    print(f\"D.MEM  :  {MEM}\")\n    print(f\"-\" * 48)\n    return\n\n\ndef show_display_reg():\n    \"\"\"Display contents of DISPLAY registers.\"\"\"\n    global DISPLAY_C, DISPLAY_E\n    coefficient = DISPLAY_C\n    exponent = DISPLAY_E\n    if exponent[1:3] == \"00\":\n        exponent = \"   \"\n    # If needed, pad coefficient with spaces\n    coefficient = coefficient + (\" \" * (12 - len(coefficient)))\n    led_display.text = coefficient + exponent\n    return\n\n\ndef display_error(text=\"\"):\n    \"\"\"Flash error indicator on display.\"\"\"\n    tone(board.A0, 440, 0.6)\n    global STATE, ERROR\n    STATE = ERROR\n    clr()\n    led_display.text = \".\" * 15\n    time.sleep(0.2)\n    led_display.text = \" \" * 15\n    time.sleep(0.2)\n    led_display.text = \".\" * 15\n    display_status(text, None)  # Hold error description in status area\n    return\n\n\ndef display_status(text=\"\", duration=0.5):\n    \"\"\"Display message in status area for duration in seconds or None to hold\n    text in status message area.\"\"\"\n    case_group.status.text = text\n    case_group.status.color = Colors.BLUE\n    if duration:\n        time.sleep(duration)\n        case_group.status.color = None\n    return\n\n\ndef update_x_reg_from_display_reg():\n    \"\"\"Move DISPLAY registers' content to the X_REG.\"\"\"\n    global X_REG, DISPLAY_C, DISPLAY_E\n    if DISPLAY_E == \"-00\":\n        DISPLAY_E = \" 00\"\n    X_REG = convert_display_to_decimal(DISPLAY_C, DISPLAY_E)\n    return\n\n\ndef update_display_reg_from_x_reg():\n    \"\"\"Update the LED display with X_REG value.\"\"\"\n    global X_REG, DISPLAY_C, DISPLAY_E\n    coefficient, exponent = convert_decimal_to_display(X_REG)\n    DISPLAY_C = coefficient\n    DISPLAY_E = exponent\n    # If needed, pad coefficient with spaces\n    coefficient = coefficient + (\" \" * (12 - len(coefficient)))\n    led_display.text = coefficient + exponent\n    return\n\n\ndef convert_degrees_to_radians(value):\n    \"\"\"Convert Decimal degrees value to radians.\"\"\"\n    return (value % 360) * (PI * 2) / 360\n\n\ndef convert_radians_to_degrees(value):\n    \"\"\"Convert Decimal radians value to degrees.\"\"\"\n    return (value % (PI * 2)) * 360 / (PI * 2)\n\n\ntft.root_group = calculator\n\ngc.collect()\nfree_memory = gc.mem_free()\nframe = time.monotonic() - t0\nprint(\"CG-35 Calculator    Cedar Grove Studios\")\nprint(f\"setup: {frame:5.02f}sec   free memory: {free_memory/1000:6.03f}kb\")\nprint(f\"Calculator STATE: {STATE}\")\ntone(board.A0, 440, 0.25)  # Startup beep\ndisplay_status(\"... READY ...\", 1)\n\nclr()\nupdate_display_reg_from_x_reg()\n\nwhile True:\n    t0 = time.monotonic()  # Reset timer for start of frame\n\n    key_name = get_key()  # Wait for key press\n    display_status(\"\", 0)  # Clear any status messages\n\n    # Display Entry Keys Cluster: 0-9, ., CHS, EEX\n    if key_name in (\n        \"0\",\n        \"1\",\n        \"2\",\n        \"3\",\n        \"4\",\n        \"5\",\n        \"6\",\n        \"7\",\n        \"8\",\n        \"9\",\n        \".\",\n        \"CHS\",\n        \"EEX\",\n    ):\n        printd(f\"Display entry key: {key_name}\")\n        if \"ENTRY\" not in STATE:\n            # Prepare for digit entry when previous operation has finished\n            if STATE == DYADIC:\n                #  Automatic ENTER only after dyadic operations\n                push_stack()\n            clr(\"x\")\n            dp_flag = False\n            STATE = C_ENTRY\n\n        if (\n            (STATE == C_ENTRY)\n            and (STATE != E_ENTRY)\n            and len(DISPLAY_C) \u003c 12\n            and (key_name not in (\"CHS\", \"EEX\"))\n        ):\n            getcontext().prec = DISPLAY_PRECISION\n            if DISPLAY_C[1:] == \"0.\" and key_name == \".\":\n                # First digit entry\n                dp_flag = True\n                DISPLAY_C = DISPLAY_C.replace(\"0.\", key_name)\n            elif DISPLAY_C[1:] == \"0.\":\n                DISPLAY_C = DISPLAY_C.replace(\"0\", key_name)\n            else:\n                dp_index = DISPLAY_C.index(\".\")\n                if key_name != \".\":\n                    if dp_flag:\n                        # Fractional portion of coefficient (right of decimal separator)\n                        DISPLAY_C = DISPLAY_C + key_name\n                    else:\n                        # Integer portion of coefficient (left of decimal separator)\n                        DISPLAY_C = (\n                            DISPLAY_C[0:dp_index] + key_name + DISPLAY_C[dp_index:]\n                        )\n                else:\n                    dp_flag = True\n\n        if (STATE == E_ENTRY) and (key_name not in (\"CHS\", \"EEX\")):\n            # First digit entry\n            if DISPLAY_E[1:3] == \"00\":\n                DISPLAY_E = DISPLAY_E[0:2] + key_name\n                # Second digit entry\n            elif DISPLAY_E[1] == \"0\":\n                DISPLAY_E = DISPLAY_E[0] + DISPLAY_E[2] + key_name\n\n        if key_name == \"CHS\":\n            if (STATE == C_ENTRY) and DISPLAY_C[1:3] != \"0.\":\n                # Change the coefficient sign\n                if DISPLAY_C[0] == \"-\":\n                    DISPLAY_C = \" \" + DISPLAY_C[1:]\n                else:\n                    DISPLAY_C = \"-\" + DISPLAY_C[1:]\n            elif STATE == E_ENTRY:\n                # Change the exponent sign\n                if DISPLAY_E[0] == \"-\":\n                    DISPLAY_E = \" \" + DISPLAY_E[1:]\n                else:\n                    DISPLAY_E = \"-\" + DISPLAY_E[1:]\n\n        if key_name == \"EEX\":\n            STATE = E_ENTRY\n\n        show_display_reg()\n        update_x_reg_from_display_reg()\n\n    # Stack Management and Constant Key Cluster: ENTER, CLR, CLX, STO, RCL, R, x\u003c\u003ey, π\n    if key_name in (\n        \"ENTER\",\n        \"CLR\",\n        \"CLX\",\n        \"STO\",\n        \"RCL\",\n        \"R\",\n        \"x\u003c\u003ey\",\n        \"π\",\n    ):\n        STATE = STACK\n        printd(f\"stack management and constant key: {key_name}\")\n        if key_name == \"ENTER\":\n            push_stack()\n        if key_name == \"CLR\":\n            clr()\n        if key_name == \"CLX\":\n            clr(\"x\")\n        if key_name == \"STO\":\n            MEM = X_REG\n        if key_name == \"RCL\":\n            push_stack()\n            X_REG = MEM\n        if key_name == \"R\":\n            roll_stack()\n        if key_name == \"x\u003c\u003ey\":\n            temp = X_REG\n            X_REG = Y_REG\n            Y_REG = temp\n        if key_name == \"π\":\n            X_REG = PI\n\n    # Monadic Operator Key Cluster: LOG, LN, e^x, √x, ARC, SIN, COS, ,TAN, 1/x\n    if key_name in (\n        \"LOG\",\n        \"LN\",\n        \"e^x\",\n        \"√x\",\n        \"ARC\",\n        \"SIN\",\n        \"COS\",\n        \"TAN\",\n        \"1/x\",\n    ):\n        STATE = MONADIC\n        printd(f\"monadic operator key: {key_name}\")\n        try:\n            getcontext().prec = INTERNAL_PRECISION\n            if key_name == \"LOG\":\n                X_REG = Decimal.log10(X_REG)\n            if key_name == \"LN\":\n                X_REG = Decimal.ln(X_REG)\n            if key_name == \"e^x\":\n                X_REG = Decimal.exp(X_REG)\n            if key_name == \"√x\":\n                X_REG = Decimal.sqrt(X_REG)\n            if key_name == \"ARC\":\n                ARC_FLAG = True\n            if key_name == \"SIN\":\n                if ARC_FLAG:\n                    X_REG = convert_radians_to_degrees(Decimal.asin(X_REG))\n                else:\n                    X_REG = Decimal.sin(convert_degrees_to_radians(X_REG))\n                ARC_FLAG = False\n            if key_name == \"COS\":\n                if ARC_FLAG:\n                    X_REG = convert_radians_to_degrees(Decimal.acos(X_REG))\n                else:\n                    X_REG = Decimal.cos(convert_degrees_to_radians(X_REG))\n                ARC_FLAG = False\n            if key_name == \"TAN\":\n                if ARC_FLAG:\n                    X_REG = convert_radians_to_degrees(Decimal.atan(X_REG))\n                else:\n                    X_REG = Decimal.tan(convert_degrees_to_radians(X_REG))\n                ARC_FLAG = False\n            if key_name == \"1/x\":\n                X_REG = 1 / X_REG\n        except Exception as err:\n            print(\"Exception:\", err)\n            display_error(str(type(err))[8:-2])\n        if X_REG.is_infinite():\n            print(\"Error: Infinite value result\")\n            display_error(\"InfiniteValue\")\n\n    # Dyadic Operator Key Cluster: x^y, -, +, *, ÷\n    if key_name in (\n        \"x^y\",\n        \"-\",\n        \"+\",\n        \"*\",\n        \"÷\",\n    ):\n        STATE = DYADIC\n        printd(f\"dyadic operator key: {key_name}\")\n        try:\n            getcontext().prec = INTERNAL_PRECISION\n            if key_name == \"x^y\":\n                X_REG = X_REG**Y_REG\n            if key_name == \"-\":\n                Y_REG = Y_REG - X_REG\n                pull_stack()\n            if key_name == \"+\":\n                Y_REG = Y_REG + X_REG\n                pull_stack()\n            if key_name == \"*\":\n                Y_REG = Y_REG * X_REG\n                pull_stack()\n            if key_name == \"÷\":\n                Y_REG = Y_REG / X_REG\n                pull_stack()\n        except Exception as err:\n            print(\"Exception:\", err)\n            display_error(str(type(err))[8:-2])\n        if X_REG.is_infinite() or Y_REG.is_infinite():\n            print(\"Error: Infinite value in X_REG and/or Y_REG\")\n            display_error(\"InfiniteValue\")\n\n    if STATE not in (\"C_ENTRY\", \"E_ENTRY\", \"ERROR\"):\n        STATE = IDLE\n        update_display_reg_from_x_reg()\n        gc.collect()  # Clean-up memory heap space\n        print_stack()\n        frame = time.monotonic() - t0\n        free_memory = gc.mem_free()\n        print(f\"frame: {frame:5.02f}sec   free memory: {free_memory/1000:6.03f}kb\")\n        print(f\"Calculator STATE: {STATE}\")","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003e\u003cstrong\u003eBuild the Keys\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe CalculatorButtons class\u0026nbsp;\u003ccode\u003ecedargrove_calculator.buttons.py\u003c/code\u003e defines the calculator keys from a table of button characteristics. The class provides a \u003ccode\u003eread_buttons\u003c/code\u003e function to detect when a button is touched.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"# SPDX-FileCopyrightText: 2022, 2024 Cedar Grove Maker Studios\n# SPDX-License-Identifier: MIT\n\n\"\"\"\ncedargrove_calculator.buttons.py  2024-03-11 v2.0\nmodified for 3.5-inch TFT Capacitive FeatherWing with Feather RP2040\n=================================================\n\nCalculator buttons class.\n\n* Author(s): JG for Cedar Grove Maker Studios\n\"\"\"\n\nimport board\nimport time\nimport displayio\nfrom adafruit_bitmap_font import bitmap_font\nfrom adafruit_button import Button\nfrom simpleio import tone\n\n\nclass Colors:\n    BLACK = 0x000000\n    BLUE = 0x4040F0\n    GRAY = 0x202020\n    GRAY_DK = 0x101010\n    RED = 0xFF0000\n    WHITE = 0xC0C0C0\n    OUTLINE = GRAY_DK\n\n    black_palette = displayio.Palette(1)\n    black_palette[0] = BLACK\n    gray_palette = displayio.Palette(1)\n    gray_palette[0] = GRAY\n    gray_dk_palette = displayio.Palette(1)\n    gray_dk_palette[0] = GRAY_DK\n\n\n# (x, y), (width, height), name, fill_color, outline_color, pressed_color\n# object order: back to front\nHP_BUTTONS = [\n    (( 61, 123), (29, 22), \"x^y\", Colors.BLACK, Colors.OUTLINE),\n    ((103, 123), (29, 22), \"LOG\", Colors.BLACK, Colors.OUTLINE),\n    ((146, 123), (29, 22), \"LN\", Colors.BLACK, Colors.OUTLINE),\n    ((187, 123), (29, 22), \"e^x\", Colors.BLACK, Colors.OUTLINE),\n    ((230, 123), (29, 22), \"CLR\", Colors.BLUE, Colors.OUTLINE),\n    (( 61, 167), (29, 22), \"√x\", Colors.BLACK, Colors.OUTLINE),\n    ((103, 167), (29, 22), \"ARC\", Colors.BLACK, Colors.OUTLINE),\n    ((146, 167), (29, 22), \"SIN\", Colors.BLACK, Colors.OUTLINE),\n    ((187, 167), (29, 22), \"COS\", Colors.BLACK, Colors.OUTLINE),\n    ((230, 167), (29, 22), \"TAN\", Colors.BLACK, Colors.OUTLINE),\n    (( 61, 212), (29, 22), \"1/x\", Colors.BLACK, Colors.OUTLINE),\n    ((103, 212), (29, 22), \"x\u003c\u003ey\", Colors.BLACK, Colors.OUTLINE),\n    ((146, 212), (29, 22), \"R\", Colors.BLACK, Colors.OUTLINE),\n    ((187, 212), (29, 22), \"STO\", Colors.BLACK, Colors.OUTLINE),\n    ((230, 212), (29, 22), \"RCL\", Colors.BLACK, Colors.OUTLINE),\n    (( 61, 257), (69, 22), \"ENTER\", Colors.BLUE, Colors.OUTLINE),\n    ((146, 257), (29, 22), \"CHS\", Colors.BLUE, Colors.OUTLINE),\n    ((187, 257), (29, 22), \"EEX\", Colors.BLUE, Colors.OUTLINE),\n    ((230, 257), (29, 22), \"CLX\", Colors.BLUE, Colors.OUTLINE),\n    (( 61, 301), (29, 22), \"-\", Colors.BLUE, Colors.OUTLINE),\n    ((105, 301), (33, 22), \"7\", Colors.WHITE, Colors.OUTLINE),\n    ((165, 301), (33, 22), \"8\", Colors.WHITE, Colors.OUTLINE),\n    ((225, 301), (33, 22), \"9\", Colors.WHITE, Colors.OUTLINE),\n    (( 61, 346), (22, 22), \"+\", Colors.BLUE, Colors.OUTLINE),\n    ((105, 346), (33, 22), \"4\", Colors.WHITE, Colors.OUTLINE),\n    ((165, 346), (33, 22), \"5\", Colors.WHITE, Colors.OUTLINE),\n    ((225, 346), (33, 22), \"6\", Colors.WHITE, Colors.OUTLINE),\n    (( 61, 391), (22, 22), \"*\", Colors.BLUE, Colors.OUTLINE),\n    ((105, 391), (33, 22), \"1\", Colors.WHITE, Colors.OUTLINE),\n    ((165, 391), (33, 22), \"2\", Colors.WHITE, Colors.OUTLINE),\n    ((225, 391), (33, 22), \"3\", Colors.WHITE, Colors.OUTLINE),\n    (( 61, 435), (22, 22), \"÷\", Colors.BLUE, Colors.OUTLINE),\n    ((105, 435), (33, 22), \"0\", Colors.WHITE, Colors.OUTLINE),\n    ((165, 435), (33, 22), \".\", Colors.WHITE, Colors.OUTLINE),\n    ((225, 435), (33, 22), \"π\", Colors.WHITE, Colors.OUTLINE),\n]\n\n\nclass CalculatorButtons(displayio.Group):\n    def __init__(self, l_margin=0, timeout=1.0, click=True,  display=None, touch=None):\n        \"\"\"Instantiate on-screen buttons and build button_group.\"\"\"\n\n        self._timeout = timeout\n        self._click = click\n        self._l_margin = l_margin\n        self.ts = touch\n        WIDTH = display.width\n        HEIGHT = display.height\n        SIZE_FACTOR = 1.2\n\n        # Create a list of button names for button creation\n        self._button_names = [name[2] for name in HP_BUTTONS]\n\n        self.FONT_0 = bitmap_font.load_font(\"/fonts/OpenSans-9.bdf\")\n\n        # Build displayio button group\n        self._buttons = []\n        self._buttons_index = []  # The list of buttons used for detection\n        button_group = displayio.Group()\n\n        # Create the displayio button definitions\n        for key in HP_BUTTONS:\n            button = Button(\n                x=key[0][0],\n                y=key[0][1],\n                width=int(key[1][0] * SIZE_FACTOR),\n                height=int(key[1][1] * SIZE_FACTOR),\n                style=Button.RECT,\n                fill_color=key[3],\n                outline_color=key[4],\n                name=key[2],\n                label=key[2],\n                label_font=self.FONT_0,\n                label_color=Colors.WHITE,\n                selected_fill=Colors.RED,\n                selected_outline=Colors.RED,\n            )\n            if button.fill_color == Colors.WHITE:\n                button.label_color = Colors.BLACK\n            button_group.append(button)\n            self._buttons.append(button)\n            self._buttons_index.append(button.name)\n\n        super().__init__()\n        self.append(button_group)\n        return\n\n    @property\n    def timeout(self):\n        \"\"\"Button timeout duration setting.\"\"\"\n        return self._timeout\n\n    @timeout.setter\n    def timeout(self, hold_time=1.0):\n        \"\"\"Select timeout duration value in seconds, positive float value.\"\"\"\n        if hold_time \u003c 0 or hold_time \u003e= 10:\n            print(\n                \"Invalid button timeout duration value. Must be between 0 and 10 seconds.\"\n            )\n            return\n        self._timeout = hold_time\n        return\n\n    def read_buttons(self):\n        button_pressed = button_name = None\n        hold_time = 0\n        touch = self.ts.points\n        if touch:\n            for button in self._buttons:\n                if button.contains(touch[0]):  # read only the first point touched\n                    button.selected = True\n                    if self._click:\n                        # Make a click sound when button is pressed\n                        tone(board.A0, 2000, 0.025, length=8)\n                    button_pressed = button.name\n                    button_name = self._buttons_index.index(button_pressed)\n                    timeout_beep = False\n                    while self.ts.points:\n                        time.sleep(0.1)\n                        hold_time += 0.1\n                        if hold_time \u003e= self._timeout and not timeout_beep:\n                            if self._click:\n                                # Play a beep tone if button is held\n                                tone(board.A0, 1320, 0.100, length=8)\n                            timeout_beep = True\n                    button.selected = False\n                    if self._click:\n                        # Make a click sound when button is released\n                        tone(board.A0, 2000, 0.025, length=8)\n        return button_pressed, button_name, hold_time","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003e\u003cstrong\u003eMake a Case for Calculation\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe\u0026nbsp;\u003cspan\u003eCalculatorCase, LEDDisplay, and Colors classes\u003c/span\u003e \u003ccode\u003ecedargrove_calculator.case.py\u003c/code\u003e defines the graphic elements needed to display the calculator case and numeric display.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"# SPDX-FileCopyrightText: 2022, 2024 JG for Cedar Grove Maker Studios\n# SPDX-License-Identifier: MIT\n\n\"\"\"\ncedargrove_calculator.case.py  2024-04-14 v2.2\nFor the ESP32-S3 4Mb/2Mb Feather and 3.5-inch TFT Capacitive FeatherWing\n==============================================\n\nCalculator case graphics classes.\n\n\"\"\"\n\nimport displayio\nimport vectorio\nfrom adafruit_bitmap_font import bitmap_font\nfrom adafruit_display_text.label import Label\n\n\nclass Colors:\n    BLACK = 0x000000\n    BLUE = 0x606060\n    GRAY = 0x202020\n    GRAY_DK = 0x101010\n\n    black_palette = displayio.Palette(1)\n    black_palette[0] = BLACK\n    gray_palette = displayio.Palette(1)\n    gray_palette[0] = GRAY\n    gray_dk_palette = displayio.Palette(1)\n    gray_dk_palette[0] = GRAY_DK\n\n\n# (x, y), (width, height), name, fill_color\n# object order: back to front\nHP_CASE = [\n    ((39, 0), (241, 78), \"DISPLAY outline\", Colors.gray_dk_palette),\n    ((39, 78), (241, 402), \"KEYS outline\", Colors.gray_palette),\n    ((89, 89), (33, 11), \"PWR outline\", Colors.gray_dk_palette),\n    ((106, 89), (17, 11), \"PWR switch\", Colors.black_palette),\n    ((48, 18), (223, 42), \"DISPLAY area\", Colors.black_palette),\n]\n\n\nclass LEDDisplay(displayio.Group):\n    def __init__(self, scale=1, display_color=0xFF0000, display=None):\n        \"\"\"Instantiate LED display and build led_display_group.\"\"\"\n\n        _scale = scale\n\n        FONT_0 = bitmap_font.load_font(\"/fonts/SevenSeg-12.bdf\")\n\n        # Build displayio LED display group\n        led_display_group = displayio.Group()\n\n        # LED display label\n        self._led_digits = Label(\n            font=FONT_0,\n            text=\"\",\n            color=display_color,\n        )\n        self._led_digits.anchor_point = (0, 0.5)\n        self._led_digits.anchored_position = (57, 40)\n        led_display_group.append(self._led_digits)\n\n        super().__init__(scale=_scale)\n        self.append(led_display_group)\n        return\n\n    @property\n    def text(self):\n        return self._led_digits.text\n\n    @text.setter\n    def text(self, text=\"\"):\n        self._led_digits.text = text[0:15]\n        return\n\n\nclass CalculatorCase(displayio.Group):\n    def __init__(self, display=None):\n        \"\"\"Instantiate case graphic and build case group.\"\"\"\n        self._l_margin = HP_CASE[0][0][0]\n\n        FONT_1 = bitmap_font.load_font(\"/fonts/OpenSans-9.bdf\")\n\n        # Build displayio case group\n        case_group = displayio.Group()\n\n        for part in HP_CASE:\n            case_part = vectorio.Rectangle(\n                pixel_shader=part[3],\n                x=part[0][0],\n                y=part[0][1],\n                width=part[1][0],\n                height=part[1][1],\n            )\n            case_group.append(case_part)\n\n        # Status message area\n        self._status = Label(\n            font=FONT_1,\n            text=\"\",\n            color=None,\n        )\n        self._status.anchor_point = (0.5, 0.5)\n        self._status.anchored_position = (160, 69)\n        case_group.append(self._status)\n\n        # Power switch label\n        pwr_text = Label(\n            font=FONT_1,\n            text=\"OFF\" + (\" \" * 14) + \"ON\" + (\" \" * 26) + \"CG-35\",\n            color=Colors.BLACK,\n        )\n        pwr_text.anchor_point = (0, 0)\n        pwr_text.anchored_position = (61, HP_CASE[2][0][1])\n        case_group.append(pwr_text)\n\n        super().__init__()\n        self.append(case_group)\n        return\n\n    @property\n    def l_margin(self):\n        \"\"\"Left margin spacing in pixels.\"\"\"\n        return self._l_margin\n\n    @property\n    def status(self):\n        \"\"\"Status message text.\"\"\"\n        return self._status\n\n    @status.setter\n    def status(self, text=\"\"):\n        self._status = text\n        return","metadata":{"language":"python","linenums":false}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/833/original/IMG_1990.jpeg?1713204027","metadata":{"caption":""}}]