# CircuitPython on the Xteink X4 eReader

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/143/088/medium800thumb/hacks_bitmpToRepl.jpg?1773856440)

Many folks in our maker community find an intriguing device with a familiar microcontroller inside and think, "Will it run DOOM?". Here at Adafruit, _of course,_ that thought crosses our minds, but the more pertinent question is always "Can it run CircuitPython?".

The latest object to make us wonder this is the Xteink X4 eReader, a super slim pocket-sized eReader. It has an ESP32-C3 inside with serial over JTAG peripheral available through the USB port, making it easy to interface with, to load alternative firmware; no teardown required [(but here's one if you're curious)](https://github.com/sunwoods/Xteink-X4).

## OpenX4 E-Paper Community SDK

There is a fantastic project for the Xteink X4 called the [OpenX4 E-Paper Community SDK](https://github.com/open-x4-epaper/community-sdk). It is written for use with ESP-IDF and PlatformIO. The work that's been done with that project made porting the Xteink X4 to CircuitPython super easy.

Primary: For working with the Xteink X4 and CircuitPython, Web Serial support in your browser is needed, and requires at least Firefox 151 or a Chrome-89-based browser.

## Parts
### Part: Xteink X4 eReader
quantity: 1
Xteink X4 eReader
[Xteink X4 eReader](https://www.xteink.com/products/xteink-x4)

### USB Type A to Type C Cable - approx 1 meter / 3 ft long

[USB Type A to Type C Cable - approx 1 meter / 3 ft long](https://www.adafruit.com/product/4474)
As technology changes and adapts, so does Adafruit. This&nbsp;&nbsp; **USB Type A to Type C** cable will help you with the transition to USB C, even if you're still totin' around a USB Type A hub, computer or laptop.

USB C is the latest industry-standard connector for...

In Stock
[Buy Now](https://www.adafruit.com/product/4474)
[Related Guides to the Product](https://learn.adafruit.com/products/4474/guides)
![Angled shot of a coiled black, USB-C to USB-A cable.](https://cdn-shop.adafruit.com/640x480/4474-02.jpg)

# CircuitPython on the Xteink X4 eReader

## Pinouts

![](https://cdn-learn.adafruit.com/assets/assets/000/143/082/medium800/hacks_edited_P1490475.jpg?1773845367)

## ESP32-C3

The [ESP32-C3](https://documentation.espressif.com/esp32-c3_datasheet_en.pdf) is the brains of the Xteink X4. It's an ultra low power chip from Espressif and is really popular in IoT devices. The only downside is it doesn't have any PSRAM and only has 400 KB of SRAM. However, the Xteink X4 has 16 MB of flash for a ton of storage.

## eInk Display

The Xteink X4 uses an SSD1677 controller with a [4.26" 800x480 GDEQ0426T82 display](https://www.good-display.com/product/457.html). It is available as a built-in monochrome eInk display with the CircuitPython firmware. It is connected via SPI:

- SCLK: GPIO8 (`board.EPD_SCK`)
- MOSI: GPIO10 (`board.EPD_MOSI`)
- CS: GPIO21 (`board.EPD_CS`)
- DC: GPIO4 (`board.EPD_DC`)
- RST: GPIO5 (`board.EPD_RST`)
- BUSY: GPIO6 (`board.EPD_BUSY`)

## Buttons

There are 7 buttons available on the Xteink X4: the power button, up button, down button, right button, left button, confirm button and back button.

There is a bit of a twist though: only the power button is connected directly to a GPIO pin ( **GPIO3** / `board.BUTTON`). The rest of the six buttons are read with a resistor ladder that are connected to two ADC pins ( **GPIO1** / `board.BUTTON_ADC_1` and&nbsp; **GPIO2** / `board.BUTTON_ADC_1`). Each button reads a different set of analog values when pressed and that's how they are read in software. The [CircuitPython helper library](https://github.com/adafruit/Adafruit_CircuitPython_Xteink_X4) takes care of reading the two ADC inputs.

## microSD Card

On the right side of the eReader is a microSD card. It uses the same SPI bus as the display and uses **GPIO12** (`board.SD_CS`) as its CS pin and **GPIO7** for MISO (`board.SD_MISO`).

## Battery Monitoring

You can monitor the battery voltage with **GPIO0** (`board.A0`). That pin is connected to the battery with a voltage divider.&nbsp; **GPIO20** (`board.D20`) also detects if the battery is charging via USB.

# CircuitPython on the Xteink X4 eReader

## Back-up the Firmware

Although this guide is all about installing new firmware on the Xteink X4, what about the stock firmware? You can use ESPTool to dump the flash contents of the ESP32-C3 and save it as a .BIN file. You can then reinstall this .BIN file later if you ever want to go back to the default firmware.

If you got too excited, though, and already installed CircuitPython, you can download the .BIN file below:

[xteink-x4-firmware-backup.bin](https://cdn-learn.adafruit.com/assets/assets/000/143/089/original/firmware_backup.bin?1773856578)
## ESPTool

You can use ESPTool to read the flash over USB serial. After plugging in the Xteink X4 into your computer with a known data USB cable,&nbsp;use this command:

```terminal
python -m esptool --chip esp32c3 --port COM4 read_flash 0x0 0x1000000 firmware_backup.bin
```

Replace `COM4` with the serial port for the Xteink X4 on your system. You'll see the `read_flash` program start running. There are 16 MB of flash on the eReader, so this will take a few minutes.

When it's done, you'll be prompted to reset the board. You should see the .BIN file in your file directory. Now you have a backup of the stock firmware that you can refer back to.

# CircuitPython on the Xteink X4 eReader

## Install CircuitPython

[CircuitPython](https://github.com/adafruit/circuitpython)&nbsp;is a derivative of&nbsp;[MicroPython](https://micropython.org/)&nbsp;designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. ESP32-C3 CircuitPython firmware is uploaded to the board via the USB JTAG serial port.

Follow this step-by-step to get CircuitPython running on your board.

## CircuitPython Download
[Click here to download the latest .BIN file for the Xteink X4](https://cdn-learn.adafruit.com/assets/assets/000/143/032/original/adafruit-circuitpython-xteink_x4-en_US-20260310-main-PR10873-5630edf.bin?1773762712)
 **Click the link above to download the latest CircuitPython .bin file.**

Save it wherever is convenient for you.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/033/medium640/hacks_install_circuitpython_on_esp32_ESP32_CircuitPython_bin_downloaded.png?1773762845)

## Connecting to the Web Flasher
Info: You do not need to put the Xteink X4 eReader into bootloader mode to upload a new .BIN file.

To begin,&nbsp; **plug your board into your computer via USB, using** &nbsp; **a known-good data-sync cable** , directly, or via an adapter if needed.

**You will have to use Firefox 151 or later, Chrome or a Chromium-based browser to install CircuitPython.** &nbsp;[For example, Edge and Opera are Chromium based](https://en.wikipedia.org/wiki/Chromium_(web_browser)).

Safari and some other browsers etc. are&nbsp;_not_&nbsp;supported -&nbsp;[they have not implemented Web Serial](https://developer.mozilla.org/en-US/docs/Web/API/Serial#browser_compatibility)!

In the&nbsp; **Chrome browser** &nbsp;visit&nbsp;[https://adafruit.github.io/Adafruit\_WebSerial\_ESPTool/](https://adafruit.github.io/Adafruit_WebSerial_ESPTool/)

The main page of the ESP Web Flasher should look something like this.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/034/medium640/hacks_Screenshot_2026-03-17_115819.png?1773763173)

Press the&nbsp; **Connect** &nbsp;button in the top right of the web browser. You will get a pop up asking you to select the COM or Serial port. Look for&nbsp; **USB JTAG/Serial debug**.

On some systems, such as MacOS, there may be additional system ports that appear in the list.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/035/medium640/hacks_Screenshot_2026-03-17_120127.png?1773763354)

The Javascript code will now try to connect to the board. It may timeout for a bit until it succeeds. On success, you will see that it is&nbsp; **Connected** &nbsp;and will print out a unique&nbsp; **MAC address** &nbsp;identifying the board along with other information that was detected.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/036/medium640/hacks_Screenshot_2026-03-17_120331.png?1773763472)

Once you have successfully connected, the file upload GUI will appear.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/037/medium640/hacks_Screenshot_2026-03-17_120541.png?1773763593)

## Erasing the Board Contents

If you would like to erase the entire flash area so that you can start with a clean slate, you can use the erase feature. We recommend doing this every time before installing or updating CircuitPython.

To erase the contents, click the&nbsp; **Erase** &nbsp;button. You will be prompted as to whether you want to continue. Click&nbsp; **OK** &nbsp;to continue. If you do not wish to continue, click Cancel.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/038/medium640/hacks_install_circuitpython_on_esp32_ESPCP_click_erase_ok.png?1773763714)

You'll see "Erasing flash memory. Please wait..." This will eventually be followed by "Finished." and the amount of time it took to erase.

**Do not disconnect!** &nbsp;Immediately continue on to Programming the Board.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/039/medium640/hacks_install_circuitpython_on_esp32_ESPCP_erase_finished.png?1773763773)

## Programming the Board
You can click on&nbsp; **Choose a file...** &nbsp;from any of the available buttons. It will only attempt to program buttons with a file and a unique location. Select the&nbsp; **.BIN** &nbsp;file you downloaded at the beginning of this page from the file chooser dialogue.

Verify that the&nbsp; **Offset** &nbsp;box next to the file location you used is 0x0. The offset defaults to 0x0, so unless you changed it manually, it should be good to go.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/040/medium640/hacks_install_circuitpython_on_esp32_ESPCP_choose_a_file.png?1773763842)

![](https://cdn-learn.adafruit.com/assets/assets/000/143/041/medium640/hacks_install_circuitpython_on_esp32_ESPCP_bin_to_upload.png?1773763861)

Once you choose a file, the button text will change to match your filename. You can then click the&nbsp; **Program** &nbsp;button to start flashing.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/042/medium640/hacks_install_circuitpython_on_esp32_ESPCP_bin_loaded_press_program.png?1773763995)

A progress bar will appear and after a minute or two, you will have written the firmware.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/043/medium640/hacks_Screenshot_2026-03-17_121550.png?1773764227)

You've now successfully programmed CircuitPython onto your board! As suggested in the output, press reset to run the new firmware.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/044/medium640/hacks_Screenshot_2026-03-17_121604.png?1773764274)

As the ESP32-C3 does not have native USB, no USB drive will show up on your computer when you reset. With CircuitPython firmware loaded, the REPL can be accessed over a serial/COM port.

Don't worry though! We have the&nbsp;[CircuitPython USB Workflow Code Editor](https://learn.adafruit.com/circuitpython-usb-workflow-code-editor-quick-start) or the [CircuitPython Web Workflow Code Editor](https://learn.adafruit.com/getting-started-with-web-workflow-using-the-code-editor) so that you can access the board via USB or WiFi in your Chromium-based browser.

# CircuitPython on the Xteink X4 eReader

## Setting up Web Workflow

Web workflow is enabled when a file named **settings.toml** is added to the root folder of the CircuitPython file system. This file contains local wifi info and other settings. [More info here](https://docs.circuitpython.org/en/latest/docs/workflows.html#web).

Now, normally creating a file is super easy when the board shows up as a disk drive...but as mentioned before, that's not possible on the original ESP32 because it does not have native USB so it cannot show up as a disk drive. So we have to be a little more creative!

To start out, the minimal contents of this file are:

```auto
CIRCUITPY_WIFI_SSID = "wifissid"
CIRCUITPY_WIFI_PASSWORD = "wifipassword"
CIRCUITPY_WEB_API_PASSWORD= "webpassword"
```

with each item updated with local specifics.

- `wifissid` - replace with local wifi network name
- `wifipassword` - replace with local wifi network password
- `webpassword` - used when connecting to the board via web browser, set to whatever

There are a couple of options for creating this file.

Info: Creating the **settings.toml** file will also enable the WiFi hardware, which may be off by default.

## Option 1: Create **settings.toml** file via REPL

The **settings.toml** file is simple enough that it can be created with a few simple Python commands directly via the REPL.

[What is the REPL?](https://learn.adafruit.com/welcome-to-circuitpython/the-repl)
Here are the basic commands to use. **Note these need to be updated with local WiFi specifics**. **Note also where double quotes and single quotes are used.**

```python
f = open('settings.toml', 'w')
f.write('CIRCUITPY_WIFI_SSID = "wifissid"\n')
f.write('CIRCUITPY_WIFI_PASSWORD = "wifipassword"\n')
f.write('CIRCUITPY_WEB_API_PASSWORD = "webpassword"\n')
f.close()
```

- Replace `wifissid` with the name of your local WiFi network
- Replace `wifipassword` with your local WiFi password
- The other password, `webpassword`, is used when you access the board via a web browser. Set this to whatever you want.

If you have a terminal program like PuTTY, minicom, screen or similar and you know how to use them - connect to the ESP32 board at the correct port name and 115200 baud

![wireless_image.png](https://cdn-learn.adafruit.com/assets/assets/000/114/361/medium640/wireless_image.png?1660840338)

Once connected, you can press the **Reset** button to kick the firmware, then hit return a few times to get to the REPL prompt. Now you can copy and paste the lines above (with your correct SSID/password!

Don't forget, ESP32 does not support 5 GHz networks, so use your 2.4 GHz SSID if you have two.

![](https://cdn-learn.adafruit.com/assets/assets/000/117/210/medium800/wireless_repl_file.jpg?1671730210)

Now press the&nbsp; **Reset** button again, you will see the ESP32 reboot. This time, it should get an IP address. If you have a fairly smart terminal program, the IP address will appear in the title bar.

![](https://cdn-learn.adafruit.com/assets/assets/000/117/211/medium800/wireless_repl_ip_addr1.jpg?1671730323)

Alternatively, or if you want more details - you can always query the board over the REPL to ask it the MAC address and IP address:

```auto
import wifi

print("My MAC addr: %02X:%02X:%02X:%02X:%02X:%02X" % tuple(wifi.radio.mac_address))
print("My IP address is", wifi.radio.ipv4_address)
```

![](https://cdn-learn.adafruit.com/assets/assets/000/117/212/medium800/wireless_repl_ip_addr2.jpg?1671730397)

If that's not working either, try asking what SSIDs it _thinks_ it's seeing to make sure you have no typos!

```auto
import wifi

print("Available WiFi networks:")
for n in wifi.radio.start_scanning_networks():
    print("\t%s\t\tRSSI: %d\tChannel: %d" % (str(n.ssid, "utf-8"), n.rssi, n.channel))
wifi.radio.stop_scanning_networks()
```

Note that since this code has an indented part in it, you shouldn't just paste it in directly into the REPL because it won't handle the tab/spaces correctly. Instead, once you hit **Return** to get to the REPL, type in **Control-E** to enter&nbsp;_paste mode_ . Then paste in the text and type&nbsp; **Control-D&nbsp;** to finish and run

![](https://cdn-learn.adafruit.com/assets/assets/000/114/368/medium800/wireless_image.png?1660841619)

Check that your SSID appears properly - if not, maybe its 5 GHz, maybe its not typed correctly, etc!

## Option 2: Create **settings.toml** file using Thonny

This option requires installing additional software - the [Thonny Python IDE](https://thonny.org/). Thonny provides file access and a text editor which makes creating the **settings.toml** file a little more streamlined than using direct REPL commands. **This technique requires Thonny 4.0.0 or later.**

In Thonny, open the **Tools -\> Options** dialog and select the **Interpreter** tab.

Set interpreter to CircuitPython (generic) and the COM port as needed.

![wireless_thonny2_arrow.png](https://cdn-learn.adafruit.com/assets/assets/000/114/369/medium640/wireless_thonny2_arrow.png?1660842902)

Enter the file contents in the editor window. Update `wifissid` etc. as needed.

Click save.

![wireless_thonny3_arrow.jpg](https://cdn-learn.adafruit.com/assets/assets/000/114/370/medium640/wireless_thonny3_arrow.jpg?1660842926)

Select **CircuitPython Device**

![wireless_thonny4_arrow.png](https://cdn-learn.adafruit.com/assets/assets/000/114/371/medium640/wireless_thonny4_arrow.png?1660842956)

Save the file as **settings.toml**.

![wireless_thonny5_arrow.jpg](https://cdn-learn.adafruit.com/assets/assets/000/117/209/medium640/wireless_thonny5_arrow.jpg?1671730006)

Now the **settings.toml** file shows up on the CircuitPython device.

![wireless_thonny6_arrow.jpg](https://cdn-learn.adafruit.com/assets/assets/000/117/208/medium640/wireless_thonny6_arrow.jpg?1671729900)

# CircuitPython on the Xteink X4 eReader

## Connecting via Web Browser

Once the web workflow is enabled and the board connects to the local wifi network with an IP address, the board can be accessed by opening a web browser and entering the board's network address. But what is the address to enter?

## Connect using MDNS Address

The CircuitPython web workflow uses [MDNS](https://en.wikipedia.org/wiki/Multicast_DNS) so that connecting to the board can be as simple as using the address `circuitpython.local`. Try using that first and if it works, great. Open a web browser and navigate to:

```auto
http://circuitpython.local
```

Or just click the button below:

[circuitpython.local](http://circuitpython.local)
## Connect using IP Address

If MDNS does not work for some reason, the fallback approach is to use the actual IP address, like `192.168.0.121`, the CircuitPython board was assigned when it connected to the local wifi network.

```auto
http://192.168.0.121
```

There are several options for determining this address.

### Terminal Window Title Bar

The web workflow serial output includes some special escape sequences that are intended to update the window title bar of the terminal program being used.

For example, here's what a terminal window running screen to connect to the board looks like:

![](https://cdn-learn.adafruit.com/assets/assets/000/114/330/medium800/wireless_addr_terminal.png?1660757031)

### Via REPL

The IP address can be obtained fairly easily via the REPL with a few commands:

```python
>>> import wifi
>>> wifi.radio.ipv4_address
192.168.0.121
>>>
```

### Serial output in Thonny
In Thonny, look for the IP address in the banner text of the serial output in the Shell window.

Note the **settings.toml** file has been added to the CircuitPython device.

![wireless_thonny_ip_addr.jpg](https://cdn-learn.adafruit.com/assets/assets/000/117/213/medium640/wireless_thonny_ip_addr.jpg?1671732126)

# CircuitPython on the Xteink X4 eReader

## Using Web Workflow

## Welcome! Page

This is the initial page seen when first connecting.

Launch a web browser and navigate to `circuitpython.local` **OR** the numeric IP address like `192.168.0.121`

![wireless_Screenshot_from_2022-08-17_10-29-59.png](https://cdn-learn.adafruit.com/assets/assets/000/114/332/medium640/wireless_Screenshot_from_2022-08-17_10-29-59.png?1660757426)

The browser should find the board and show the **Welcome!** screen.

![wireless_Screenshot_from_2022-08-17_10-31-34.png](https://cdn-learn.adafruit.com/assets/assets/000/114/333/medium640/wireless_Screenshot_from_2022-08-17_10-31-34.png?1660757528)

## Password Entry

When first accessing things from the **Welcome!** page, a password must be entered.

Enter the password that was setup for `CIRCUITPY_WEB_API_PASSWORD` in the **settings.toml** file.

**Leave Username blank.**

![wireless_Screenshot_from_2022-08-17_10-33-16.png](https://cdn-learn.adafruit.com/assets/assets/000/114/334/medium640/wireless_Screenshot_from_2022-08-17_10-33-16.png?1660757636)

## Serial Terminal

From the Welcome! page, click the **serial terminal** link to access the serial output as well as REPL for entering commands.

The serial window initially looks blank.

![wireless_Screenshot_from_2022-08-17_10-37-56.png](https://cdn-learn.adafruit.com/assets/assets/000/114/336/medium640/wireless_Screenshot_from_2022-08-17_10-37-56.png?1660757897)

Commands can be entered in the input field at the bottom.

The results will be shown above and scroll up.

![wireless_Screenshot_from_2022-08-17_10-40-02.png](https://cdn-learn.adafruit.com/assets/assets/000/114/337/medium640/wireless_Screenshot_from_2022-08-17_10-40-02.png?1660758020)

## File Browser

From the Welcome! page, click the **file browser** link on the Welcome! page to access files and folders.

The files and folders should show up.

There are buttons for various actions.

![wireless_Screenshot_from_2022-12-22_10-14-34.png](https://cdn-learn.adafruit.com/assets/assets/000/117/214/medium640/wireless_Screenshot_from_2022-12-22_10-14-34.png?1671732968)

For example, click the **Edit** button for **code.py** bring up a simple editor to allow changing the contents.

![wireless_Screenshot_from_2022-08-17_10-48-05.png](https://cdn-learn.adafruit.com/assets/assets/000/114/338/medium640/wireless_Screenshot_from_2022-08-17_10-48-05.png?1660758518)

Library files and folders from the [Bundle](https://circuitpython.org/libraries) can be uploaded to the **/lib** folder using the **Browse...** buttons.

There's a separate button for files and folders (directories).

![wireless_ww_files.png](https://cdn-learn.adafruit.com/assets/assets/000/114/407/medium640/wireless_ww_files.png?1660923923)

# CircuitPython on the Xteink X4 eReader

## CircuitPython Helper Library

![](https://cdn-learn.adafruit.com/assets/assets/000/143/085/medium800/hacks_edited_P1490479.jpg?1773845417)

As mentioned on the Pinouts page, six of the seven buttons on the Xteink X4 are connected to a resistor ladder read by two analog pins. The Xteink X4 helper library makes reading those buttons a lot easier. There's also a few helper functions for reading the battery voltage.

## Library Usage with Web Workflow

To use with CircuitPython, you need to first install the Xteink X4 library into the&nbsp; **lib** &nbsp;folder on the Xteink X4. Then you need to update&nbsp; **code.py** &nbsp;with the example script.

Thankfully, we can do this in one go. In the example below, click the&nbsp; **Download Project Bundle** &nbsp;button below to download the necessary libraries and the&nbsp; **code.py** &nbsp;file in a zip file.

https://github.com/adafruit/Adafruit_CircuitPython_Xteink_X4/blob/main/examples/xteink_x4_simpletest.py

Extract the contents of the zip file. You'll see the following contents in the extracted folder:

![CIRCUITPY drive](https://adafruit.github.io/Adafruit_CircuitPython_Bundle/xteink_x4_xteink_x4_simpletest.py.png )

## Upload the Library
In the [Web Workflow Code Editor](https://code.circuitpython.org/), click the **Open** button.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/045/medium640/hacks_Screenshot_2026-02-03_144651.png?1773769019)

This opens the file browser. Click the **Upload** button.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/046/medium640/hacks_Screenshot_2026-02-04_134356.png?1773769047)

Click on **Upload Folders**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/047/medium640/hacks_Screenshot_2026-02-04_134515.png?1773769071)

Select the **/lib** folder in the Project Bundle and click **Upload**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/049/medium640/hacks_Screenshot_2026-02-04_135143.png?1773769102)

You'll be asked to confirm the folder upload. Click **Upload** to continue.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/050/medium640/hacks_Screenshot_2026-03-17_135152.png?1773769957)

After the upload finishes, you'll be able to see the library in the **/lib** folder on the device.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/051/medium640/hacks_Screenshot_2026-03-17_135247.png?1773770007)

## Upload **code.py**
Next, you'll upload the **code.py** file to the Xteink X4. Click the&nbsp; **Upload** button and then **Upload Files**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/052/medium640/hacks_Screenshot_2026-02-04_142624.png?1773770045)

Select the **code.py** file from the Project Bundle and then click **Open**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/053/medium640/hacks_Screenshot_2026-02-04_142802.png?1773770062)

## Run the Code
To run the new **code.py** file, click **Save + Run**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/054/medium640/hacks_Screenshot_2026-03-17_135455.png?1773770121)

After running the code, you'll see the battery voltage print out before going into the loop. In the loop, the button you press will print out to the serial monitor along with the battery voltage. You'll see the CircuitPython REPL on the eInk display.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/055/medium800/hacks_Screenshot_2026-03-17_135711.png?1773770249)

# CircuitPython on the Xteink X4 eReader

## eInk Display

![](https://cdn-learn.adafruit.com/assets/assets/000/143/087/medium800thumb/hacks_buttonEink.jpg?1773856409)

The Xteink X4 has a lovely 800x480 eInk display. That's a lot of pixels! The display can be accessed as `board.DISPLAY` in CircuitPython or you can use the [SSD1677 driver](https://github.com/adafruit/Adafruit_CircuitPython_ssd1677). The demo code below lets you show a different dithered image starring the Circuit Playground characters every time you press one of the buttons on the eReader.

## Library Usage with Web Workflow

To use with CircuitPython, you need to first install the libraries into the&nbsp; **lib** &nbsp;folder and upload the bitmap images onto the Xteink X4. Then you need to update&nbsp; **code.py** &nbsp;with the example script.

In the example below, click the&nbsp; **Download Project Bundle** &nbsp;button below to download the necessary libraries and the&nbsp; **code.py** &nbsp;file in a zip file.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/Xteink_X4_Examples/Xteink_X4_eInk_Demo/code.py

Extract the contents of the zip file. You'll see the following contents in the extracted folder:

![CIRCUITPY drive](https://adafruit.github.io/Adafruit_Learning_System_Guides/Xteink_X4_Examples_Xteink_X4_eInk_Demo.png )

## Upload the Library File
In the [Web Workflow Code Editor](https://code.circuitpython.org/), click the **Open** button.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/045/medium640/hacks_Screenshot_2026-02-03_144651.png?1773769019)

This opens the file browser. Click the **Upload** button.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/046/medium640/hacks_Screenshot_2026-02-04_134356.png?1773769047)

Click on **Upload Folders**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/047/medium640/hacks_Screenshot_2026-02-04_134515.png?1773769071)

Select the **/lib** folder in the Project Bundle and click **Upload**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/049/medium640/hacks_Screenshot_2026-02-04_135143.png?1773769102)

You'll be asked to confirm the folder upload. Click **Upload** to continue.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/050/medium640/hacks_Screenshot_2026-03-17_135152.png?1773769957)

After the upload finishes, you'll be able to see the library in the **/lib** folder on the device.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/051/medium640/hacks_Screenshot_2026-03-17_135247.png?1773770007)

## Upload the Code and Bitmaps
Next, you'll upload the **code.py** file and bitmap files to the Xteink X4. Click the&nbsp; **Upload** button and then **Upload Files**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/052/medium640/hacks_Screenshot_2026-02-04_142624.png?1773770045)

Select the **code.py** file and the **.BMP** files from the Project Bundle and then click&nbsp; **Open**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/072/medium640/hacks_Screenshot_2026-03-18_091536.png?1773839791)

## Run the Code
To run the new **code.py** file, click **Save + Run**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/073/medium640/hacks_Screenshot_2026-03-18_091732.png?1773839873)

The seven bitmap images are loaded as `OnDiskBitmap`s. In the loop, the button you press will update the display to show the associated bitmap image.

# CircuitPython on the Xteink X4 eReader

## Weather Demo

![](https://cdn-learn.adafruit.com/assets/assets/000/143/083/medium800/hacks_edited_P1490465.jpg?1773845375)

Everyone loves a weather display and especially one on a chonky eInk display with low power usage. This weather project is based on the&nbsp;[MagTag Daily Weather Forecast Display Learn Guide by Carter Nelson](https://learn.adafruit.com/magtag-weather) with a few adjustments to better suit the Xteink X4.

Since the display is so big, the graphics have to match in size and multiple sprite sheets will quickly get you into&nbsp;`MemoryError` territory on an ESP32-C3. In this version, only today's weather is shown but it is still using the&nbsp;[OpenMeteo API](https://open-meteo.com/) and [deep sleep](https://learn.adafruit.com/deep-sleep-with-circuitpython) to save battery.

## Library Usage with Web Workflow

To use with CircuitPython, you need to first install the libraries into the&nbsp; **lib** &nbsp;folder and the **bmp** folder onto the Xteink X4. Then you need to update&nbsp; **code.py** &nbsp;with the example script.

n the example below, click the&nbsp; **Download Project Bundle** &nbsp;button below to download the necessary libraries and the&nbsp; **code.py** &nbsp;file in a zip file.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/Xteink_X4_Examples/Xteink_X4_Weather/code.py

Extract the contents of the zip file. You'll see the following contents in the extracted folder:

![CIRCUITPY drive](https://adafruit.github.io/Adafruit_Learning_System_Guides/Xteink_X4_Examples_Xteink_X4_Weather.png )

## Upload the Libraries
In the [Web Workflow Code Editor](https://code.circuitpython.org/), click the **Open** button.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/045/medium640/hacks_Screenshot_2026-02-03_144651.png?1773769019)

This opens the file browser. Click the **Upload** button.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/046/medium640/hacks_Screenshot_2026-02-04_134356.png?1773769047)

Click on **Upload Folders**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/047/medium640/hacks_Screenshot_2026-02-04_134515.png?1773769071)

Select the **/lib** folder in the Project Bundle and click **Upload**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/049/medium640/hacks_Screenshot_2026-02-04_135143.png?1773769102)

You'll be asked to confirm the folder upload. Click **Upload** to continue.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/074/medium640/hacks_Screenshot_2026-03-18_092405.png?1773840341)

After the upload finishes, you'll be able to see the library in the **/lib** folder on the device.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/075/medium640/hacks_Screenshot_2026-03-18_092556.png?1773840378)

## Upload the Bitmaps and Fonts

Next, you'll upload the&nbsp; **/fonts** and **/bmps** folders.

Select **Upload Folders** and then select the **/bmps** folder in the Project Bundle. Click **Upload**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/076/medium640/hacks_Screenshot_2026-03-18_092317.png?1773840455)

You'll be asked to confirm the folder upload. Click&nbsp; **Upload** &nbsp;to continue.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/077/medium640/hacks_Screenshot_2026-03-17_145403.png?1773840594)

Select&nbsp; **Upload Folders** &nbsp;and then select the&nbsp; **/fonts** &nbsp;folder in the Project Bundle. Click&nbsp; **Upload**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/078/medium640/hacks_Screenshot_2026-03-18_092927.png?1773840634)

You'll be asked to confirm the folder upload. Click&nbsp; **Upload** &nbsp;to continue.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/079/medium640/hacks_Screenshot_2026-03-18_092436.png?1773840664)

## Upload **code.py**
Next, you'll upload the **code.py** file to the Xteink X4. Click the&nbsp; **Upload** button and then **Upload Files**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/052/medium640/hacks_Screenshot_2026-02-04_142624.png?1773770045)

Select the **code.py** file from the Project Bundle and then click **Open**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/080/medium640/hacks_Screenshot_2026-03-18_093200.png?1773840737)

## Run the Code
To run the new **code.py** file, click **Save + Run**.

![](https://cdn-learn.adafruit.com/assets/assets/000/143/081/medium640/hacks_Screenshot_2026-03-18_093411.png?1773840875)

The code connects to WiFi, requests weather information from Open-Meteo, populates the different text elements with the information from the API and then creates a display buffer with the bitmap images and text elements to show on the eInk display. After this, the Xteink X4 goes into deep sleep. You can wake up the Xteink X4 by pressing the power button.


## Featured Products

### USB Type A to Type C Cable - approx 1 meter / 3 ft long

[USB Type A to Type C Cable - approx 1 meter / 3 ft long](https://www.adafruit.com/product/4474)
As technology changes and adapts, so does Adafruit. This&nbsp;&nbsp; **USB Type A to Type C** cable will help you with the transition to USB C, even if you're still totin' around a USB Type A hub, computer or laptop.

USB C is the latest industry-standard connector for...

In Stock
[Buy Now](https://www.adafruit.com/product/4474)
[Related Guides to the Product](https://learn.adafruit.com/products/4474/guides)

## Related Guides

- [Internet of Things Printer](https://learn.adafruit.com/internet-of-things-printer.md)
- [MagTag Slideshow](https://learn.adafruit.com/magtag-slideshow.md)
- [PyPortal MQTT Sensor Node/Control Pad for Home Assistant](https://learn.adafruit.com/pyportal-mqtt-sensor-node-control-pad-home-assistant.md)
- [CircuitPython on Raspberry Pi (Bare Metal / No OS)](https://learn.adafruit.com/circuitpython-on-raspberry-pi-bare-metal-no-os.md)
- [Bluefruit Luminary Lanterns with Capacitive Touch](https://learn.adafruit.com/bluefruit-luminary-lanterns-with-capacitive-touch.md)
- [Python Debouncer Library for Buttons and Sensors](https://learn.adafruit.com/debouncer-library-python-circuitpython-buttons-sensors.md)
- [Making a PyPortal User Interface with DisplayIO](https://learn.adafruit.com/making-a-pyportal-user-interface-displayio.md)
- [CircuitPython Powered AT Hand-Raiser](https://learn.adafruit.com/at-hand-raiser.md)
- [ESP-NOW in CircuitPython](https://learn.adafruit.com/esp-now-in-circuitpython.md)
- [RGB & HSV NeoPixel Dialer](https://learn.adafruit.com/rgb-hsv-neopixel-dialer.md)
- [Adafruit PCF8523 Real Time Clock](https://learn.adafruit.com/adafruit-pcf8523-real-time-clock.md)
- [CircuitPython Hardware: PCA9685 PWM & Servo Driver](https://learn.adafruit.com/micropython-hardware-pca9685-pwm-and-servo-driver.md)
- [CRICKIT Snake Bot](https://learn.adafruit.com/crickit-snake-bot.md)
- [Three Button Foot Switch](https://learn.adafruit.com/three-button-foot-switch.md)
- [USB SNES Gamepad](https://learn.adafruit.com/usb-snes-gamepad.md)
