[{"element_type":"text","content":"\n        \u003ch3\u003e\u003cem\u003eA \u003cstrong\u003eCircuitPython\u003c/strong\u003e project for indoor \"windless\" garden chimes that play along with the outdoor wind speed.\u003c/em\u003e\u003c/h3\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/CedarGroveStudios/Weather_Chimes/tree/main\" target=\"_blank\"\u003eWeather_Chimes GitHub repository\u003c/a\u003e\u003c/p\u003e\n      ","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/324/original/IMG_1717.jpeg?1700027940","metadata":{"caption":"IoT Weather Chimes running on QT PY ESP32-S2 + I2S Amp + LiPo Charger stack"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/180/original/Weather_Chimes_glamour_lores.png?1694814463","metadata":{"caption":"Prototype IoT Weather Chimes running on UM FeatherS2"}},{"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        \u003ch2\u003eIntroduction\u003c/h2\u003e\n\u003cp\u003eOur patio garden chime collection playfully dances along with the wind to compose and perform new melodies and songs. Because it's quite breezy here, our chimes are also a windstorm warning system, giving us notice that we may be visited by neighborhood trash cans and trampolines. When the wind is strong, we can hear the outdoor chimes describing the wind speed in many of our indoor rooms. Except my office. I need a way to listen to the chimes.\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\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/181/original/trash_can_forecast.jpeg?1695013889","metadata":{"caption":"A local television station's wind speed forecast."}},{"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\u003eThe \u003cspan\u003eWeather Chimes\u003c/span\u003e project fills that need. It connects to the \u003cem\u003eAdafruit NTP\u003c/em\u003e service for network time and to \u003cem\u003eOpenWeatherMap.org \u003c/em\u003efor wind speed data. The wind speed data is retrieved every twenty minutes and is used to adjust wind chime playback in a pseudo random pattern. The chime voice synthesizer is provided by the \u003ccode\u003e\u003cspan\u003eCircuitPython_Chimes\u003c/span\u003e\u003c/code\u003e class and for this project, is sent to an \u003cem\u003eAdafruit MAX98357A\u003c/em\u003e I2S amplifier driving an Adafruit 40mm 4-ohm 3-watt speaker. Although an \u003cem\u003eUnexpected Maker Feather S2\u003c/em\u003e was used for this project, the code should work on just about any ESP32 device that's capable of running CircuitPython.\u003cbr\u003e\u003cbr\u003eThe \u003cspan\u003eWeather Chimes\u003c/span\u003e project consists of two primary code files, \u003ccode\u003e\u003cspan\u003eweather_chimes_code.py\u003c/span\u003e\u003c/code\u003e and \u003ccode\u003e\u003cspan\u003eweather_chimes_wifi.py\u003c/span\u003e\u003c/code\u003e. The \u003ccode\u003e\u003cspan\u003eweather_chimes_code.py\u003c/span\u003e\u003c/code\u003e code is imported via the default \u003ccode\u003e\u003cspan\u003ecode.py\u003c/span\u003e\u003c/code\u003e file contained in the Feather S2's root directory. This code contains the primary non-wifi device definitions and the master \u003ccode\u003e\u003cspan\u003ewhile...\u003c/span\u003e\u003c/code\u003e loop that plays the chimes. It also imports the \u003ccode\u003e\u003cspan\u003eWeatherChimesWiFi\u003c/span\u003e\u003c/code\u003e class from \u003ccode\u003e\u003cspan\u003eweather_chimes_wifi.py\u003c/span\u003e\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cbr\u003eThe \u003ccode\u003eWeatherChimesWiFi\u003c/code\u003e class takes care of all the networking details for connecting and retrieving data from the internet. It also provide helpers for updating and retrieving time and weather as well as properties for including the local time and wind speed. The WiFi class uses the \u003ccode\u003esettings.toml\u003c/code\u003e file for connecting to a home WiFi router as well as parameters needed for \u003cem\u003eAdafruit NTP\u003c/em\u003e and the \u003cem\u003eOpenWeatherMap.org\u003c/em\u003e API.\u003c/p\u003e\n\u003cp\u003eA fictional \u003ccode\u003esettings.toml\u003c/code\u003e file:\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":"CIRCUITPY_WIFI_SSID=\"MyLocalWiFiRouter\"\nCIRCUITPY_WIFI_PASSWORD=\"secretpassword\"\n\nCIRCUITPY_WEB_API_PASSWORD=\"passw0rd\"\nCIRCUITPY_WEB_API_PORT=80\n\nlocation=\"LosAngeles, CA, US\"\ntimezone=\"America/LosAngeles\"\nopenweather_token=\"ABCDEF0123456789ghijklmnoPQRSTUVWXYZ\"","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  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eBesides the primary code files, the project depends on a typical collection of Adafruit libraries, a library from the community bundle, as well as a custom class to reproduce the voice of the chimes with synthio.\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\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  \n  \n        \u003ch2\u003eSimulating the Chimes\u003c/h2\u003e\n\u003cp\u003eThe imported \u003ccode\u003e\u003cspan\u003eCircuitPython_Chime\u003c/span\u003e\u003c/code\u003e class does the hard work of building a \u003ccode\u003e\u003cspan\u003esynthio\u003c/span\u003e\u003c/code\u003e object with all the overtones and ADSR envelope characteristics of a set of tubular chimes. The class also contains a collection of selectable musical songs (\"scales\"). For this project, an emulation of our family heirloom 1970's \u003cem\u003eHarry and David Pear\u003c/em\u003e six-tube garden chime was selected from the collection. To further customize the chime voice, refer to the documentation in the \u003ca href=\"https://github.com/CedarGroveStudios/CircuitPython_Chime\" target=\"_blank\"\u003e\u003cem\u003eCircuitPython_Chime\u003c/em\u003e GitHub repository\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe synthesized chime voice adds some unique overtones to the root frequency signal to achieve its realistic sound. For a metallic chime, the overtones are not \"perfect scientific harmonics\" with integer frequency multipliers of 1 (the root fundamental frequency), 2, 3, 4, and 5 with amplitude levels of 60%, 20%, 10%, 5%, and 5% respectively.\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/522/original/Screenshot_2023-12-08_at_10.26.56%E2%80%AFPM.png?1702103475","metadata":{"caption":"The Overtone Harmonics of the Perfect Scientific Chime Model"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n        \u003cp\u003e\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eInstead, because the metal tube is open on both ends, the overtones have frequency multipliers of 1.00 (the root fundamental), 2.76, 5.40, and 8.93 with amplitude levels of 60%, 20%, 10%, and 10% respectively. The resultant output signal is more complex than the \"perfect\" combination and more characteristic of real tubular chimes.\u003c/p\u003e\n      \n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/523/original/Screenshot_2023-12-08_at_10.18.13%E2%80%AFPM.png?1702103549","metadata":{"caption":"The Overtones of the Empirical Chime Model"}},{"element_type":"text","content":"\n  \n        \u003cp\u003e\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eIf one end of the tubular chime is closed it sounds more like a bell. Empirical measurements of a well-designed metal bell found the overtone frequency multipliers to be 1.00 (root fundamental), 1.48, 1.35, and 1.72 with amplitude levels of 80%, 19%, 1%, and \u0026lt;1% respectively.\u003c/p\u003e\n      \n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/524/original/Screenshot_2023-12-09_at_10.21.56%E2%80%AFAM.png?1702146164","metadata":{"caption":"The Overtones of the Empirical Bell Model"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003e\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eA simple test of the chime voice:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"import time\nimport board\nimport audiobusio\nimport audiomixer\nfrom cedargrove_chime import Chime, Scale\n\n# Instantiate chime synthesizer\naudio_output = audiobusio.I2SOut(bit_clock=board.D12, word_select=board.D9, data=board.D6)\nmixer = audiomixer.Mixer(sample_rate=11020, buffer_size=4096, voice_count=1, channel_count=1)\naudio_output.play(mixer)\nmixer.voice[0].level = 0.8\n\nchime = Chime(mixer.voice[0], scale=Scale.HarryDavidPear)\n\n# Sequentially play all the notes in the scale\nfor index, note in enumerate(chime.scale):\n    chime.strike(note, 1)\n    time.sleep(0.4)\ntime.sleep(1)","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  \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\u003eResponding to the Wind\u003c/h2\u003e\n\u003cp\u003eTo simulate a typical garden wind chime, it's assumed that the note tubes are mounted in a circle and that no more than half the tubes will sound when the striker moves due to wind. The \u003ccode\u003e\u003cspan\u003eChime.strike()\u003c/span\u003e\u003c/code\u003e method randomly selects the first note and the number of notes to play from the chime scale. The initial note will be followed by a cluster of adjacent notes either to the right or left as determined by a random direction variable and the number of notes to play, each played in sequence after a random delay that ranges from 0.1 to 0.5 seconds. This algorithm mimics the observed behavior of the chime striker since it usually moves away from the first struck tube, hitting a few adjacent chime tubes in a left-to-right or right-to-left circular pattern.\u003cbr\u003e\u003cbr\u003eFurther achieving a realistic pseudo-random chime playback proportional to wind speed involves two additional factors. First, the amplitude (audio volume) of the chime notes is directly proportional to wind speed, varying from 40% to 100% amplitude. Next, the delay between clusters of notes is inversely proportional to wind speed; the delay time interval ranges proportionally from 2 seconds to 10 milliseconds. A random delay of up to 0.5 second is added to the calculated cluster interval delay time to reduce the possibility of discernible patterns.\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\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  \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  \n  \n  \n  \n  \n        \u003cp\u003eAn excerpt from \u003ccode\u003eweather_chimes_code.py\u003c/code\u003e showing the wind speed simulation algorithm:\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\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":"\"\"\"Populate the chime_index list with the initial note then add the\nadditional adjacent notes.\"\"\"\nchime_index = []\nchime_index.append(random.randrange(len(chime.scale)))\n\ndirection = random.choice((-1, 1))\nfor count in range(1, len(chime.scale) // 2):\n    chime_index.append((chime_index[count - 1] + direction) % len(chime.scale))\n\n\"\"\"Randomly select the number of notes to play in the sequence.\"\"\"\nnotes_to_play = random.randrange(len(chime_index) + 1)\n\n\"\"\"Play the note sequence with a random delay between each.\"\"\"\nnote_amplitude = map_range(corr_wifi.wind_speed, 0, 50, 0.4, 1.0)\nfor count in range(notes_to_play):\n    chime.strike(chime.scale[chime_index[count]], note_amplitude)\n    time.sleep(\n        random.randrange(10, 60) * 0.01\n    )  # random delay of 0.10 to 0.50 seconds\n\n\"\"\"Delay the next note sequence inversely based on wind speed plus a\nrandom interval.\"\"\"\nif corr_wifi.wind_speed \u003c 1:\n    time.sleep(30)\nelse:\n    time.sleep(\n        map_range(corr_wifi.wind_speed, 0, 50, 2.0, 0.01) + (random.random() / 2)\n    )","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  \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\u003eDependencies\u003c/h2\u003e\n\u003cp\u003eThis project depends on:\u003cbr\u003e\u003cspan\u003e\u003ca href=\"https://github.com/adafruit/circuitpython\" target=\"_blank\"\u003eAdafruit CircuitPython\u003c/a\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan\u003e\u003ca href=\"https://github.com/CedarGroveStudios/CircuitPython_Chime\" target=\"_blank\"\u003eCedarGrove CircuitPython_Chime\u003c/a\u003e\u003cbr\u003e\u003c/span\u003e\u003cspan\u003e\u003ca href=\"https://github.com/CedarGroveStudios/CircuitPython_MIDI_Tools\" target=\"_blank\"\u003eCedarGrove CircuitPython_MIDI_Tools\u003c/a\u003e\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\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\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  \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\u003eExamples\u003c/h2\u003e\n\u003cp\u003e\u003cspan\u003e\u003c/span\u003e\u003cspan\u003e\u003ca href=\"https://youtu.be/85vy7aG6j2c?si=KKtJh1J6P5rkMtYC\" target=\"_blank\"\u003eYouTube video of a test of the Weather_Chimes project\u003c/a\u003e\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\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":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/182/original/weather_chimes_youtube.png?1695056517","metadata":{"caption":""}},{"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  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003e\u003cspan\u003eAcknowledgements and Thanks\u003c/span\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cspan\u003e\u003cstrong\u003eLee Hite\u003c/strong\u003e, \u003c/span\u003e\u003ca href=\"http://leehite.org/documents/Tubular%20Bell%20Chimes%20Design%20Handbook.pdf\" target=\"_blank\"\u003e\u003cem\u003eTubular Bell Chimes Design Handbook\u003c/em\u003e\u003c/a\u003e\u003cspan\u003e for the analysis of tubular chime physics and overtones.\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eC. McKenzie, T. Schweisinger, and J. Wagner\u003c/strong\u003e, \u003ca href=\"https://peer.asee.org/a-mechanical-engineering-laboratory-experiment-to-investigate-the-frequency-analysis-of-bells-and-chimes-with-assessment\" target=\"_blank\"\u003e\u003cem\u003eA Mechanical Engineering Laboratory Experiment\u003c/em\u003e \u003cem\u003eto Investigate the Frequency Analysis of Bells and Chimes with Assessment\u003c/em\u003e\u003c/a\u003e for the analysis of bell overtones.\u003c/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eLiz Clark\u003c/strong\u003e, \u003ca href=\"https://learn.adafruit.com/circle-of-fifths-euclidean-synth-with-synthio-and-circuitpython\" target=\"_blank\"\u003e\u003cspan\u003e\u003cem\u003eCircle of Fifths Euclidean Synth with synthio and CircuitPython\u003c/em\u003e\u003c/span\u003e\u003c/a\u003e Adafruit Learning Guide for the waveform and noise methods.\u003c/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eTodd Kurt\u003c/strong\u003e for the fundamentally essential \u003ca href=\"https://github.com/todbot/circuitpython-synthio-tricks\" target=\"_blank\"\u003e\u003cspan\u003esynthio\u003c/span\u003e hints, tricks, and examples.\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eJohn Park\u003c/strong\u003e for the foundational Adafruit Learning Guide, \u003ca href=\"https://learn.adafruit.com/audio-synthesis-with-circuitpython-synthio\" target=\"_blank\"\u003e\u003cem\u003eAudio Synthesis with CircuitPython synthio\u003c/em\u003e\u003c/a\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAlso, special thanks to \u003cstrong\u003eJeff Epler\u003c/strong\u003e and \u003cstrong\u003eAdafruit\u003c/strong\u003e for the comprehensive design and implementation of the amazing CircuitPython \u003cspan\u003esynthio\u003c/span\u003e\u0026nbsp;module.\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\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/298/original/IMG_1695.jpeg?1699311682","metadata":{"caption":"Not our back yard. Apparently this problem is somewhat common."}},{"element_type":"product","content":"https://www.adafruit.com/product/4769","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/5325","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/3006","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/5770","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/5397","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/3968","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/3898","metadata":{}}]