The Arduino code for the MEMENTO and cat printer is available as a pre-compiled .UF2 file that you can drag and drop onto your MEMENTO board.
Plug your board into your computer, using a known-good data-sync USB cable, directly, or via an adapter if needed.
Double-click the reset button (highlighted in red above), wait about a half a second and then tap reset again.
The code will begin running by initializing the MEMENTO and then connecting to the cat printer. After a successful connection, you'll see the camera frame show on the display with the gameboy filter.
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries // // SPDX-License-Identifier: MIT #include <Arduino.h> #include "Adafruit_PyCamera.h" #include <CatGFX.h> // CatGFX library source is included in the project with modifications // https://github.com/TheNitek/CatGFX Adafruit_PyCamera pycamera; bool frame = true; String frame_info[] = {"NO", "YES"}; // Buffer which can hold 400 lines byte buffer[48 * 400] = {0}; // Create a printer supporting those 400 lines CatPrinter cat(400); // hard reset void(* resetFunc) (void) = 0; // atkinson dithering algorithm parameters from CircuitPython struct DitherAlgorithmInfo { int mx, my; int divisor; struct { int dx, dy, dl; } terms[4]; } atkinson = { 1, 1, 8, // mx, my, divisor { {1, 0, 1}, {-1, 1, 1}, {0, 1, 1}, {1, 1, 1} // dx, dy, dl } }; // this sends a full bitmap to the printer void gameboy(uint16_t *source, int width, int height, bool createForPrinter) { uint16_t *bitmap = new uint16_t[width * height]; int16_t *errorBuffer = new int16_t[(width + 2) * (height + 2)](); uint8_t *bitmapForPrinter = nullptr; if (createForPrinter) { bitmapForPrinter = new uint8_t[(width * height)](); } // dither the pixels // referenced from CP module for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int idx = (y + 1) * (width + 2) + (x + 1); uint16_t rgb = source[y * width + x]; uint8_t r = (rgb & 0xF800) >> 8; uint8_t g = (rgb & 0x07E0) >> 3; uint8_t b = (rgb & 0x001F) << 3; uint8_t grayscale = (r * 30 + g * 59 + b * 11) / 100; grayscale += errorBuffer[idx] / atkinson.divisor; uint16_t outPixel = grayscale > 127 ? 0xFFFF : 0x0000; // White or Black for RGB bitmap bitmap[y * width + x] = outPixel; if (createForPrinter) { if (grayscale <= 127) { bitmapForPrinter[(y * width + x) / 8] |= (1 << (7 - (x % 8))); } } int16_t error = grayscale - (outPixel ? 255 : 0); for (int i = 0; i < 4; i++) { int dx = atkinson.terms[i].dx, dy = atkinson.terms[i].dy; errorBuffer[idx + dy * (width + 2) + dx] += error * atkinson.terms[i].dl; } } } if (createForPrinter == true) { cat.fillScreen(1); cat.drawBitmap(72, 25, bitmapForPrinter, 240, 240, 0); if (frame == true) { cat.drawRect(50, 0, 285, 350, 0); cat.drawRect(71, 24, 242, 242, 0); cat.setTextSize(2); cat.setTextColor(0); cat.setCursor(72, 300); cat.print("shot on MEMENTO"); } delay(500); Serial.println("Printing..."); cat.printBuffer(); delay(500); Serial.println("scrolling.."); cat.feed(50); Serial.println("Done!"); } pycamera.drawRGBBitmap(0, 0, bitmap, width, height); pycamera.refresh(); delete[] bitmap; delete[] errorBuffer; delete[] bitmapForPrinter; } void setup() { Serial.begin(115200); // while (!Serial) delay(10); Serial.println(); if (!pycamera.begin()) { Serial.println("Failed to initialize pyCamera interface"); while (1) yield(); } Serial.println("pyCamera hardware initialized!"); pycamera.fillScreen(pycamera.color565(0, 0, 0)); pycamera.setCursor(10, 10); pycamera.setTextSize(2); pycamera.setTextColor(pycamera.color565(0, 0, 255)); pycamera.print("CONNECTING.."); Serial.print("Connecting to cat printer.."); cat.begin(buffer, sizeof(buffer)); Serial.print("."); cat.resetNameArray(); Serial.print("."); cat.addNameArray((char *)"MX06"); Serial.println("."); if (cat.connect()) { Serial.println("Connected!"); } else { Serial.println("Could not find printer! Resetting.."); pycamera.fillScreen(pycamera.color565(0, 0, 0)); pycamera.setCursor(10, 10); pycamera.setTextSize(2); pycamera.setTextColor(pycamera.color565(255, 0, 0)); pycamera.print("Could not\nfind printer!\nResetting.."); delay(200); resetFunc(); } Serial.println("going into loop"); } void loop() { pycamera.readButtons(); pycamera.captureFrame(); pycamera.setNeopixel(0x00FF00); // blits gameboy filter to display but does not print gameboy((uint16_t *)pycamera.fb->getBuffer(), 240, 240, false); // pressing shutter prints the frame if (pycamera.justPressed(SHUTTER_BUTTON)) { pycamera.setNeopixel(0x0000FF); pycamera.setCursor(25, 25); pycamera.setTextSize(3); pycamera.setTextColor(pycamera.color565(255, 0, 255)); pycamera.print("PRINTING.."); gameboy((uint16_t *)pycamera.fb->getBuffer(), 240, 240, true); Serial.println("SHUTTER"); } if (pycamera.justPressed(AWEXP_BUTTON_SEL)) { frame = not frame; pycamera.fillScreen(pycamera.color565(0, 0, 0)); pycamera.setCursor(10, 10); pycamera.setTextSize(3); pycamera.setTextColor(pycamera.color565(255, 0, 0)); pycamera.println("Print with \nframe?"); pycamera.println(frame_info[frame]); delay(2000); Serial.println("SEL"); } if (pycamera.justPressed(AWEXP_BUTTON_DOWN)){ cat_reconnect(); Serial.println("DOWN"); } } void cat_reconnect() { Serial.println("Disconnecting.."); pycamera.fillScreen(pycamera.color565(0, 0, 0)); pycamera.setCursor(10, 10); pycamera.setTextSize(2); pycamera.setTextColor(pycamera.color565(0, 0, 255)); pycamera.print("DISCONNECTING.."); cat.disconnect(); Serial.println("Disconnected."); delay(1000); pycamera.fillScreen(pycamera.color565(0, 0, 0)); pycamera.setCursor(10, 10); pycamera.setTextSize(2); pycamera.setTextColor(pycamera.color565(0, 0, 255)); pycamera.print("CONNECTING.."); cat.resetNameArray(); cat.addNameArray((char *)"MX06"); if (cat.connect()) { Serial.println("Connected!"); pycamera.fillScreen(pycamera.color565(0, 0, 0)); pycamera.setCursor(10, 10); pycamera.setTextSize(2); pycamera.setTextColor(pycamera.color565(0, 0, 255)); pycamera.print("CONNECTED!"); } else { Serial.println("Could not find printer!"); pycamera.fillScreen(pycamera.color565(0, 0, 0)); pycamera.setCursor(10, 10); pycamera.setTextSize(2); pycamera.setTextColor(pycamera.color565(255, 0, 0)); pycamera.print("Could not \nfind printer!\nResetting.."); delay(200); resetFunc(); } delay(500); }
The gameboy()
function is pulled from the bitmaptools CircuitPython module. It uses the Atkinson dithering algorithm to create the iconic grayscale pixelated image. The function takes a bool
argument called createForPrinter
. If it is true
, then a bitmap
is prepared for printing and is sent to the printer. It also checks if a frame is included for printing. Otherwise, only the RGBBitmap
is prepared for the display.
In the setup
, the PyCamera library is initialized, followed by the CatGFX printer library. Once a connection is established over BLE with the printer, the loop
begins. In the loop
, the filtered camera preview is blitted to the screen. If you press the shutter button, then the filtered image is printed. You can press the select button on the MEMENTO to toggle the frame on or off for printing. You can also press the down button to reconnect to the printer if you lose connection.
Text editor powered by tinymce.