Playing around with the DOOM logo

Rending the DOOM logo with an esp8266.
Table of Contents
The Source
For the fast ones, here is the source: https://github.com/pauls-3d-things/arduino-doom-fire-animation
Intro
This tutorial relies on having read my previous howto: Procedural Flame Animation with Arduino.
The core idea of this post is to convert an image to raw RGB data, save it as a header file to be included as an array of bytes of RGB pixel data, to be referenced when pushing these pixels to the screen.
Steps
Create an image with the right size first:
convert doom_image.png -scale 160x80 doom_image.rgb # result is 160x77
Then, create a header file doom_image.h
that contains the data.
xxd -i doom_image.rgb > src/doom_image.h
This, we can now include in our main.cpp
…
#include #include <doom_image.h>
… and update the draw function
// draw fire
tft.startWrite();
tft.setAddrWindow(0, 0, 160, 80);
for (uint8_t y = 0; y < 80; y++) {
for (uint8_t x = 0; x < 160; x++) {
if (firePixels[y][x]) { // draw fire
tft.writePixel(doomColorMap[firePixels[y][x]]);
} else if (y < 77) { // draw DOOM logo (it is only 77 pixels high)
uint16_t o = (y * 160 + x) * 3; // offset because of rgb data
tft.writePixel(rgb(doom_image[o], doom_image[o + 1], doom_image[o + 2]));
}
}
}
tft.endWrite();
Adding an MPU6050 Accelerometer
If you have an MPU6050 lying around, you can add this to the setup and play around with it.
Include the wire library, and add some variables:
#include <Wire.h>
#define MPUADDR 0x68
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;
Initialize the sensor:
void setup(void) {
Wire.begin();
Wire.beginTransmission(MPUADDR);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
Wire.endTransmission(true);
// [...]
}
Read sensor data in every loop, and filter it to avoid noise:
void loop(void) {
Wire.beginTransmission(MPUADDR);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPUADDR, 14); // request a total of 14 registers
// low pass filter to remove noise:
AcX = AcX * 0.95 + 0.05 * (Wire.read() << 8 | Wire.read()); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY = AcY * 0.95 + 0.05 * (Wire.read() << 8 | Wire.read()); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ = AcZ * 0.95 + 0.05 * (Wire.read() << 8 | Wire.read()); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
Tmp = Wire.read() << 8 | Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
GyX = Wire.read() << 8 | Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
GyY = Wire.read() << 8 | Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
GyZ = Wire.read() << 8 | Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
// [...]
}
Then, let the sensor values affect the flame calculation. The x
axis for speed, and y
axis for wind.
// recalculate fire
for (uint8_t y = 0; y < 79; y++) {
for (uint8_t x = 0; x < 160; x++) {
uint8_t wind = x + random(2) + (abs(AcY) / 15000); // here
wind = wind >= 160 ? wind - 160 : wind;
uint8_t speed = y + random(2) + (abs(AcX) / 15000); // here
speed = speed >= 80 ? 79 : speed;
firePixels[y][x] = firePixels[speed][wind] - random(3);
firePixels[y][x] = firePixels[y][x] > 35 ? 0 : firePixels[y][x]; // fix overflow
}
}