Install & Use the Demo

First, make sure you can see the Kaluga CIRCUITPY drive and connect to the REPL. Open the REPL and double check that import espcamera works without showing an error. Then, copy the correct bundle to your device. It will automatically reload and start displaying the image from the camera on the serial REPL as lo-fi ASCII art.

By clicking the BOOT button you can swap the camera between live mode & test pattern mode.

You will need to use a terminal program that understands ANSI escape codes such as screen or tio.

If the live mode image is black, remove the lens cap from the camera.

Click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, and copy the entire lib folder and the code.py file to your CIRCUITPY drive.

# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

"""
This demo is designed for the Kaluga development kit version 1.3.

To fix the MemoryError when creating a Camera object, Place the line
```toml
CIRCUITPY_RESERVED_PSRAM=1048576
```
in the file **CIRCUITPY/settings.toml** and restart.
"""

import sys

import board
import keypad
import displayio
import espcamera
import espidf

# The demo runs very slowly if the LCD display is enabled!
# It's intended to be viewed on the REPL on a host computer
displayio.release_displays()

if espidf.get_reserved_psram() < 1047586:
    print("""Place the following line in CIRCUITPY/settings.toml, then hard-reset the board:
CIRCUITPY_RESERVED_PSRAM=1048576
""")
    raise SystemExit

print("Initializing camera")
cam = espcamera.Camera(
    data_pins=board.CAMERA_DATA,
    external_clock_pin=board.CAMERA_XCLK,
    pixel_clock_pin=board.CAMERA_PCLK,
    vsync_pin=board.CAMERA_VSYNC,
    href_pin=board.CAMERA_HREF,
    pixel_format=espcamera.PixelFormat.GRAYSCALE,
    frame_size=espcamera.FrameSize.QQVGA,
    i2c=board.I2C(),
    external_clock_frequency=20_000_000,
    framebuffer_count=2)
print("initialized")

k = keypad.Keys([board.IO0], value_when_pressed=False)

chars = b" .:-=+*#%@"
remap = [chars[i * (len(chars) - 1) // 255] for i in range(256)]
width = cam.width
row = bytearray(width//2)

sys.stdout.write("\033[2J")

while True:
    if (e := k.events.get()) is not None and e.pressed:
        cam.colorbar = not cam.colorbar

    frame = cam.take(1)

    for j in range(0, cam.height, 5):
        sys.stdout.write(f"\033[{j//5}H")
        for i in range(cam.width // 2):
            row[i] =  remap[frame[width * j + 2 * i]]
        sys.stdout.write(row)
        sys.stdout.write("\033[K")
    sys.stdout.write("\033[J")
Folder

After code that is familiar from the LCD demo is the start of the code specific to the ASCII art part of the program:

  • "chars" holds the ASCII characters to use, arranged from darkest to lightest (the demo is intended to be run on a terminal with a dark background color).
  • "remap" is a 256-element look-up table from the raw brightness value to a character
  • "width" is just a shorthand way to refer to the camera's width property
  • "row" contains one byte for every 2 characters across the image, which gives a width of 80 characters, a standard terminal width.

Finally, the whole screen is cleared.

chars = b" .:-=+*#%@"
remap = [chars[i * (len(chars) - 1) // 255] for i in range(256)]
width = cam.width
row = bytearray(width//2)

sys.stdout.write("\033[2J")

The forever loop grabs a fresh frame and then converts it to ASCII.

Every 5th row of the input image is used, giving 24 lines of height; every 2nd column is taken, given 80 characters of width.

First, an escape code is printed to move the cursor to the start of the correct line.

Then, the ASCII characters for the row are calculated by using the remap array

Finally, the row is written, followed by an escape code indicating "clear to end of line".

When the whole thing is written, the remainder of the screen (if any) is cleared.

while True:
    if (e := k.events.get()) is not None and e.pressed:
        cam.colorbar = not cam.colorbar

    frame = cam.take(1)

    for j in range(0, cam.height, 5):
        sys.stdout.write(f"\033[{j//5}H")
        for i in range(cam.width // 2):
            row[i] =  remap[frame[width * j + 2 * i]]
        sys.stdout.write(row)
        sys.stdout.write("\033[K")
    sys.stdout.write("\033[J")
ESP32-S2 Kaluga Dev Kit featuring ESP32-S2 WROVER with TFT screen module, capacitive touch pads, speaker, camera and hardware.
The ESP32-S2-Kaluga-1 kit is a full featured development kit by Espressif for the ESP32-S2 that comes with everything but the kitchen sink! From TFTs to touch panels,...
$54.95
In Stock
USB Type A Plug Breakout Cable with Premium Female Jumpers
If you'd like to connect a USB-capable chip to your USB host, this cable will make the task very simple. There is no converter chip in this cable! Its basically a...
$1.95
In Stock
USB Type A Extension Cable
This handy USB extension cable will make it easy for you to extend your USB cable when it won't reach. The connectors are gold plated for years of reliability. We use these handy...
Out of Stock

This guide was first published on Feb 21, 2023. It was last updated on Feb 21, 2023.

This page (ASCII Mirror Demo) was last updated on Mar 08, 2023.

Text editor powered by tinymce.