How to Build a Cosmic Critter Pet with Raspberry Pi Pico
A tiny alien pet with tilt-based moods, nebula lights, and chirpy sounds
Updated

What you'll build
In this guide you will bring a Cosmic Critter to life on a Raspberry Pi Pico using an MPU6050 accelerometer, a 0.96-inch OLED display for its animated face, a WS2812B LED ring for nebula-inspired ambient lighting, and a piezo buzzer for chirps and purrs. The critter's mood changes in real time based on how you tilt and handle the device -- hold it level and it drifts into a calm trance with slow-pulsing blue LEDs, tilt it sharply and it gets excited with rapid rainbow sparkles and high-pitched trills, leave it still too long and it grows bored with droopy eyes and a dim amber glow. The result is a pocket-sized alien companion that reacts to your every move.
The Raspberry Pi Pico is an excellent platform for beginners because MicroPython lets you write, test, and tweak code without a compile step. This project teaches you how to read accelerometer data over I2C on the Pico, map tilt angles to discrete mood states using threshold logic, drive a NeoPixel LED ring with the Pico's PIO state machine for flicker-free animations, and generate melodic tones on a buzzer using PWM. You will also learn how to render simple bitmap animations on the OLED, giving the critter expressive eyes, a mouth, and antennae that wiggle when it is happy.
When you finish you will have a self-contained interactive toy that runs from a USB power bank or a pair of AAA batteries. The code is organized into clean modules for mood logic, LED patterns, face animation, and sound effects, so you can easily add new moods, create your own critter species with different personalities, or wire up additional sensors like a light-dependent resistor to make the critter react to darkness. It is a perfect first Pico project that teaches real embedded programming concepts while producing something genuinely fun to play with. For an ESP32-based take on the virtual pet concept, check out the ClawdBot Tamagotchi which uses buttons instead of motion for interaction.
Wiring diagram
Wiring diagram
Components needed
| Component | Type | Qty | Buy |
|---|---|---|---|
| MPU6050 Motion Sensor | sensor | 1 | €4.75 |
| SSD1306 OLED | display | 1 | €4.30 |
| WS2812B LED Ring | actuator | 1 | |
| Piezo Buzzer | actuator | 1 | €4.75 |
| Mood Button | other | 1 | €8.30 |
Prices and availability are indicative and may have been updated by the supplier. Schematik may earn a commission from purchases made through affiliate links.
Assembly
Wire the Pico control core
Connect MPU6050 and SSD1306 to Raspberry Pi Pico I2C pins (SDA GP4, SCL GP5). Add the mood button to GP14 with pull-up wiring.
- Keep I2C wires short and double-check that all modules share GND.
- Pico GPIO is 3.3V only. Do not drive GPIO pins with 5V logic.
Add lights and chirps
Connect WS2812 DIN to GP12 and buzzer signal to GP15, then upload to watch the critter react to tilt and button presses.
- If LED ring power is noisy, add a bulk capacitor across 5V and GND near the ring.
- Use a shared ground between external LED power and the Pico.
Pin assignments
| Pin | Connection | Type |
|---|---|---|
| GPIO 4 | pico-imu-1 SDA | I2C |
| GPIO 5 | pico-imu-1 SCL | I2C |
| GPIO 4 | pico-oled-1 SDA | I2C |
| GPIO 5 | pico-oled-1 SCL | I2C |
| GPIO 12 | pico-led-ring-1 DIN | DATA |
| GPIO 15 | pico-buzzer-1 SIG | PWM |
| GPIO 14 | pico-mode-button-1 SIG | DIGITAL |
Code
#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_SSD1306.h>
#include <FastLED.h>
#define SDA_PIN 4
#define SCL_PIN 5
#define BTN_PIN 14
#define BUZZER_PIN 15
#define LED_PIN 12
#define NUM_LEDS 12
Adafruit_MPU6050 mpu;
Adafruit_SSD1306 display(128, 64, &Wire, -1);
CRGB leds[NUM_LEDS];
enum Mood { HAPPY, SLEEPY, DIZZY, HYPER };
Mood mood = HAPPY;
unsigned long lastChirpMs = 0;
const char* moodName(Mood m) {
switch (m) {
case HAPPY: return "Happy";
case SLEEPY: return "Sleepy";
case DIZZY: return "Dizzy";
case HYPER: return "Hyper";
default: return "Unknown";
}
}
void renderMoodLights(Mood m) {
switch (m) {
case HAPPY:
fill_solid(leds, NUM_LEDS, CRGB(0, 120, 255));
break;
case SLEEPY:
fill_solid(leds, NUM_LEDS, CRGB(64, 0, 120));
break;
case DIZZY:
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CHSV((millis() / 12 + i * 14) % 255, 255, 120);
}
break;
case HYPER:
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = (i % 2 == 0) ? CRGB::OrangeRed : CRGB::Gold;
}
break;
}
FastLED.show();
}
void chirp(Mood m) {
switch (m) {
case HAPPY: tone(BUZZER_PIN, 1400, 70); break;
case SLEEPY: tone(BUZZER_PIN, 750, 120); break;
case DIZZY: tone(BUZZER_PIN, 1800, 40); break;
case HYPER: tone(BUZZER_PIN, 2200, 50); break;
}
}
void setup() {
Wire.begin(SDA_PIN, SCL_PIN);
mpu.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);
FastLED.setBrightness(120);
pinMode(BTN_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
}
void loop() {
sensors_event_t a, g, t;
mpu.getEvent(&a, &g, &t);
if (a.acceleration.x > 4.5f) mood = HYPER;
else if (a.acceleration.x < -4.5f) mood = SLEEPY;
else if (fabs(a.acceleration.y) > 6.0f) mood = DIZZY;
else mood = HAPPY;
if (!digitalRead(BTN_PIN)) {
mood = static_cast<Mood>((mood + 1) % 4);
chirp(mood);
delay(160);
}
if (millis() - lastChirpMs > 2800) {
chirp(mood);
lastChirpMs = millis();
}
renderMoodLights(mood);
display.clearDisplay();
display.setCursor(0, 0);
display.println("Pico Cosmic Critter");
display.print("Mood: ");
display.println(moodName(mood));
display.print("Tilt X: ");
display.println(a.acceleration.x, 1);
display.print("Tilt Y: ");
display.println(a.acceleration.y, 1);
display.println("BTN: cycle mood");
display.display();
delay(60);
}
// Run this and build other cool things at schematik.ioReady to build this?
Open this project in Schematik to get the full wiring diagram, pin assignments, and deployable code for the Cosmic Critter Pet.
Open in Schematik →


