Skip to content

Instantly share code, notes, and snippets.

@bitbank2
Created March 16, 2021 00:15
Show Gist options
  • Save bitbank2/efe01d2b67867b5bca98f3a881b880ed to your computer and use it in GitHub Desktop.
Save bitbank2/efe01d2b67867b5bca98f3a881b880ed to your computer and use it in GitHub Desktop.
Demo sketch for Mike Rankin's ESP32 Color coincell PCB
#include <Adafruit_VL53L0X.h>
#include <bb_spi_lcd.h>
#include <WiFi.h>
#include <Wire.h>
#include <BitBang_I2C.h>
#include <BLEDevice.h>
#include <esp32_gamepad.h>
BLEScan *pBLEScan;
BLEScanResults foundDevices;
static uint8_t txBuffer[4096];
SS_GAMEPAD gp;
BBI2C i2c;
uint8_t devAddr[6];
volatile bool bChanged, bConnected;
SPILCD lcd;
//#define TRY_LOGIN
#ifdef __cplusplus
extern "C" {
#endif
uint8_t temprature_sens_read();
#ifdef __cplusplus
}
#endif
uint8_t temprature_sens_read();
Adafruit_VL53L0X vlx = Adafruit_VL53L0X();
const char* ssid = "MYROUTER";
const char* password = "MYPASSWORD";
const char *szNames[] = {"Unknown","SSD1306","SH1106","VL53L0X","BMP180", "BMP280","BME280",
"MPU-60x0", "MPU-9250", "MCP9808","LSM6DS3", "ADXL345", "ADS1115","MAX44009",
"MAG3110", "CCS811", "HTS221", "LPS25H", "LSM9DS1","LM8330", "DS3231", "LIS3DH",
"LIS3DSH","INA219","SHT3X","HDC1080"};
static uint8_t imu_addr, temp_addr;
// Touch Pad1 = GPIO27, T7
// Touch Pad2 = GPIO12, T5
// Touch Pad3 = GPIO15, T3
// Touch Pad4 = GPIO2 , T2
#define BUTTON_THRESHOLD 98
uint8_t iButtons[4] = {T7,T5,T3,T2};
// Display size
#define WIDTH 160
#define HEIGHT 80
// 40x40 pattern bitmap
// Classic Mac bomb icon
uint8_t ucBombMask[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x04,0x21,0x00,0x00,
0x00,0x00,0x00,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x00,
0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x38,0xa8,0x00,
0x00,0x00,0x00,0x01,0x01,0xc0,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x80,0x00,
0x00,0x00,0x00,0x01,0x00,0x24,0x40,0x00,0x00,0x00,0x00,0x0f,0xe0,0x40,0x20,0x00,
0x00,0x00,0x00,0x0f,0xe0,0x84,0x10,0x00,0x00,0x00,0x00,0x0f,0xe0,0x00,0x00,0x00,
0x00,0x00,0x00,0x3f,0xf8,0x04,0x00,0x00,0x00,0x00,0x00,0xff,0xfe,0x00,0x00,0x00,
0x00,0x00,0x00,0xff,0xfe,0x00,0x00,0x00,0x00,0x00,0x01,0xff,0xff,0x00,0x00,0x00,
0x00,0x00,0x01,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x03,0xdf,0xff,0x80,0x00,0x00,
0x00,0x00,0x03,0xff,0xff,0x80,0x00,0x00,0x00,0x00,0x03,0xbf,0xff,0x80,0x00,0x00,
0x00,0x00,0x03,0xbf,0xff,0x80,0x00,0x00,0x00,0x00,0x03,0xbf,0xff,0x80,0x00,0x00,
0x00,0x00,0x03,0xff,0xff,0x80,0x00,0x00,0x00,0x00,0x03,0xdf,0xff,0x80,0x00,0x00,
0x00,0x00,0x01,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x01,0xf7,0xff,0x00,0x00,0x00,
0x00,0x00,0x00,0xff,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xfe,0x00,0x00,0x00,
0x00,0x00,0x00,0x3f,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xe0,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00};
void SS_Callback(int iEvent, SS_GAMEPAD *pGamepad)
{
if (iEvent == EVENT_DISCONNECT)
bConnected = false;
else if (iEvent == EVENT_CONNECT)
bConnected = true;
else
{
bChanged = 1;
memcpy(&gp, pGamepad, sizeof(SS_GAMEPAD));
}
} /* SS_Callback() */
void LIS3DHInit(byte bAddr)
{
uint8_t uc[4];
imu_addr = bAddr;
uc[0] = 0x20; // CTRL_REG1
uc[1] = 0x77; // Turn on the sensor with ODR = 400Hz normal mode.
I2CWrite(&i2c, imu_addr, uc, 2);
// High res & BDU enabled
uc[0] = 0x23; // CTRL_REG4
uc[1] = 0x88;
I2CWrite(&i2c, imu_addr, uc, 2);
// DRDY on INT1
uc[0] = 0x22; // CTRL_REG3
uc[1] = 0x10;
I2CWrite(&i2c, imu_addr, uc, 2);
// enable adcs
uc[0] = 0x1f; // TEMP_CFG_REG
uc[1] = 0x80;
I2CWrite(&i2c, imu_addr, uc, 2);
return;
uc[0] = 0x21; // CTRL_REG2
uc[1] = 0x01; // High-pass filter (HPF) enabled with 0.2Hz cut-off frequency for INT1 (AOI1) interrupt generation only.
I2CWrite(&i2c, imu_addr, uc, 2);
uc[0] = 0x22; // CTRL_REG3
uc[1] = 0x40; // ACC AOI1 interrupt signal is routed to INT1 pin.
I2CWrite(&i2c, imu_addr, uc, 2);
uc[0] = 0x23; // CTRL_REG4
uc[1] = 0x88; // Full Scale = +/-2 g with BDU and HR bits enabled.
I2CWrite(&i2c, imu_addr, uc, 2);
uc[0] = 0x24; // CTRL_REG5
// uc[1] = 0x00; // INT1 pin is not latched, no need to read INT1_SRC to clear the int
uc[1] = 0x08; // INT1 pin is latched; need to read the INT1_SRC register to clear the interrupt signal.
I2CWrite(&i2c, imu_addr, uc, 2);
// configurations for wakeup and motionless detection
uc[0] = 0x32; // INT1_THS
uc[1] = 0x02; // Threshold (THS) = 2LSBs * 15.625mg/LSB = 31.25mg.
I2CWrite(&i2c, imu_addr, uc, 2);
uc[0] = 0x33; // INT1_DURATION
uc[1] = 0x01; // Duration = 1LSBs * (1/10Hz) = 0.1s
I2CWrite(&i2c, imu_addr, uc, 2);
uc[0] = 0x30; // INT1_CFG
uc[1] = 0x15; // Enable XLIE, YLIE and ZLIE (low events) interrupt generation
// uc[1] = 0xaa; // Enable ZHIE, YHIE, XHIE (high events) interrupt generation
I2CWrite(&i2c, imu_addr, uc, 2);
} /* LIS3DHInit() */
void LIS3DHReadAccel(int16_t *X, int16_t *Y, int16_t *Z)
{
int i;
uint8_t ucTemp[8];
i = I2CReadRegister(&i2c, imu_addr, 0xa8, ucTemp, 6);
if (i > 0)
{
*X = (ucTemp[1] << 8) + ucTemp[0];
*Y = (ucTemp[3] << 8) + ucTemp[2];
*Z = (ucTemp[5] << 8) + ucTemp[4];
}
} /* LIST3DHReadAccel() */
void TryConnect(void)
{
int counter = 0;
spilcdFill(&lcd, 0,DRAW_TO_LCD);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED && WiFi.status() != WL_CONNECT_FAILED) //if not connected to wifi
{
spilcdWriteString(&lcd, 0,0,(char *)"Connecting to wifi", 0xffff,0, FONT_8x8, DRAW_TO_LCD);
spilcdWriteString(&lcd, counter*8,8,(char *)".", 0xffff, 0, FONT_8x8, DRAW_TO_LCD);
delay(1500);
counter++;
if (counter > 20)
{
spilcdWriteString(&lcd, 0,16,(char *)"Failed", 0xf800, 0, FONT_8x8, DRAW_TO_LCD);
break;
}
if (WiFi.status() == WL_CONNECTED) //if it connects to wifi
{
spilcdWriteString(&lcd, 0,16,(char *)"Connected!", 0x6e0,0,FONT_8x8, DRAW_TO_LCD);
IPAddress myIP = WiFi.softAPIP();
// display.println(myIP);
// display.print("RSSI:");
// display.print(WiFi.RSSI());
delay(1000);
}
} // while
} /* TryConnect() */
#define SDA_PIN 13
#define SCL_PIN 14
void I2CDetect() {
uint8_t map[16];
char szTemp[32];
uint8_t i;
int iDevice, iCount;
spilcdFill(&lcd, 0, DRAW_TO_LCD);
iCount = 0;
spilcdWriteString(&lcd, 0,0,(char *)"Starting I2C Scan...", 0xffff,0,FONT_8x8, DRAW_TO_LCD);
I2CScan(&i2c, map); // get bitmap of connected I2C devices
if (map[0] == 0xfe) // something is wrong with the I2C bus
{
spilcdWriteString(&lcd, 0,8,(char *)"I2C pins are not correct", 0xf800,0,FONT_8x8,DRAW_TO_LCD);
spilcdWriteString(&lcd, 0,16,(char *)"or bad device; scan failed", 0xf800,0,FONT_8x8,DRAW_TO_LCD);
}
else
{
for (i=1; i<128; i++) // skip address 0 (general call address) since more than 1 device can respond
{
if (map[i>>3] & (1 << (i & 7))) // device found
{
iCount++;
iDevice = I2CDiscoverDevice(&i2c, i);
sprintf(szTemp, "Device at 0x%x: %s", i, szNames[iDevice]);
spilcdWriteString(&lcd, 0,iCount*8,szTemp, 0xf81f,0,FONT_6x8,DRAW_TO_LCD);
}
} // for i
sprintf(szTemp, "%d device(s) found", iCount);
spilcdWriteString(&lcd, 0,(iCount+1)*8,szTemp,0x6e0,0,FONT_8x8,DRAW_TO_LCD);
}
spilcdWriteString(&lcd, 0,72,(char *)"Press any button to exit", 0xffe0,0,FONT_6x8, DRAW_TO_LCD);
while (GetButtons() != 0) // wait until any pressed are released
{ };
while (GetButtons() == 0) // wait until any are pressed
{ };
} /* I2CDetect() */
int GetButtons(void)
{
static int iOldState;
int iState, iPressed;
int i, j;
int iCounts[4] = {0};
iState = 0;
if (bConnected) // use the gamepad
{
if (gp.u16Buttons & 0x20) // down
iState |= 1;
if (gp.u16Buttons & 0x10) // up
iState |= 2;
if (gp.u16Buttons & 4) // (1) button
iState |= 4;
if (gp.u16Buttons & 8) // (2) button
iState |= 8;
}
else // use the touch buttons
{
// remove the 'flicker' of the buttons
for (j=0; j<5; j++)
{
for (i=0; i<4; i++)
{
if (touchRead(iButtons[i]) < BUTTON_THRESHOLD)
iCounts[i]++;
}
delay(4);
}
for (i=0; i<4; i++)
if (iCounts[i] == 5)
iState |= (1<<i);
}
// Test button bits for which ones changed from LOW to HIGH
iPressed = (iState ^ iOldState) & iState; // tells us which ones just changed to 1
iOldState = iState;
iPressed = iPressed | (iState << 8); // prepare combined state
return iPressed;
} /* GetButtons() */
void ButtonTest(void)
{
int iCount = 0;
int iButts;
int i;
uint16_t usColor;
char szTemp[16];
spilcdFill(&lcd, 0,DRAW_TO_LCD);
spilcdWriteString(&lcd, 36,0,(char *)"Button Test", 0xffff,0,FONT_8x8, DRAW_TO_LCD);
spilcdWriteString(&lcd, 8,72,(char *)"Press 2 buttons to exit", 0xffff,0,FONT_6x8, DRAW_TO_LCD);
while (iCount < 2)
{
iButts = GetButtons() >> 8; // get the currently pressed bits
iCount = 0;
for (i=0; i<4; i++)
{
if (iButts & 1)
{
usColor = 0xf800; // red for pressed
iCount++;
}
else
{
usColor = 0x6e0; // green for not pressed
}
sprintf(szTemp, "T%d", i+1);
spilcdWriteString(&lcd, i*40,24,szTemp,usColor,0,FONT_16x32,DRAW_TO_LCD);
iButts >>= 1;
} // for i
}
} /* ButtonTest() */
void DrawMainMenu(int iMenuItem)
{
static int i = 0;
uint16_t iFG, iBG;
char *pGP;
uint8_t u8Temp[8*40]; // holds the rotated mask
static int x[4], y[4], dx[4], dy[4];
int j;
uint16_t pal[4] = {0xffe0,0xf81f,0x1f,0xffff};
if (i == 0) // first time through
{
for (j=0; j<4; j++)
{
x[j] = random(WIDTH-40);
y[j] = random(HEIGHT-40);
dx[j] = (j & 1) ? -1:1;
dy[j] = (j & 1) ? -1:1;
}
}
iFG = 0xffff; iBG = 0x6e0;
spilcdRectangle(&lcd, 0, 0, WIDTH, HEIGHT, 0x0000, 0xf800, 1, DRAW_TO_RAM);
spilcdWriteString(&lcd, 44,0,(char *)"Main Menu", 0x6ff,-1,FONT_8x8, DRAW_TO_RAM);
spilcdWriteString(&lcd, 0,16,(char *)"I2C Scan", (iMenuItem == 0) ? iFG:iBG,-1,FONT_8x8, DRAW_TO_RAM);
spilcdWriteString(&lcd, 0,24,(char *)"WiFi Scan", (iMenuItem == 1) ? iFG:iBG,-1,FONT_8x8, DRAW_TO_RAM);
spilcdWriteString(&lcd, 0,32,(char *)"BLE Scan", (iMenuItem == 2) ? iFG:iBG,-1,FONT_8x8, DRAW_TO_RAM);
spilcdWriteString(&lcd, 0,40,(char *)"Button Test", (iMenuItem == 3) ? iFG:iBG,-1,FONT_8x8, DRAW_TO_RAM);
spilcdWriteString(&lcd, 0,48,(char *)"Proximity Test", (iMenuItem == 4) ? iFG:iBG,-1,FONT_8x8, DRAW_TO_RAM);
spilcdWriteString(&lcd, 0,56,(char *)"IMU Test", (iMenuItem == 5) ? iFG:iBG,-1,FONT_8x8, DRAW_TO_RAM);
spilcdWriteString(&lcd, 0,64,(char *)"Temp/Humidity Test", (iMenuItem == 6) ? iFG:iBG,-1,FONT_8x8, DRAW_TO_RAM);
if (bConnected)
pGP = (char *)"Gamepad: disconnect";
else
pGP = (char *)"Gamepad: connect";
spilcdWriteString(&lcd, 0,72,pGP, (iMenuItem == 7) ? iFG:iBG,-1,FONT_8x8, DRAW_TO_RAM);
spilcdRotateBitmap(ucBombMask, u8Temp, 1, 40, 40, 8, 20, 20, i % 360);
for (j=0; j<4; j++)
{
spilcdDrawPattern(&lcd, u8Temp, 8, x[j], y[j],40,40,pal[j],16);
x[j] += dx[j]; y[j] += dy[j];
if (x[j] <= 0 || x[j] >= WIDTH-40)
dx[j] = -dx[j];
if (y[j] <= 0 || y[j] >= HEIGHT-40)
dy[j] = -dy[j];
}
spilcdShowBuffer(&lcd, 0,0,WIDTH,HEIGHT, DRAW_TO_LCD);
i++;
} /* DrawMainMenu() */
// Number of grains of sand - this is how many pixels in the first line of text
#define N_GRAINS 720
// The 'sand' grains exist in an integer coordinate space that's 256X
// the scale of the pixel grid, allowing them to move and interact at
// less than whole-pixel increments.
#define MAX_X (WIDTH * 256 - 1) // Maximum X coordinate in grain space
#define MAX_Y (HEIGHT * 256 - 1) // Maximum Y coordinate
struct Grain {
uint16_t x, y; // Position
int16_t vx, vy; // Velocity
uint16_t color; // pixel color
} grain[N_GRAINS];
void ResetGrains(int bRandom)
{
int i, j, x, y;
uint16_t *pBitmap = spilcdGetBuffer(&lcd);
uint16_t color, Pal[] = {0xf800,0xffff,0xffe0,0xf81f,0x1f,0x6e0,0x6ff,0xaaaa};
spilcdFill(&lcd, 0, DRAW_TO_LCD | DRAW_TO_RAM);
if (bRandom)
{
for(i=0; i<N_GRAINS; i++) { // For each sand grain...
do {
grain[i].x = random(WIDTH * 256); // Assign random position within
grain[i].y = random(HEIGHT * 256); // the 'grain' coordinate space
// Check if corresponding pixel position is already occupied...
for(j=0; (j<i) && (((grain[i].x / 256) != (grain[j].x / 256)) ||
((grain[i].y / 256) != (grain[j].y / 256))); j++);
} while(j < i); // Keep retrying until a clear spot is found
x = grain[i].x / 256; y = grain[i].y / 256;
// because the display is rotated 90
grain[i].vx = grain[i].vy = 0; // Initial velocity is zero
grain[i].color = Pal[random(7)];
pBitmap[(x*HEIGHT) + (HEIGHT-1-y)] = grain[i].color; // Mark it
}
} // random
else
{
spilcdWriteString(&lcd, 40,28,(char *)"C",0xf800,0,FONT_16x32,DRAW_TO_LCD | DRAW_TO_RAM);
spilcdWriteString(&lcd, 56,28,(char *)"O",0x6e0,0,FONT_16x32,DRAW_TO_LCD | DRAW_TO_RAM);
spilcdWriteString(&lcd, 72,28,(char *)"L",0x1f,0,FONT_16x32,DRAW_TO_LCD | DRAW_TO_RAM);
spilcdWriteString(&lcd, 88,28,(char *)"O",0xf81f,0,FONT_16x32,DRAW_TO_LCD | DRAW_TO_RAM);
spilcdWriteString(&lcd, 104,28,(char *)"R",0xffe0,0,FONT_16x32,DRAW_TO_LCD | DRAW_TO_RAM);
i = 0;
for (y=0; y<HEIGHT; y++)
{
for (x=0; x<WIDTH; x++)
{
color = pBitmap[(y*WIDTH)+x];
if (color != 0) // pixel set?
{
grain[i].x = x*256; grain[i].y = y*256;
grain[i].vx = grain[i].vy = 0; // Initial velocity is zero
grain[i].color = color;
i++;
if (i == N_GRAINS) return;
}
} // for x
} // for y
// Serial.println(i, DEC);
}
} /* ResetGrains() */
void IMUTest(void)
{
int16_t x, y, z;
int32_t v2; // Velocity squared
int16_t ax, ay, az;
//signed int oldidx, newidx;
signed int newx, newy;
signed int x1, y1, x2, y2;
int i;
uint16_t *pBitmap = spilcdGetBuffer(&lcd);
uint16_t u16Flags[5]; // divide the display into 16x16 blocks for quicker refresh
int iFrame = 0;
ResetGrains(0);
delay(2000);
memset(u16Flags,0,sizeof(u16Flags));
while (1)
{
iFrame++;
if ((iFrame & 7) == 0)
{
if (GetButtons())
return;
}
if (iFrame & 1) // update display if we didn't check the buttons
{
spilcdShowBuffer(&lcd, 0,0,WIDTH,HEIGHT, DRAW_TO_LCD);
}
if (bConnected) // use the left analog stick to simulate gravity
{
ax = gp.iLJoyX / 4;
ay = gp.iLJoyY / 4;
}
else // use the accelerometer
{
LIS3DHReadAccel(&x, &y, &z);
ax = x / 512; // Transform accelerometer axes
ay = -y / 512; // to grain coordinate space
az = abs(z) / 2048; // Random motion factor
az = (az >= 3) ? 1 : 4 - az; // Clip & invert
ax -= az; // Subtract motion factor from X, Y
ay -= az;
}
// Apply 2D accelerometer vector to grain velocities...
//
// Theory of operation:
// if the 2D vector of the new velocity is too big (sqrt is > 256), this means it might jump
// over pixels. We want to limit the velocity to 1 pixel as a maximum.
// To avoid using floating point math (sqrt + 2 multiplies + 2 divides)
// Instead of normalizing the velocity to keep the same direction, we can trim the new
// velocity to 5/8 of it's value. This is a reasonable approximation since the maximum
// velocity impulse from the accelerometer is +/-64 (16384 / 256) and it gets added every frame
//
for(i=0; i<N_GRAINS; i++) {
grain[i].vx += ax;// + random(5); // Add a little random impulse to each grain
grain[i].vy += ay;// + random(5);
v2 = (int32_t)(grain[i].vx*grain[i].vx) + (int32_t)(grain[i].vy*grain[i].vy);
if (v2 >= 65536) // too big, trim it
{
grain[i].vx = (grain[i].vx * 5)/8; // quick and dirty way to avoid doing a 'real' divide
grain[i].vy = (grain[i].vy * 5)/8;
}
} // for i
// Update the position of each grain, one at a time, checking for
// collisions and having them react. This really seems like it shouldn't
// work, as only one grain is considered at a time while the rest are
// regarded as stationary. Yet this naive algorithm, taking many not-
// technically-quite-correct steps, and repeated quickly enough,
// visually integrates into something that somewhat resembles physics.
// (I'd initially tried implementing this as a bunch of concurrent and
// "realistic" elastic collisions among circular grains, but the
// calculations and volument of code quickly got out of hand for both
// the tiny 8-bit AVR microcontroller and my tiny dinosaur brain.)
//
// (x,y) to bytes mapping:
// The SSD1306 has 8 rows of 128 bytes with the LSB of each byte at the top
// In other words, bytes are oriented vertically with bit 0 as the top pixel
// Part of my optimizations were writing the pixels into memory the same way they'll be
// written to the display. This means calculating an offset and bit to test/set each pixel
//
for(i=0; i<N_GRAINS; i++) {
newx = grain[i].x + grain[i].vx; // New position in grain space
newy = grain[i].y + grain[i].vy;
if(newx > MAX_X) { // If grain would go out of bounds
newx = MAX_X; // keep it inside, and
grain[i].vx /= -2; // give a slight bounce off the wall
} else if(newx < 0) {
newx = 0;
grain[i].vx /= -2;
}
if(newy > MAX_Y) {
newy = MAX_Y;
grain[i].vy /= -2;
} else if(newy < 0) {
newy = 0;
grain[i].vy /= -2;
}
x1 = grain[i].x / 256; y1 = grain[i].y / 256; // old position
x2 = newx / 256; y2 = newy / 256;
if((x1 != x2 || y1 != y2) && // If grain is moving to a new pixel...
(pBitmap[(y2*WIDTH)+x2] != 0)) { // but if that pixel is already occupied...
// Try skidding along just one axis of motion if possible (start w/faster axis)
if(abs(grain[i].vx) > abs(grain[i].vy)) { // X axis is faster
y2 = grain[i].y / 256;
if(pBitmap[(y2*WIDTH)+x2] == 0) { // That pixel's free! Take it! But...
newy = grain[i].y; // Cancel Y motion
grain[i].vy = (grain[i].vy /-2) + random(8); // and bounce Y velocity
} else { // X pixel is taken, so try Y...
y2 = newy / 256; x2 = grain[i].x / 256;
if(pBitmap[(y2*WIDTH)+x2] == 0) { // Pixel is free, take it, but first...
newx = grain[i].x; // Cancel X motion
grain[i].vx = (grain[i].vx /-2) + random(8); // and bounce X velocity
} else { // Both spots are occupied
newx = grain[i].x; // Cancel X & Y motion
newy = grain[i].y;
grain[i].vx = (grain[i].vx /-2) + random(8); // Bounce X & Y velocity
grain[i].vy = (grain[i].vy /-2) + random(8);
}
}
} else { // Y axis is faster
y2 = newy / 256; x2 = grain[i].x / 256;
if(pBitmap[(y2*WIDTH)+x2] == 0) { // Pixel's free! Take it! But...
newx = grain[i].x; // Cancel X motion
grain[i].vx = (grain[i].vx /-2) + random(8); // and bounce X velocity
} else { // Y pixel is taken, so try X...
y2 = grain[i].y / 256; x2 = newx / 256;
if(pBitmap[(y2*WIDTH)+x2] == 0) { // Pixel is free, take it, but first...
newy = grain[i].y; // Cancel Y motion
grain[i].vy = (grain[i].vy /-2) + random(8); // and bounce Y velocity
} else { // Both spots are occupied
newx = grain[i].x; // Cancel X & Y motion
newy = grain[i].y;
grain[i].vx = (grain[i].vx /-2) + random(8); // Bounce X & Y velocity
grain[i].vy = (grain[i].vy /-2) + random(8);
}
}
}
}
grain[i].x = newx; // Update grain position
grain[i].y = newy; // possibly only a fractional change
y2 = newy / 256; x2 = newx / 256;
if (x1 != x2 || y1 != y2)
{
pBitmap[(y1*WIDTH)+x1] = 0; // erase old pixel
pBitmap[(y2*WIDTH)+x2] = grain[i].color; // Set new pixel
}
} // for i
} // while (1)
} /* IMUTest() */
//
// Temperature and humidity test
//
void TempTest(void)
{
char szTemp[32];
uint8_t ucTemp[4];
uint32_t T, H;
temp_addr = 0x40;
spilcdFill(&lcd, 0,DRAW_TO_LCD);
spilcdWriteString(&lcd, 0,72,(char *)"Press any button to exit", 0xffe0,0,FONT_6x8, DRAW_TO_LCD);
// configure it
ucTemp[0] = 0x02; // config register
ucTemp[1] = 0x10; // temp+humidity, 14-bit resolution
ucTemp[2] = 0x00;
I2CWrite(&i2c, temp_addr, ucTemp, 3);
delay(100);
while (GetButtons() == 0)
{
ucTemp[0] = 0x00;
I2CWrite(&i2c, temp_addr, ucTemp, 1); // read temp register
delay(15); // wait for conversion we just triggered
I2CRead(&i2c, temp_addr, ucTemp, 4); // read temp+humidity
T = (ucTemp[0]<<8) + ucTemp[1];
H = (ucTemp[2]*256) + ucTemp[3];
// convert to percent
H = (H * 100) >> 16;
T = ((T * 1650) >> 16) - 400; // 10x temp
T -= 84; // adjustment that seems to give a more accurate result
sprintf(szTemp,"Humidity: %d%%", H); // display humidity value
spilcdWriteString(&lcd, 0,0,szTemp,0xffff,0,FONT_8x8,DRAW_TO_LCD);
sprintf(szTemp,"Air Temp = %d.%dC", (int)(T/10), (int)(T % 10)); // display temperature value
spilcdWriteString(&lcd, 0,8,szTemp,0xffff,0,FONT_8x8, DRAW_TO_LCD);
T = temprature_sens_read();
if (T == 128) // invalid, must not be present
spilcdWriteString(&lcd, 0,16,(char *)"CPU Temp = N/A", 0xffff,0,FONT_8x8,DRAW_TO_LCD);
else
{
sprintf(szTemp,"CPU Temp = %dF", T); // display CPU temperature
spilcdWriteString(&lcd, 0,16,szTemp,0xffff,0,FONT_8x8, DRAW_TO_LCD);
}
}
} /* TempTest() */
void TOFTest()
{
char szTemp[32];
VL53L0X_RangingMeasurementData_t measure;
int iFrame = 0;
int16_t butts = 0;
spilcdFill(&lcd, 0,DRAW_TO_LCD); // fill to black
spilcdWriteString(&lcd, 0,72,(char *)"Press any button to exit", 0xffe0,0,FONT_6x8, DRAW_TO_LCD);
while (1)
{
iFrame++;
if ((iFrame & 7) == 0) // check buttons every once in a while
butts = GetButtons();
if ((butts & 0xff) != 0) return; // exit at first button press
vlx.rangingTest(&measure, false); // pass in 'true' to get debug data printout!
sprintf(szTemp, "Dist = %03dmm", measure.RangeMilliMeter);
spilcdWriteString(&lcd, 0,0,szTemp, 0xf81f, 0, FONT_16x32, DRAW_TO_LCD);
}
} /* TOFTest() */
#define MAX_APS 8
void WiFiScan()
{
String ssidList[MAX_APS]; // top SSIDs found in the area
uint8_t ssidEncrypt[MAX_APS];
int ssidRSSI[MAX_APS];
int i, n;
char szTemp[64];
spilcdFill(&lcd, 0, DRAW_TO_LCD);
spilcdWriteString(&lcd, 0,0,(char *)"Scanning Wifi...", 0xffff,0,FONT_8x8, DRAW_TO_LCD);
// Find nearby networks (up to 9)
WiFi.mode(WIFI_STA);
WiFi.disconnect(); // make sure we're not connected to a network
delay(100);
// workaround for a bug which doesn't power wifi unless we try to connect to something
// WiFi.begin();
// WiFi.scanNetworks will return the number of networks found
// WiFi.scanDelete(); // delete the last scan result from memory
n = WiFi.scanNetworks();
sprintf(szTemp,"Found: %d", n);
if (n > MAX_APS)
strcat(szTemp, (char *)" (showing top 8)");
if (n > MAX_APS) n = MAX_APS; // we only care about the N strongest
spilcdFill(&lcd, 0,DRAW_TO_LCD);
spilcdWriteString(&lcd, 0,0,szTemp, 0xffff,0, FONT_8x8, DRAW_TO_LCD);
for (i = 0; i < n; ++i)
{
ssidList[i] = WiFi.SSID(i);
ssidEncrypt[i] = (WiFi.encryptionType(i) != WIFI_AUTH_OPEN);
ssidRSSI[i] = WiFi.RSSI(i);
sprintf(szTemp, "%s, %c, %ddBm", ssidList[i].c_str(), (ssidEncrypt[i]) ? '*':' ',ssidRSSI[i]);
spilcdWriteString(&lcd, 0,8+(i*8), szTemp, 0x6e0,0,FONT_6x8, DRAW_TO_LCD);
} // for each ssid found
spilcdWriteString(&lcd, 0,72,(char *)"Press any button to exit", 0xffe0,0,FONT_6x8, DRAW_TO_LCD);
while (GetButtons() == 0)
{
delay(10);
}
} /* WiFiScan() */
#define MAX_BLE_DEVICES 10
static char Scanned_BLE_Name[MAX_BLE_DEVICES][32];
static char Scanned_BLE_Address[MAX_BLE_DEVICES][32];
static int iBLECount;
static uint32_t BLEAddrMatch[64]; // up to 64 unqiue ones
// Called for each device found during a BLE scan by the client
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks
{
void onResult(BLEAdvertisedDevice advertisedDevice) {
uint32_t cksum = 0;
// Serial.printf("Scan Result: %s \n", advertisedDevice.toString().c_str());
// if (strcmp(Band_BLE_Name.c_str(), advertisedDevice.getName().c_str()) == 0) { // this is what we want
// see if we already found this one
const char *szAddr = advertisedDevice.getAddress().toString().c_str();
int i, j, iLen = strlen(szAddr);
for (j=0; j<iLen; j++)
cksum += ((uint32_t)szAddr[j] << j);
for (i=0; i<iBLECount; i++) {
if (BLEAddrMatch[i] == cksum)
return; // we already have this one
}
BLEAddrMatch[iBLECount] = cksum; // store this as a new pattern to match
if (iBLECount < MAX_BLE_DEVICES) {
strcpy(Scanned_BLE_Address[iBLECount], szAddr);
strcpy(Scanned_BLE_Name[iBLECount], advertisedDevice.getName().c_str());
}
iBLECount++;
// }
}
};
void BLEScan()
{
char szTemp[64];
int i, j;
iBLECount = 0;
memset(BLEAddrMatch, 0, sizeof(BLEAddrMatch));
spilcdFill(&lcd, 0, DRAW_TO_LCD);
spilcdWriteString(&lcd, 0,0,(char *)"Scanning for BLE... ", 0xffff,0,FONT_8x8, DRAW_TO_LCD);
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
foundDevices = pBLEScan->start(10); // scan for 10 seconds
pBLEScan->stop(); // stop scanning
sprintf(szTemp, "%d device(s) found ", iBLECount);
spilcdWriteString(&lcd, 0,0,szTemp, 0xffff,0,FONT_8x8, DRAW_TO_LCD);
j = (iBLECount > 8) ? 8 : iBLECount;
for (i=0; i<j; i++)
{
sprintf(szTemp, "%s,%s", Scanned_BLE_Address[i], Scanned_BLE_Name[i]);
spilcdWriteString(&lcd, 0,(i+1)*8,szTemp, 0x6e0,0,FONT_6x8, DRAW_TO_LCD);
}
spilcdWriteString(&lcd, 0,72,(char *)"Press any button to exit", 0xffe0,0,FONT_6x8, DRAW_TO_LCD);
while (GetButtons() != 0) // wait until any pressed are released
{ };
while (GetButtons() == 0)
{ };
} /* BLEScan() */
// Connect or disconnect the gamepad
void Gamepad(void)
{
if (bConnected) // disconnect
{
//SS_Disconnect();
}
else // connect it
{
spilcdFill(&lcd, 0,DRAW_TO_LCD);
spilcdWriteString(&lcd, 0,0,(char *)"Starting discovery...", 0xffff, 0, FONT_8x8, DRAW_TO_LCD);
if (SS_Scan(6, devAddr))
{
spilcdWriteString(&lcd, 0,16,(char *)"SS found!", 0x6e0, 0, FONT_16x16, DRAW_TO_LCD);
if (SS_Connect(devAddr))
{
spilcdWriteString(&lcd, 0,32,(char *)"Connected!", 0x6e0, 0, FONT_16x16, DRAW_TO_LCD);
}
else
spilcdWriteString(&lcd, 0,32,(char *)"Not Connected", 0xf800, 0, FONT_16x16, DRAW_TO_LCD);
}
else
{
spilcdWriteString(&lcd, 0,16,(char *)"Not found", 0xf800, 0, FONT_16x16, DRAW_TO_LCD);
}
delay(2000);
}
} /* Gamepad() */
void MainMenu(void)
{
int iButts, iMenuItem = 0;
int iFrame = 0;
iButts = 0;
while (1)
{
DrawMainMenu(iMenuItem);
if (iFrame & 1)
iButts = GetButtons();
if (iButts & 1 && iMenuItem < 7) // first button = down
{
iMenuItem++;
}
else if (iButts & 2 && iMenuItem > 0) // second button = up
{
iMenuItem--;
}
else if (iButts & 4) // action
{
switch (iMenuItem)
{
case 0:
I2CDetect();
break;
case 1:
WiFiScan();
break;
case 2:
BLEScan();
break;
case 3:
ButtonTest();
break;
case 4:
TOFTest();
break;
case 5:
IMUTest();
break;
case 6:
TempTest();
break;
case 7:
Gamepad();
break;
}
}
iButts = 0; // don't let it double press on even frames
iFrame++;
}
} /* MainMenu() */
void gotTouch1()
{
// iButtons[0]++;
}
void gotTouch2()
{
// iButtons[1]++;
}
void gotTouch3()
{
// iButtons[2]++;
}
void gotTouch4()
{
// iButtons[3]++;
}
void setup() {
Serial.begin(9600);
i2c.iSDA = -1;
i2c.iSCL = -1;
i2c.bWire = 1;
I2CInit(&i2c, 400000L);
Wire.begin(SDA_PIN, SCL_PIN);
delay(80); // need this small delay or the vlx.begin will hang
SS_Init();
SS_RegisterCallback(SS_Callback);
bConnected = false;
LIS3DHInit(0x19);
vlx.begin();
//spilcdSetTXBuffer(txBuffer, 4096); //XXXXXXX Causes IMU Lockup
spilcdInit(&lcd, LCD_ST7735S_B, FLAGS_SWAP_RB | FLAGS_INVERT, 32000000, 4, 21, 22, 26, -1, 23, 18); // Mike's coin cell pin numbering
spilcdSetOrientation(&lcd, LCD_ORIENTATION_90);
spilcdAllocBackbuffer(&lcd);
// touchAttachInterrupt(T7, gotTouch1, threshold);
// touchAttachInterrupt(T5, gotTouch2, threshold);
// touchAttachInterrupt(T3, gotTouch3, threshold);
// touchAttachInterrupt(T2, gotTouch4, threshold);
} /* setup() */
void loop() {
// put your main code here, to run repeatedly:
int i, j, x, y;
#define WIDTH 160
#define HEIGHT 80
MainMenu();
I2CDetect();
//TryConnect();
while (1) {};
static int iColorOffset = 0;
static int iColorDelta = 1;
static int yLogo = 0;
static int yDelta = 1;
int dx, dy;
x = y = 0;
dx = dy = 1;
i = 0; // frame
while (1)
{
uint16_t r, g, b, usColor1, usColor2;
// color offset goes between 0 and 0x20
// use to transition between 2 starting and 2 ending colors for the gradient
r = iColorOffset;
g = 1-((1*iColorOffset)>>5);
b = 0x1f - iColorOffset;
usColor1 = (r<<11) | (g<<5) | b; // start
r = 0x1f - iColorOffset;
g = 0x32 - (((0x17)*iColorOffset)>>5);
b = iColorOffset;
usColor2 = (r<<11) | (g<<5) | b; // end
iColorOffset += iColorDelta; // increase green
if (iColorOffset < 0)
{
iColorOffset = 0;
iColorDelta = -iColorDelta;
}
if (iColorOffset >= 0x1f)
{
iColorOffset = 0x1f;
iColorDelta = -iColorDelta;
}
yLogo += yDelta;
if (yLogo == 0 || yLogo == 155)
yDelta = -yDelta;
spilcdRectangle(&lcd, 0, 0, WIDTH, HEIGHT, usColor1, usColor2, 1, DRAW_TO_RAM);
spilcdWriteString(&lcd, x,y,(char *)"Mike's Magical", 0xffff,-1,FONT_8x8, DRAW_TO_RAM);
spilcdWriteString(&lcd, x,y+8,(char *)"Color CoinCell!", 0xffff,-1,FONT_8x8, DRAW_TO_RAM);
//void spilcdDrawPattern(uint8_t *pPattern, int iSrcPitch, int iDestX, int iDestY, int iCX, int iCY, uint16_t usColor, int iTranslucency);
spilcdDrawPattern(&lcd, ucBombMask, 8, x+16,y,40,40,0x6e0,((y>>1)&31)+1);
spilcdDrawPattern(&lcd, ucBombMask, 8, x+48,y,40,40,0xf800,((y>>1)&31)+1);
spilcdDrawPattern(&lcd, ucBombMask, 8, x+80,y,40,40,0x1f,((y>>1)&31)+1);
{
uint8_t u8Temp[8*40]; // holds the rotated mask
spilcdRotateBitmap(ucBombMask, u8Temp, 1, 40, 40, 8, 20, 20, i % 360);
j = i % 120;
spilcdDrawPattern(&lcd, u8Temp, 8, j,0,40,40,0x6ff,16);
spilcdDrawPattern(&lcd, u8Temp, 8, 120-j,0,40,40,0xf81f,16);
}
// spilcdDrawBMP((uint8_t *)ucLogoBMP, 8, yLogo, 0, 0, DRAW_TO_RAM);
spilcdShowBuffer(&lcd, 0,0,WIDTH,HEIGHT, DRAW_TO_LCD);
if (i & 1) // change pos every other frame
{
x += dx; y += dy;
if (x == 0) dx = -dx;
else if (x >= 39) dx = -dx;
if (y == 0) dy = -dy;
else if (y > 63) dy = -dy;
}
i++;
} // while (1)
} // loop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment