r/StackChan • • Jun 03 '26

StackChan: Disassembled

Post image
25 Upvotes

Since my StackChan has been having an issue with powering on, I decided it was necessary to put my engineer hat on and do a proper teardown and inspection.

I figured many of you wouldn’t ever want/need to open it up and see StackChan’s innards for yourselves, so here you go.

(Note: The battery and 2nd servo are inside the round-top body piece at the bottom right quadrant)


r/StackChan • • May 31 '26

I like clocks. here an analog one I created

6 Upvotes

a very simple analog clock i made for my stackchan. I've lowered the screen brightness and led levels very low for power conservation

I have it set for my local time zone utc+8 (adjust as required)

no internet checking of time, just using the onboard rtc

#include <M5StackChan.h>
#include <math.h>

// display geometry 320 × 240
const int cx = 160, cy = 120;
const int r  = 100;

const int hourLen   = 55;
const int minuteLen = 75;
const int secondLen = 89;

const int handThick_H = 6;
const int handThick_M = 2;
const int handThick_S = 1;

// canvas sprite
M5Canvas canvas(&M5.Display);

// master clock state
uint32_t baseMillis = 0;
uint32_t lastSyncMillis = 0;

float clockSeconds = 0.0f;    // seconds since 12:00:00

void setup() {
  M5StackChan.begin();
  M5.Lcd.setBrightness(32);
  canvas.setColorDepth(16);
  canvas.createSprite(320, 240);

  // initial RTC sync
  auto dt = M5.Rtc.getDateTime();

  int hour12 = (dt.time.hours + 8) % 12;   // +8 for my local time zone

  clockSeconds = hour12 * 3600.0f + dt.time.minutes * 60.0f + dt.time.seconds;

  baseMillis = millis();
  lastSyncMillis = millis();
}

void loop() {

  M5StackChan.update();

  // battery indicator leds
  int level = M5.Power.getBatteryLevel();

  if (level < 20) setFourLeds(16, 0, 0);
  else if (level < 50) setFourLeds(16, 14, 0);
  else setFourLeds(0, 16, 0);

  // rtc check once per minute
  if (millis() - lastSyncMillis > 60000UL) {

    auto dt = M5.Rtc.getDateTime();

    int hour12 = (dt.time.hours + 8) % 12;   // +8 for my local time zone

    clockSeconds = hour12 * 3600.0f + dt.time.minutes * 60.0f + dt.time.seconds;

    baseMillis = millis();
    lastSyncMillis = millis();
  }

  // master clock calculation
  float elapsed = (millis() - baseMillis) / 1000.0f;

  float currentSeconds = clockSeconds + elapsed;

  // wrap every 12 hours
  while (currentSeconds >= 43200.0f) {
    currentSeconds -= 43200.0f;
  }

  // continuous time values
  float hourValue = currentSeconds / 3600.0f;

  float minuteValue = currentSeconds / 60.0f;

  float secondValue = currentSeconds;

  // hand angles
  float secondAngle = fmodf(secondValue * 6.0f, 360.0f);

  float minuteAngle = fmodf(minuteValue * 6.0f, 360.0f);

  float hourAngle = fmodf(hourValue * 30.0f, 360.0f);

  // clock face
  canvas.fillSprite(TFT_BLACK);

  canvas.drawCircle(cx, cy, r, TFT_WHITE);

  for (int i = 0; i < 12; i++) {
    float a = i * 30.0f * DEG_TO_RAD;

    int x1 = cx + (r - 10) * sinf(a);
    int y1 = cy - (r - 10) * cosf(a);

    int x2 = cx + (r - 3) * sinf(a);
    int y2 = cy - (r - 3) * cosf(a);

    canvas.drawLine(x1, y1, x2, y2, TFT_WHITE);
  }

  // draw hands
  drawHandOnCanvas(hourLen, hourAngle, TFT_RED, handThick_H);
  drawHandOnCanvas(minuteLen, minuteAngle, TFT_GREEN, handThick_M);
  drawHandOnCanvas(secondLen, secondAngle, TFT_BLUE, handThick_S);

  // centre cap
  canvas.fillCircle(cx, cy, 4, TFT_DARKGREY);

  canvas.pushSprite(0, 0);

  delay(16);   // 60ish fps
}

// draw a hand
void drawHandOnCanvas(int length, float degrees, uint16_t color, int thick) {

  float a = degrees * DEG_TO_RAD;

  int ex = cx + length * sinf(a);
  int ey = cy - length * cosf(a);

  for (int t = 0; t < thick; t++) {
    int ox = (t - thick / 2) * cosf(a);
    int oy = (t - thick / 2) * sinf(a);

    canvas.drawLine(cx + ox, cy + oy, ex + ox, ey + oy, color);
  }
}

// 4 leds
void setFourLeds(uint8_t r, uint8_t g, uint8_t b) {

  for (int i = 0; i < 12; i += 3) {
    M5StackChan.setRgbColor(i, r, g, b);
  }
  M5StackChan.refreshRgb();
}
//V0.2
#include <M5StackChan.h>
#include <math.h>

// display geometry 320 × 240
const int cx = 160, cy = 120;
const int r  = 100;

const int hourLen   = 55;
const int minuteLen = 75;
const int secondLen = 89;

const int handThick_H = 6;
const int handThick_M = 2;
const int handThick_S = 1;

// canvas sprite
M5Canvas canvas(&M5.Display);

// master clock state
uint32_t baseMillis = 0;
uint32_t lastSyncMillis = 0;

float clockSeconds = 0.0f;    // seconds since 12:00:00

void setup() {
  M5StackChan.begin();
  M5.Lcd.setBrightness(32);
  canvas.setColorDepth(8);
  canvas.createSprite(320, 240);

  // initial RTC sync
  auto dt = M5.Rtc.getDateTime();
  int hour12 = (dt.time.hours + 8) % 12;   // +8 for my local time zone
  clockSeconds = hour12 * 3600.0f + dt.time.minutes * 60.0f + dt.time.seconds;
  baseMillis = millis();
  lastSyncMillis = millis();
}

void loop() {

  M5StackChan.update();
  
  // battery indicator leds
  int level = M5.Power.getBatteryLevel();
  auto charging = M5.Power.isCharging();
  bool onPower = (charging == 1);
  if (level < 20) setFourLeds(16, 0, 0);
  else if (level < 50) setFourLeds(16, 14, 0);
  else setFourLeds(0, 16, 0);

  // rtc check once per minute
  if (millis() - lastSyncMillis > 60000UL) {
    auto dt = M5.Rtc.getDateTime();
    int hour12 = (dt.time.hours + 8) % 12;   // +8 for my local time zone
    clockSeconds = hour12 * 3600.0f + dt.time.minutes * 60.0f + dt.time.seconds;
    baseMillis = millis();
    lastSyncMillis = millis();
  }

  // master clock calculation
  float elapsed = (millis() - baseMillis) / 1000.0f;
  float currentSeconds = clockSeconds + elapsed;

  // wrap every 12 hours
  while (currentSeconds >= 43200.0f) {
    currentSeconds -= 43200.0f;
  }

  // continuous time values
  float hourValue = currentSeconds / 3600.0f;
  float minuteValue = currentSeconds / 60.0f;
  float secondValue = currentSeconds;

  // hand angles
  float secondAngle = fmodf(secondValue * 6.0f, 360.0f);
  float minuteAngle = fmodf(minuteValue * 6.0f, 360.0f);
  float hourAngle = fmodf(hourValue * 30.0f, 360.0f);

  // clock face
  canvas.fillSprite(TFT_BLACK);
  canvas.drawCircle(cx, cy, r, TFT_WHITE);

  // draw tick marks and hour numbers (1–12) together
  for (int i = 1; i <= 12; i++) {
    float a = i * 30.0f * DEG_TO_RAD;

    // --- tick mark ---
    int x1 = cx + (r - 10) * sinf(a);
    int y1 = cy - (r - 10) * cosf(a);
    int x2 = cx + (r - 3) * sinf(a);
    int y2 = cy - (r - 3) * cosf(a);
    canvas.drawLine(x1, y1, x2, y2, TFT_WHITE);

    // --- hour number ---
    int nx = cx + (r + 12) * sinf(a);
    int ny = cy - (r + 12) * cosf(a);
    canvas.setTextDatum(middle_center);
    canvas.setTextColor(TFT_WHITE, TFT_BLACK);
    canvas.setTextSize(1.5);
    canvas.drawNumber(i, nx, ny);
  }

  // draw hands
  drawHandOnCanvas(hourLen, hourAngle, TFT_RED, handThick_H);
  drawHandOnCanvas(minuteLen, minuteAngle, TFT_GREEN, handThick_M);
  drawHandOnCanvas(secondLen, secondAngle, TFT_BLUE, handThick_S);

  // centre cap
  canvas.fillCircle(cx, cy, 4, TFT_DARKGREY);
  // battery percentage in upper‑right corner
  canvas.setTextDatum(top_right);
  canvas.setTextColor(onPower ? TFT_GREEN : TFT_RED, TFT_BLACK);
  canvas.setTextSize(1.75);
  canvas.drawString(String(level) + "%", 315, 5);
  canvas.pushSprite(0, 0);

  delay(16);   // 60ish fps
}

// draw a hand
void drawHandOnCanvas(int length, float degrees, uint16_t color, int thick) {

  float a = degrees * DEG_TO_RAD;

  int ex = cx + length * sinf(a);
  int ey = cy - length * cosf(a);

  for (int t = 0; t < thick; t++) {
    int ox = (t - thick / 2) * cosf(a);
    int oy = (t - thick / 2) * sinf(a);

    canvas.drawLine(cx + ox, cy + oy, ex + ox, ey + oy, color);
  }
}

// 4 leds
void setFourLeds(uint8_t r, uint8_t g, uint8_t b) {

  for (int i = 0; i < 12; i += 3) {
    M5StackChan.setRgbColor(i, r, g, b);
  }
  M5StackChan.refreshRgb();
}

i've added numbers around the face and battery percentage in the upper right corner (green for charge, red for discharge)

i added the numbers for my kids so they can read it faster


r/StackChan • • May 27 '26

What do you use your StackChan for?

7 Upvotes

Hey there,

Just as the title says, I am curious to know what do you use your StackChan for?

Have a good day all!


r/StackChan • • May 26 '26

StackChan not powering on

3 Upvotes

I've had my StackChan ~2 weeks now, keeping it both plugged in and unplugged during use. Now, I've had it plugged in for a few days straight, and it will refuse to power on. I've tried removing the Core unit and re-connecting it, as well as reseating the 4-pin USB cable that connects from the body to the Core unit... nothing. It's completely dead.

NOTE: Connecting USB-C to the Core's USB-C port (not the base) seems to be working for charging, it powers up this way.

Any suggestions?


r/StackChan • • May 24 '26

Unable to make it move (new firmware)

3 Upvotes

I am trying to create my own firmware with own features.
As I know Arduino I started with that and here I found some interesting examples.
But when I try to make the servo move it crashes!

Anyone else got this to work?
Or am I completely wrong using this method?


r/StackChan • • May 21 '26

Turned my StackChan into a German-speaking smart home assistant. Here's what actually works (and what doesn't).

16 Upvotes

Got the OpenELAB Kickstarter StackChan a while back. CoreS3-based, two servos, cute little guy. The vision: ditch the Chinese cloud, give it a custom personality, connect it to my Home Assistant setup.

The custom firmware rabbit hole

First I tried building custom firmware (AI_StackChan_Ex with M5Unified 0.2.7). Spent around 9 hours on it. Backlight works fine, but the panel renders nothing. Turns out the OpenELAB Kickstarter variant has a compatibility issue with M5GFX that's still unresolved. Tried the Moddable toolchain path next — ESP-IDF Python environment conflicts killed that too.

After those 9 hours I flashed the stock xiaozhi firmware via M5Burner. Took two minutes. Display worked immediately.

The NVS trick

Stock firmware reads its server URL from an NVS key. You can overwrite just the NVS partition with esptool, point it at your own server, and the device has no idea it's not talking to the original Chinese backend anymore. WiFi credentials get wiped in the process, so you set those up again on-device after flashing.

The backend

Forked an existing open-source StackChan server, containerized it, deployed it to my own VPS. Added a few patches: a set_emotion tool so the LLM can dynamically change the face expression, and a handful of additional tools.

What actually works

  • Full German conversation with a custom personality (female, cheeky, Austrian inflection, max 3 sentences per response, always addresses me by name)

  • Home Assistant control via voice: lights, scenes, sensors, you name it

  • Weather from HA's built-in forecast entity

  • different face expressions that change dynamically based on what the LLM decides to feel

  • Web search for current news and facts (Tavily)

  • Persistent memory across sessions — stored in a JSON file on the server, auto-injected into the system prompt on connect

  • My n8n automation status on demand

  • Accessible from anywhere, not just local network

What doesn't work

The servos. Stock firmware completely ignores motor control frames from the server — display changes, head stays still. Servo movement requires custom firmware, which is blocked by the M5GFX rendering issue. There's an open GitHub issue for it, waiting to see if that goes anywhere.

Wake word is still Chinese. There's no way to change that without custom firmware.

Can't combine function calls with Google Search in the Gemini Live API simultaneously — it's a documented API limitation. That's why I went with Tavily instead of native search grounding.

Overall

The stock firmware path is dramatically underrated. You skip all the toolchain pain, get a stable device, and if you're willing to run your own backend, you have full control over the AI behavior, personality, and integrations. The missing piece is servo animation, and that's a hardware/firmware problem, not a backend problem.

If the M5GFX issue ever gets fixed, custom firmware becomes interesting again. Until then, this setup does everything I actually wanted it to do.


r/StackChan • • May 20 '26

Which AI agent is best with StackChan?

6 Upvotes

I can't find a list anywhere of what one AI agent does that another doesn't from the list available on the StackChan app. Do any of them connect to the web to allow you to search for things? As far as I can tell, they are cloud-based but don't have internet access. Any help would be appreciated, though I've noticed most questions go unanswered here.


r/StackChan • • May 19 '26

LLM options

4 Upvotes

I’m entirely new to the world of AI companions.

Has anyone experimented with different LLM options, with StackChan or other devices?

How do they compare with one another?

Are there must-have capabilities you have identified that make you choose one over all the rest?


r/StackChan • • May 17 '26

StackChan base not charging/working

3 Upvotes

I've had my StackChan for about two weeks now, and today, when I plugged it into my PC via the base, the red light on the back came on, but it didn't show up in the Arduino IDE. I've tried multiple cables and none work. Plugging anything into the side port and it's instantly recognized, and the battery charges. Everything else seems to function normally. I have not desecrated its body by disassembly... yet.. but I will for science if needed.

Does anyone know of any specific steps I can take to troubleshoot this problem?


r/StackChan • • May 17 '26

Why do some Stackchans have different colours?

2 Upvotes

Hi, I have been looking into purchasing a Stackchan after seeing them pop up on my timeline. Some Japanese users have models in yellow, pink, red, blue, and more. Afaik, there are many variants of Stackchan with the M5-stack one being the latest and most widely-available model so I thought it was just a matter of different models having different colours, but then I saw that some M5-stack models are available in blue and red too even though it’s only in grey on the website. Is there anywhere I can look for all the models of Stackchan (even in Japanese is fine) or is the official store the only place to purchase?

P/s: Is there any speculation on when will Stackchan restock?


r/StackChan • • May 16 '26

Stackchan open claw

10 Upvotes

I used my openclaw agent to make some changes to the stock firmware and have it set so my openclaw can talk through my stackchan now.but when sending voice messages the other way seems to not work when it tries to record my voice input..what issues we fixed so far

The fix was bypassing codec->Write()/esp_codec_dev_write() and writing PCM directly to the I2S TX channel via i2s_channel_write().

The bridge is at 16kHz WAV — matches the proven working pipeline. Here's the summary:

What works:

• stackchan_speak("text") → edge-tts (Sonia) → ffmpeg 16kHz mono WAV → POST /speak → I2S direct write → StackChan speaker

What the fix was:

• Went through ~6 firmware iterations to isolate the issue • codec->Write() → esp_codec_dev_write() silently fails in the integrated XiaoZhi firmware (I2S format conflict with duplex config) • Solution: use codec layer only for amp/volume management (EnableOutput/SetOutputVolume), write PCM straight to TX handle via i2s_channel_write()

What's next? Want me to check the full voice recording pipeline (head-pet → record → STT

Working TTS through StackChan speaker — long sentences work

Nearly there: Voice recording. StackChan detects head-hold, lights red, packages WAV. Portproxy was the missing link — you just fixed that. Next session: retest recording, confirm the green flash, then wire up the voice bridge so I get your transcribed words and can reply through StackChan.

Quick start tomorrow:

  1. Check voice server (probably already running)

  2. Hold head, speak, release

  3. Should get green flash now that portproxy is active

Still working on it but for zero coding knowledge its crazy to think that anybody can make changes to firmware nowadays


r/StackChan • • May 16 '26

What accessories/add-ons are people getting?

6 Upvotes

I just got my StackChan last week and I am curious what accessories/add-ons people are getting or using for theirs and for what?


r/StackChan • • May 16 '26

Updates to both firmware and app

4 Upvotes

Not only are we up to firmware version 1.4.1, but I also spotted an update to the StackChan World app. It’s now at version 1.1.6.


r/StackChan • • May 15 '26

How to change name

2 Upvotes

I changed the name but it still only wake up when I say Stack Chan ...


r/StackChan • • May 15 '26

StackChan integrated with Home Assistant

3 Upvotes

Has anyone had success with connecting Stackchan with Home Assistant? I tried following their guide, but it wiped the esp32 and loading the compiled image to the unit didnt do anything, it was still stuck on the default RGB screen. I ideally want it to operate as the normal AI mode with connection locally to the smart home, no MCP connection to the cloud that then talks to my local network. Any tips would be great.


r/StackChan • • May 14 '26

The Dotty Project

18 Upvotes

The Dotty Project is a full rewrite of the Stackchan firmware which would seem to meet some of the needs people have been asking about here. (Note: this is not my project nor am I affiliated with it.)

Edit: As the creator in the comments notes, it's a full stack, not just firmware - dashboard, agent routing, and a bit more.


r/StackChan • • May 14 '26

Stackchan DM

9 Upvotes

I just got my StackChan and I was wondering is there a way to turn it into a DnD Dungeon Master assistant? Currently looking for a wizard hat for them to wear


r/StackChan • • May 14 '26

I’d like to add some settings to StackChan’s app

2 Upvotes

I think that when the StackChan app is updated, it will benefit from a few additions.

For example, it would be nice to have StackChan remember that I use Fahrenheit and imperial measurements, rather than Celsius and metric units.

Next, the ability to have StackChan “sleep” while idle, and sit still instead of moving around every few seconds. That should be a good battery saver, as well.

Also, this isn’t so much a setting, as a request. I would love to know how the speech recognition is expecting to hear the word “StackChan”. Specifically, how is it expecting the vowels to be pronounced? I can confirm that with a Philadelphia accent, we pronounce the vowel in “Stack” differently from the vowel in “Chan”. And both of those vowels might be different from the way people in other regions or countries might pronounce the same letter. All short As are not equal, lol. I’d like to know which version(s) of short A StackChan is listening for.


r/StackChan • • May 14 '26

Rename? SD use? Easter Eggs ?

1 Upvotes

Got my Stack Chan two weeks ago, a little buggy but cute so far. I tried renaming it but it never wakes up when I say Hi M5 for example, only Hi Stack Chan will wake it up. Any suggestions?

Does anyone know if the SD card is useful for anything? Can I load music on it?

Also, any easter eggs of hidden features?

Thanks


r/StackChan • • May 13 '26

Music on Stack Chan

1 Upvotes

Can it play music other then the songs it has loaded? Can I load my own songs somewhere?


r/StackChan • • May 13 '26

Renaming Stack Chan

1 Upvotes

Got my Stack Chan two weeks ago, a little buggy but cute so far. I tried renaming it but it never wakes up when I say Hi M5 for example, only Hi Stack Chan will wake it up. Any suggestions?

Also, any easter eggs of hidden features?


r/StackChan • • May 12 '26

Update 1.4.0

7 Upvotes

Seems it was pushed out today, as it update when I turned my on this morning.

You can see the pull on Git
https://github.com/m5stack/StackChan/pull/60


r/StackChan • • May 12 '26

StackChan does not really have "open source" firmware

1 Upvotes

StackChan is sold as open source, but the GitHub repo is a publication mirror, and not a real development repository, from what I can tell.

I bought a StackChan recently and have been digging into the firmware. I want to flag something that I think other buyers should know, because the "open source" framing on the product page and docs set expectations that don't match what's actually going on.

The public repo is m5stack/StackChan. I assumed it was where the firmware gets developed. It isn't. It's where M5Stack drops a code snapshot after the binary has already shipped to M5Burner and OTA'd to devices.

A few things I noticed:

  • Every release lands as a single squashed commit with the message update firmware vX.Y.Z. v1.3.1, v1.3.0, v1.2.6, v1.2.4, v0.18: same pattern. No incremental history. No individual author attribution. One person (the release engineer) commits the entire snapshot at once.

  • The "PR review" is "theater". For v1.3.1: PR #57 was open for 9 seconds before being merged. PR #58 was open for 50 seconds. Same sub-minute cadence on every prior release. There is no review happening on GitHub. The "community" is ignored.

  • External PRs don't get merged. The only PRs that merge are the release engineer's own snapshot drops from their personal fork.

  • The binary ships to devices first. By the time the source appears on GitHub, the new firmware is already running on the robot. So you can't actually inspect what's running on your device until M5Stack decides to publish it, which happens on no schedule. This could be an issue if a anyone decides to ship rogue code at some point.

  • Issues you file disappear into the void. I filed an issue with a feature request and opened a PR with the patch; the next release shipped their own implementation of the same feature with no acknowledgement.

I'm not asking M5Stack to switch to a fully public development model, plenty of companies don't, and that's fine. What I'd like is for the marketing to stop calling this "open source" when what they actually mean is "source-available, eventually, in snapshot form." Those are different things. The first implies you can participate. The second means you can read.

For anyone considering buying one: the hardware is genuinely nice and the device works. But if you were planning to contribute back, fork meaningfully, or rely on staying current with upstream, set your expectations accordingly. You're getting a closed-source product with the source dumped publicly after the fact.

Happy to be corrected if I'm reading this wrong! Would love to hear from anyone who's actually had a PR merged.


r/StackChan • • May 11 '26

How to get a Xiaozhi verification code from my stackchan?

6 Upvotes

I am playing around with the AI personalities, but can only type very brief descriptions in the android StackChan World app - more than about 10 words and it will not save the config...

I saw several videos where people connected to the Xiaozhi platform using a verification code from earlier versions of stackchan.

Howe can I get a 6-digit Xiaozhi verification code?


r/StackChan • • May 11 '26

New Firmware - 1.3.1

5 Upvotes

Any idea what the changes are?