In week 15 and 17 we had the group assignment to design a machine that included mechanics, actuation and automation. This week the focus was on machine design. The machine should be actuated and automated.
Our work can be also found on HRW FabLab site, abowe in tool line "Last years students". Like my pages, it is also separated into two weeks.
Great oportunity to test ourselves and to see what we have learned so far and how we would connect more areas from FabAcademy. From the begining to the end it was a great and fun task. It was also great to see how we work in team from the ideas to the final product. I think we had a big plus because there were not segment in our building that we couldn't solve, it was always one of us that knows that segment better so it was great to see the colaboration betwean us and to see what and how much you can learn in such a short time. This was a task were the electronics got a bit serious and when we were working on the electronics, I just turned into eyes and ears to absorb as much the informations as I can. Think that really cool thing is that from begining we were talking that our machine needs to be abble to connect to smartphone, because it's great to see how you can operate with the machine over the phone.
After we finished machine, we all agreed that from time to time we will work on the machine to upgrade it and to change little things. For example the design is something that we want to work on, we want to make it more atractive so it's not only skeleton. Also maybe to add the touchscreen on it to control all settings and to upgrade the application for smartphone. Well we'll see...hope that we'll be able to work on the upgrades in some near future.
First, we reviewed all the requirements the board needed. We wanted to use a DOIT Esp32 DevKit v1 board for control. This should address the stepper motor, the pumps, the endstop, the servos and later the LEDs. To control the servos we use the Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685 via I2C for PWM control, so our PWM driver. First we tried the whole thing out one by one and then we started with the design. At the beginning we developed the following:
During the first iteration different arrangements were changed. Like some components were on the wrong side.
For the second iteration we had to use the PINOUTS, which you can really use. Due to the first iteration there were design changes which were reflected in the PIN mapping. Next time we’ll pay a lot of attention to which PINs actually support which functions.
Here are some pictures from revision 1 and 2: It was nice with you.
Next a few pictures of the production of revision 3, the soldering and the following connection.
We’ve got three power sources on the whole setup. Via the ESP32 and the integrated LM1017 we have a 3.3V source at max. 1A. In idle mode the ESP32 needs about 200mA. The built-in ATX power supply provides the system with the necessary 12V and 5V. The 12 V are used for the stepper motors. To start the power supply directly, two cables must be connected to each other:
A long time ago we removed pumps and magnetic valves from old blood pressure monitors. Finally they have a purpose. The pumps run without problems up to 12 V, but should be operated at 5 V. The magnetic valves close when voltage is applied. For these pumps we have made an additional PCB so that we can connect everything with the cables more easily. This looked like this and was then attached directly to the pumps.
These were then attached as follows:
We connected the 8 servos to the 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685. The power supply is then connected via a separate power connection. The color coding should be observed.
Then we did the first tests step by step. The servos were controlled alternately left and right:
Then the stepper motor had to be connected and react to the end stop as interrupt. It works. Then we calibrated steps that 10cm were really 10cm.
The pumps could be controlled: The water bubbled.
The ESP32 has opened a WLAN access point. You could then connect to it, which looked like this depending on the device:
Now some more pictures of the machine as it looked at the end:
The cocktail machine outdoors:
Some lines have been programmed. On the one hand the control for the stepper motors, the servos via the PWM servo driver, the end stop, the control of the pumps and the interface. Accordingly the whole code follows now:
#include <WiFi.h> #include <WiFiClient.h> #include <WiFiAP.h> #include <Arduino.h> #include <Wire.h> #include <Adafruit_PWMServoDriver.h> #include "settings.h" Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); WiFiServer server(80); boolean homed = false; struct endstop { const uint8_t PIN; uint32_t numberKeyPresses; boolean pressed; }; endstop end1 = {endstop_pin, 0, false}; // everytime an interrupt has been recognized, number will be count up and "pressed" will be true to call next function void IRAM_ATTR isr() { //end1.numberKeyPresses += 1; end1.pressed = true; } void setup() { Serial.begin(115200); delay(1000); Serial.println("WANNA DRINK SOMETHING?"); // init onboard LED for debugging // pinMode(LED_BUILTIN, OUTPUT); // init motor pins pinMode(stepPin, OUTPUT); pinMode(dirPin, OUTPUT); pinMode(enablePin, OUTPUT); for (uint8_t i = 0; i < 7; i++) { pinMode( pumps[i], OUTPUT); delay(100); digitalWrite(pumps[i], LOW); } // SERVO PCA9685 pwm.begin(); pwm.setPWMFreq(60); // Analog servos run at ~60 Hz updates delay(10); // WIFI // You can remove the password parameter if you want the AP to be open. WiFi.softAP(ssid, password); IPAddress myIP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(myIP); server.begin(); Serial.println("Server started"); // ENDSTOP pinMode(end1.PIN, INPUT); attachInterrupt(end1.PIN, isr, FALLING); } uint16_t count = 0; void loop() { //checkClient(); // first, check if any endstop has been hit before action will start static uint32_t lastMillis = 0; if (end1.pressed) {// && millis() - lastMillis > debounceTime_endstop) { Serial.printf("ENDSTOP 1 has been pressed %u times\n", end1.numberKeyPresses); end1.pressed = false; homed = true; } if (homed == false) { digitalWrite(enablePin, LOW); // Enables the motor to move in a particular direction //delay(10); digitalWrite(dirPin, 0); // Enables the motor to move in a particular direction digitalWrite(stepPin, HIGH); delayMicroseconds(70); digitalWrite(stepPin, LOW); delayMicroseconds(70); } else { digitalWrite(enablePin, HIGH); // Enables the motor to move in a particular direction checkClient(); } } void checkClient() { // WIFI ACTION WiFiClient client = server.available(); // listen for incoming clients if (client && cocktailReady == true) { // if you get a client, Serial.println("New Client."); // print a message out the serial port count = 0; String currentLine = ""; // make a String to hold incoming data from the client while (client.connected()) { // loop while the client's connected if (client.available()) { // if there's bytes to read from the client, char c = client.read(); // read a byte, then Serial.write(c); // print it out the serial monitor if (c == '\n') { // if the byte is a newline character // if the current line is blank, you got two newline characters in a row. // that's the end of the client HTTP request, so send a response: if (currentLine.length() == 0) { // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) // and a content-type so the client knows what's coming, then a blank line: client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println(); // use HTML autorefresh to update values on http // simple example, refresh and print now value of count // count will be count up by one each refresh client.println("<meta http-equiv='refresh' content='10; URL=/' />"); count++; client.println("Count: " + (String)count); if (count > 10) { count = 0; } // the content of the HTTP response follows the header: client.print("Click <a href=\"/cocktail1\">here</a> to select cocktail #1 <br>"); client.print("Click <a href=\"/cocktail2\">here</a> to select cocktail #2 <br>"); // The HTTP response ends with another blank line: client.println(); // break out of the while loop: break; } else { // if you got a newline, then clear currentLine: currentLine = ""; } } else if (c != '\r') { // if you got anything else but a carriage return character, currentLine += c; // add it to the end of the currentLine } // Check to see if the client request was "GET /H" or "GET /L": if (currentLine.endsWith("GET /cocktail1") && cocktailReady == true) { //cocktail = 1; cocktailReady == false; // stepper motor stepperTask(1, 32); // alc#5 delay(1000); // One second delay servoTask(5); delay(5000); servoTask(5); delay(2000); // One second delay stepperTask(1, 24); // juice delay(1000); // One second delay pump(1, 10000); delay(5000); homed = false; end1.pressed = false; // call servo action // servoTask(0); homed = false; end1.pressed = false; cocktail = 0; cocktailReady == true; #ifdef debugEN Serial.println("cocktail 1 is ready"); Serial.println("enjoy your drink and have nice time"); #endif } if (currentLine.endsWith("GET /cocktail2") && cocktailReady == true) { cocktailReady == false; cocktailReady == true; #ifdef debugEN Serial.println("cocktail 2 is ready"); Serial.println("enjoy your drink and have nice time"); #endif } } } // close the connection: client.stop(); Serial.println("Client Disconnected."); } } void stepperTask(boolean dir, uint16_t mm) { #ifdef debugEN Serial.printf("move cup by %d \n", mm); #endif digitalWrite(enablePin, LOW); // Enables the motor to move in a particular direction delay(10); digitalWrite(dirPin, dir); // Enables the motor to move in a particular direction for (int16_t x = 0; x < (stepsMM * mm); x++) { digitalWrite(stepPin, HIGH); delayMicroseconds(stepperBreaktime); digitalWrite(stepPin, LOW); delayMicroseconds(stepperBreaktime); } digitalWrite(enablePin, HIGH); // Enables the motor to move in a particular direction } void servoTask(uint8_t servonum) { #ifdef debugEN Serial.printf("Select Servo: %d \n", servonum); #endif for (uint16_t pulselen = servomax; pulselen > servomin; pulselen--) { pwm.setPWM(servonum, 0, pulselen); } delay(servoBreaktime); for (uint16_t pulselen = servomin; pulselen < servomax; pulselen++) { pwm.setPWM(servonum, 0, pulselen); } } void pump(int nr, uint16_t breaktime_pump) { digitalWrite( pumps[nr], HIGH); #ifdef debugEN Serial.printf("Starting pumps: %d for %d \n", pumps[nr], breaktime_pump); #endif delay(breaktime_pump); digitalWrite( pumps[nr], LOW); }
And related setting:
#define debugEN // un/-comment to en-/disable of extra information by Serial Port /* STEPPER SETTINGS */ // defines pins numbers #define stepPin 12 #define dirPin 14 #define enablePin 13 #define stepperBreaktime 70 // how long breaktime between each step, shorter will block motor const int stepsMM = 320; // calc steps for one mm #define endstop_pin 25 #define debounceTime_endstop 10000 /* SERVO SETTINGS */ // Depending on your servo make, the pulse width min and max may vary, you // want these to be as small/large as possible without hitting the hard stop // for max range. You'll have to tweak them as necessary to match the servos you // have! #define servomin 150 // standard 150 this is the 'minimum' pulse length count (out of 4096) #define servomax 600 // standard 600 this is the 'maximum' pulse length count (out of 4096) #define servoBreaktime 5000 /* COCKTAIL */ uint8_t cocktail = 0; /* WIFI SETTINGS */ // Set these to your desired credentials. const char *ssid = "cocktail_time"; const char *password = "NixFuerKinder"; boolean cocktailReady = true; /* PUMPS */ const int pumps[] = {2,4,16,17,5,18}; #define pumps_1 2 #define pumps_2 4 #define pumps_3 16 #define pumps_4 17 #define pumps_5 5 #define pumps_6 18 #define pumps_7 19
Also a video of the assembly and all the tests that were done.
Last but not least, a final video on how the process can work:
The cocktail machine closes with the fact that it still has a lot of potential. There were still countless ideas to be explored after the Fab Academy.
One of the hardest weeks so fare because it was like a little project in team. Also great oportunity for us to push our self and to manage our time wisley to finish wverithing in time. In our team we work together on every step from beginig to the end because it is better to have more heads for idas and progress. So the contribution was equal for our team on all segments, offcours I was a little weaker on the programing but learned a lot from the team.