20. Project development¶
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 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¶
PROJECT VIDEO¶
Click here access the presentation video.
PROJECT SLIDE¶
Click here access the presentation slide.
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.