#include <M5Unified.h>
#define MAX_PINS 7
int cutDepths[MAX_PINS] = {0};
int currentCut = 0;
int keyFormat = 0; // 0 = Kwikset KW1, 1 = Schlage SC1, 2 = Yale Y2
// Key format data (pin spacing in mm, scaled to pixels; 1mm ≈ 4 pixels)
struct KeyFormat {
const char* name;
int pinCount;
int maxDepth; // Max bitting depth (0 to maxDepth)
float pinSpacingMm; // Pin spacing in mm
};
KeyFormat keyFormats[] = {
{"Kwikset KW1", 7, 6, 3.9}, // 3.9mm pin spacing
{"Schlage SC1", 7, 6, 3.968}, // 3.968mm pin spacing
{"Yale Y2", 7, 6, 3.9} // 3.9mm pin spacing
};
int numFormats = 3;
// Colors
#define WHITE 0xFFFF
#define RED 0xF800
#define BLUE 0x001F
#define GREEN 0x07E0
#define SILVER 0xC618
#define DARK_SILVER 0x7BEF
#define BRASS 0xFD20
#define SHADOW 0x4208
void drawGradientRect(int x, int y, int w, int h, uint16_t startColor, uint16_t endColor) {
for (int i = 0; i < h; i++) {
uint16_t color = M5.Lcd.color565(
map(i, 0, h, (startColor >> 11) * 8, (endColor >> 11) * 8),
map(i, 0, h, ((startColor >> 5) & 0x3F) * 4, ((endColor >> 5) & 0x3F) * 4),
map(i, 0, h, (startColor & 0x1F) * 8, (endColor & 0x1F) * 8)
);
M5.Lcd.drawFastHLine(x, y + i, w, color);
}
}
void drawKey() {
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
// Draw instructions
M5.Lcd.setCursor(10, 10);
M5.Lcd.print("Align key with blue contour");
// Draw key bow
int bowX = 10, bowY = 30, bowW = 50, bowH = 50;
drawGradientRect(bowX, bowY, bowW, bowH, SILVER, DARK_SILVER);
M5.Lcd.fillEllipse(bowX + bowW / 2, bowY + bowH / 2, 12, 12, BLACK);
// Draw blade
int bladeX = bowX + bowW, bladeY = bowY + 10, bladeWidth = 140, bladeHeight = 30;
drawGradientRect(bladeX, bladeY, bladeWidth, bladeHeight, BRASS, SILVER);
// Draw key tip
M5.Lcd.fillTriangle(bladeX + bladeWidth, bladeY, bladeX + bladeWidth, bladeY + bladeHeight,
bladeX + bladeWidth + 15, bladeY + bladeHeight / 2, SILVER);
// Draw key contour (for alignment)
M5.Lcd.drawRect(bladeX - 2, bladeY - 2, bladeWidth + 17, bladeHeight + 4, BLUE);
M5.Lcd.drawLine(bladeX + bowW - 10, bladeY + bladeHeight + 2, bladeX + bladeWidth + 15, bladeY + bladeHeight + 2, BLUE);
// Draw lock cylinder outline
M5.Lcd.drawRect(bladeX - 5, bladeY - 15, bladeWidth + 25, bladeHeight + 30, SHADOW);
// Draw pins with realistic notches
int numPins = keyFormats[keyFormat].pinCount;
float pinSpacingMm = keyFormats[keyFormat].pinSpacingMm;
int pinSpacing = pinSpacingMm * 4; // 1mm ≈ 4 pixels
int pinWidth = 8;
for (int i = 0; i < numPins; i++) {
int cut = cutDepths[i];
int notchDepth = map(cut, 0, keyFormats[keyFormat].maxDepth, 4, 24);
int pinX = bladeX + pinSpacing * i + pinSpacing / 2 - pinWidth / 2;
int pinY = bladeY;
// Draw smooth notch (parabolic shape)
for (int x = pinX; x < pinX + pinWidth; x++) {
int yOffset = notchDepth * (1 - pow((x - pinX - pinWidth / 2.0) / (pinWidth / 2.0), 2));
M5.Lcd.drawFastVLine(x, pinY, yOffset, RED);
M5.Lcd.drawPixel(x, pinY + yOffset + 1, SHADOW); // Shadow for depth
}
// Draw ramps between notches
if (i < numPins - 1) {
int nextCut = cutDepths[i + 1];
int nextNotchDepth = map(nextCut, 0, keyFormats[keyFormat].maxDepth, 4, 24);
int nextPinX = bladeX + pinSpacing * (i + 1) + pinSpacing / 2 - pinWidth / 2;
M5.Lcd.drawLine(pinX + pinWidth, pinY + notchDepth, nextPinX, pinY + nextNotchDepth, RED);
}
if (i == currentCut) {
M5.Lcd.drawRect(pinX - 1, pinY - 1, pinWidth + 2, notchDepth + 2, GREEN);
}
}
// Draw metadata
M5.Lcd.setCursor(10, 90);
M5.Lcd.printf("Format: %s", keyFormats[keyFormat].name);
M5.Lcd.setCursor(10, 100);
M5.Lcd.printf("Pins: %d", numPins);
M5.Lcd.setCursor(10, 110);
M5.Lcd.printf("Cut %d: %d", currentCut + 1, cutDepths[currentCut]);
// Draw bitting code
M5.Lcd.setCursor(10, 120);
M5.Lcd.print("Bitting: ");
for (int i = 0; i < numPins; i++) {
M5.Lcd.print(cutDepths[i]);
}
}
void resetCuts() {
for (int i = 0; i < MAX_PINS; i++) {
cutDepths[i] = 0;
}
currentCut = 0;
drawKey();
M5.Lcd.setCursor(100, 80);
M5.Lcd.setTextColor(GREEN);
M5.Lcd.print("Reset!");
delay(500);
drawKey();
}
void setup() {
auto cfg = M5.config();
M5.begin(cfg);
M5.Lcd.setRotation(1);
Serial.begin(115200); // For debugging button presses
drawKey();
}
void loop() {
M5.update();
if (M5.BtnA.wasPressed()) {
Serial.println("Button A pressed (cycle pin)");
currentCut = (currentCut + 1) % keyFormats[keyFormat].pinCount;
drawKey();
}
if (M5.BtnA.pressedFor(2000)) {
Serial.println("Button A long press (cycle format)");
keyFormat = (keyFormat + 1) % numFormats;
currentCut = 0;
drawKey();
M5.Lcd.setCursor(100, 80);
M5.Lcd.setTextColor(GREEN);
M5.Lcd.print("Format Changed!");
delay(500);
drawKey();
}
if (M5.BtnB.wasPressed()) {
Serial.println("Button B pressed (increment cut depth)");
cutDepths[currentCut] = (cutDepths[currentCut] + 1) % (keyFormats[keyFormat].maxDepth + 1);
drawKey();
}
if (M5.BtnB.pressedFor(2000)) {
Serial.println("Button B long press (reset)");
resetCuts();
}
}
Comments