Home Electronics Mechanical Random

Alexa controlled Pac-Man ghost

Kirstie's Pac-Man ghost light started cooking it's LEDs a while ago, and I replaced the original controller and LEDs with some RGB LED tape and added an ESP8266 for Alexa control. After moving house, the wifi SSID changed, so I had to swap the ESP module for one with updated details.

Pinout
For the ESP01 module. Change the #define's and pinMode statements to use larger modules and keep serial.

  1. Gnd
  2. GPIO1 / TX - Green (Via a diode)
  3. GPIO2 - Blue
  4. CH_EN (pull high)
  5. GPIO0 - Red
  6. Reset
  7. GPIO3 / RX - White (Eyes)
  8. VCC
GPIO1(TX) is a troublesome pin to use for IO. It has a very weak pull up (too weak for the ULM), so you need to add a diode (pointing to the ESP) so that it can pull the pin down, AND a pullup on the ULM input. Or at least I did... YMMV.

Source Code
This was done in a bit of a hurry, so could probably be tidied up a lot! If you try this code and it jumps into a psychadelic mess without doing the blinky-eyes during the wifi connection, check your programmer settings.
Also be aware, the Alexa app on phones and tablets sends multiple on/off messages. The Echo devices work fine.

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include "fauxmoESP.h"  // There are better libraries available now!

// This is for the ESP01, using the serial port pins as outputs.
// Check programming mode and clock settings if it's a psychadelic chaos....
// www.ymmv.co.uk 2017-2019

/* Wifi */
#define WIFI_SSID "YourAPName"
#define WIFI_PASS "YourAPPassword"

/* Belkin WeMo emulation */
fauxmoESP fauxmo;

/* Set LED Pins */
#define PIN_RED 0
#define PIN_GREEN 1
#define PIN_BLUE 2
#define PIN_WHITE 3  // eyes

#define ARR_LEN 6
#define RGB_MAX 255
#define MAX_STEPS ARR_LEN * RGB_MAX

// 3 bit Gray code for colour changes.
int rgbRainbowMap[ARR_LEN][3] = {
  { 1, 0, 0 },
  { 1, 1, 0 },
  { 0, 1, 0 },
  { 0, 1, 1 },
  { 0, 0, 1 },
  { 1, 0, 1 },
};

bool bCycle, bParty;
unsigned long lastMillis;
int partyStep;
int cycleStep;
volatile bool bShutdown;

void setup() {
  delay(2000);
  bShutdown = false;
  pinMode(PIN_RED, OUTPUT);
  pinMode(PIN_GREEN, FUNCTION_3);
  pinMode(PIN_GREEN, OUTPUT);
  pinMode(PIN_BLUE, OUTPUT);
  pinMode(PIN_WHITE, FUNCTION_3);
  pinMode(PIN_WHITE, OUTPUT);
  digitalWrite(PIN_WHITE, HIGH);
  analogWriteRange(256);
  setColourRgb(0,0,0);
  bCycle = true;
  bParty = false;
  lastMillis = 0;
  partyStep = 0;
  
  wifiSetup();
  fauxmo.addDevice("Ghost");
  fauxmo.addDevice("Black Ghost");
  fauxmo.addDevice("Blue Ghost");
  fauxmo.addDevice("Cyan Ghost");
  fauxmo.addDevice("Green Ghost");
  fauxmo.addDevice("Magenta Ghost");
  fauxmo.addDevice("Party Ghost");
  fauxmo.addDevice("Red Ghost");
  fauxmo.addDevice("White Ghost");
  fauxmo.addDevice("Yellow Ghost");
  fauxmo.onMessage(callback);
}

void loop() {
  
  fauxmo.handle();

  if (bShutdown){
    snooze();
    bShutdown = false;
  }
  
  
  if (bCycle) {
    if (delayDone(15)) {
      if (cycleStep >= MAX_STEPS)  cycleStep = 0;
      setFade(cycleStep++);
    }
  } else

  if (bParty) {
    if (delayDone(500)) {
      switch (partyStep) {
        case 0 : setColourRgb(255,0,0); break;
        case 1 : setColourRgb(255,255,0); break;
        case 2 : setColourRgb(0,255,0); break;
        case 3 : setColourRgb(0,255,255); break;
        case 4 : setColourRgb(0,0,255); break;
        default : setColourRgb(255,0,255); partyStep = -1; break;
      }
      partyStep++;
    }
  } else
    delay(100);

}

void setFade(int step){
  int rgb[3] = {0, 0, 0};
  const int index = step / RGB_MAX;
  const int _mod = step % RGB_MAX;
  const int next = (index + 1 < ARR_LEN) ? index + 1 : 0;

  for (int i = 0; i < 3; i++) {
    const int section = rgbRainbowMap[index][i];
    const int nextSection = rgbRainbowMap[next][i];
    if (section == nextSection)
      rgb[i] = section * RGB_MAX;
    else if (section > nextSection)
      rgb[i] = RGB_MAX - _mod;
    else
      rgb[i] = _mod;
  }
  
  setColourRgb(rgb[0], rgb[1], rgb[2]); 

}

bool delayDone(unsigned int ms) {
  unsigned long now;
  now = millis();
  if (lastMillis < now) {
    // millis looped!
    lastMillis = 0;
  }
  if (now >= (lastMillis + ms)) {
    lastMillis = now;
    return true;
  } else 
    return false;
}


void callback(uint8_t device_id, const char * device_name, bool state) 
{
  if (!state) {
    bShutdown = true;
  } else {
    lastMillis = millis();
    bCycle = false;
    bParty = false;
    if (strcmp(device_name, "Ghost") == 0) {
      bCycle = true;
    } else
    if (strcmp(device_name, "Party Ghost") == 0) {
      bParty = true;
    } else
    if (strcmp(device_name, "Red Ghost") == 0) {
      setColourRgb(255,0,0);
    } else
    if (strcmp(device_name, "Green Ghost") == 0) {
      setColourRgb(0,255,0);
    } else
    if (strcmp(device_name, "Blue Ghost") == 0) {
      setColourRgb(0,0,255);
    } else
    if (strcmp(device_name, "Cyan Ghost") == 0) {
      setColourRgb(0,255,255);
    } else
    if (strcmp(device_name, "Magenta Ghost") == 0) {
      setColourRgb(255,0,255);
    } else
    if (strcmp(device_name, "Yellow Ghost") == 0) {
      setColourRgb(255,255,0);
    } else
    if (strcmp(device_name, "White Ghost") == 0) {
      setColourRgb(255,255,255);
    } else
    if (strcmp(device_name, "Black Ghost") == 0) {
      setColourRgb(0,0,0);
    }
    digitalWrite(PIN_WHITE, HIGH);
  }
}

void snooze(){  // Do the blue/white pacman danger flash, then fade out.
 
  bCycle = false;
  bParty = false;
  
  digitalWrite(PIN_WHITE, true);  // Eyes ON
  for (int p=0; p < 5; p++){  // Flash blue and white
    setColourRgb(255,255,255);
    delay(500);
    setColourRgb(0,0,255);
    delay(500);
  }
  setColourRgb(0,0,0);  // Ghost off, just leave eyes lit
  delay(500);
  digitalWrite(PIN_WHITE, LOW);  // Blink
  delay(75);
  digitalWrite(PIN_WHITE, HIGH);
  delay(350);
  digitalWrite(PIN_WHITE, LOW);
  delay(75);
  digitalWrite(PIN_WHITE, HIGH);

  for (int i=255; i >= 0; i--){ // fade out eyes
    analogWrite(PIN_WHITE, i);
    delay(8);
  }
}


void setColourRgb(unsigned int red, unsigned int green, unsigned int blue) {
  analogWrite(PIN_RED, red);
  analogWrite(PIN_GREEN, green);
  analogWrite(PIN_BLUE, blue);
}

void wifiSetup() 
{
   WiFi.mode(WIFI_STA);
   WiFi.begin(WIFI_SSID, WIFI_PASS);
   while (WiFi.status() != WL_CONNECTED) 
   {
      digitalWrite(PIN_WHITE, LOW);
      delay(10);
      digitalWrite(PIN_WHITE, HIGH);
      delay(490);
   } 
}