Skip to content

Final Project Overall Summary

Presentation Slide

Final Presentation Video

Bill of Material

Component Quantity Price ($) Total Price ($) Source
Seeed XIAO RP2040 1 5 5 JNWSFL
Resistors 1K ohm 1206 SMD 1 $0.01 $0.01 JNWSFL
Resitor 499 ohm 1206 SMD 2 $0.01 $0.02 JNWSFL
LED blue clear 1206 SMD 2 $0.35 $0.7 JNWSFL
DFPlayer Mini 1 $4.99 $4.99 JNWSFL
Speaker 4ohm 3W 1 $4.4 $4.4 JNWSFL
Potentiometer(WH148 B10K Potentiometer 15mm Shaft 3Pins Variable Resistor) 1 $0.5 $0.5 JNWSFL
PIR sensor(AM312 Mini Pir Motion Sensor Module HC-SR312 IR Human Sensor) 1 $1.8 $1.8 JNWSFL
Slide Button 1 $0.7 $0.7 JNWSFL
Aduio Jack(3.5MM Audio Jack Breakout MP3 Jack Module) $1.2 $1.2 JNWSFL
NeoPixels(Addressable RGB Leds: 24X2(48 PIXELS)) 1 $3.6 $3.6 JNWSFL
5V 2.3A adaptor 1 $6 $6
12mm Plywood 7 $16.4 $114.8 Bhutan Board
12mm Particle Board 2 $16.4 $32.8 Bhutan Board
Total $175.52

Electonics Design and Production

For my final Project Circuit Board, I decided to use Xioa RP2040.

For my final project, I designed my PCB board so that it will have a input and output devices that are decided for my final project.

  • Here is the schematic diagram of my PCB. The components used are:
  • 1X Seeed Studio XIAO RP2040.
  • 21X Screw_Terminal_01x03_P3.5mm
  • 2X LED BLUE CLEAR 1206 SMD/PTH
  • 1X RES 1K OHM 1% 1/4W 1206
  • 2X RES 499 OHM 1% 1/4W 1206
  • 1X Conn_PWRJack_2x5.5mm_CUIDevices_PJ-002AH-SMT-TR
  • 1X Diode_Schottky_SOD123
  • 1X DFPlayer mini DFR0299(I designed a custom SMD FOOTPRINT)

  • Here’s the final PCB layout in KiCad:

  • Here the how my board looks after rendering in KiCAD. It looks quite good to me.

  • Here is my PCB milled in SRM20.

  • Here is the picture of my assembled PCB board.

I soldered all the components on one side of my PCB to keep the design compact while incorporating a user interface—including capacitive touch sensors, a potentiometer, and two slide buttons—into the casing.

Typically, through-hole components are soldered on the opposite side of the board, but I use rivets to assemble everything on a single side. This allows for more comfortable soldering without compromising connectivity and helps maintain a compact PCB and casing design.

More information of my PCB board design

  1. I placed all some components towards the edge of the board deliberately so that I can access those from outside my casing. Those components are:

  2. Xiao-USB: just incase if I have to update my program after system integration.

  3. DFPlayer Mini’s SD card slot: to update music files in it
  4. Power Port: to power up my board.
  5. Audio Jack; to plug in head phones.

  6. Added Schottky Diode as recommended between 5V Vin of Xiao ESP32 C3 and power supply.

  7. Also added two led to indicate 3V and 5V power in my board.

Programming

My entire program is build on top of capacitive touch sensor example code provided by Prof. Neil. I worked around that example code to make three capacitive touch sensors. I have also added program to control volume using potentiometer and PIR sensor to detect user inside my NAP-POD. I programed one pull-down slide button to make my capacitive touch sensor for multi-function which is to control both music and lighting system in my NAP-POD.

There is one additional slide button to turn of external speaker and user can conveniently user earphone feature using the audio jack designed on my PCB.

//--------------------------------------------------------
// this section is for DFPlayer Mini
#include <DFRobotDFPlayerMini.h>
#include <SoftwareSerial.h>
//--------------------------------------------------------

#include <Adafruit_NeoPixel.h>

//--------------------------------------------------------
// This section is for capacitive touch sensors
#define Tx D4 // transmit pin (D7)
#define Rx1 D1 // receive pin (A1) // prev pause sensor
#define Rx2 D2 // receive pin (A2) // play_pause sensor
#define Rx3 D3 // receive pin (A3) // next sensor

#define NUM_PIXELS 24
#define PIN_STRIP1 D9
#define PIN_STRIP2 D8

const int pirPin = D10;
bool motionDetected = false;
unsigned long lastTriggerTime = 0;
unsigned long debounceTime = 2000;  // 2-second cooldown

#define settle 20 // settle time in microseconds
#define samples 5000 // number of samples to accumulate
//--------------------------------------------------------

Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(NUM_PIXELS, PIN_STRIP1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(NUM_PIXELS, PIN_STRIP2, NEO_GRB + NEO_KHZ800);

// Potentiometer pin
const int potPin = D0;
const int buttonPin = D7; // music_lighting_toggle

// Volume variables
const int minVolume = 0;
const int maxVolume = 30;

int play_pause;
bool first_play = 1;
bool play_pause_toggle = 1;
bool play_from_pause = 0; //start the mp3 from the pause
int next;
int prev;

// Initialize software serial on pins D6 (Tx) and D5 (Rx)( Tx and Rx of xiao) to connecte with DFPlayer Mini
SoftwareSerial mySerial(D6, D5); 

// Create DFPlayer object
DFRobotDFPlayerMini myDFPlayer;

void setup() {

  strip1.begin(); //neopixels
  strip2.begin(); //neopixels

  pinMode(buttonPin, INPUT); // No internal pull-up/down

  pinMode(pirPin, INPUT);

  pinMode(D0, INPUT);
  Serial.begin(115200);  // Start hardware serial for debugging
  mySerial.begin(9600);  // Start software serial for DFPlayer

  pinMode(Tx, OUTPUT);  // I am using one Tx for all the capacitive sensors
  analogReadResolution(12); // Set ADC resolution to 12 bits which will be required for capacitive touch sensors

  Serial.println(F("Initializing DFPlayer..."));

  // Initialize DFPlayer
  if (!myDFPlayer.begin(mySerial)) {
    Serial.println(F("DFPlayer initialization failed!"));
    Serial.println(F("1. Check RX/TX connections (must be crossed)"));
    Serial.println(F("2. Insert SD card with MP3 files"));
    while(true);  // Halt if initialization fails
  }

  Serial.println(F("DFPlayer Mini ready!"));
  myDFPlayer.volume(25);// Set initial volume

  // if(!motionDetected){
  //   motionDetected = true;
  //   activateNapPod();
  //   myDFPlayer.play(1);
  //   delay(4000);
  //   myDFPlayer.next();  //Play next mp3
  //   myDFPlayer.pause();
  //   // Reset all pixels to off
  //   resetAllPixels();
  // }
  Wait for motion detection before continuing
  while(!motionDetected) {
    if (digitalRead(pirPin) == HIGH) {
      if (!motionDetected && (millis() - lastTriggerTime > debounceTime)) {
        motionDetected = true;
        lastTriggerTime = millis();
        Serial.println("Real motion detected!");
        activateNapPod();
        myDFPlayer.play(1);
        delay(4000);
        myDFPlayer.next();
        myDFPlayer.pause();
        resetAllPixels();
      }
    }else {
      motionDetected = false;
    }
  }
}

void resetAllPixels() {
  for(int i=0; i<NUM_PIXELS; i++) {
    strip1.setPixelColor(i, 0); // Turn off pixel
    strip2.setPixelColor(i, 0); // Turn off pixel
  }
  strip1.show();
  strip2.show();
}

// 1. Calm Blue Lighting
void blueGlow() {
  strip1.setBrightness(100);
  strip2.setBrightness(100);
  for(int i=0; i<NUM_PIXELS; i++) {
    strip1.setPixelColor(i, strip1.Color(0,0,255)); 
    strip2.setPixelColor(i, strip2.Color(0,0,255));
  }
  strip1.show();
  strip2.show();
}

// 2. Warm Yellow Lighting  
void warmYellowGlow() {
  strip1.setBrightness(100);
  strip2.setBrightness(100);
  for(int i=0; i<NUM_PIXELS; i++) {
    strip1.setPixelColor(i, strip1.Color(255, 190, 70)); 
    strip2.setPixelColor(i, strip2.Color(255, 190, 70));
  }
  strip1.show();
  strip2.show();
}

// 3. Soothing Red Lighting
void redGlow() {
  strip1.setBrightness(100);
  strip2.setBrightness(100);
  for(int i=0; i<NUM_PIXELS; i++) {
    strip1.setPixelColor(i, strip1.Color(255, 0, 0)); // Soft red
    strip2.setPixelColor(i, strip2.Color(255, 0, 0));
  }
  strip1.show();
  strip2.show();
}

// 4. NapPod Activation (Green)
void activateNapPod() {
  // Bright green flash 3 times
  for(int x=0; x<3; x++) {
    for(int i=0; i<NUM_PIXELS; i++) {
      strip1.setPixelColor(i, strip1.Color(0, 255, 0));
      strip2.setPixelColor(i, strip2.Color(0, 255, 0));
    }
    strip1.setBrightness(255);
    strip2.setBrightness(255);
    strip1.show();
    strip2.show();
    delay(300);

    // Turn off
    for(int i=0; i<NUM_PIXELS; i++) {
      strip1.setPixelColor(i, 0);
      strip2.setPixelColor(i, 0);
    }
    strip1.show();
    strip2.show();
    delay(300);
  }

  // Steady green confirmation
  for(int i=0; i<NUM_PIXELS; i++) {
    strip1.setPixelColor(i, strip1.Color(0, 200, 0));
    strip2.setPixelColor(i, strip2.Color(0, 200, 0));
  }
  strip1.setBrightness(100);
  strip2.setBrightness(100);
  strip1.show();
  strip2.show();
  delay(1000);

  // Turn off
  for(int i=0; i<NUM_PIXELS; i++) {
    strip1.setPixelColor(i, 0);
    strip2.setPixelColor(i, 0);
  }
  strip1.show();
  strip2.show();
}

void loop() {

  int buttonState = digitalRead(buttonPin); // music_lighting toggle

  // Read the potentiometer value
  int potValue = analogRead(potPin);
  Serial.println(potValue);

  // // Map the potentiometer value to volume range (0-30)
  int newVolume = map(potValue, 0, 4096, minVolume, maxVolume);
  Serial.println(newVolume);
  myDFPlayer.volume(newVolume);

  int32_t up1 = 0, down1 = 0;
  int32_t up2 = 0, down2 = 0;
  int32_t up3 = 0, down3 = 0;

  for (int i = 0; i < samples; ++i) {
    digitalWrite(Tx, HIGH); // charge up
    up1 += analogRead(Rx1); // read
    up2 += analogRead(Rx2); // read
    up3 += analogRead(Rx3); // read

    delayMicroseconds(settle); // settle
    digitalWrite(Tx, LOW); // charge down
    down1 += analogRead(Rx1); // read
    down2 += analogRead(Rx2); // read
    down3 += analogRead(Rx3); // read

    delayMicroseconds(settle); // settle
  }

  // Calculate differences (op-amp inverts)
  prev = -1 * ((down1 - up1) / samples);
  play_pause = -1 * ((down2 - up2) / samples);
  next = -1 * ((down3 - up3) / samples);

  // Debug prints
  Serial.println("prev: " + String(prev));
  Serial.println("play_pause: " + String(play_pause));
  Serial.println("next: " + String(next));

 if (buttonState == HIGH) {
    Serial.println("Lighting");
    if (play_pause < 20) {
      blueGlow();
    }else if (next < 60) {
      warmYellowGlow();
    }else if (prev < 20) {
      redGlow();
    }

  }else if (buttonState == LOW){
    Serial.println("buttons in Music control mode");
    if (play_pause < 20) {
      if (first_play){
        //activateNapPod(); // Run activation sequence on startup
        Serial.println(" play button pressed for the first time");
        myDFPlayer.play(2);  //Play the first mp3
        play_pause_toggle = !play_pause_toggle;
        first_play = !first_play;

      }else if(!play_pause_toggle) {
        Serial.println("stop button pressed");
        myDFPlayer.pause();  //pause the mp3
        play_from_pause = !play_from_pause;
        play_pause_toggle = !play_pause_toggle;

      }else if (play_from_pause) {
        Serial.println(" play from pause button pressed");
        myDFPlayer.start();  //start the mp3 from the pause
        play_pause_toggle = !play_pause_toggle;
        play_from_pause = !play_from_pause;
      }
    }else if (next < 70) {
      Serial.println("'next' button pressed");
      myDFPlayer.next();  //Play next mp3

    }else if (prev < 30) {
      Serial.println("'prev' button pressed");
      myDFPlayer.previous();  //Play previous mp3
    }
  }
}

Structural Design

  • Rendered 360 View of my NAP-POD Structure:

I designed my NAP-POD structure in Fusion360 using both simple geometry tools and spline to create organic shapes. I used parametric design principle wherever possible but I faced some difficulty in defining a organic shaped created using spline.

  • Here is how I fabricated and assembled my NAP-POD. I export the NAP-POD design file as dxf file and milled/cut in ShopBot Machine.

  • I then sand all of them to get rid of rough surfaces left after machining.

  • At first, I design fingerjoints for my NAP-POD but considering the strength of the plywood and particleboard, I redesigned and decided to use dowel to assembled my NAP-POD.

  • I used these small rectangular wood as a spacer.

  • Assembly Done.

Casing/User Interface Design

  • Rendered View of my Casing/User Interface Design:

  • I want to use casing of my PCB both as casing and a user interface of my NAP-POD.

  • I considered accessibility of memory card, USB port and Audio Jack.

Integrating PCB, casing and Nap-pod structure

  • Testing single capacitive touch sensor with an led.

  • Testing the program for three capacitive touch sensors as I need three.

  • Testing my program with my final PCB board.

  • More Testing

  • Integrating PCB inside casing and I/Os.

  • Integrating the assembled casing with user interface inside my NAP-POD. For this, I tried to figure our a best position to integrate the Casing/Interface.

Visit this link for more details on system Integration.

Final Result

Answers and Planning

Here is the link to week 17 assignment.

Licence

Throughout my journey in developing the NAP-POD, one of the most inspiring realizations has been the power of open collaboration and shared knowledge. This project was built upon the foundation of others’ work—like Prof. Neil’s example code for capacitive sensing— which lies at the heart of my project and it is only right to give back to the community that enabled my progress.

My goal is to create a comfortable, functional space for office workers to rest and recharge, helping them manage work-life balance. If my design proves useful, I want to make it accessible to others who might benefit from it or even improve upon it.

I planed to release the NAP-POD as an open-source project under the: Creative Commons License: Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)

Here is the Legal Code of CC BY-NC-SA.

Files

Full Program of my Final Project.

3D Design file of my NAP-POD structure.

3D Design of my PCB Casing.

PCB Design file(Kicad).

PCB Milling file in png format.