[{"element_type":"markdown","content":"\u003ch1\u003eAdding WIFI the Easy Way\u003c/h1\u003e\n\n\u003cp\u003eIf you start from scratch, the best thing is to buy a board with integrated WIFI.\nBut this is not possible in all cases. Many older boards just don't have a\nsuitable chip. This is especially true for boards that are based on the RP2040.\u003c/p\u003e\n\n\u003cp\u003eFor example I recently bought the\n\u003ca href=\"https://shop.pimoroni.com/products/tinyfx?variant=42086044827731\"\u003eTinyFX\u003c/a\u003e\nfrom Pimoroni. That is a very small board about the size of a Qt-Py for controlling\nLego lighting kits:\u003c/p\u003e\n\n\u003cp\u003e\u003cimg src=\"https://circuitpython.org/assets/images/boards/large/pimoroni_tinyfx.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\n\u003cp\u003eAnother example is the\n\u003ca href=\"https://shop.pimoroni.com/products/tufty-2040?variant=40036912595027\"\u003eTufty2040\u003c/a\u003e,\na large sized display with a RP2040 on board:\u003c/p\u003e\n\n\u003cp\u003e\u003cimg src=\"https://shop.pimoroni.com/cdn/shop/products/tufty-2040-2_1500x1500_crop_center.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\n\u003cp\u003eBut I have also quite a few other boards lying around that would\nprofit from connectivity e.g. the \u003ca href=\"https://www.adafruit.com/product/3800\"\u003eItsyBitsy M4\nExpress\u003c/a\u003e.  And of course I also\nown some of the shiny new Pico2 boards.\u003c/p\u003e\n\n\u003ch2\u003eAirlift\u003c/h2\u003e\n\n\u003cp\u003eThe standard solution for adding WIFI is\n\u003ca href=\"https://www.adafruit.com/product/4201\"\u003eAirlift\u003c/a\u003e.  Airlift is a\nWIFI-coprocessor running a special firmware on an Espressif\nESP32-WROOM-32.  Besides the \"pure\" chip on a breakout, there are also\naddons for specific form-factors like a\n\u003ca href=\"https://www.adafruit.com/product/4264\"\u003eFeatherWing\u003c/a\u003e or a\n\u003ca href=\"https://www.adafruit.com/product/4363\"\u003eBitsyWing\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eBut Airlift has two major problems. The first is \u003cem\u003ewiring\u003c/em\u003e. Airlift\nis based on SPI and in addition uses a number of control GPIOs. So if\nyou don't use one of the wings (that block all of your pins) you end\nup with wiring 7-9 jumper-cables. That is not only cumbersome, but the\nboards above don't even provide that many pins (the TinyFx e.g. just\nexposes what you see in the image).\u003c/p\u003e\n\n\u003cp\u003eThe second problem is software. The Airlift uses a special\n\u003ca href=\"https://github.com/adafruit/adafruit_circuitpython_esp32spi\"\u003eCircuitPython library\u003c/a\u003e\nthat does it's job, but in a slightly different way than the standard\nWIFI support of CircuitPython does. E.g. instead of connecting with\n\u003ccode class=\"inline\"\u003ewifi.radio.connect()\u003c/code\u003e you use \u003ccode class=\"inline\"\u003eesp.connect_AP()\u003c/code\u003e and instead of using\n\u003ccode class=\"inline\"\u003ewifi.radio.connected\u003c/code\u003e you check the connection state with\n\u003ccode class=\"inline\"\u003eesp.is_connected\u003c/code\u003e. There are many other differences in the API. So all\nthe boilerplate code you have or download from the internet has to be\nadapted for the Airlift. As long as this is your main \u003ccode class=\"inline\"\u003ecode.py\u003c/code\u003e file,\nthis is no major problem. But once you start using libraries things get\ncomplicated. You have to install the source version of the libs and\nadapt them. Maintaining this is no fun.\u003c/p\u003e\n\n\u003ch2\u003eESP-AT\u003c/h2\u003e\n\n\u003cp\u003eAn alternative to Airlift is\n\u003ca href=\"https://github.com/espressif/esp-at\"\u003eESP-AT\u003c/a\u003e. This is a firmware\nmaintained and provided by Espressif turning many of their chips into\nWIFI-coprocessors.  ESP-AT is highly customizable but in its standard\nversions it uses plain UART for the communication with the main\nMCU. So you basically only need four wires: VCC, GND, RX and TX.\u003c/p\u003e\n\n\u003cp\u003eThe drawback of ESP-AT is its textual interface: you send so called\n\"AT-commands\" to the co-processor and then you have to parse the\nresponse to extract the relevant data. Some of these commands are\nreally complicated, e.g. to connect to an AP you would send\u003c/p\u003e\n\u003ccode class=\"\"\u003e\u003cpre\u003eAT+CWJAP=\"myssid\",\"mypasswd\",,0,1,3,1,5,1\n\u003c/pre\u003e\u003c/code\u003e\n\u003cp\u003eWrapping all the complexity in a library really makes sense. For this\nreason, Adafruit had created the library\n\u003ca href=\"https://github.com/adafruit/Adafruit_Circuitpython_ESP_ATcontrol\"\u003eCircuitPython\u003cem\u003eESP\u003c/em\u003eAT_Control\u003c/a\u003e, but it was abandoned in favor of Airlift and is no\nlonger maintained.  That library also suffers from the same drawback\nas the Airlift library, i.e. it uses a different API than standard\nCircuitPython.\u003c/p\u003e\n\n\u003ch2\u003eCircuitPython-ESP32AT\u003c/h2\u003e\n\n\u003cp\u003eFor this reason I created a new library called\n\u003ca href=\"https://github.com/bablokb/circuitpython-esp32at\"\u003eCircuitPython-ESP32AT\u003c/a\u003e. This\nlibrary talks to the ESP-AT on an ESP-coprocessor, but instead of\ninventing yet another API it uses the standard CircuitPython API for\n\u003ccode class=\"inline\"\u003ewifi\u003c/code\u003e, \u003ccode class=\"inline\"\u003essl\u003c/code\u003e, \u003ccode class=\"inline\"\u003esocketpool\u003c/code\u003e and so on.\u003c/p\u003e\n\n\u003cp\u003eAt first, this might seem confusing. On boards with native WIFI these\nmodules don't need to be installed, since the CircuitPython firmware\nalready provides them. For other boards, the ESP32AT project now\nprovides drop-in modules with the same names providing (almost) the\nsame set of functionality.\u003c/p\u003e\n\n\u003cp\u003eOne important function the external modules don't provide is the\nweb-workflow which really needs native WIFI. Otherwise, you need to\nlook hard to spot differences.\u003c/p\u003e\n\n\u003cp\u003eYou will find installation and setup instructions as well as notes\nabout various hardware alternatives in the repo of the library. Instead of\nreproducing this information here, I only show some examples I\nactually use.\u003c/p\u003e\n\n\u003ch2\u003eHardware\u003c/h2\u003e\n\n\u003cp\u003eFrom hardware side, you need a board with a supported ESP-chip. Any\nESP32, ESP32-S2 and ESP32-Cx will do.  ESP32-S3 and the ESP32-Hx\nseries are \u003cem\u003enot\u003c/em\u003e supported. You can even use very old ESP8266 hardware\nas long as it has 1MB of flash. But this is not really recommended\nsince the ESP8266 only supports operation in TCP/UDP/SSL \u003cem\u003eclient\nmode\u003c/em\u003e. In addition, you have to compile the firmware yourself (which in\nfact is easy and a matter of a quarter of an hour - see the repo for\ndetails).\u003c/p\u003e\n\n\u003cp\u003eWiring is easy: you must connect RX/TX of your host MCU to the AT-UART\nports TX/RX of the coprocessor. Besides RX/TX you should also connect\nGND. \u003cem\u003ePowering\u003c/em\u003e the coprocessor directly from the host MCU might or\nmight not work. For example, I had no problems powering the\nESP32C3-SuperMini from the 3V3 pin of the Pico/Pico2, but I did not\nuse any other peripherals:\u003c/p\u003e\n\n\u003cp\u003e\u003cimg src=\"https://github.com/bablokb/circuitpython-esp32at/blob/main/doc/picowbell+adapter.jpg?raw=true\" alt=\"\"\u003e\u003c/p\u003e\n\n\u003cp\u003eThe connection in this image uses a trick: it redefines the I2C-pins\nof the PiCowbell adapter from I2C to UART. Of course you loose I2C on\nthese pins, so this is not a solution for all problems but it demonstrates\nhow simple wiring can be compared to the Airlift.\u003c/p\u003e\n\n\u003cp\u003eI am using the same trick for the TinyFX board from above. The board\ndoes not have any dedicated UART-pins, but the pins of the Stemma/Qt\ncan also be repurposed to UART instead of I2C. This allows a very simple\nconnection using a Stemma/Qt-DuPont adapter cable for the connection\nof the TinyFX to again to an ESP32C3:\u003c/p\u003e\n","metadata":{"markdown":"Adding WIFI the Easy Way\n========================\n\nIf you start from scratch, the best thing is to buy a board with integrated WIFI.\nBut this is not possible in all cases. Many older boards just don't have a\nsuitable chip. This is especially true for boards that are based on the RP2040.\n\nFor example I recently bought the\n[TinyFX](https://shop.pimoroni.com/products/tinyfx?variant=42086044827731)\nfrom Pimoroni. That is a very small board about the size of a Qt-Py for controlling\nLego lighting kits:\n\n![](https://circuitpython.org/assets/images/boards/large/pimoroni_tinyfx.jpg)\n\nAnother example is the\n[Tufty2040](https://shop.pimoroni.com/products/tufty-2040?variant=40036912595027),\na large sized display with a RP2040 on board:\n\n![](https://shop.pimoroni.com/cdn/shop/products/tufty-2040-2_1500x1500_crop_center.jpg)\n\nBut I have also quite a few other boards lying around that would\nprofit from connectivity e.g. the [ItsyBitsy M4\nExpress](https://www.adafruit.com/product/3800).  And of course I also\nown some of the shiny new Pico2 boards.\n\n\nAirlift\n-------\n\nThe standard solution for adding WIFI is\n[Airlift](https://www.adafruit.com/product/4201).  Airlift is a\nWIFI-coprocessor running a special firmware on an Espressif\nESP32-WROOM-32.  Besides the \"pure\" chip on a breakout, there are also\naddons for specific form-factors like a\n[FeatherWing](https://www.adafruit.com/product/4264) or a\n[BitsyWing](https://www.adafruit.com/product/4363).\n\nBut Airlift has two major problems. The first is *wiring*. Airlift\nis based on SPI and in addition uses a number of control GPIOs. So if\nyou don't use one of the wings (that block all of your pins) you end\nup with wiring 7-9 jumper-cables. That is not only cumbersome, but the\nboards above don't even provide that many pins (the TinyFx e.g. just\nexposes what you see in the image).\n\nThe second problem is software. The Airlift uses a special\n[CircuitPython library](https://github.com/adafruit/adafruit_circuitpython_esp32spi)\nthat does it's job, but in a slightly different way than the standard\nWIFI support of CircuitPython does. E.g. instead of connecting with\n`wifi.radio.connect()` you use `esp.connect_AP()` and instead of using\n`wifi.radio.connected` you check the connection state with\n`esp.is_connected`. There are many other differences in the API. So all\nthe boilerplate code you have or download from the internet has to be\nadapted for the Airlift. As long as this is your main `code.py` file,\nthis is no major problem. But once you start using libraries things get\ncomplicated. You have to install the source version of the libs and\nadapt them. Maintaining this is no fun.\n\n\nESP-AT\n------\n\nAn alternative to Airlift is\n[ESP-AT](https://github.com/espressif/esp-at). This is a firmware\nmaintained and provided by Espressif turning many of their chips into\nWIFI-coprocessors.  ESP-AT is highly customizable but in its standard\nversions it uses plain UART for the communication with the main\nMCU. So you basically only need four wires: VCC, GND, RX and TX.\n\nThe drawback of ESP-AT is its textual interface: you send so called\n\"AT-commands\" to the co-processor and then you have to parse the\nresponse to extract the relevant data. Some of these commands are\nreally complicated, e.g. to connect to an AP you would send\n\n    AT+CWJAP=\"myssid\",\"mypasswd\",,0,1,3,1,5,1\n\nWrapping all the complexity in a library really makes sense. For this\nreason, Adafruit had created the library\n[CircuitPython_ESP_AT_Control](https://github.com/adafruit/Adafruit_Circuitpython_ESP_ATcontrol), but it was abandoned in favor of Airlift and is no\nlonger maintained.  That library also suffers from the same drawback\nas the Airlift library, i.e. it uses a different API than standard\nCircuitPython.\n\n\nCircuitPython-ESP32AT\n---------------------\n\nFor this reason I created a new library called\n[CircuitPython-ESP32AT](https://github.com/bablokb/circuitpython-esp32at). This\nlibrary talks to the ESP-AT on an ESP-coprocessor, but instead of\ninventing yet another API it uses the standard CircuitPython API for\n`wifi`, `ssl`, `socketpool` and so on.\n\nAt first, this might seem confusing. On boards with native WIFI these\nmodules don't need to be installed, since the CircuitPython firmware\nalready provides them. For other boards, the ESP32AT project now\nprovides drop-in modules with the same names providing (almost) the\nsame set of functionality.\n\nOne important function the external modules don't provide is the\nweb-workflow which really needs native WIFI. Otherwise, you need to\nlook hard to spot differences.\n\nYou will find installation and setup instructions as well as notes\nabout various hardware alternatives in the repo of the library. Instead of\nreproducing this information here, I only show some examples I\nactually use.\n\n\nHardware\n--------\n\nFrom hardware side, you need a board with a supported ESP-chip. Any\nESP32, ESP32-S2 and ESP32-Cx will do.  ESP32-S3 and the ESP32-Hx\nseries are *not* supported. You can even use very old ESP8266 hardware\nas long as it has 1MB of flash. But this is not really recommended\nsince the ESP8266 only supports operation in TCP/UDP/SSL *client\nmode*. In addition, you have to compile the firmware yourself (which in\nfact is easy and a matter of a quarter of an hour - see the repo for\ndetails).\n\nWiring is easy: you must connect RX/TX of your host MCU to the AT-UART\nports TX/RX of the coprocessor. Besides RX/TX you should also connect\nGND. *Powering* the coprocessor directly from the host MCU might or\nmight not work. For example, I had no problems powering the\nESP32C3-SuperMini from the 3V3 pin of the Pico/Pico2, but I did not\nuse any other peripherals:\n\n![](https://github.com/bablokb/circuitpython-esp32at/blob/main/doc/picowbell+adapter.jpg?raw=true)\n\nThe connection in this image uses a trick: it redefines the I2C-pins\nof the PiCowbell adapter from I2C to UART. Of course you loose I2C on\nthese pins, so this is not a solution for all problems but it demonstrates\nhow simple wiring can be compared to the Airlift.\n\nI am using the same trick for the TinyFX board from above. The board\ndoes not have any dedicated UART-pins, but the pins of the Stemma/Qt\ncan also be repurposed to UART instead of I2C. This allows a very simple\nconnection using a Stemma/Qt-DuPont adapter cable for the connection\nof the TinyFX to again to an ESP32C3:\n\n\n\n"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/391/original/tinyfx_esp32c3-supermini.jpg?1731411023","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003cp\u003eEven simpler is the usage together with the \u003ca href=\"https://www.cytron.io/c-development-tools/c-maker-series/p-maker-pi-pico-simplifying-raspberry-pi-pico-for-beginners-and-kits\"\u003eMaker Pi Pico\nBoard\u003c/a\u003e. This\nboard has a socket designed for the ESP-01S:\u003c/p\u003e\n\n\u003cp\u003e\u003cimg src=\"https://github.com/bablokb/circuitpython-esp32at/raw/main/doc/esp01s.jpg?raw=true\" alt=\"\"\u003e\u003c/p\u003e\n\n\u003cp\u003eLiligo has created a chip with the same form-factor and basically the\nsame pinout, which is called \u003ca href=\"https://www.lilygo.cc/products/t-01c3\"\u003eLilygo T-01\nC3\u003c/a\u003e. This board works also in the\nsocket and gives full performance and provides all WIFI-functionality.\u003c/p\u003e\n\n\u003cp\u003eThe Swedish company \"iLabs\" even has a Feather-sized RP2350 board with\nan integrated ESP32C6:\u003c/p\u003e\n\n\u003cp\u003e\u003cimg src=\"https://github.com/bablokb/circuitpython-esp32at/raw/main/doc/challenger+rp2350_wifi.jpg?raw?true\" alt=\"\"\u003e\u003c/p\u003e\n\n\u003cp\u003eOk, we will see a Pico2-W soon, but until then, this is the best solution\nfor a WIFI-enabled RP2350.\u003c/p\u003e\n\n\u003ch2\u003ePerformance\u003c/h2\u003e\n\n\u003cp\u003eFor most use-cases like updating the time, querying an API (e.g. for\nweather information) or running an AP with a webserver the performance\nis just on par with native WIFI. There are some special cases though\nwhere native WIFI is better: e.g. I have a high-frequency\ndata-sampling application that sends data using UDP to a\nreciever. Native WIFI can send with a rate of 0.001s per message,\nusing ESP-AT this is much slower (I measured 0.015s per message).\u003c/p\u003e\n\n\u003cp\u003eAlso, if you are dealing with large responses the default chunk size\nof 32 bytes that the \u003ccode class=\"inline\"\u003eadafruit_requests\u003c/code\u003e library uses will slow you\ndown, since every chunk is an AT-transaction. Using\n\u003ccode class=\"inline\"\u003eresponse.iter_content()\u003c/code\u003e directly is a workaround to speed up\nprocessing (this also helps native WIFI a bit and Airlift too).\u003c/p\u003e\n","metadata":{"markdown":"Even simpler is the usage together with the [Maker Pi Pico\nBoard](https://www.cytron.io/c-development-tools/c-maker-series/p-maker-pi-pico-simplifying-raspberry-pi-pico-for-beginners-and-kits). This\nboard has a socket designed for the ESP-01S:\n\n![](https://github.com/bablokb/circuitpython-esp32at/raw/main/doc/esp01s.jpg?raw=true)\n\nLiligo has created a chip with the same form-factor and basically the\nsame pinout, which is called [Lilygo T-01\nC3](https://www.lilygo.cc/products/t-01c3). This board works also in the\nsocket and gives full performance and provides all WIFI-functionality.\n\nThe Swedish company \"iLabs\" even has a Feather-sized RP2350 board with\nan integrated ESP32C6:\n\n![](https://github.com/bablokb/circuitpython-esp32at/raw/main/doc/challenger+rp2350_wifi.jpg?raw?true)\n\nOk, we will see a Pico2-W soon, but until then, this is the best solution\nfor a WIFI-enabled RP2350.\n\n\nPerformance\n-----------\n\nFor most use-cases like updating the time, querying an API (e.g. for\nweather information) or running an AP with a webserver the performance\nis just on par with native WIFI. There are some special cases though\nwhere native WIFI is better: e.g. I have a high-frequency\ndata-sampling application that sends data using UDP to a\nreciever. Native WIFI can send with a rate of 0.001s per message,\nusing ESP-AT this is much slower (I measured 0.015s per message).\n\nAlso, if you are dealing with large responses the default chunk size\nof 32 bytes that the `adafruit_requests` library uses will slow you\ndown, since every chunk is an AT-transaction. Using\n`response.iter_content()` directly is a workaround to speed up\nprocessing (this also helps native WIFI a bit and Airlift too).\n"}}]