2026年7月29日 星期三

6 Button RP2040-Zero Macro Pad

 3D printer Case

https://www.printables.com/model/1559069-6-button-rp2040-zero-macro-pad/comments






Software Design Specification

(1)RP2040-Zero pinout definitions are as follows:
    #define KEY1     8
    #define KEY2     9
    #define KEY3     10
    #define KEY4     11
    #define KEY5     12
    #define KEY6     13
    #define KEY7     7
    #define PUSH_SW  6
    #define INTA     5
    #define INTB     4
    #define RGB_LED  14


(2)The button definitions are as follows:
    KEY6 = Switch Mode, Mode = 0 to 3.
    
    Mode 0 :
    KEY1 = WIN+d
    KEY2 = win+r -> wait 500ms -> "devmgmt.msc" -> enter -> wait 2000ms -> enter
    KEY3 = win+r -> wait 500ms -> "calc.exe" -> enter
    KEY4 = enter key
    KEY5 = "PassWord" + enter key
    Rotary encoder push SW = PAUSE/PLAY
    Rotary encoder right turn = volume up
    Rotary encoder left turn = volume down
    
    Mode 1 :
    KEY1 = CTRL+X
    KEY2 = CTRL+C
    KEY3 = CTRL+V
    KEY4 = CTRL+Z
    KEY5 = CTRL+Y
    Rotary encoder push SW = PAUSE/PLAY
    Rotary encoder right turn = volume up
    Rotary encoder left turn = volume down
    
    Mode 2 :
    KEY1 = left key
    KEY2 = down key
    KEY3 = right key
    KEY4 = mouse button left
    KEY5 = up key
    Rotary encoder push SW = PAUSE/PLAY
    Rotary encoder right turn = mouse wheel down
    Rotary encoder left turn = mouse wheel up
    
    Mode 3 :
    KEY1 = '<' key
    KEY2 = key keypad subtract
    KEY3 = '>' key
    KEY4 = WIN+d
    KEY5 = key keypad add
    Rotary encoder push SW = PAUSE/PLAY
    Rotary encoder right turn = volume up
    Rotary encoder left turn = volume down

(3)RGB_LED color 
    Mode 0 = The breathing light breathes approximately every 2 seconds, changing color with each breath. 
             The colors cycle through R, R+G, G, G+B, B, B+R. 
    Mode 1 = all color (R)
    Mode 2 = all color (G)
    Mode 3 = all color (B + 30%G)

Arduino Code:

/*
 * 6Key_3D_case.ino
 * Waveshare RP2040-Zero  +  6 keys + rotary encoder + WS2812B x8
 *
 * Board  : Raspberry Pi Pico / RP2040 (earlephilhower arduino-pico core)
 * USB    : Tools -> USB Stack -> "Adafruit TinyUSB"  (or "Pico SDK")
 * Libs   : Adafruit NeoPixel
 */

#include <Keyboard.h>
#include <Mouse.h>
#include <Adafruit_NeoPixel.h>
#include <math.h>

// ---------------- Pin map ----------------
#define KEY1        8
#define KEY2        9
#define KEY3        10
#define KEY4        11
#define KEY5        12
#define KEY6        13     // Mode switch
#define KEY7        7      // reserved
#define PUSH_SW     6      // encoder push
#define INTA        5      // encoder A
#define INTB        4      // encoder B
#define RGB_LED     14

#define NUM_LEDS       8
#define LED_BRIGHTNESS 80        // 0-255 overall brightness cap
#define DEBOUNCE_MS    20

Adafruit_NeoPixel strip(NUM_LEDS, RGB_LED, NEO_GRB + NEO_KHZ800);

// ---------------- Globals ----------------
uint8_t mode = 0;                            // 0..3

const uint8_t keyPins[6] = { KEY1, KEY2, KEY3, KEY4, KEY5, KEY6 };
bool          keyState[6]      = { HIGH, HIGH, HIGH, HIGH, HIGH, HIGH };
unsigned long keyLastChange[6] = { 0, 0, 0, 0, 0, 0 };

bool          pushState      = HIGH;
unsigned long pushLastChange = 0;

// Rotary encoder (quadrature, interrupt-driven)
volatile int8_t  encoderDelta = 0;
volatile uint8_t encoderLastAB = 0;

// LED breathing
unsigned long modeStartMs = 0;
uint8_t       colorIndex  = 0;

// ---------------- Forward decl ----------------
void handleEncoderISR();
void updateLED();
void handleKeyPress(uint8_t idx);
void handlePushClick();
void handleEncoderStep(int8_t dir);

// ---------------- Helpers ----------------
static inline void tapMod(uint8_t mod, uint8_t key) {
  Keyboard.press(mod);
  Keyboard.press(key);
  delay(30);
  Keyboard.releaseAll();
}

static inline void tapWinKey(uint8_t key) { tapMod(KEY_LEFT_GUI,  key); }
static inline void tapCtrlKey(uint8_t key){ tapMod(KEY_LEFT_CTRL, key); }

static inline void tapShiftKey(uint8_t key) { tapMod(KEY_LEFT_SHIFT, key); }

// ---------------- Setup ----------------
void setup() {
  for (uint8_t i = 0; i < 6; i++) pinMode(keyPins[i], INPUT_PULLUP);
  pinMode(KEY7,    INPUT_PULLUP);
  pinMode(PUSH_SW, INPUT_PULLUP);
  pinMode(INTA,    INPUT_PULLUP);
  pinMode(INTB,    INPUT_PULLUP);

  encoderLastAB = (digitalRead(INTA) << 1) | digitalRead(INTB);
  attachInterrupt(digitalPinToInterrupt(INTA), handleEncoderISR, CHANGE);
  attachInterrupt(digitalPinToInterrupt(INTB), handleEncoderISR, CHANGE);

  Keyboard.begin();
  Mouse.begin();

  strip.begin();
  strip.setBrightness(LED_BRIGHTNESS);
  strip.clear();
  strip.show();

  modeStartMs = millis();
}

// ---------------- Main loop ----------------
void loop() {
  unsigned long now = millis();

  // ---- 6 keys ----
  for (uint8_t i = 0; i < 6; i++) {
    bool s = digitalRead(keyPins[i]);
    if (s != keyState[i] && (now - keyLastChange[i]) > DEBOUNCE_MS) {
      keyLastChange[i] = now;
      keyState[i] = s;
      if (s == LOW) handleKeyPress(i);       // pressed
    }
  }

  // ---- Encoder push ----
  bool p = digitalRead(PUSH_SW);
  if (p != pushState && (now - pushLastChange) > DEBOUNCE_MS) {
    pushLastChange = now;
    pushState = p;
    if (p == LOW) handlePushClick();
  }

  // ---- Encoder rotation ----
  noInterrupts();
  int8_t d = encoderDelta;
  encoderDelta = 0;
  interrupts();
  // Full detent = 4 quadrature steps (typical mechanical encoder)
  static int8_t acc = 0;
  acc += d;
  while (acc >=  4) { handleEncoderStep(+1); acc -= 4; }
  while (acc <= -4) { handleEncoderStep(-1); acc += 4; }

  // ---- LED ----
  updateLED();
}

// ---------------- Encoder ISR (quadrature decode) ----------------
void handleEncoderISR() {
  uint8_t ab   = (digitalRead(INTA) << 1) | digitalRead(INTB);
  uint8_t comb = (encoderLastAB << 2) | ab;
  switch (comb) {
    case 0b0001: case 0b0111: case 0b1110: case 0b1000: encoderDelta++; break;
    case 0b0010: case 0b1011: case 0b1101: case 0b0100: encoderDelta--; break;
    default: break; // invalid / no change
  }
  encoderLastAB = ab;
}

// ---------------- Key actions ----------------
void handleKeyPress(uint8_t idx) {
  // idx 5 == KEY6 : switch mode
  if (idx == 5) {
    mode = (mode + 1) & 0x03;
    modeStartMs = millis();
    colorIndex  = 0;
    return;
  }

  switch (mode) {

    // ============ Mode 0 ============
    case 0:
      switch (idx) {
        case 0: tapWinKey('d'); break;                    // KEY1  Win+D
        case 1:                                           // KEY2  devmgmt.msc
          tapWinKey('r');
          delay(500);
          Keyboard.print("devmgmt.msc");
          Keyboard.write(KEY_RETURN);
          delay(2000);
          Keyboard.write(KEY_RETURN);
          break;
        case 2:                                           // KEY3  calc.exe
          tapWinKey('r');
          delay(500);
          Keyboard.print("calc.exe");
          Keyboard.write(KEY_RETURN);
          break;
        case 3: Keyboard.write(KEY_RETURN); break;        // KEY4  Enter
        case 4:                                           // KEY5  PassWord + Enter
          Keyboard.print("PassWord");
          Keyboard.write(KEY_RETURN);
          break;
      }
      break;

    // ============ Mode 1 ============
    case 1:
      switch (idx) {
        case 0: tapCtrlKey('x'); break;
        case 1: tapCtrlKey('c'); break;
        case 2: tapCtrlKey('v'); break;
        case 3: tapCtrlKey('z'); break;
        case 4: tapCtrlKey('y'); break;
      }
      break;

    // ============ Mode 2 ============
    case 2:
      switch (idx) {
        case 0: Keyboard.write(KEY_LEFT_ARROW);  break;
        case 1: Keyboard.write(KEY_DOWN_ARROW);  break;
        case 2: Keyboard.write(KEY_RIGHT_ARROW); break;
        case 3: Mouse.click(MOUSE_LEFT);         break;
        case 4: Keyboard.write(KEY_UP_ARROW);    break;
      }
      break;

    // ============ Mode 3 ============
    case 3:
      switch (idx) {
        case 0: tapShiftKey(',');            break;   // '<'
        case 1: Keyboard.write(KEY_KP_MINUS);break;   // keypad -
        case 2: tapShiftKey('.');            break;   // '>'
        case 3: tapWinKey('d');              break;   // Win+D
        case 4: Keyboard.write(KEY_KP_PLUS); break;   // keypad +
      }
      break;
  }
}

// Encoder push (all modes): Play / Pause
void handlePushClick() {
  Keyboard.write(KEY_PLAY_PAUSE);
}

// Encoder rotate: dir = +1 (CW / right) or -1 (CCW / left)
void handleEncoderStep(int8_t dir) {
  if (mode == 2) {
    // Mouse wheel: right = wheel down (negative), left = wheel up (positive)
    Mouse.move(0, 0, (dir > 0) ? -1 : +1);
  } else {
    Keyboard.write((dir > 0) ? KEY_VOLUME_INCREMENT : KEY_VOLUME_DECREMENT);
  }
}

// ---------------- LED ----------------
void updateLED() {
  static unsigned long lastShow = 0;
  unsigned long now = millis();
  if (now - lastShow < 20) return;             // ~50 Hz refresh
  lastShow = now;

  uint32_t color = 0;

  switch (mode) {

    case 0: {
      // Breathing ~2 s per breath, cycle color each breath
      const unsigned long PERIOD = 2000;
      unsigned long elapsed = now - modeStartMs;
      if (elapsed >= PERIOD) {
        modeStartMs = now;
        colorIndex  = (colorIndex + 1) % 6;
        elapsed = 0;
      }
      float ph = (float)elapsed / (float)PERIOD;               // 0..1
      uint8_t v = (uint8_t)(sinf(ph * (float)PI) * 255.0f);    // 0..255..0
      uint8_t r = 0, g = 0, b = 0;
      switch (colorIndex) {                                    // R, R+G, G, G+B, B, B+R
        case 0: r = v;               break;
        case 1: r = v; g = v;        break;
        case 2:        g = v;        break;
        case 3:        g = v; b = v; break;
        case 4:               b = v; break;
        case 5: r = v;        b = v; break;
      }
      color = strip.Color(r, g, b);
      break;
    }

    case 1: color = strip.Color(255,   0,   0); break;         // Red
    case 2: color = strip.Color(  0, 255,   0); break;         // Green
    case 3: color = strip.Color(  0,  77, 255); break;         // Blue + 30% Green
  }

  for (uint8_t i = 0; i < NUM_LEDS; i++) strip.setPixelColor(i, color);
  strip.show();
}

沒有留言:

張貼留言