Final Project¶
PROJECT VIDEO¶
Click here access the presentation video.
PROJECT SLIDE¶
Click here access the presentation slide.
This week I worked on defining my final project idea and started to getting used to the documentation process.
Initial idea and sketching of the project¶
My plan is to create a dynamic sculpture with a 15x15 matrix disc flap display. One side of the rotating/flipping disc is painted black, while the other side is painted white or any other color. When the permanent magnet on the disc is attracted by the excited electromagnet which is installed in the disc holder, the colored side of the disc flips up. Each holder represents one of the matrix’s elements. As part of the beginning phase of my final project, I created a hand sketch.
New idea (കുമിള ഘടികാരം)¶
My plan is to create a display with bubbles with 8 solinoid valves. simbly by turnig these of and on we can produce bubbles in any liqid so with properly calibrated opening and closing of these valve we can make such a display so i am trying to make a bubble clock.
Gantt Chart¶
for downloading/viewing the file Click here
Bill og Meterials¶
SI.NO | DEVICE | VALUE | QTY |
1 | MOSFET | NSOT23 | 10 |
2 | SOLENOID VALVE | NORMALY CLOSED 2 WAY VALVE | 8 |
3 | VR_REGULATOR | LM1117SOT223 | 1 |
4 | CONN_06_FTDI-SMD-HEADER_2SIDE | CONN_06_FTDI-SMD-HEADER | 1 |
5 | CONN_00X03-SMD-HEADER_2SIDE | CONN_03-SMD-HEADER_2SIDE | 1 |
6 | CONN_02X02-PINHEAD-SMD | CONN_02X2-PINHEAD-SMD | 1 |
7 | DIODE | CDBM1100-G | 8 |
8 | LED | RED | 1 |
9 | LED | GREEN | 9 |
10 | CAPASITOR | 1UF | 2 |
11 | CAPASITOR | 100NF | 2 |
12 | POWER TERMINAL | 0X2 TERMINAL | 9 |
13 | RESISTOR | 0K | 3 |
14 | RESISTOR | 1K | 8 |
15 | RESISTOR | 10K | 12 |
16 | RESISTOR | 5K | 2 |
17 | RESISTOR | 500K | 8 |
18 | ESP32-S2-WROOM-I (32MB) | 1 | |
19 | NEOPIXEL LED | 8 | |
20 | OLED DISPLAY. | 1 | |
21 | SILICONE TUBE. | 6m | |
22 | '4 WAY' JOINT | 6 | |
23 | 'I' JOINT | 10 | |
24 | 'T' JOINT | 1 | |
25 | AQUARIUM AIR PUMP. | 1 |
Electronics Design¶
I was planning to make a bord which have a ability to controll 8 solinoid valves and have wifi connecting capablities so i thought of using ESP-32 as the microcontroller as it have inbuild wifi and more than enough pins for connecting solinoid, OLED, and neo pixel and RTOS capablity so I took fusion and strted creating the schematic
and went to PCB Documents and arranged the components and ruted out the PCB wich was ready to be milled out.
the files were exported as monochrome images and milled out.
for downloading file Click here
this wouldn’t have been possible if it wasn’t for Shaheen because he is simply mess in electronics.
3D modeling¶
initally started designing the container by measuring size of a small aquerium i bought some months back during lockdown and reverse engineerring and redesigning it on fusion.
and thought of creating seperation chamber and how to hide the tubeings providing the air inside the chamber.
Lasercutting parts¶
The bubble seperating structure is converted into dxf file and cutted in laser cutter. for downloading file Click here
After cutting and assembling¶
programming¶
Being a mechanical engineer, I had little experience with programming, but Senior Abeleetans’s logic and Instructor Jogin’s magic touch in my programme made the project work.
// Header definition
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <SPI.h>
#include "time.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_NeoPixel.h>
#define LED_COUNT 2
#define LED_PIN 27
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
//---------------------------------Delays -----------------------------------------------
#define BUBBLE_BREAK_DELAY 6 //Time in mills to open the solinoid
#define LINE_DEALY 60 //Time in mills to wait for next line
#define NUMBER_DEALY 800 //Time in mills to wait for next Number
//---------------------------------Display Settings--------------------------------------
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
//---------------------------------------------------------------------------------------
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//---------------------------------------------------------------------------------------
String txtMsg = ""; // a string for incoming text
int lastStringLength = 0; // previous length of the String
char alphabet[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
int wantedpos;
// Number metrix defenition
int number[11][8] = {{ // 0
0b00111000,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b00111000
}, { // 1
0b00001000,
0b00011000,
0b00101000,
0b00001000,
0b00001000,
0b00001000,
0b00001000,
0b00111110
}, { // 2
0b00111000,
0b01000100,
0b00000100,
0b00001000,
0b00010000,
0b00100000,
0b01111100
}, { // 3
0b00111000,
0b01000100,
0b00000100,
0b00011000,
0b00000100,
0b01000100,
0b00111000
}, { // 4
0b00001000,
0b00011000,
0b00101000,
0b01001000,
0b11111100,
0b00001000,
0b00001000
}, { // 5
0b01111100,
0b01000000,
0b01000000,
0b01111000,
0b00000100,
0b00000100,
0b01111000
}, { // 6
0b00111000,
0b01000100,
0b01000000,
0b01111000,
0b01000100,
0b01000100,
0b00111000
}, { // 7
0b01111111,
0b00000001,
0b00000001,
0b00000010,
0b00000100,
0b00001000,
0b00010000,
0b00100000
}, { // 8
0b00111000,
0b01000100,
0b01000100,
0b00111000,
0b01000100,
0b01000100,
0b00111000
}, { // 9
0b00111000,
0b01000100,
0b01000100,
0b00111100,
0b00000100,
0b01000100,
0b00111000
}, { // :(10)
0b00000000,
0b00000000,
0b00011000,
0b00000000,
0b00000000,
0b00011000,
0b00000000,
0b00000000
}
};
// Char metrix defenition
int text[26][8] = {{ // a
0b00111000,
0b01000100,
0b01000100,
0b01111100,
0b01000100,
0b01000100,
0b01000100,
0b01000100
}, { // b
0b01111000,
0b01000100,
0b01000100,
0b01111100,
0b01001000,
0b01000100,
0b01000100,
0b01111000
}, { // c
0b00111000,
0b01000100,
0b01000000,
0b01000000,
0b01000000,
0b01000000,
0b01000100,
0b00111000,
}, { // d
0b01111000,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01111000
}, { // e
0b01111100,
0b01000000,
0b01000000,
0b01111000,
0b01000000,
0b01000000,
0b01000000,
0b01111100
}, { // f
0b01111100,
0b01000000,
0b01000000,
0b01111000,
0b01000000,
0b01000000,
0b01000000,
0b01000000
}, { // g
0b01111000,
0b01000100,
0b01000000,
0b01000000,
0b01011100,
0b01000100,
0b01000100,
0b01111000
}, { // h
0b01000100,
0b01000100,
0b01000100,
0b01111100,
0b01000100,
0b01000100,
0b01000100,
0b01000100
}, { // i
0b01111100,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b01111100
}, { // j
0b00111100,
0b00000100,
0b00000100,
0b00000100,
0b00000100,
0b00000100,
0b01000100,
0b00111000
}, { // k
0b01000100,
0b01000100,
0b01001000,
0b01110000,
0b01001000,
0b01000100,
0b01000100,
0b01000100
}
, { // l
0b01000000,
0b01000000,
0b01000000,
0b01000000,
0b01000000,
0b01000000,
0b01000000,
0b01111100
}
, { // m
0b01000100,
0b01101100,
0b01010100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100
}, { // n
0b01000010,
0b01000010,
0b01100010,
0b01010010,
0b01001010,
0b01000110,
0b01000010,
0b01000010
}, { // o
0b00111000,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b00111000
}, { // p
0b01111000,
0b01000100,
0b01000100,
0b01111000,
0b01000000,
0b01000000,
0b01000000,
0b01000000
}, { // q
0b00111000,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01010100,
0b01001100,
0b00111100
}, { // r
0b01111000,
0b01000100,
0b01000100,
0b01111000,
0b01010000,
0b01001000,
0b01000100,
0b00000100
}, { // s
0b01111100,
0b01000000,
0b01000000,
0b01111000,
0b00000100,
0b00000100,
0b01111000
}, { // t
0b01111100,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b00010000
}, { // u
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b00111000
}, { // v
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b00101000,
0b00010000
}, { // w
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01010100,
0b01101100,
0b01000100
}, { // x
0b01000100,
0b01000100,
0b00101000,
0b00010000,
0b00101000,
0b01000100,
0b01000100,
0b00000000
}, { // y
0b01000100,
0b01000100,
0b00101000,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b00010000
}, { // z
0b01111100,
0b00000100,
0b00001000,
0b00010000,
0b00100000,
0b01000000,
0b01111100,
0b00000000
}
};
//---------------------------------------------------------------------------------------
// Solinoid pin defenition
int pinMap[8] = {18, 19, 5, 17, 14, 4, 12, 16};
//--------------------------------- Time -----------------------------------------------
volatile int h = 0;
volatile int m = 0;
volatile int s = 0;
volatile int hr2 = h % 10; // seperating 1st digit of hour
volatile int hr1 = (h - hr2) / 10; //seperating 2nd digit of hour
volatile int mit2 = m % 10; //seperating 1st digit of muinits
volatile int mit1 = (m - mit2) / 10; //seperating 1st digit of munits
//---------------------------------------------------------------------------------------
// WiFi Settings
const char* ssid = "Your Wifi Name"; //WiFi Name
const char* password = "Your Wifi Password"; // WiFi Password
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "1.asia.pool.ntp.org", 19800);
//--------------- Task Handle -------------------------------
TaskHandle_t Print_Time;
TaskHandle_t Get_Time;
TaskHandle_t NeoPixel;
//--------------- Function Initilizations -------------------------------
void initDisplay();
void initSolinoid();
void initWifi();
void neoPixel(void * parameter);
void printTime(void * parameter);
void setup()
{
Serial.begin(115200);
strip.begin();
strip.show();
strip.setBrightness(50);
//display setup
initDisplay();
//Setting solinoid pins
initSolinoid();
//connect to WiFi
initWifi();
// Create Tasks --------------------------------------
xTaskCreatePinnedToCore(
printTime, /* Task function. */
"Print_Time", /* String with name of task. */
10000, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
3, /* Priority of the task. */
&Print_Time, /* Task handle. */
1); /* Core where the task should run */
xTaskCreatePinnedToCore(
getTime, /* Task function. */
"Get_Time", /* String with name of task. */
10000, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
3, /* Priority of the task. */
&Get_Time, /* Task handle. */
1); /* Core where the task should run */
xTaskCreatePinnedToCore(
neoPixel, /* Task function. */
"NeoPixel", /* String with name of task. */
10000, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
3, /* Priority of the task. */
&NeoPixel, /* Task handle. */
0); /* Core where the task should run */
}
void loop() {
vTaskDelay(10);
}
//---------------------------------------------------------------------------------------
// Task 1 -> neoPixel --------------------
void neoPixel(void * parameter) {
Serial.print("neoPixel running on core ");
Serial.println(xPortGetCoreID());
for (;;) {
rainbow(10);
}
vTaskDelete( NULL );
}
// Task 2 -> get Time --------------------
void getTime(void * parameter) {
Serial.print("getTime running on core ");
Serial.println(xPortGetCoreID());
for (;;) {
timeClient.update(); //gets the current time from the NTP server.
h = timeClient.getHours(); //gets hour seperatly from the NTP server.
m = timeClient.getMinutes(); //gets minuits seperatly from the NTP server.
s = timeClient.getSeconds(); //gets seconds seperatly from the NTP server.
hr2 = h % 10; // seperating 1st digit of hour
hr1 = (h - hr2) / 10; //seperating 2nd digit of hour
mit2 = m % 10; //seperating 1st digit of muinits
mit1 = (m - mit2) / 10; //seperating 1st digit of munits
vTaskResume(Print_Time);
vTaskDelay(1000);
}
vTaskDelete( NULL );
}
// Task 3 -> printTime --------------------
void printTime(void * parameter) {
Serial.print("printTime running on core ");
Serial.println(xPortGetCoreID());
vTaskSuspend(NULL);
for (;;) {
display.clearDisplay();
display.display();
display.setTextSize(3);
display.setCursor(21, 30);
display.print(hr1);
display.print(hr2);
display.print(":");
display.print(mit1);
display.print(mit2);
display.display();
//---------------------------------------------------------------------------------------
// calling function activate the solinoids
/*
printNum(hr1);
vTaskDelay(NUMBER_DEALY);
printNum(hr2);
vTaskDelay(NUMBER_DEALY);
printNum(10);
vTaskDelay(NUMBER_DEALY);
printNum(mit1);
vTaskDelay(NUMBER_DEALY);
printNum(mit2);
vTaskDelay(NUMBER_DEALY);
}
vTaskDelete( NULL );*/
if (Serial.available() > 0)
{
txtMsg = Serial.readString();
//Serial.println(txtMsg);
int k = txtMsg.length();
for (int i = 0; i < k; i++)
{
char l = txtMsg.charAt(i);
for (int j = 0; j <= 25; j++) {
if (l == alphabet[j]) {
wantedpos = j;
Serial.print("-");
Serial.print(wantedpos);
printtext(wantedpos);
vTaskDelay(NUMBER_DEALY);
break;
}
}
}
Serial.flush();
}
else
{
printNum(hr1);
vTaskDelay(NUMBER_DEALY);
printNum(hr2);
vTaskDelay(NUMBER_DEALY);
printNum(10);
vTaskDelay(NUMBER_DEALY);
printNum(mit1);
vTaskDelay(NUMBER_DEALY);
printNum(mit2);
vTaskDelay(NUMBER_DEALY);
}
}
vTaskDelete( NULL );
}
void printNum(int num) { // activating the solinoids
for (int j = 0; j < 8; j++) {
for (int i = 0; i < 8; i++) {
bool out = ((number[num][j] << i) & 0b10000000) & 0b10000000;
digitalWrite(pinMap[i], out);
}
vTaskDelay(BUBBLE_BREAK_DELAY);
for (int i = 0; i < 8; i++) {
digitalWrite(pinMap[i], 0);
}
vTaskDelay(LINE_DEALY);
}
}
void printtext(int tex) { // activating the solinoids
for (int j = 0; j < 8; j++) {
for (int i = 0; i < 8; i++) {
bool out = ((text[tex][j] << i) & 0b10000000) & 0b10000000;
digitalWrite(pinMap[i], out);
}
vTaskDelay(BUBBLE_BREAK_DELAY);
for (int i = 0; i < 8; i++) {
digitalWrite(pinMap[i], 0);
}
vTaskDelay(LINE_DEALY);
}
}
void initDisplay() {
//display setup
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
vTaskDelay(2000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 20);
display.display();
}
void initSolinoid() {
for (int i = 0; i < 8; i++) {
pinMode(pinMap[i], OUTPUT);
}
}
void initWifi() {
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(NUMBER_DEALY);
Serial.print(".");
}
Serial.println(" CONNECTED");
}
void rainbow(int wait) {
for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256) {
for (int i = 0; i < strip.numPixels(); i++) {
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
strip.show();
vTaskDelay(wait);
}
}
Testing¶
the program and the divice was tested and itrated mulitiple times to get the final output.
solinoid valve testing¶
at first i needed to check wether all the valves are working properly and the pump used have the capasity to make bubbles
now i had to check how it will look like in lighted condition.
NTP and digit testing¶
it was the 1st itration of showing numbers.
Final time test with calibrated digits with RTOS¶
Packaging¶
after all the testing i needed to make a casing for holding the solinoid PCB and valves so i went ro fusion and started designing a box container for holding the pcb and and solinoidal valves wich could be cut in acleric sheet
for downloading file Click here
.the parts were cut out of acleric in laser cutter
for making the box look better i was planing to give it a mat black vinayle wrap so i send the same file linked above to vinyle cutter via mods and wraped it over the earlier cutted aclerics.
and 3d printed a oled holder and tube organizeres Mufeed Sir’s assistance here was invaluable.
Completed 3D Model¶
completed the whole 3D model of how the divice will look.
Rendered output.¶
as am new to fusion and most of the other weeks i used inventor 3D it was bit difficut to design and Render a proper output so my friend Ajith/തല helped me to do this who is pro in fusion rendering. for downloading file Click here
Real Outcome¶
Final Outcome video¶
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
About old idea.¶
2D and 3D Modeling¶
Priliminary designe of the Final Project¶
In Inventor 3D, I considered making a little animated representation of my project. I chose this software for modeling because this model has a lot of components and subassemblies, thus I thought of utilizing Autodesk inventor.
Sketching 2D¶
The first stage in any design is to sketch the 2D drawings, therefore I began sketching the 2D designs for individual parts.
3D Modeling¶
I created a 3D model of each component of my final project using basic 3D modeling tools such as extrude, sweep, coil, pattern, and others.
Assembling the model¶
The magnetizing coil, flip disc, and base were first assembled as a sub-assembly for the flipping mechanism. After that, the subassemblies were assembled inside the frame.
Animation/Motion Study¶
To animate, we must design drives for the constraint in order to control the mobility of the pieces within the assembly. As shown in the figure, click the constraint to be animated and then click on the drive to establish the drive.
Now go to environment on the top ribbon and select inventor studio
Animate the drives to achieve the desired effect.
Rendering¶
For rendering click on the render animation on the ribbon then select the following setup to get a fast rendered output.