Group Assignment Individual Assignment Input Devices SAMD21 Designing TouchNav Programming Design Files
Week 9

Input Devices

This week, I focused on designing, fabricating, and assembling a custom PCB featuring a SAMD21 MCU, a rotary encoder, and a capacitive touch slider. I also learned about the functionality and integration of these input devices.

Hero Shot

Learning Objectives

  • Demonstrate workflows used in sensing something with input device(s) and MCU board

    Assignments

    Group Assignments

  • probe an input device's analog levels and digital signals

  • Individual Assignments

  • measure something: add a sensor to a microcontroller board that you have designed and read it

  • Group Assignment

    Group Assignment

    Individual Assignments

    Input Devices

    In this week, I decided to work with Capacitive Touch Slider and Rotary Encoder. I wanted to explore the capabilities of capacitive touch sensors and how they can be integrated into a microcontroller board.

    Step Response/ Capacitive Touch

    Capacitive touch sensors are devices that detect touch or proximity by measuring changes in capacitance. They work by using a conductive pad that forms a capacitor with the human body. When a finger approaches the pad, it alters the capacitance, which can be measured to determine if a touch has occurred.

    I refered to some previous works of students who used capacitive touch and SAMD21

  • SAMD21 - Quentin
  • Quentin's SAMD21 devkit board

    Photo credit: Quentin Bolsee

    Peripheral Touch Controller (PTC)

    The QTouch® Peripheral Touch Controller (PTC) offers built-in hardware for capacitive touch measurement on sensors that function as buttons, sliders, and wheels. The PTC has been designed to perform capacitive touch acquisition on sensors independently from the CPU, resulting in low CPU utilization and reduced power consumption.

    Rotary Encoder

    Rotary Encoder Module

    Photo credit: simple-circuit

    The rotary encoder generates (when rotating) two square waves on pins A (CLK) and B (DT) with 90° out of phase as shown in the figure below:

    Encoder: Output Waveform

    Photo credit: Arduino Forum

    Since the normal state of pin A (CLK) and pin B (DT) are logic high we've to detect falling (transition from high to low) of one of them, here pin A is used to detect the movement of the rotary encoder in both directions (falling of pin A signal). Direction of rotation can be detected by knowing the status of pin B, if pin B is logic high this means the direction of rotation is clockwise (CW), and if pin B is logic low this means the direction of rotation is counter clockwise (CCW).

    SAMD 21

    SAMD21 Pinout

    Photo credit: Microchip

    SAMD21 Documentation

    The device has several different power supply pins:

    • VDDIO: Powers I/O lines, OSC8M and XOSC. Voltage is 1.62V to 3.63V.
    • VDDIN: Powers I/O lines and the internal regulator. Voltage is 1.62V to 3.63V.
    • VDDANA: Powers I/O lines and the ADC, AC, DAC, PTC, OSCULP32K, OSC32K, XOSC32K. Voltage is 1.62V to 3.63V.
    • VDDCORE: Internal regulated voltage output. Powers the core, memories, peripherals, FDPLL96M, and DFLL48M. Voltage is 1.2V.
    The same voltage must be applied to both VDDIN, VDDIO and VDDANA. This common voltage is referred to as VDD in the datasheet. The ground pins, GND, are common to VDDCORE, VDDIO and VDDIN. The ground pin for VDDANA is GNDANA

    SAMD21 Recommended Power connection

    Photo credit: Microchip

    Designing the Microcontroller Board

    I decided to try something which involved buttons, sliders and enncoders for my final project - Symphoni The Record Player. But after seeing the class, I was amused by the capacitive touch and its capabilities. Therefore I decided my sensors for the week - Capacitive Touch Slider and Rotary Encoder.

    I decided to use a 5 Capacitive Pad slider with a rotary encoder. And I decided to explore a new microcontroller - SAMD21 E17A.

    TouchNav SAMD21

    The WS2812B uses 5V but my MCU uses 3.3 V logic level, therefore I needed the 5V output. So I had to make a logic level shifter. So I learned more about Logic Level Shifter.

    I refered to this documentation Logic Level Shifters

    Bi Directional Logic Level Shifter

    Photo credit: electrocredible

    Logic Level Shifter

    Schematic Design

    The SAMD21 E17 requires some mandatory connections for its basic functionality. The connections are mainly for the power connections and Serial Debug Wire Pins . The below shown image shows the connections in the schematic Design.

    VDD Connections

    Some newer elements in the schematic are ProshPlay Type C Breakout Board and Rotary Encoder Connection. I have also labelled the capacitive touch pad pins as S1,S2,S3,S4,S5,

    The below image shows the full schematic Design of the board.

    Final Schematic

    You can refer to the below Schematic PDF for clearer viewing.

    TouchNav Schematic PDF

    PCB Design

    After the schematic design, I started designing the PCB in KiCad/p>

    TouchNav PCB

    For making the copper pads for capacitive touch sensors, I used the copper fill feature available in KiCad. I allocatted a label for every touch pad and gave it names S1, S2, S3, S4 and S5. Then I assigned every rectangle with each label and copper filled it.

    The below images how a step by step process of adding a copper fill zone in KiCad.

    Adding Rectangular Pad
    Select Draw Filled Zone and selcect a corner
    Select Label
    Select Bounding Area and Close Outline
    After Drawing Copper Zone
    Fill All Zones
    Copper Zone

    I used the similiar technique for the rest of my touch pads and connected them to the respective pins.

    Production and Assembly

    After routing the PCB in KiCad, I milled the PCB using the Roland MDX Milling Machine. After inspection I realised that the traces at some points were very thin and uneven at certain places. The cause of this was not complying to the design constraints and rules of our machining process. I missed the step and got this result, but still the output was decent ignoring some thin traces.

    Design Rules Failure

    The microscope inscpection shows the thin traces at certain parts of the PCB

    Design Rules Failure
    Assembly

    After preparing the BOM and requesting the components from the fablab inventory using Fab Stash. Below are the components before soldering.

    Components List

    As the SAMD21 board is USB compatible, I decided to use the ProshPlay Module to program the board. The below are the componenets of the Proshplay Module: USB C receptecale, ProshPlay PCB, 2 Resistors

    The Proshplay Breakout Board is designed and developed by our instructorSaheen PalayiYou can refer to the documentation of ProshPlay Adapter for further information.

    Proshplay Type C Breakout Board
    ProshPlay: Components

    I had never soldered a board with such fine spacing, I tieed to solder the USB C receptecale on the PCB but it was difficult to not solder the legs together somehow. Saheen helped me to solder the PCB. The below picture shows the breakout board assembled.

    ProshPlay Assembled

    After assembling the Proshplay Breakout board, I soldered it to my TouchNav PCB along with the other components.

    Below are some hero shots of my PCB - TouchNav. I named the board TouchNav because it features a touch slider and a rotary encoder to navigate.

    Hero Shots
    Hero Shots
    Hero Shots

    Programing TouchNav(SAMD21 Board)

    The TouchNav Board has a SAMD21 E17 at its core, so to program the SAMD21 we need to add the bootloader to the board. To flash the bootloader initially, we need the Serial Debug Pins. Only after that can we program the board using USB.

    TouchNav SAMD21 Components

    The above image shows the components of the board: Capacitive Touch Sensor Pads, Rotary Encoder, WS2812B LED Array, Serial Debug Pins, USB C and SAMD21 E17

    TouchNav Pinout

    The pinouts are as follows:

    • LED - 7
    • WS2812B - 11
    • Rotary Encoder - A - 9, B - 8, Switch - 10
    • Capacitive Touch Slider - GPIO 2,3,4,5,6

    TouchNav Serial Debug Pins

    This is the connection for Bootloader Programming, using the SAMD DAP programmer made by Saheen.

    Bootloader Programming
    Saheen's SAMD Programmer
    Image 1
    Programming Setup
    Image 2
    Serial Debug Connection
    Adding the FAB SAMD Board to Arduino

    To program the SAMD21 Board using Arduino IDE, we need to add the FAB SAMD Board to the Board Manager

    To install, simply add the following URL to "Additional Boards Manager URLs" in the Arduino IDE: https://raw.githubusercontent.com/qbolsee/ArduinoCore-fab-sam/master/json/package_Fab_SAM_index.json

    FAB SAMD Board - Quentin Bolse

    Install the board using the Board Manager

    Adding Board
    Selecting SAMD Board

    We need to burn the bootloader first using Saheens's SAMD DAP programmer. Only then can I program using the USB C Cable. This step is only needed initially.

    My programming process was to initially test the functionality of all my connected devices such as the LED, rotary encoder and the capacitive touch pads. After testing the boards I wanted to use the board as a HID Device to control Media Playback.

    Blink

    After burning the bootloader the first step was to test the builtin example 'Blink'

    // the setup function runs once when you press reset or power the board
    void setup() {
      // initialize builtin LED at pin 7 as an output.
      pinMode(7, OUTPUT);
    }
    
    // the loop function runs over and over again forever
    void loop() {
      digitalWrite(7, HIGH);  // turn the LED on (HIGH is the voltage level)
      delay(1000);                      // wait for a second
      digitalWrite(7, LOW);   // turn the LED off by making the voltage LOW
      delay(1000);                      // wait for a second
    }
    								

    SAMD21 Serial Demo

    The next test was serial communication. After research, I realised the usual arduino command Serial.print doesn't work for the SAMD21, therefore we have to use SerialUSB.print. The below code is taken from Embedded Programming class. SAMD21 Echo Code

    
    //
    // hello.D21E.echo.ino
    //
    // ATSAMD21E USB echo hello-world
    //
    // Neil Gershenfeld 12/24/19
    //
    // This work may be reproduced, modified, distributed,
    // performed, and displayed for any purpose, but must
    // acknowledge this project. Copyright is retained and
    // must be preserved. The work is provided as is; no
    // warranty is provided, and users accept all liability.
    //
    
    void setup() {
      SerialUSB.begin(115200);
    }
    
    #define max_buffer 25
    
    void loop() {
      static char chr;
      static char buffer[max_buffer] = { 0 };
      static int index;
      if (SerialUSB.available()) {
        chr = SerialUSB.read();
        SerialUSB.print("hello.D21E.echo: you typed \"");
        buffer[index++] = chr;
        if (index == (max_buffer - 1))
          index = 0;
        SerialUSB.print(buffer);
        SerialUSB.println("\"");
      }
    }

    Rotary Encoder

    // Rotary Encoder Inputs
    #define CLK 9
    #define DT 8
    #define SW 10
    
    int counter = 0;
    int currentStateCLK;
    int lastStateCLK;
    String currentDir ="";
    unsigned long lastButtonPress = 0;
    
    void setup() {
    		
    	// Set encoder pins as inputs
    	pinMode(CLK,INPUT);
    	pinMode(DT,INPUT);
    	pinMode(SW, INPUT_PULLUP);
    
    	// Setup Serial Monitor
    	SerialUSB.begin(9600);
    
    	// Read the initial state of CLK
    	lastStateCLK = digitalRead(CLK);
    }
    
    void loop() {
    		
    	// Read the current state of CLK
    	currentStateCLK = digitalRead(CLK);
    
    	// If last and current state of CLK are different, then pulse occurred
    	// React to only 1 state change to avoid double count
    	if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){
    
    		// If the DT state is different than the CLK state then
    		// the encoder is rotating CCW so decrement
    		if (digitalRead(DT) != currentStateCLK) {
    			counter ++;
    			currentDir ="CW";
    		} else {
    			// Encoder is rotating CW so increment
    			counter --;
    			currentDir ="CCW";
    		}
    
    		SerialUSB.print("Direction: ");
    		SerialUSB.print(currentDir);
    		SerialUSB.print(" | Counter: ");
    		SerialUSB.println(counter);
    	}
    
    	// Remember last CLK state
    	lastStateCLK = currentStateCLK;
    
    	// Read the button state
    	int btnState = digitalRead(SW);
    
    	//If we detect LOW signal, button is pressed
    	if (btnState == LOW) {
    		//if 50ms have passed since last LOW pulse, it means that the
    		//button has been pressed, released and pressed again
    		if (millis() - lastButtonPress > 50) {
    			SerialUSB.println("Button pressed!");
    		}
    
    		// Remember last button press event
    		lastButtonPress = millis();
    	}}

    Capacitive Touch

    The Capcitive touch can be tested by using the test code provided by Neil in the Input Devices Class. The required library for this is Adafruit FreeTouch library which is given below.

    Adafruit FreeTouch Library
    
    //
    // hello.touch.D21.ino
    //    SAMD21 XIAO FreeTouch hello-world
    //
    // Neil Gershenfeld 7/23/24
    //
    // This work may be reproduced, modified, distributed,
    // performed, and displayed for any purpose, but must
    // acknowledge this project. Copyright is retained and
    // must be preserved. The work is provided as is; no
    // warranty is provided, and users accept all liability.
    //
    
    #include "Adafruit_FreeTouch.h"
    
    Adafruit_FreeTouch t6 = Adafruit_FreeTouch(2,OVERSAMPLE_64,RESISTOR_0,FREQ_MODE_NONE);
    Adafruit_FreeTouch t7 = Adafruit_FreeTouch(3,OVERSAMPLE_64,RESISTOR_0,FREQ_MODE_NONE);
    Adafruit_FreeTouch t8 = Adafruit_FreeTouch(4,OVERSAMPLE_64,RESISTOR_0,FREQ_MODE_NONE);
    Adafruit_FreeTouch t9 = Adafruit_FreeTouch(5,OVERSAMPLE_64,RESISTOR_0,FREQ_MODE_NONE);
    Adafruit_FreeTouch t10 = Adafruit_FreeTouch(6,OVERSAMPLE_64,RESISTOR_0,FREQ_MODE_NONE);
    
    int t6min,t7min,t8min,t9min,t10min;
    
    void setup() {
    	Serial.begin(115200);  
    	while (!Serial);
    	t6.begin();
    	t7.begin();
    	t8.begin();
    	t9.begin();
    	t10.begin();
    	t6min = t7min = t8min = t9min = t10min = 1e6;
    	}
    
    void loop() {
    	int result;
    	//
    	// plotting scale limits
    	//
    	SerialUSB.print(0);
    	SerialUSB.print(",");
    	SerialUSB.print(300);
    	SerialUSB.print(",");
    	//
    	// read touch
    	//
    	result = t6.measure();
    	if (result < t6min) t6min = result;
    	SerialUSB.print(result-t6min);
    	SerialUSB.print(",");
    	result = t7.measure();
    	if (result < t7min) t7min = result;
    	SerialUSB.print(result-t7min);
    	SerialUSB.print(",");
    	result = t8.measure();
    	if (result < t8min) t8min = result;
    	SerialUSB.print(result-t8min);
    	SerialUSB.print(",");
    	result = t9.measure();
    	if (result < t9min) t9min = result;
    	SerialUSB.print(result-t9min);
    	SerialUSB.print(",");
    	result = t10.measure();
    	if (result < t10min) t10min = result;
    	SerialUSB.println(result-t10min);
    	//
    	// pause
    	//
    	delay(50);
    }
    HID COntroller

    The below code allows me to control the media playback using the various capacitive touch button. I tmainly uses the HID-Project, HID-Settings, Adafruit_FreeTouch libraries. The major function that i used was Consumer.write The below given code is generated with the help of ChatGPT. Prompt: Using the above example generate the code to add mediaplayback using capacitive touch and volume control using the rotary encoder.

    
    #include <HID-Project.h>
    #include <HID-Settings.h>
    #include "Adafruit_FreeTouch.h"
    
    
    Adafruit_FreeTouch t0 = Adafruit_FreeTouch(2, OVERSAMPLE_64, RESISTOR_0, FREQ_MODE_NONE);
    Adafruit_FreeTouch t1 = Adafruit_FreeTouch(3, OVERSAMPLE_64, RESISTOR_0, FREQ_MODE_NONE);
    Adafruit_FreeTouch t2 = Adafruit_FreeTouch(4, OVERSAMPLE_64, RESISTOR_0, FREQ_MODE_NONE);
    Adafruit_FreeTouch t3 = Adafruit_FreeTouch(5, OVERSAMPLE_64, RESISTOR_0, FREQ_MODE_NONE);
    Adafruit_FreeTouch t4 = Adafruit_FreeTouch(6, OVERSAMPLE_64, RESISTOR_0, FREQ_MODE_NONE);
    
    // Rotary Encoder Inputs
    #define CLK 9
    #define DT 8
    #define SW 10
    
    int counter = 0;
    int currentStateCLK;
    int lastStateCLK;
    unsigned long lastButtonPress = 0;
    
    int baseline[5] = {1e6, 1e6, 1e6, 1e6, 1e6};
    
    void setup() {
    	// Set encoder pins as inputs
    	pinMode(CLK, INPUT);
    	pinMode(DT, INPUT);
    	pinMode(SW, INPUT_PULLUP);
    
    	// Start USB HID (Keyboard & Consumer Control)
    	Consumer.begin();
    	Keyboard.begin();
    	
    	// Setup Serial Monitor
    	SerialUSB.begin(9600);
    
    		while (!Serial);
    
    	// Initialize FreeTouch sensors
    	t0.begin(); t1.begin(); t2.begin(); t3.begin(); t4.begin();
    
    	// Calibrate baseline
    	for (int i = 0; i < 100; i++) {
    	baseline[0] = min(baseline[0], t0.measure());
    	baseline[1] = min(baseline[1], t1.measure());
    	baseline[2] = min(baseline[2], t2.measure());
    	baseline[3] = min(baseline[3], t3.measure());
    	baseline[4] = min(baseline[4], t4.measure());
    	delay(10);
    	}
    	// Read the initial state of CLK
    	lastStateCLK = digitalRead(CLK);
    }
    
    void loop() {
    	// Read the current state of CLK
    	currentStateCLK = digitalRead(CLK);
    
    	// Detect rotation
    	if (currentStateCLK != lastStateCLK && currentStateCLK == 1) {
    		if (digitalRead(DT) != currentStateCLK) {
    			// Clockwise Rotation (Increase Volume)
    			counter++;
    			Consumer.write(MEDIA_VOLUME_UP);
    			SerialUSB.println("Volume UP");
    		} else {
    			// Counterclockwise Rotation (Decrease Volume)
    			counter--;
    			Consumer.write(MEDIA_VOLUME_DOWN);
    			SerialUSB.println("Volume DOWN");
    		}
    	}
    
    	// Remember last CLK state
    	lastStateCLK = currentStateCLK;
    
    	// Read the button state
    	int btnState = digitalRead(SW);
    
    	// Detect button press (Play/Pause)
    	if (btnState == LOW) {
    		if (millis() - lastButtonPress > 200) {  // Debounce button
    			SerialUSB.println("Play/Pause");
    			Consumer.write(MEDIA_PLAY_PAUSE);
    		}
    		lastButtonPress = millis();
    	}
    
    	int touchValues[5] = {
    	t0.measure() - baseline[0],
    	t1.measure() - baseline[1],
    	t2.measure() - baseline[2],
    	t3.measure() - baseline[3],
    	t4.measure() - baseline[4]
    	};
    
    	int total = 0, weightedSum = 0;
    	for (int i = 0; i < 5; i++) {
    	if (touchValues[i] > 10) {  // Ignore noise
    		total += touchValues[i];
    		weightedSum += touchValues[i] * i;
    			
    	}
    	
    	}
    
    if(touchValues[2]>9){
    	Consumer.write(MEDIA_PLAY_PAUSE);
    	SerialUSB.println("Play/Pause");
    	SerialUSB.print(touchValues[2]);
    	delay(1000);
    }
    
    if(touchValues[1]>9){
    	Consumer.write(MEDIA_PREVIOUS);
    	SerialUSB.println("Previous");
    	SerialUSB.print(touchValues[1]);
    	delay(1000);
    }
    
    if(touchValues[3]>9){
    	Consumer.write(MEDIA_NEXT);
    	SerialUSB.println("Next");
    	SerialUSB.print(touchValues[3]);
    	delay(1000);
    }
    
    	delay(5);  // Small delay to stabilize readings
    }
    									

    Spotify Player

    Using the board I wanted to make a HID Controller which I could plug in my laptop and control playback. As the SAMD21 was a USB compatible device I could directly plug in my board a nd use it as a USB Controller.

    I used the HID Library to make the board into a controller. I had mainly 5 Touch pads and a rotary encoder along with its inbuilt push button switch. I wanted to use the touch pads as playback controls such as Paly/Pause, Next, Previous and Seek Forward/Backward.

    HID Wiki Documentation

    Yu can follow the above link to know more about HID and how it works and its various libraries.

    I used the HID Project and HID Settings Library which is available in the Library Manager of Arduino by default. We just have to install it to use the library from the library manager. The main function that I used was the Consumer function whih allows to control Media Playback and some functions. There are twothings happening mainly: Control the Media Playback and opening and closing Spotify on the Windows Device.

    To control the Media Playback, the consumer function provides with functions like:

    
    // Media key definitions, see official USB docs for more
    #define MEDIA_FAST_FORWARD	0xB3
    #define MEDIA_REWIND	0xB4
    #define MEDIA_NEXT	0xB5
    #define MEDIA_PREVIOUS	0xB6
    #define MEDIA_STOP	0xB7
    #define MEDIA_PLAY_PAUSE	0xCD
    
    #define MEDIA_VOLUME_MUTE	0xE2
    #define MEDIA_VOLUME_UP	0xE9
    #define MEDIA_VOLUME_DOWN	0xEA
    
    #define CONSUMER_EMAIL_READER	0x18A
    #define CONSUMER_CALCULATOR	0x192
    #define CONSUMER_EXPLORER	0x194
    
    #define CONSUMER_BROWSER_HOME	0x223
    #define CONSUMER_BROWSER_BACK	0x224
    #define CONSUMER_BROWSER_FORWARD	0x225
    #define CONSUMER_BROWSER_REFRESH	0x227
    #define CONSUMER_BROWSER_BOOKMARKS	0x22A		
    	

    YOu can refere to thislink to know more about the Consumer API.

    
    
    	

    For opening and closing Spotify App on the Windows PC, I am using the standard keyboard functions to emulate key presses to open the run command and typing the command to open Spotify and Close Spotify.

    ChatGPT prompts:

    • I have a rotary encoder connected to a custom SAMD21 board. I want to use it to control PC volume and playback via USB using HID-Project.h
    • I've added 5 capacitive touch pads using Adafruit_FreeTouch, I want to control media playback using the consumer functions. Give me code
    • I want to open and close spotify using the keyboard functions, I want to do this when a key is held for longer than 2 seconds.

    #include #include #include "Adafruit_FreeTouch.h" #include "Adafruit_NeoPixel.h" Adafruit_FreeTouch t0 = Adafruit_FreeTouch(2, OVERSAMPLE_64, RESISTOR_0, FREQ_MODE_NONE); Adafruit_FreeTouch t1 = Adafruit_FreeTouch(3, OVERSAMPLE_64, RESISTOR_0, FREQ_MODE_NONE); Adafruit_FreeTouch t2 = Adafruit_FreeTouch(4, OVERSAMPLE_64, RESISTOR_0, FREQ_MODE_NONE); Adafruit_FreeTouch t3 = Adafruit_FreeTouch(5, OVERSAMPLE_64, RESISTOR_0, FREQ_MODE_NONE); Adafruit_FreeTouch t4 = Adafruit_FreeTouch(6, OVERSAMPLE_64, RESISTOR_0, FREQ_MODE_NONE); // Rotary Encoder Inputs #define CLK 9 #define DT 8 #define SW 10 #define PIN 11 #define NUMPIXELS 5 #define IDLE_TIME 3000 // Time in milliseconds to wait before idle effect int lastDirection = 0; // 1 = Right, -1 = Left int isPlaying = 1; int counter = 0; int currentStateCLK; int lastStateCLK; unsigned long lastButtonPress = 0; int baseline[5] = {1e6, 1e6, 1e6, 1e6, 1e6}; Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); unsigned long lastActivityTime = 0; bool isIdle = true; void setup() { pinMode(CLK, INPUT); pinMode(DT, INPUT); pinMode(SW, INPUT_PULLUP); Consumer.begin(); Keyboard.begin(); Consumer.write(MEDIA_STOP); SerialUSB.begin(9600); pixels.begin(); // Initialize FreeTouch sensors t0.begin(); t1.begin(); t2.begin(); t3.begin(); t4.begin(); // Calibrate baseline for (int i = 0; i < 100; i++) { baseline[0] = min(baseline[0], t0.measure()); baseline[1] = min(baseline[1], t1.measure()); baseline[2] = min(baseline[2], t2.measure()); baseline[3] = min(baseline[3], t3.measure()); baseline[4] = min(baseline[4], t4.measure()); delay(10); } lastStateCLK = digitalRead(CLK); } void showFlow(int direction) { isIdle = false; lastActivityTime = millis(); for (int i = 0; i < NUMPIXELS; i++) { pixels.clear(); for (int j = 0; j <= i; j++) { int index = (direction == 1) ? j : (NUMPIXELS - 1 - j); if (direction == 1) { pixels.setPixelColor(index, pixels.Color(0, 255 - (j * 50), 0)); // Green Flow (Right) } else { pixels.setPixelColor(index, pixels.Color(255 - (j * 50), 0, 0)); // Red Flow (Left) } } pixels.show(); delay(50); } } void openSpotify() { Keyboard.press(KEY_LEFT_GUI); // Press Windows key (for Windows) or Command key (for Mac) Keyboard.press('r'); // Press 'R' to open Run dialog (Windows only) delay(200); Keyboard.releaseAll(); delay(200); Keyboard.print("spotify"); // Type "spotify" delay(200); Keyboard.press(KEY_RETURN); // Press Enter Keyboard.releaseAll(); } void showIdleEffect() { static int brightness = 0; static int fadeDirection = 1; brightness += fadeDirection * 5; if (brightness >= 150 || brightness <= 0) { fadeDirection *= -1; } for (int i = 0; i < NUMPIXELS; i++) { if (isPlaying) { pixels.setPixelColor(i, pixels.Color(0, brightness, 0)); // Green when playing } else { pixels.setPixelColor(i, pixels.Color(300, 0, 0)); // Purple when paused } } pixels.show(); delay(50); } void loop() { pixels.clear(); currentStateCLK = digitalRead(CLK); if (currentStateCLK != lastStateCLK && currentStateCLK == 1) { if (digitalRead(DT) != currentStateCLK) { counter++; Consumer.write(MEDIA_VOLUME_UP); SerialUSB.println("Volume UP"); lastDirection = 1; showFlow(lastDirection); } else { counter--; Consumer.write(MEDIA_VOLUME_DOWN); SerialUSB.println("Volume DOWN"); lastDirection = -1; showFlow(lastDirection); } } lastStateCLK = currentStateCLK; int btnState = digitalRead(SW); if (btnState == LOW) { if (millis() - lastButtonPress > 200) { SerialUSB.println("Play/Pause"); Consumer.write(MEDIA_PLAY_PAUSE); } lastButtonPress = millis(); } int touchValues[5] = { t0.measure() - baseline[0], t1.measure() - baseline[1], t2.measure() - baseline[2], t3.measure() - baseline[3], t4.measure() - baseline[4] }; if (touchValues[0] > 9) { Consumer.write(MEDIA_REWIND); SerialUSB.println("Rewind"); delay(1000); } if (touchValues[1] > 9) { Consumer.write(MEDIA_PREVIOUS); SerialUSB.println("Previous"); delay(1000); } static unsigned long touchStartTime = 0; static bool touchHeld = false; static bool isSpotifyOpen = false; // Track Spotify state if (touchValues[2] > 9) { if (!touchHeld) { touchStartTime = millis(); // Start timer touchHeld = true; } if (millis() - touchStartTime > 2000) { // If held for more than 2 seconds if (isSpotifyOpen) { Keyboard.press(KEY_LEFT_GUI); Keyboard.press('r'); // Open Run delay(200); Keyboard.releaseAll(); delay(200); Keyboard.print("taskkill /IM spotify.exe /F"); delay(200); Keyboard.press(KEY_RETURN); Keyboard.releaseAll(); } else { SerialUSB.println("Opening Spotify"); openSpotify(); } isSpotifyOpen = !isSpotifyOpen; // Toggle state touchHeld = false; // Reset flag to prevent repeated triggers } } else if (touchHeld) { // If released before 2 seconds, Play/Pause if (millis() - touchStartTime <= 2000) { isPlaying = !isPlaying; Consumer.write(MEDIA_PLAY_PAUSE); SerialUSB.println("Play/Pause"); } touchHeld = false; // Reset flag } if (touchValues[3] > 9) { Consumer.write(MEDIA_NEXT); SerialUSB.println("Next"); delay(1000); } if (touchValues[4] > 9) { Consumer.write(MEDIA_FAST_FORWARD); SerialUSB.println("Fast Forward"); delay(500); if (touchValues[4] > 9) { Consumer.write(MEDIA_FAST_FORWARD); } } delay(5); if (millis() - lastActivityTime > IDLE_TIME) { isIdle = true; lastDirection = 0; // **Fix: Reset direction when going idle** } if (isIdle) { showIdleEffect(); } }

    I have added the light functions during the output week's assignment which will be covererd in that documentation.

    Issues faced

    During the schematic and PCB design I missed out on some important power connections and also the Serial Debug Pins which are crucial to the programming and functioning of the board. Saheen helped me to realsie the need for those connections and thus I made those changes and milled a new PCB.

    While programming the SAMD21, I faced an issue with the USB C connection. The board was not being detected by the computer. After some debugging with Saheen, we realised that the USB C Cable was an issue, I had to use another USB Cable provide by fellow classmate Thomas and an additional USB Hub. I think it might be an powering issue.

    The rotary encoder was not very accurate, as it as not reading values when I was turning the knob faster. I think I need to add capacitors to the rotary encoder pins.

    Design Files

    You can download my design files from below

    • TouchNav SAMD21 Board

    • KiCad Files

    x