17. Applications and implications - Project Development¶
Hero Shot¶
Assignment :
- Propose a final project masterpiece that integrates the range of units covered, answering:
- What will it do?
- Who’s done what beforehand?
- What will you design?
- What materials and components will be used?
- Where will come from?
- How much will they cost?
- What parts and systems will be made?
- What processes will be used?
- What questions need to be answered?
- How will it be evaluated?
Your project should incorporate 2D and 3D design, additive and subtractive fabrication processes, electronics design and production, embedded microcontroller interfacing and programming, system integration and packaging where possible, you should make rather than buy the parts of your project. Projects can be separate or joint, but need to show individual mastery of the skills, and be independently operable.
- Complete your final project, tracking your progress:
- what’s working? what’s not?
- what questions need to be resolved?
- what will happen when?
- what have you learned?
Propose a final project masterpiece¶
Questions to answer¶
What will it do ?¶
My project is a remote device thought as a stim toy or fidget and that embed the possibility to share one’s mstate of mind. Using a rotary encoder, the user can share their mood and using a set of rocker switches the level of interaction/sociability they accept.
The informations are displayed as smiley face as it’s an international form of communicating even without reading hability. The program matches both informations to display the third one that mix the previous. I used Emojikitchen that allow uses for other projects.
It is display on a screen near the user (Phone, bracer or pendant) to be shared with people around.
Who’s done what beforehand?¶
Fidgets and stim toys are pretty commons and became extremely popular with the hand spinner but they existed long before in numbers of form (worry beads,Stress Ball,…). They are used to release stress, to help focus or for “stimming”.
My design is a bit inspired by the Fidget Cube that has multiple fidgets on it especially for proprioceptive and tactile stimming.
I thought I could make my own that could also help at self-awareness and communication.
What will you design?¶
I’ll design the remote control fidget shell, the electronics, the knob and the interface.
What materials and components will be used?¶
Nb | Element | Link | Price (Units) | Price (total) | Remarks |
---|---|---|---|---|---|
1 | Micro-controller ESP32-WROOM-32UE | digikey | 2.50$ | 2.50$ | |
1 | KY-040 Rotary Encoder | Amazon | ~1.50$ | 1.50$ | |
5 | Rocker Switch | e-bay | ~1$ | 5$ | Cut-out 19x13mm |
1 | Button | Digikey | ~1$ | 1$ | |
1 | Regulator 3.3V 1A | Digikey | 0.51$ | 0.51$ | Nolonger manufactured. Prefer [this one(https://www.digikey.com/en/products/detail/diodes-incorporated/AZ1117IH-3-3TRG1/5699682)] |
1 | vertical connector SMD 1x05 2.54mm | Digikey | 0.68$ | 0.68$ | suppressed for direct soldering |
1 | horizontal connector SMD 1x06 2.54mm | Digikey | 0.93$ | 0.93$ | FTDI |
1 | vertical connector SMD 2x05 2.54mm | Digikey | 0.84$ | 0.84$ | for switches |
3 | JST connector 2 pins + sockets | Digikey | 0.54$ (8.95$for 100 pins kit) | 0.54$ | 1 modified to be soldered SMD |
1 | resistor 10k | Digikey | 0.10$ | 0.10$ | |
1 | resistor 1K | Digikey | 0.10$ | 0.10$ | |
2 | resistor O ohm | Digikey | 0.10$ | 0.20$ | |
1 | LED Clear Blue | Digikey | 0.38$ | 0.38$ | |
1 | capacitor 0.1 uF | Digikey | 0.33$ | 0.33$ | |
1 | capacitor 1 uF | Digikey | 0.25$ | 0.25$ | |
1 | capacitor 10 uF | Digikey | 0.60$ | 0.60$ | different packaging in the lab |
1 | switch slide | Digikey | 1.39$ | 1.39$ | |
15 | cable 1.27mm*10 | Digikey | ~0.012$ (88.32$ for 30.48m) | ~ 0.17$ | ~ 4cm length each |
10 | single dupont connectors + sockets | Amazon | 0.03$ (12.59$ for 400 pins) | 0.30$ | |
5 | Hook-up Wire Red and Black | RS components | 0.03$ (8.67$/100m) | 0.03$ | ~ 6 cm medium length |
1 | CCL FR1 5.00” x 4.00” | Digikey | 2.56$ (64.04/25pcs) | 2.56$ | |
1 | electrical tape | Digikey | 0.12$ (140.16$ the tape of 54’) | 0.12$ | 1/2” used for battery holder |
10 | heat shrinkables tube D:4.8mm * 20 mm | PVN | ~0.04$ (14.21€ for 85*100mm pcs) | ~ 0.40 $ | about 2 x 100mm tubes |
1 | PLA black | MatterHackers | 0.8$ (57$/kg) | 0.8$ | 14g - local reference missing |
1 | PLA-PHA yellow | colorFabb | 5.75$ (19.86$/750g) | 5.75$ | 217g |
1 | screw M4 12mm | RS components | 0.18$ | 0.18$ | to fix the knob |
1 | nut 4mm | RS components | 0.03 $ | 0.03$ | included in the knob print |
Display part
Nb | Element | Link | Price (Units) | Price (total) | Remarks |
---|---|---|---|---|---|
1 | ESP32-2432S028 | AliExpress | 19.84$ (18.47€) | 19.84$ | usb cable included |
1 | Powerbank | Leclerc | 15.72$ (14.64€) | 15.72$ | From Mall. Dim: 62.5 x 14.5 x 9.9 |
1 | Micro SD Card | Digikey | 3.61$ | 3.61$ | 3 Mo needed |
1 | PLA black | MatterHackers | 1.54$ (57$/kg) | 1.54$ | For holder and screen case - 27g - local reference missing |
1 | PETG black | Colorfabb | 0.04$ (158.15$/8kg) | 0.04$ | For clips(*2) - 2g |
1 | Fabrics 50*6cm | Mondial tissus | 0.15$ (7.62$ for 1*1.50m) | 0.15$ | cutted part from curtains |
1 | Sewing thread | Mondial tissus | >0.01$(4.47$/2743m) | >0.01$ | less than 1m |
Where will come from?¶
All components are provided by the Fablab, most of the components came from [Digikey(https://www.digikey.com/rs)] and RS Components.
How much will they cost?¶
27.19 $ for the remote control and 40.90 $ for the display
What parts and systems will be made?¶
For the remote control, the pcb, the battery holder, the shell and the knob are made.
What processes will be used?¶
I’ll use 3d printing, pcb milling, soldering, programming (C++, html, javascript). I also added vinylcutting for information on the case.
What questions need to be answered?¶
I still need to figure out how to make it more comprehensive for people not sensibilize to this kind of disorder. Also, all my parts are additive fabrication processes and I need to find a way to implement substractive fabrication processes.
How will it be evaluated?¶
I will test it with different people to figure if usage is comprehensive. In the absolute, testing with people with social interaction disorder and ask if they find it useful in their daily living or during specific activities.
Project Development - Tracking progress¶
Electronics¶
I started by finalizing the electronics production. PCB was milled during previous week and I had to solder components I already prepared on it. ESP32 chip was trickier to solder than previous Xiao Board but I managed to do it. I made quite a mess on the pins near the vertical connectors, almost all them (except the GND one) are connected to each other but hopefully none of them are used on my board. Next times, I will avoid soldering pins that aren’t connected to anything. I tested it and could upload a blink program in it.
I then prepared the wiring for the switches. I modified Dupont wires to solder them on the switches (I found out later I could have make my own connectors).
I added shrinkables tube to avoid contacts. Having no spring to maintain the contact in the battery holder, I chose to use a piece of sponge. To make the actual, I used copper tape with a lot of tin With all this parts the electronics of the remote device was complete.
A bit later, I added a switch between the battery and the board to turn off the device.
Pin references :¶
SW : IO34
DT : IO35
CLK : IO32
Rocker Switch 1 : IO33 (internal pull-up)
Rocker Switch 2 : IO25 (internal pull-up)
Rocker Switch 3 : IO26 (internal pull-up)
Rocker Switch 4 : IO27 (internal pull-up)
Rocker Switch 5 : IO14 (internal pull-up)
LED : IO23
Shell¶
I made 2 tries of the outer shell. I printed the design I made previous week. That first one had a good grip but not enough space for the components. The cut out for the switches was good and they perfectly fit in it. I also found out that four rocker switches shoud be sufficient and it would feel more natural when handling it as each fingers will have contact whith one except for the thumb that will maintain the grip.
I made another design trying to reduce the volume of it at the same time than making more space in it.
All the compenents fit in the second shell, but the grip sensation was very bad. The shape wasn’t ergonomic at all.
The knob was still printing at the end of this week.
Programming¶
I could make a WIFI communication and generate an html page.
To display an html page with images from the ESP32, I needed to upload the images in it. The chip had not enough memory to store the 35 jpeg needed. I used Riot to convert the emojis to the smallest yet recognizible PNGs. We see on the screenshot tahat I could go from a ~ 60KB jpg file to a 6KB png file.
I used ESP32FS tutorial to upload the 35 images on the ESP32 chip. this tool only works on older version of Arduino IDE.
Using SPIFFS, I could display the webpage with the images embedded in the chip.
I had to remake all the programming, because the debounce didn’t work anymore and the javascript was using serial communication and that didn’t work anymore with this configuration. Unfortunately, I wrote over the code as I was making progress with suggestion of chatGPT and I haven’t saved all the step that lead to the final code.
#include <WiFi.h>
#include <WebServer.h>
// Define pins for rotary encoder and button
#define pinCLK 32
#define pinDT 35
#define pinSW 34
#define pinLED 23
// Network credentials
const char* ssid = "your_SSID";
const char* password = "your_password";
// Create a web server on port 80
WebServer server(80);
// Variables to track the count and last action
int rotaryCount = 0; // Variable to keep track of the rotary encoder count
int previousStateSW; // Variable to store the previous state of the SW pin
int previousStateCLK; // Variable to store the previous state of the CLK pin
String last_action = "";
// HTML content
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP32 Rotary Encoder</title>
</head>
<body>
<h1>ESP32 Rotary Encoder</h1>
<div id="count">Count: </div>
<div id="last-action">Last Action: </div>
<script>
async function fetchData() {
try {
const response = await fetch('/data');
const data = await response.json();
document.getElementById('count').innerText = "Count: " + data.rotaryCount;
document.getElementById('last-action').innerText = "Last Action: " + data.last_action;
} catch (error) {
console.error('Error fetching data:', error);
}
}
setInterval(fetchData, 1000);
</script>
</body>
</html>
)rawliteral";
void setup() {
Serial.begin(115200); // Initialize serial communication at 115200 baud rate
pinMode(pinSW, INPUT_PULLUP); // Set SW pin as input with internal pull-up resistor
pinMode(pinDT, INPUT); // Set DT pin as input
pinMode(pinCLK, INPUT); // Set CLK pin as input
pinMode(pinLED, OUTPUT); // Set LED pin as output
previousStateSW = digitalRead(pinSW); // Read initial state of the SW pin
previousStateCLK = digitalRead(pinCLK); // Read initial state of the CLK pin
// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
// Print the IP address
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// Define server routes
server.on("/", []() {
server.send_P(200, "text/html", index_html);
});
server.on("/data", []() {
String json = "{\"rotaryCount\":" + String(rotaryCount) + ", \"last_action\":\"" + last_action + "\"}";
server.send(200, "application/json", json);
});
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
int actualStateCLK = digitalRead(pinCLK); // Read current state of the CLK pin
int actualStateDT = digitalRead(pinDT); // Read current state of the DT pin
int actualStateSW = digitalRead(pinSW); // Read current state of the SW pin
// Push Button verification
if (actualStateSW != previousStateSW) { // Check if SW state has changed
previousStateSW = actualStateSW; // Update the previous SW state
delay(50); // Debounce delay
if (actualStateSW == LOW) {
Serial.println(F("SW Button pushed")); // Print message when button is pressed
last_action = "SW Button pushed";
} else {
Serial.println(F("SW Button released")); // Print message when button is released
last_action = "SW Button released";
}
}
// Rotary encoder verification
if (actualStateCLK != previousStateCLK) {
delay(10); // Debounce delay
if (digitalRead(pinCLK) == actualStateCLK) { // Check if the CLK signal has settled
previousStateCLK = actualStateCLK;
if (actualStateCLK == LOW) {
if (actualStateCLK != actualStateDT) { // Comparing CLK and DT, if they're different, direction is counter-clockwise
rotaryCount--; // Decrement counter
Serial.println(F("counter-clockwise")); // Display value on Serial Monitor
last_action = "Counter-clockwise";
} else { // When CLK and DT are similar, direction is clockwise
rotaryCount++; // Increment counter
Serial.println(F("clockwise")); // Display value on Serial Monitor
last_action = "Clockwise";
}
Serial.println(rotaryCount);
// Ensure count stays within bounds (1-5 for PWM)
if (rotaryCount < 1) {
rotaryCount = 1;
} else if (rotaryCount > 5) {
rotaryCount = 5;
}
// Map rotaryCount to PWM range (0-255)
int pwmValue = map(rotaryCount, 1, 5, 0, 128);
analogWrite(pinLED, pwmValue); // Set LED brightness
}
}
}
}
#include <WiFi.h>
#include <WebServer.h>
// Define pins for rotary encoder, button, LED, and rocker switches
#define pinCLK 32
#define pinDT 35
#define pinSW 34
#define pinLED 23
#define pinRocker1 25
#define pinRocker2 26
#define pinRocker3 27
#define pinRocker4 14
// Network credentials
const char* ssid = "your_SSID";
const char* password = "your_password";
// Create a web server on port 80
WebServer server(80);
// Variables to track the count, states, and direction
int rotaryCount = 0;
int previousStateSW;
int previousStateCLK;
bool isClockwise = false;
int previousRockerState1;
int previousRockerState2;
int previousRockerState3;
int previousRockerState4;
int activatedRockerCount = 0;
// HTML content with JavaScript for dynamic updates
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindset Selector</title>
<script>
document.addEventListener('DOMContentLoaded', function() {
function fetchData() {
fetch('/data')
.then(response => response.json())
.then(data => {
document.getElementById('rotary-count').textContent = 'Rotary Count: ' + data.rotaryCount;
document.getElementById('rocker-count').textContent = 'Activated Rocker Switches: ' + data.activatedRockerCount;
document.getElementById('direction').textContent = 'Direction: ' + data.direction;
});
}
setInterval(fetchData, 1000);
});
</script>
</head>
<body>
<div id="rotary-count">Rotary Count: </div>
<div id="rocker-count">Activated Rocker Switches: </div>
<div id="direction">Direction: </div>
</body>
</html>
)rawliteral";
void setup() {
Serial.begin(115200);
pinMode(pinSW, INPUT_PULLUP);
pinMode(pinDT, INPUT);
pinMode(pinCLK, INPUT);
pinMode(pinLED, OUTPUT);
pinMode(pinRocker1, INPUT_PULLUP);
pinMode(pinRocker2, INPUT_PULLUP);
pinMode(pinRocker3, INPUT_PULLUP);
pinMode(pinRocker4, INPUT_PULLUP);
previousStateSW = digitalRead(pinSW);
previousStateCLK = digitalRead(pinCLK);
previousRockerState1 = digitalRead(pinRocker1);
previousRockerState2 = digitalRead(pinRocker2);
previousRockerState3 = digitalRead(pinRocker3);
previousRockerState4 = digitalRead(pinRocker4);
analogWrite(pinLED, 0);
// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// Define server routes
server.on("/", []() {
server.send_P(200, "text/html", index_html);
});
server.on("/data", []() {
String direction = isClockwise ? "Clockwise" : "Counterclockwise";
String json = "{\"rotaryCount\":" + String(rotaryCount) + ",\"activatedRockerCount\":" + String(activatedRockerCount) + ",\"direction\":\"" + direction + "\"}";
server.send(200, "application/json", json);
});
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
int actualStateCLK = digitalRead(pinCLK);
int actualStateDT = digitalRead(pinDT);
int actualStateSW = digitalRead(pinSW);
int currentRockerState1 = digitalRead(pinRocker1);
int currentRockerState2 = digitalRead(pinRocker2);
int currentRockerState3 = digitalRead(pinRocker3);
int currentRockerState4 = digitalRead(pinRocker4);
// Update direction based on the rotary encoder movement
if (actualStateCLK != previousStateCLK) {
delayMicroseconds(200); // Debounce delay
if (digitalRead(pinCLK) == actualStateCLK) {
previousStateCLK = actualStateCLK;
if (actualStateCLK == LOW) {
isClockwise = (actualStateCLK != actualStateDT);
rotaryCount += isClockwise ? 1 : -1;
rotaryCount = constrain(rotaryCount, 1, 5);
Serial.println(rotaryCount);
int pwmValue = map(rotaryCount, 1, 5, 0, 128);
analogWrite(pinLED, pwmValue);
}
}
}
// Update activated rocker switch count
if (currentRockerState1 != previousRockerState1 ||
currentRockerState2 != previousRockerState2 ||
currentRockerState3 != previousRockerState3 ||
currentRockerState4 != previousRockerState4) {
delay(50);
activatedRockerCount = (currentRockerState1 == LOW) + (currentRockerState2 == LOW) +
(currentRockerState3 == LOW) + (currentRockerState4 == LOW);
Serial.print("Activated rocker switches: ");
Serial.println(activatedRockerCount);
previousRockerState1 = currentRockerState1;
previousRockerState2 = currentRockerState2;
previousRockerState3 = currentRockerState3;
previousRockerState4 = currentRockerState4;
}
// Push button verification
if (actualStateSW != previousStateSW) {
delay(50); // Debounce delay
previousStateSW = actualStateSW;
Serial.println(actualStateSW == LOW ? "SW Button pushed" : "SW Button released");
}
}
#include <WiFi.h>
#include <WebServer.h>
#include <SPIFFS.h>
// Define pins for rotary encoder and button
#define pinCLK 32
#define pinDT 35
#define pinSW 34
#define pinLED 23
#define pinRocker1 25 // Rocker switch 1 connected to GPIO 25
#define pinRocker2 26 // Rocker switch 2 connected to GPIO 26
#define pinRocker3 27 // Rocker switch 3 connected to GPIO 27
#define pinRocker4 14 // Rocker switch 4 connected to GPIO 14
// Network credentials
const char* ssid = "your_SSID";
const char* password = "your_password";
// Create a web server on port 80
WebServer server(80);
// Variables to track the count and last action
int rotaryCount = 1; // Variable to keep track of the rotary encoder count
int previousStateSW; // Variable to store the previous state of the SW pin
int previousStateCLK; // Variable to store the previous state of the CLK pin
int previousRockerState1; // Variable to store the previous state of Rocker switch 1
int previousRockerState2; // Variable to store the previous state of Rocker switch 2
int previousRockerState3; // Variable to store the previous state of Rocker switch 3
int previousRockerState4; // Variable to store the previous state of Rocker switch 4
int activatedRockerCount = 1; // Counter for the total number of activated rocker switches
int actualStateSW_global; // Global variable to store the state of SW pin
// HTML content
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindset Selector</title>
<style>
.container {
display: flex;
justify-content: space-around;
margin-bottom: 20px;
max-height: 20%;
}
.image-container {
text-align: center;
}
.image-container img {
max-width: 100%;
max-height: 100px;
}
.btn-container {
text-align: center;
margin-bottom: 20px;
}
.btn {
padding: 10px 20px;
background-color: #007bff;
color: #fff;
border: none;
cursor: pointer;
margin: 0 10px;
}
#result-container {
text-align: center;
margin-top: 20px;
}
#result-button-container {
text-align: center;
margin-bottom: 20px;
}
#result-image {
max-width: 50%;
margin: 0 auto;
}
#result-image img {
max-width: 100%;
height: auto;
}
</style>
</head>
<body>
<div class="container">
<div class="image-container">
<h2>Mood</h2>
<div id="image-container1">
<img src="/03_smiley.png" alt="Image" data-value="3">
</div>
<div class="btn-container">
<button class="btn" onclick="previousImage(1)">Previous</button>
<button class="btn" onclick="nextImage(1)">Next</button>
</div>
</div>
<div class="image-container">
<h2>Interaction acceptance</h2>
<div id="image-container2">
<img src="/10_smiley.png" alt="Image" data-value="30">
</div>
<div class="btn-container">
<button class="btn" onclick="previousImage(2)">Previous</button>
<button class="btn" onclick="nextImage(2)">Next</button>
</div>
</div>
</div>
<div id="result-button-container">
<button class="btn" onclick="addValues()">Set</button>
</div>
<div id="result-container">
<h2>Mindset</h2>
<div id="result-image"></div>
</div>
<script>
var currentIndex1 = 2;
var currentIndex2 = 2;
var images1 = ["/05_smiley.png", "/04_smiley.png", "/03_smiley.png","/02_smiley.png", "/01_smiley.png"];
var images2 = ["/50_smiley.png", "/40_smiley.png", "/30_smiley.png", "/20_smiley.png", "/10_smiley.png"];
var values1 = [5, 4, 3, 2, 1];
var values2 = [50, 40, 30, 20, 10];
var allImages = [
{ src: "/smiley_mix11.png", value: 11 },
{ src: "/smiley_mix12.png", value: 12 },
{ src: "/smiley_mix13.png", value: 13 },
{ src: "/smiley_mix14.png", value: 14 },
{ src: "/smiley_mix15.png", value: 15 },
{ src: "/smiley_mix21.png", value: 21 },
{ src: "/smiley_mix22.png", value: 22 },
{ src: "/smiley_mix23.png", value: 23 },
{ src: "/smiley_mix24.png", value: 24 },
{ src: "/smiley_mix25.png", value: 25 },
{ src: "/smiley_mix31.png", value: 31 },
{ src: "/smiley_mix32.png", value: 32 },
{ src: "/smiley_mix33.png", value: 33 },
{ src: "/smiley_mix34.png", value: 34 },
{ src: "/smiley_mix35.png", value: 35 },
{ src: "/smiley_mix41.png", value: 41 },
{ src: "/smiley_mix42.png", value: 42 },
{ src: "/smiley_mix43.png", value: 43 },
{ src: "/smiley_mix44.png", value: 44 },
{ src: "/smiley_mix45.png", value: 45 },
{ src: "/smiley_mix51.png", value: 51 },
{ src: "/smiley_mix52.png", value: 52 },
{ src: "/smiley_mix53.png", value: 53 },
{ src: "/smiley_mix54.png", value: 54 },
{ src: "/smiley_mix55.png", value: 55 },
];
async function fetchData() {
try {
const response = await fetch('/data');
const data = await response.json();
// Update currentIndex based on the response
currentIndex1 = data.rotaryCount - 1; // Using the rotaryCount for currentIndex1
currentIndex2 = data.activatedRockerCount - 1; // Using activatedRockerCount for currentIndex2
// Check the value of triggerAddValues
if (data.triggerAddValues) {
addValues(); // Call addValues function if triggerAddValues is true
}
console.log("Updated indices:", currentIndex1, currentIndex2); // Debugging log
updateImage(1);
updateImage(2);
} catch (error) {
console.error('Error fetching data:', error);
}
}
setInterval(fetchData, 100);
function previousImage(containerIndex) {
if (containerIndex === 1) {
if (currentIndex1 === 0) return;
currentIndex1--;
updateImage(1);
} else if (containerIndex === 2) {
if (currentIndex2 === 0) return;
currentIndex2--;
updateImage(2);
}
}
function nextImage(containerIndex) {
if (containerIndex === 1) {
if (currentIndex1 === images1.length - 1) return;
currentIndex1++;
updateImage(1);
} else if (containerIndex === 2) {
if (currentIndex2 === images2.length - 1) return;
currentIndex2++;
updateImage(2);
}
}
function updateImage(containerIndex) {
var displayedImage;
if (containerIndex === 1) {
displayedImage = document.getElementById("image-container1").getElementsByTagName("img")[0];
displayedImage.src = images1[currentIndex1];
displayedImage.dataset.value = values1[currentIndex1];
} else if (containerIndex === 2) {
displayedImage = document.getElementById("image-container2").getElementsByTagName("img")[0];
displayedImage.src = images2[currentIndex2];
displayedImage.dataset.value = values2[currentIndex2];
}
}
function addValues() {
var value1 = values1[currentIndex1];
var value2 = values2[currentIndex2];
var resultValue = value1 + value2;
var resultImage = allImages.find(image => image.value === resultValue);
if (resultImage) {
document.getElementById("result-image").innerHTML = `<img src="${resultImage.src}" alt="Result Image">`;
} else {
document.getElementById("result-image").innerHTML = "No matching image found.";
}
}
</script>
</body>
</html>
)rawliteral";
// Function to initialize the SPIFFS
void initSPIFFS() {
if (!SPIFFS.begin(true)) {
Serial.println("An error occurred while mounting SPIFFS");
return;
}
Serial.println("SPIFFS mounted successfully");
}
void setup() {
Serial.begin(115200); // Initialize serial communication at 115200 baud rate
pinMode(pinSW, INPUT_PULLUP); // Set SW pin as input with internal pull-up resistor
pinMode(pinDT, INPUT); // Set DT pin as input
pinMode(pinCLK, INPUT); // Set CLK pin as input
pinMode(pinLED, OUTPUT); // Set LED pin as output
pinMode(pinRocker1, INPUT_PULLUP); // Set Rocker switch 1 as input with internal pull-up resistor
pinMode(pinRocker2, INPUT_PULLUP); // Set Rocker switch 2 as input with internal pull-up resistor
pinMode(pinRocker3, INPUT_PULLUP); // Set Rocker switch 3 as input with internal pull-up resistor
pinMode(pinRocker4, INPUT_PULLUP); // Set Rocker switch 4 as input with internal pull-up resistor
previousStateSW = digitalRead(pinSW); // Read initial state of the SW pin
previousStateCLK = digitalRead(pinCLK); // Read initial state of the CLK pin
previousRockerState1 = digitalRead(pinRocker1); // Read initial state of Rocker switch 1
previousRockerState2 = digitalRead(pinRocker2); // Read initial state of Rocker switch 2
previousRockerState3 = digitalRead(pinRocker3); // Read initial state of Rocker switch 3
previousRockerState4 = digitalRead(pinRocker4); // Read initial state of Rocker switch 4
analogWrite(pinLED, 0); // Initialize the LED with 0 brightness (off)
// Initialize SPIFFS
initSPIFFS();
// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
// Print the IP address
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// Define server routes
server.on("/", []() {
server.send_P(200, "text/html", index_html);
});
server.on("/data", []() {
actualStateSW_global = digitalRead(pinSW); // Read the current state of SW pin
bool triggerAddValues = (actualStateSW_global == LOW); // Set triggerAddValues based on actualStateSW_global
String json = "{\"rotaryCount\":" + String(rotaryCount) + ",\"activatedRockerCount\":" + String(activatedRockerCount) + ",\"triggerAddValues\":" + (actualStateSW_global == LOW ? "true" : "false") + "}";
server.send(200, "application/json", json);
});
server.serveStatic("/", SPIFFS, "/");
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
int actualStateCLK = digitalRead(pinCLK); // Read current state of the CLK pin
int actualStateDT = digitalRead(pinDT); // Read current state of the DT pin
int actualStateSW = digitalRead(pinSW); // Read current state of the SW pin
// Read current states of rocker switches again after debounce delay
int currentRockerState1 = digitalRead(pinRocker1);
int currentRockerState2 = digitalRead(pinRocker2);
int currentRockerState3 = digitalRead(pinRocker3);
int currentRockerState4 = digitalRead(pinRocker4);
// Check if the state has changed and is stable
if (currentRockerState1 != previousRockerState1 ||
currentRockerState2 != previousRockerState2 ||
currentRockerState3 != previousRockerState3 ||
currentRockerState4 != previousRockerState4) {
// Debounce delay for rocker switches
delay(50);
// Update activatedRockerCount only if there is a change in any rocker switch state
activatedRockerCount = 1;
if (currentRockerState1 == LOW) {
activatedRockerCount++;
}
if (currentRockerState2 == LOW) {
activatedRockerCount++;
}
if (currentRockerState3 == LOW) {
activatedRockerCount++;
}
if (currentRockerState4 == LOW) {
activatedRockerCount++;
}
// Print the number of activated rocker switches to the Serial Monitor
Serial.print(F("Activated rocker switches: "));
Serial.println(activatedRockerCount);
// Update previous rocker states
previousRockerState1 = currentRockerState1;
previousRockerState2 = currentRockerState2;
previousRockerState3 = currentRockerState3;
previousRockerState4 = currentRockerState4;
}
// Push Button verification
if (actualStateSW != previousStateSW) { // Check if SW state has changed
previousStateSW = actualStateSW; // Update the previous SW state
delay(50); // Debounce delay
if (actualStateSW == LOW) {
Serial.println(F("SW Button pushed")); // Print message when button is pressed
} else {
Serial.println(F("SW Button released")); // Print message when button is released
}
}
// Rotary encoder verification
if (rotaryCount < 1) {
rotaryCount = 1;
} else if (rotaryCount > 5) {
rotaryCount = 5;
}
if (actualStateCLK != previousStateCLK) {
delayMicroseconds(200); // Debounce delay
if (digitalRead(pinCLK) == actualStateCLK) { // Check if the CLK signal has settled
previousStateCLK = actualStateCLK;
if (actualStateCLK == LOW) {
if (actualStateCLK != actualStateDT) { // Comparing CLK and DT, if they're different, direction is counter-clockwise
rotaryCount--; // Decrement counter
Serial.println(F("counter-clockwise")); // Display value on Serial Monitor
} else { // When CLK and DT are similar, direction is clockwise
rotaryCount++; // Increment counter
Serial.println(F("clockwise")); // Display value on Serial Monitor
}
Serial.println(rotaryCount);
// Ensure count stays within bounds (0-5 for PWM)
if (rotaryCount < 1) {
rotaryCount = 1;
} else if (rotaryCount > 5) {
rotaryCount = 5;
}
// Map rotaryCount to PWM range (0-128)
int pwmValue = map(rotaryCount, 1, 5, 0, 128);
analogWrite(pinLED, pwmValue); // Set LED brightness
}
}
}
}
Here’s the code working :
Video editing¶
Also, I downloaded DaVinci Resolve and mad a first try at video editing.
Class archives¶
- Shell 3D model v1
- Shell 3D model V2 (derived)
- Basic Webserver code for rotary encoder
- Webserver code updated with Rocker Switches
- Webserver code with updating images
Impressions of the week¶
I was glad to finally succeed to make the bare minimum for my final project to work. I could spend the time left on improvement and there is a lot I can do. First, I had to upgrade the shell and debug the bouncing on the rotary encoder. I also wish to use a dedicated display so I will work on the Cheap Yellow Display. Also, I need to find a way to implement substractive fabrication processes. I thought about vinyl cutting to add information on the device or lasercutting to make a support for the screen (if the screen works).