[{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/345/original/IMG_20231119_001742_326_%28Custom%29.jpg?1700354003","metadata":{"caption":""}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/347/original/IMG_20231119_000225_858_%28Custom%29_%281%29.jpg?1700354266","metadata":{"caption":""}},{"element_type":"text","content":"\n        \u003cp\u003eA quick and dirty example of something I wanted for my devices, allowing inexperienced users to setup the device themselves, and once setup they can also remotely modify the code / download data.\u003cbr\u003e\u003cbr\u003eAlso always be sure which network it's on, the IP address, and have a handy way to jump on the same network.\u003c/p\u003e\n\u003cp\u003eI plan to have this run on bootup for 10seconds before the main program runs (depending on network need etc). I may also alternate between this and the sensor data screens. Long term they'd all be pages/tiles in lvgl.\u003c/p\u003e\n\u003cp\u003eThe \u003cem\u003emain reason for this note\u003c/em\u003e, was the observation that \u003cspan style=\"text-decoration: underline;\"\u003eBlack and White QRcodes are too bright for my phone to read\u003c/span\u003e, and I had to change both colours to reduce the contrast as theoretically the brightness is already at 25mA minimum.\u003c/p\u003e\n\u003cp\u003eI pondered on less blue being the most effective, as it's almost treated like white by the phone. I settled on these colours that look incredibly dark rendered as HTML, while on screen look like mid-toned blue with strong backlight and black dotted coding QRcodes, but on camera look almost white and black and work much more reliably.\u003cbr\u003e\u003ccode\u003eqr_color=\u003c/code\u003e\u003cspan\u003e\u003ccode\u003e0x121212\u003c/code\u003e  (HTML #121212)\u003c/span\u003e\u003cspan\u003e\u003cbr\u003e\u003c/span\u003e\u003ccode\u003e\u003cspan\u003eqr_bg_color==\u003c/span\u003e\u003c/code\u003e\u003cspan\u003e\u003ccode\u003e0x0000aa\u003c/code\u003e (html\u003ccode\u003e#0000AA\u003c/code\u003e)\u003c/span\u003e\u003c/p\u003e\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  \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\u003eFeature List:\u003c/h2\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\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n        \u003cul\u003e\n\u003cli\u003eLogs onto network if present, showing QRcode for the Wifi Access point for easy joining and remote configuration, also displays SSID. \u003cbr\u003e\u003cbr\u003eNext to that is the Web Workflow QRcode, built from the IP address of the device and web workflow port if specified.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e \u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIf the user hasn't configured SSID or fails to get an IP in 5seconds, then the help page QRcode is displayed along with a touch sensitive Circuitpython logo.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e \u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThere is code to adapt the returned touch events to be rotated (the BAR 320x820 display is vertically orientated so coordinates of touch events need adjusting).\u003cbr\u003e\u003cbr\u003eThe touch code cycles through the tilegrid and group objects looking for a callback on a tilegrid to trigger if the x + y (plus tile_width and tile_height) match the touch point.\u003cbr\u003e\u003cbr\u003eIt works for the bitmap, but not the qrcode tilegrids, nor labels.\u003c/li\u003e\n\u003c/ul\u003e\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  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eShow the QRcodes work:\u003c/h2\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","metadata":{}},{"element_type":"alert","content":"Please don't judge me too harshly for how messy my coffee machine area is... (it's where I do some of my best work, nice speakers and good coffee lead to wonderful things)","metadata":{"class":"element row-fluid alert-element build-alert alert-warning","alert_type":"warning"}},{"element_type":"embed","content":"https://youtu.be/5Hw7_4jtX0A"},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eGet the code:\u003c/h2\u003e\n\u003cp\u003eMessing around on \u003ca href=\"https://github.com/tyeth/Adafruit_CircuitPython_PortalBase/tree/touch_example\"\u003ehttps://github.com/tyeth/Adafruit_CircuitPython_PortalBase/tree/touch_example\u003c/a\u003e as it requires modification to portalbase to allow the qrcode bg colour and return_group flag rather than only supporting one QRcode it now supports multiple. You could also, instead of using the modified portalbase, just use miniqr directly and replace the display_qr_and_text method.\u003c/p\u003e\n      \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        \u003ch2\u003esetttings.toml\u003c/h2\u003e\n\u003cp\u003e\u003cspan\u003eYou also should set wifi details (eventually - boot it once without) in settings.toml, including an additional one for WiFi network encryption type which is used when generating the wifi joining QRcode.\u003c/span\u003e\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","metadata":{}},{"element_type":"code","content":"# Comments are supported, by commenting out the SSID the device will not connect to WiFi\n\n#CIRCUITPY_WIFI_SSID=\"free4all_2G\"\n\n# WEP WPA supported, other support may vary\nCIRCUITPY_WIFI_TYPE=\"WPA\"\n\nCIRCUITPY_WIFI_PASSWORD=\"YOUR_PASSWORD_HERE\"\n\nCIRCUITPY_WEB_API_PORT=80\nCIRCUITPY_WEB_API_PASSWORD=\"YOUR_PASSWORD_HERE\"\n\ntest_variable=\"this is a test\"\nthumbs_up=\"\\U0001f44d\"","metadata":{"language":"terminal","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        \u003ch2\u003eCode.py\u003c/h2\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","metadata":{}},{"element_type":"code","content":"# SPDX-FileCopyrightText: 2021 Jose David M.\n# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries\n# SPDX-FileCopyrightText: 2023 Tyeth Gundry messing around\n#\n# SPDX-License-Identifier: MIT\n\n# NOTE: Make sure you've set up your settings.toml file before running this example\n# https://learn.adafruit.com/getting-started-with-web-workflow-using-the-code-editor/\n\"\"\"\nThis example shows a wifi connection and a web address QR on the display, along with a help page fallback, and image display that hide/shows on touch\n\"\"\"\n\nimport time\nimport adafruit_qualia\nimport os\nimport gc\nimport json\nfrom adafruit_qualia.graphics import Graphics, Displays\nfrom adafruit_qualia.peripherals import Peripherals\nimport displayio\nimport terminalio\nimport vectorio\n\n\nbase = adafruit_qualia.Qualia(Displays.BAR320X820, rotation=90)\n\n# Background Information\ngraphics = base.graphics\n\n# Set up Peripherals\nperipherals = base.peripherals\n\n# Set display to show\ndisplay = base.display\n\n# display.auto_refresh=False\n\ndef display_qr_and_text(qr_data, text, x=0, y=0, relative_x_from_center=None, relative_y_from_center=None, scale=10, include_qr_offset=False, include_text_x_offset=-20):\n    global graphics, display, base\n    qr_size = scale\n    if relative_x_from_center is not None:\n        x = (display.width // 2) + relative_x_from_center\n    if relative_y_from_center is not None:\n        y = display.height // 2 + relative_y_from_center\n    if include_qr_offset:\n        x_offset = (qr_size + 5) * scale\n        y_offset = (qr_size + 4) * scale\n        x -= x_offset\n        y -= y_offset\n    qr = graphics.qrcode(qr_data, qr_size=scale, x=x, y=y, qr_color=0x121212, qr_bg_color=0x0000aa, return_group=True)\n    display.root_group.append(qr)\n    base.add_text(text_position=(x + include_text_x_offset, 0.9 * display.height), text=text, text_scale=3, text_wrap=0, text_maxlen=180, text_color=0xFFFFFF)\n    return (x,y)\n\ndef get_item_at(x, y, group):\n    for item in group:\n        if not hasattr(item, \"x\") or not hasattr(item, \"y\"):\n            continue\n        #print(\"item pos+size\", item.x, item.y, item.width if hasattr(item,\"width\") else item.tile_width if hasattr(item,\"tile_width\") else \"unknown\", item.height if hasattr(item,\"height\") else item.tile_height if hasattr(item,\"tile_height\") else \"unknown\")\n        print(\"item position:\", x,y)\n        print(\"item tile_width:\", item.tile_width if hasattr(item,\"tile_width\") else \"unknown\")\n        print(\"item tile_height:\", item.tile_height if hasattr(item,\"tile_height\") else \"unknown\")\n        print(\"item width:\", item.width if hasattr(item,\"width\") else \"unknown\")\n        print(\"item height:\", item.height if hasattr(item,\"height\") else \"unknown\")\n        if isinstance(item, displayio.TileGrid):\n            print(\"Checking TileGrid\", json.dumps(item))\n            if item.x \u003c= x and x \u003c (item.x + item.tile_width) and item.y \u003c= y and y \u003c (item.y + item.tile_height):\n                print(\"Touched TileGrid\", json.dumps(item))\n                return item\n        elif isinstance(item, displayio.Group):\n            print(\"Checking Group\", json.dumps(item))\n            data = get_item_at(x, y, item)\n            if data is not None:\n                return data\n    return None\n\ndef print_items(group):\n    print(\"Printing items in group\", json.dumps(group))\n    for item in group:\n        if not hasattr(item, \"x\") or not hasattr(item, \"y\"):\n            continue\n        elif isinstance(item, displayio.TileGrid):\n            print(\"printing TileGrid\", json.dumps(item), json.dumps(dir(item)))\n        if isinstance(item, displayio.Group):\n            print(\"Checking Group\", json.dumps(item))\n            print_items(item)\n        else:\n            print(\"Skipping\", json.dumps(item), json.dumps(dir(item)))\n    print(\"Finished printing items in group\", json.dumps(group), json.dumps(dir(group)))\n\n\ncallbacks = {}\n\n# Define the triggerTouch function or import it from a module\ndef triggerTouch(x, y, finger):\n    global display\n    print(\"Touched\", x, y, finger)\n    #iterate through the tilegrids and groups (which contain tilegrids) inside root_group, testing the x,y against the bounding box of each tilegrid\n    item = get_item_at(x, y, display.root_group)\n    if item is None:\n        print(\"No item found\")\n        return False\n    print(\"Found item\", json.dumps(item), json.dumps(dir(item)))        \n    gc.collect()\n    if item in callbacks:\n        callbacks[item](item, x, y, finger)\n        return True\n    return False\n\ndef example_touch_callback(item, x, y, finger):\n    print(\"Touched Example Callback, toggling hidden status\")\n    item.hidden = not item.hidden\n\nwifi = adafruit_qualia.network.WiFi()\n\nif wifi.is_connected and wifi.ip_address in (None, \"0.0.0.0\"):\n    print(\"Wifi almost connected, Waiting for IP\")\n    counter=50\n    while not wifi.is_connected:\n        print(\".\", end=\"\")\n        time.sleep(0.1)\n        if(counter==0):\n            break\n\n\nif not wifi.is_connected and wifi.ip_address not in (None, \"0.0.0.0\"):\n    # using circuitpython get portalbase wifi information so we can construct the url from IP and port and password (both read from settings.toml using os.getenv(\"field\", default_value) )\n    ip = wifi.ip_address\n    port = os.getenv(\"CIRCUITPY_WEB_API_PORT\", \"80\")\n    password = os.getenv(\"CIRCUITPY_WEB_API_PASSWORD\", \"password\")\n\n    # WebPage to show in the QR\n    webpage = f\"http://{ip}:{port}/\"\n\nelse:\n    # if not then craft a fake url that starts with http:// but infact has a clever use of @ (%40) and :(%3a) etc to really just link to about:blank except about:wifi_not_connected, like a low grade url escape that only a QR reader would fall for as it's technically not a valid url; be better if it was a javascript: type url that supported alerts etc, maybe just a website with good customisation, a bit like lmgtfy.com or a meme generator\n    #webpage=\"http://%40%3a%2f%2fabout%3awifi_not_connected\"\n    webpage = \"https://learn.adafruit.com/circuitpython-with-esp32-quick-start/setting-up-web-workflow\"\n    ip=None\n\n\n# QR size Information\nqr_size = 9  # Pixels\nscale = 8\n\n# Generate QR code bitmap for webpage\ndisplay_qr_and_text(webpage, \"IP: \" + str(ip) if ip else \"\", relative_x_from_center=100,y=10, scale=scale, include_text_x_offset=-30)\n\n\n# Generate Wifi joining QR code\nqrdata=f\"WIFI:S:{os.getenv('CIRCUITPY_WIFI_SSID', '*Unset*')};T:{os.getenv('CIRCUITPY_WIFI_TYPE', 'WPA')};P:{os.getenv('CIRCUITPY_WIFI_PASSWORD', '*Unset*')};\" if ip else \"https://www.adafruit.com\"\n(x,y)=display_qr_and_text(qrdata, \"SSID: \" + os.getenv(\"CIRCUITPY_WIFI_SSID\", \"*Unset* read help-\u003e\"), relative_x_from_center=-330,y=10, scale=scale, include_text_x_offset=-30)\n\nif not ip:\n    # Create a Bitmap from a PNG file\n    image = displayio.OnDiskBitmap(\"/circuitpythonlogo-color888.bmp\")\n\n    palette = displayio.ColorConverter(input_colorspace=displayio.Colorspace.RGB888)\n\n    # Create a TileGrid\n    tilegrid = displayio.TileGrid(\n        bitmap=image,\n        pixel_shader=palette,\n        x=x,\n        y=y,\n    )\n    callbacks[tilegrid] = example_touch_callback\n\n\n    img_group = displayio.Group()\n    img_group.append(tilegrid)\n\n    display.auto_refresh=False\n    # Add the TileGrid to the display\n    display.root_group.append(img_group)\nelse:\n    for item in display.root_group:\n        if isinstance(item, displayio.TileGrid):\n            callbacks[item] = example_touch_callback\n\ndisplay.refresh()\ndisplay.auto_refresh=True\n\ndef fix_x_y_for_rotation(x,y):\n    global graphics\n    if graphics.display.rotation == 90:\n        return (y, graphics.display.height- x -1)\n    elif graphics.display.rotation == 180:\n        return (graphics.display.width - x -1, graphics.display.height - y -1)\n    elif graphics.display.rotation == 270:\n        return (graphics.display.width - y -1, x)\n    else:\n        return (x,y)\n\nprint_items(display.root_group)\n\ncounter=0\nwhile True:\n    counter+=1\n\n    if graphics.touch.touched:\n        try:\n            finger=-1\n            for touch in graphics.touch.touches:\n                finger+=1\n                x = touch[\"x\"]\n                y = touch[\"y\"]\n                # todo: refactor this into a PR for qualia graphics or touch directly\n                print(\"touch before fix\")\n                print(graphics.display.width, graphics.display.height, x, y)\n                (x,y) = fix_x_y_for_rotation(x,y)\n                print(\"touch after fix\")\n                print(graphics.display.width, graphics.display.height, x, y)\n                if (\n                    not 0 \u003c= x \u003c graphics.display.width\n                    or not 0 \u003c= y \u003c graphics.display.height\n                ):\n                    print(\"skipping out of bounds touch\")\n                    continue  # Skip out of bounds touches\n                triggerTouch(x, y, finger)\n        except Exception as e:\n            print(e)\n            pass\n    if peripherals.button_up:\n        peripherals.backlight = True\n    if peripherals.button_down:\n        peripherals.backlight = False\n    time.sleep(0.1)","metadata":{"language":"python","linenums":false}}]