Final project: Project A.M.E.C.
Slide Presentation
Video Presentation
What is it?
The idea of the project A.M.E.C. (Auditive Modern Electronic Clothing) is to create tech-clothe that by bluetooth and a tactil sensor be capable to make the same tasks that a smartwatch does answer calls or control the music you are listening to.
Characteristics
Connectivity: Connected to your phone via bluetooth
Tactil sensor: A tactil sensor to answer calls, control your music.
How is it going to work?
The way the hoodie will know what gesture you are doing is by using a touchpad sensor made of conductive thread. The touchpad will send the positions of your finger to the PCB and, the PCB, will interpret those position and will know what gesture it is. The PCB will be placed on the arm and it will be easily removable so it can be charge.
What will it do?
By using conductive threas will detect the positions of your finger and using that information will know what kind of gestures the user is doing. With this gestures the user can make certain actions like change the song, turn up or down the volume, accept calls, etc.
WorkFlow
To have an order on this project I decided to make a cronogram with all the tasks I have to do, it starts on January 24th with the creation of the sketch and final project page, and it is planned to end on June 8th with the creation of the Slide and Video for the presentation.
I also decided to make a calendar to show the exact day when I make the individual tasks. You can click on the task to see what exactly a did on that day.
Sun | Mon | Tue | Wed | Thu | Fri | Sat |
---|
Previous Works
Project Jacquard
Google's Advanced Technology and Projects group (ATAP) created a project called "Jacquard". The idea of the project seeks to create smart garments capable of performing the same actions as a smart watch but unfortunately, Google shut down the project for unkown reasons. The only launched a jacket in collaboration with Levis.
Madison Maxey
Masison Maxey, the CEO and founder of Loomia, devoled a proof of concept for a foldable, drapable, large surface area capacitive touch textile using an Adafruit Flora microcontroller. The idea is a large user interface that can be folded and carried for creative work. This Textile trackpad can be used to replace a computer mouse and could also be used to model, sketch and draw in the computer.
Materials
Material | Qty | Price (Dollars) | Link |
---|---|---|---|
XIAO ESP32C3 | 1 | $20.02 | Buy Here! |
MPR121 | 1 | $5.82 ea. | Buy Here! |
Conductive Thread | 1 | $18.16 | Buy Here! |
Male Pin Header | 12 | $13.55 | Buy Here! |
Female Pin Header | 12 | $9.95 | Buy Here! |
Push Button | 1 | $0.16 | Buy Here! |
Switch button | 1 | $0.33 ea. | Buy Here! |
NeoPixel WS2812B | 1 | $0.49 | Buy Here! |
Magnets | 6 | $0.081 ea. | Buy Here! |
Athletic Sleeve | 1 | $10.92 | Buy Here! |
3.7V Lipo Battery | 1 | $4.31 | Buy Here! |
Total: $84.196 |
MPR121 TEST
Before starting the project I have to cheeck if the MPR121 Module will workm to do this .
Matrix
I had to know how to make the matrix in order to test if the sensor was going to work. To do this I started with a prototype made with masking tape copper tape.
The idea is simple, first you paste the copper tape stripes in one direction, then you cover it with masking tape (this will prevent the tapes of both direction from being in contact) and place the opposite direction copper in top. Finally you will have to cut the excess of the masking tape.
This will prevent the copper tapes to touch between them but when you put your finger in it, beacuse the size of the finger is bigger, will touch both and I will know which row and columm is being touch.
Code
With the prototype done it was time to test the module, I made the following code to test it:
#include < Wire.h > //Library to communicate with I2C
#include < Adafruit_MPR121.h > //Library of the MPR121 module
// Create the sensor object
Adafruit_MPR121 cap = Adafruit_MPR121();
int X_Position = 0; // Track the value of x position
int Y_Position = 0; // Track the value of y position
void setup() {
Serial.begin(9600);
// Initialize the sensor
if (!cap.begin(0x5A)) { // Default I2C address for MPR121 is 0x5A
while (1);
}
}
void loop() {
// Get the currently touched pads
int touched = cap.touched();
// Reset x and y positions
X_Position = 0;
Y_Position = 0;
// Check each pad to see if it is currently touched
for (int i = 0; i < 12; i++) {
if (touched & (1 << i)) {
if (i < 6) { // Pins 0-5
X_Position = i + 1; // Save the position (1-based)
}
else { // Pins 6-11
Y_Position = (i - 6) + 1; // Save the position (1-based)
}
}
}
// Print the status of x and y positions
Serial.print("X Position: ");
Serial.print(X_Position);
Serial.print(" - Y Position: ");
Serial.println(Y_Position);
delay(100); // Small delay to avoid flooding the serial output
}
Final Result
PCBs Creation
To design my PCBs I used the software we learn on our week 8 assignment, KiCad. I used this software beacuse I am already familiar with it and beacuse I have almost all the components' footprints on it thanks to the libraries our local evaluators gave us.
microcontroller's PCB
Here is the Schematic of the microcontroller's PCB.
Microcontroller (XIAO ESP32C3)
As you can see, I only needed to connect a few thing to the XIAO.
Vi pin: This is the pin that will reacieve the voltaje from the battery.
Vo pin: This pin will turned that voltaje in 3V, and will be directed to the Neopixel and the MPR121 module.
GND pin: This pin is the one connected to the ground from the battery.
D1 pin: This pin is the one connected to the NeoPixel.
D7 pin: This pin is the one connected to the Push Butten.
SDA and SCL pins:This pins come from the Xiao and will send the data to it via I2C communication.
MPR121 Module
The are the pins that I had to connect for the module.
Vo pin: This pin will come from the Xiao beacuse the max voltaje that the MPR121 allows is 3.3V.
GND pin: This pin is the one connected to the ground from the battery.
SDA and SCL pins: This pins are connected to the MPR121 module, it is a I2C communication, so the module can send which pads are baing touch.
Voltajes pins
This are the voltaje pins and they will be connected to the battery and the switch button. The first two pins of the top are the ones connected to the switch button and their purpouse is to control if or not the voltaje from the battery to the Xiao.
The other two pins of the button are the ones that will be connected to the battery, one is for the voltaje and the other for the ground.
Button
Here is how I connect the button that will switch the different profiles that the device will have.
NeoPixel WS2812B
Here is how I connected the Neo Pixel that will show which profile is being use.
Extra
I had trouble connecting the things while design the PCB that why I will used a 0k Resister to use it as bridge.
The designed of the PCB was something simple but complicated to connect beacuse I wanted to make it as small as possible.
Finally I cut the PCB on the Mini-Mill
MPR121's PCB
I also had to design a PCB to change the direction of the touchpad pins of the module. Also I need to have 6 pins on top and 6 on the botton, thats why I made two.
Here is the desing of these PCBs, as you can see the direction of the pins 90°.
I needed to make them as small and thin as posible thats why I made them flexible.
Using some twizzers I took out the extra copper.
Finally I just solder to the pins and to the MPR121 Module.
Here is the Final Result of the electonics.
You can find the files of the PCBs at the bottom the page on the File section.
Case Creation
Designing
With the PCBs done the next thing I did was its case, to do this I used the software SolidWorkd because I am very familiar with it.
I needed to have in mind certain aspects when designing the case:
Try to make it small as possible
Make spaces for the different components (Neopixel, buttons and pins).
With this in mind I started designing the upper part of the case.
Then, I made the lower part of the case which contains the spaces for the buttons and neopixel.
Here is a render of the assembly.
With both parts designed I saved them as .stk files and used the software Cura to convert it in .gcode files.
Printing
I used the Ender 3 S1 PRO to print the case.
With the case printed, I juts put all the electronic inside and this si how it end up.
You can find the files of the PCB Case at the bottom the page on the File section.
Sleeve Creation
To create the sleeve that is going to detect the position and gestures of the finger I used an athletic sleeve as the main part, then I will laser cut a piece of clothe in which I am going to make the matrix with the conductive thread.
I also need to make the 3d parts that will support pins.
With the matrix and 3d part done I just sewed them all in the sleeve.
You can find the file of the pin support at the bottom the page on the File section.
App Creation
To create the app I used the MIT App Inventor page beacuse I am already familiar with it.
Design
I got the symbols from Google Fonts. This symbols are open source free and without cost.
I used Power Point to make a "draft" of the app so I can make an idea of how I want the app to look like.
Here is the design part of the app on App Inventor.
Program
The coding of App Inventor is with blocks, her is an image of all the blocks that make the app work.
Initial Values
Here all all the values that the app starts with.
Bluetooth
This blocks make the bluetooth connection work. When the app opens it automatically looks for the device and if it is not found, it will look again until is connected.
App customization
All this blocks make the app works, they are "in charge" of the customization. The blocks of the first column save the customization the user made and sends it to the divice.
The ones on the second and third column are the ones that modify and save the customization but only for the app.
Song tracks
Lastly this are the blocks that when they recieve the what gesterures the user is doing they make actions of that gesture.
You can find the link of the app at the bottom the page on the File section. (It is a link beacuse, the size of the app its too big to upload in the page.)
Programming
The code is in C++ and I used Arduino to make it. I want to show you a few sections of the code that I consider are important before showing you de whole code.
Libraries
This are the libraries that I needed to make the code work.
//BLE libraries
#include < BLEDevice.h >
#include < BLEUtils.h >
#include < BLEServer.h >
#include < Adafruit_NeoPixel.h > //Library of the NeoPixel
#include < Arduino.h > //Arduino library
#include < Preferences.h > //Library to save the data into the xiao
#include < Wire.h > //Libraryto communicate with I2C
#include < Adafruit_MPR121.h > Library of the MPR121 Module
Variables
The are the variables that the code will work with.
Preferences preferences; //Iniate an instance of the preference
// Create the sensor object
Adafruit_MPR121 cap = Adafruit_MPR121();
String Gesture; //String of the gestures
int currentXPosition = 0; // Track the current value of x position
int currentYPosition = 0; // Track the current value of y position
int previousXPosition = 0; // Track the previous value of x position
int previousYPosition = 0; // Track the previous value of y position
unsigned long lastTapTime = 0; // Time of the last tap
// Maximum delay between taps for double tap detection (milliseconds)
const unsigned long doubleTapDelay = 400;
//Button
int Mode = 0; //Change modes of the device
int flag = 0; //Avoid over pressing
//Neopixel
int PIN = 2;
#define NUMPIXELS 1
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
//Bt DATA - The data that the device will recieve from the app
String redBT;
String greenBT;
String blueBT;
String modeBT;
int change = 1;
int modeS = 0;
//DATA
int Data[3][3] = {
{255, 0, 0}, // Mode 1 {R, G, B}
{0, 255, 0}, // Mode 2
{0, 0, 255} // Mode 3
};
//Rows and colummns to save the data
const int rows = 3;
const int cols = 3;
//Bluetooth
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
BLECharacteristic *pCharacteristic; // Declare pCharacteristic as a global variable
Save and Load Data
I wanted the device to save the data if the user customize these are the functions that will make those tasks.
// Function that saves data
void saveData() {
preferences.begin("matrix", false);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
String key = "cell" + String(i) + "_" + String(j);
preferences.putInt(key.c_str(), Data[i][j]);
}
}
preferences.end();
}
//Function that loads data
void loadData() {
Serial.println("Loading Data...");
preferences.begin("matrix", true);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
String key = "cell" + String(i) + "_" + String(j);
// Default 0 if not exists
Data[i][j] = preferences.getInt(key.c_str(), 0);
Serial.println(Data[i][j]);
}
}
preferences.end();
}
Recieve data via BLE
This is the part of the code that will recieve data and save it in each value.
//Recieve data via BLE
class MyCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String value = pCharacteristic->getValue();
redBT = "";
greenBT = "";
blueBT = "";
if (value.length() > 0) {
Serial.println("*********");
Serial.print("New value: ");
for (int i = 0; i < value.length(); i++) {
// MODES
if (value[i] == 'A') {
modeS = 0;
}
if (value[i] == 'B') {
modeS = 1;
}
if (value[i] == 'C') {
modeS = 2;
}
// RGB
if (value[i] == ',') {
change = change + 1;
}
if (change == 1 && isdigit(value[i])) {
redBT = redBT + value[i];
}
else if (change == 2 && isdigit(value[i])) {
greenBT = greenBT + value[i];
}
else if (change == 3 && isdigit(value[i])) {
blueBT = blueBT + value[i];
}
}
Data[modeS][0] = redBT.toInt();
Data[modeS][1] = greenBT.toInt();
Data[modeS][2] = blueBT.toInt();
Serial.print("Mode = ");
Serial.println(modeS);
Serial.println();
Serial.println("LED COLOR");
Serial.print("Red = ");
Serial.println(Data[modeS][0]);
Serial.print("Green = ");
Serial.println(Data[modeS][1]);
Serial.print("Blue = ");
Serial.println(Data[modeS][2]);
Serial.println();
Serial.println("*********");
change = 1;
modeS = 0;
saveData();
}
}
};
Final Code
//BLE libraries
#include < BLEDevice.h >
#include < BLEUtils.h >
#include < BLEServer.h >
#include < Adafruit_NeoPixel.h > //Library of the NeoPixel
#include < Arduino.h > //Arduino library
#include < Preferences.h > //Library to save the data into the xiao
#include < Wire.h > //Libraryto communicate with I2C
#include < Adafruit_MPR121.h > Library of the MPR121 Module
Preferences preferences; //Iniate an instance of the preference
// Create the sensor object
Adafruit_MPR121 cap = Adafruit_MPR121();
String Gesture; //String of the gestures
int currentXPosition = 0; // Track the current value of x position
int currentYPosition = 0; // Track the current value of y position
int previousXPosition = 0; // Track the previous value of x position
int previousYPosition = 0; // Track the previous value of y position
unsigned long lastTapTime = 0; // Time of the last tap
// Maximum delay between taps for double tap detection (milliseconds)
const unsigned long doubleTapDelay = 400;
//Button
int Mode = 0;
int flag = 0;
//Neopixel
int PIN = 2;
#define NUMPIXELS 1
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
//Bt DATA
String redBT;
String greenBT;
String blueBT;
String modeBT;
int change = 1;
int modeS = 0;
//DATA
int Data[3][3] = {
{255, 0, 0}, // Mode 1 {R, G, B}
{0, 255, 0}, // Mode 2
{0, 0, 255} // Mode 3
};
const int rows = 3;
const int cols = 3;
//Bluetooth
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
BLECharacteristic *pCharacteristic; // Declare pCharacteristic as a global variable
// Function that saves data
void saveData() {
preferences.begin("matrix", false);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
String key = "cell" + String(i) + "_" + String(j);
preferences.putInt(key.c_str(), Data[i][j]);
}
}
preferences.end();
}
//Function that loads data
void loadData() {
Serial.println("Loading Data...");
preferences.begin("matrix", true);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
String key = "cell" + String(i) + "_" + String(j);
Data[i][j] = preferences.getInt(key.c_str(), 0); // Default 0 if not exists
Serial.println(Data[i][j]);
}
}
preferences.end();
}
//Recieve data via BLE
class MyCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String value = pCharacteristic->getValue();
redBT = "";
greenBT = "";
blueBT = "";
if (value.length() > 0) {
Serial.println("*********");
Serial.print("New value: ");
for (int i = 0; i < value.length(); i++) {
// MODES
if (value[i] == 'A') {
modeS = 0;
}
if (value[i] == 'B') {
modeS = 1;
}
if (value[i] == 'C') {
modeS = 2;
}
// RGB
if (value[i] == ',') {
change = change + 1;
}
if (change == 1 && isdigit(value[i])) {
redBT = redBT + value[i];
}
else if (change == 2 && isdigit(value[i])) {
greenBT = greenBT + value[i];
}
else if (change == 3 && isdigit(value[i])) {
blueBT = blueBT + value[i];
}
}
Data[modeS][0] = redBT.toInt();
Data[modeS][1] = greenBT.toInt();
Data[modeS][2] = blueBT.toInt();
Serial.print("Mode = ");
Serial.println(modeS);
Serial.println();
Serial.println("LED COLOR");
Serial.print("Red = ");
Serial.println(Data[modeS][0]);
Serial.print("Green = ");
Serial.println(Data[modeS][1]);
Serial.print("Blue = ");
Serial.println(Data[modeS][2]);
Serial.println();
Serial.println("*********");
change = 1;
modeS = 0;
saveData();
}
}
};
void setup() {
// Serial
Serial.begin(115200);
Serial.println("Setting up");
preferences.begin("my-app", false);
loadData();
// Bt
BLEDevice::init("AMEC");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic
(CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ
| BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY);
pCharacteristic->setCallbacks(new MyCallbacks());
pCharacteristic->setValue("Hello World");
pService->start();
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->start();
// Initialize Pixel
pixels.begin();
pixels.clear();
pixels.show();
// Button
pinMode(D6, INPUT);
// Initialize the sensor
if (!cap.begin(0x5A)) { // Default I2C address for MPR121 is 0x5A
while (1);
}
}
void loop() {
// Get the currently touched pads
int touched = cap.touched();
// Reset current positions
currentXPosition = 0;
currentYPosition = 0;
// Check each pad to see if it is currently touched
for (int i = 0; i < 12; i++) {
if (touched & (1 << i)) {
if (i < 6) { // Pins 0-5
currentXPosition = i + 1; // Save the position (1-based)
}
else { // Pins 6-11
currentYPosition = (i - 6) + 1; // Save the position (1-based)
}
}
}
if (digitalRead(D6) == HIGH) {
if (Mode < 2 && flag == 0) {
flag = 1;
Mode = Mode + 1;
Serial.println("Button pressed");
}
if (Mode == 2 && flag == 0) {
Mode = -1;
}
}
else if (digitalRead(D6) == LOW) {
flag = 0;
}
if (Mode == 0) {
pixels.clear();
pixels.setPixelColor(0, pixels.Color(Data[0][0], Data[0][1], Data[0][2]));
pixels.show();
}
else if (Mode == 1) {
pixels.clear();
pixels.setPixelColor(0, pixels.Color(Data[1][0], Data[1][1], Data[1][2]));
pixels.show();
}
else if (Mode == 2) {
pixels.clear();
pixels.setPixelColor(0, pixels.Color(Data[2][0], Data[2][1], Data[2][2]));
pixels.show();
}
// Detect gestures
if (currentXPosition != 0 || currentYPosition != 0) {
// Detect double tap
unsigned long currentTime = millis();
// Detect swipe gestures
if (previousXPosition != 0 && previousYPosition != 0) {
if (currentXPosition == previousXPosition) {
if (currentYPosition < previousYPosition) {
Serial.println("Swipe Up Detected");
Gesture="SU";
// Convert std::string to C-style string
pCharacteristic->setValue(Gesture.c_str());
pCharacteristic->notify();
delay(2000);
}
else if (currentYPosition > previousYPosition) {
Serial.println("Swipe Down Detected");
Gesture="SD";
// Convert std::string to C-style string
pCharacteristic->setValue(Gesture.c_str());
pCharacteristic->notify();
delay(2000);
}
}
else if (currentYPosition == previousYPosition) {
if (currentXPosition < previousXPosition) {
Serial.println("Swipe Left Detected");
Gesture="SL";
// Convert std::string to C-style string
pCharacteristic->setValue(Gesture.c_str());
pCharacteristic->notify();
delay(2000);
}
else if (currentXPosition > previousXPosition) {
Serial.println("Swipe Right Detected");
Gesture="SR";
// Convert std::string to C-style string
pCharacteristic->setValue(Gesture.c_str());
pCharacteristic->notify();
delay(2000);
}
}
}
// Update previous positions
previousXPosition = currentXPosition;
previousYPosition = currentYPosition;
}
else {
// Reset previous positions if no touch detected
previousXPosition = 0;
previousYPosition = 0;
}
delay(100);
}
You can find the file of the Arduino code at the bottom the page on the File section.
Final Result
Here are some photos and videos of the Final Result.
Files
A.M.E.C. (Auditive Modern Electronic Clothing) by Juan Carlos Chávez Cortés is licensed under Creative Commons Attribution-NonCommercial 4.0 International