Arnov Sharma
Published © MIT

Motorola DynaTAC MAX

A Motorola DynaTAC-themed Soundboard-Bluetooth Speaker made from scratch, powered by PICO W.

IntermediateFull instructions provided2 days165

Things used in this project

Hardware components

Raspberry Pi Pico W
Raspberry Pi Pico W
×1
NextPCB  Custom PCB Board
NextPCB Custom PCB Board
×1

Software apps and online services

Fusion
Autodesk Fusion
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

STEP FILE

Schematics

SCH- PICO DRIVER

Code

code

C/C++
#include <Adafruit_NeoPixel.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>

#define S0 20
#define S1 19
#define S2 18
#define S3 17
#define SIG 16

#define BTN_GP28 28
#define BTN_GP27 27

#define LED_PIN 21
#define NUM_LEDS 18
#define DF_RX 7
#define DF_TX 8

Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
SoftwareSerial mySerial(DF_RX, DF_TX); 
DFRobotDFPlayerMini player;

// --- MAPPING ARRAYS ---
// Maps Mux Channel (0-15) to LED Index (0-15)
int muxToLedMap[16] = {
  12, 13, 14, 15, // CH0,1,2,3  -> LEDs 12,13,14,15
  6,  7,  8,  9,  // CH4,5,6,7  -> LEDs 6,7,8,9
  10, 11, 0,  1,  // CH8,9,10,11 -> LEDs 10,11,0,1
  2,  3,  4,  5   // CH12,13,14,15 -> LEDs 2,3,4,5
};

// Maps Mux Channel (0-15) to SD Track Number
int muxToTrackMap[16] = {
  17, 18, 19, 20, // CH0=17, CH1=18, CH2=19, CH3=20
  7,  8,  9,  15, // CH4=07, CH5=08, CH6=09, CH7=15
  10, 13, 1,  2,  // CH8=10, CH9=13, CH10=01, CH11=02
  3,  4,  5,  6   // CH12=03, CH13=04, CH14=05, CH15=06
};

bool lastMuxState[16];
bool lastBtn28 = HIGH;
bool lastBtn27 = HIGH;
unsigned long ledOffTime[NUM_LEDS]; 
const unsigned long displayDuration = 2000; 

void selectMuxChannel(uint8_t channel) {
  digitalWrite(S0, (channel >> 0) & 0x01);
  digitalWrite(S1, (channel >> 1) & 0x01);
  digitalWrite(S2, (channel >> 2) & 0x01);
  digitalWrite(S3, (channel >> 3) & 0x01);
  delayMicroseconds(20); 
}

void setup() {
  Serial.begin(115200);
  mySerial.begin(9600);

  strip.begin();
  strip.setBrightness(255); 
  strip.fill(0); 
  strip.show();

  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(SIG, INPUT_PULLUP);
  pinMode(BTN_GP28, INPUT_PULLUP);
  pinMode(BTN_GP27, INPUT_PULLUP);

  for (int i = 0; i < 16; i++) {
    lastMuxState[i] = HIGH;
    ledOffTime[i] = 0;
  }
  ledOffTime[16] = 0; // LED near GP28
  ledOffTime[17] = 0; // LED near GP27

  if (!player.begin(mySerial)) {
    Serial.println("DFPlayer not found.");
  } else {
    player.volume(25);
    Serial.println("System Ready - Interrupt Play Enabled.");
  }
}

void loop() {
  unsigned long now = millis();

  // 1. SCAN MULTIPLEXER (CH0 - CH15)
  for (uint8_t i = 0; i < 16; i++) {
    selectMuxChannel(i);
    bool currentState = digitalRead(SIG);

    if (currentState == LOW && lastMuxState[i] == HIGH) {
      int ledIdx = muxToLedMap[i];
      int trackNum = muxToTrackMap[i];

      // Interrupt & Play logic
      player.stop(); 
      player.play(trackNum);

      strip.setPixelColor(ledIdx, strip.Color(255, 0, 0));
      strip.show();
      ledOffTime[ledIdx] = now + displayDuration;
      
      delay(10); // Debounce
    }
    lastMuxState[i] = currentState;
  }

  // 2. BUTTON ON GP28 (Plays Track 14, LED index 16)
  bool st28 = digitalRead(BTN_GP28);
  if (st28 == LOW && lastBtn28 == HIGH) {
    player.stop();
    player.play(14);
    strip.setPixelColor(16, strip.Color(255, 0, 0));
    strip.show();
    ledOffTime[16] = now + displayDuration;
  }
  lastBtn28 = st28;

  // 3. BUTTON ON GP27 (Plays Track 16, LED index 17)
  bool st27 = digitalRead(BTN_GP27);
  if (st27 == LOW && lastBtn27 == HIGH) {
    player.stop();
    player.play(16);
    strip.setPixelColor(17, strip.Color(255, 0, 0));
    strip.show();
    ledOffTime[17] = now + displayDuration;
  }
  lastBtn27 = st27;

  // --- AUTOMATIC TIMED TURN OFF (2 Seconds) ---
  bool needsUpdate = false;
  for (int j = 0; j < NUM_LEDS; j++) {
    if (ledOffTime[j] > 0 && now >= ledOffTime[j]) {
      strip.setPixelColor(j, strip.Color(0, 0, 0));
      ledOffTime[j] = 0;
      needsUpdate = true;
    }
  }
  if (needsUpdate) strip.show();
}

Credits

Arnov Sharma
379 projects • 405 followers
I'm Arnov. I build, design, and experiment with tech—3D printing, PCB design, and retro consoles are my jam.

Comments