Skip to content

Final Project

PROJECT VIDEO

Click here access the presentation video.

PROJECT SLIDE

Click here access the presentation slide. groupmillig.jpg

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.

initial_hand_sketch.jpg

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. Screenshot 2022-07-01 085526.jpg

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 Screenshot 2022-07-01 085526.jpg and went to PCB Documents and arranged the components and ruted out the PCB wich was ready to be milled out. Screenshot 2022-07-01 085526.jpg the files were exported as monochrome images and milled out.

Screenshot 2022-07-01 085526.jpg

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.

Screenshot 2022-07-01 085526.jpg

and thought of creating seperation chamber and how to hide the tubeings providing the air inside the chamber.

Screenshot 2022-07-01 085526.jpg Screenshot 2022-07-01 085526.jpg

Lasercutting parts

The bubble seperating structure is converted into dxf file and cutted in laser cutter. Screenshot 2022-07-01 085526.jpg for downloading file Click here

After cutting and assembling

Screenshot 2022-07-01 085526.jpg

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 Screenshot 2022-07-01 085526.jpg for downloading file Click here
.the parts were cut out of acleric in laser cutter Screenshot 2022-07-01 085526.jpg
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. Screenshot 2022-07-01 085526.jpg and 3d printed a oled holder and tube organizeres Mufeed Sir’s assistance here was invaluable.
Screenshot 2022-07-01 085526.jpg

Completed 3D Model

completed the whole 3D model of how the divice will look. Screenshot 2022-07-01 085526.jpg

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. Screenshot 2022-07-01 085526.jpg for downloading file Click here

Real Outcome

Screenshot 2022-07-01 085526.jpg

Final Outcome video

groupmillig.jpg
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. 2d_Final_Pro.jpg

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. 3d_Final_Pro.jpg

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. Assembly_final.jpg

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. Drive
Now go to environment on the top ribbon and select inventor studio
How_To_Get_To_Render.jpg
Animate the drives to achieve the desired effect.
Animating.jpg

Rendering

For rendering click on the render animation on the ribbon then select the following setup to get a fast rendered output. Rendering option.jpg

Output