Good day!
I'm currently tinkering with the Arduino code for my Useless Box, which operates with 7 servo motors. I have tested all the mechanical movements with simple code segments, ensuring the mechanical and electrical aspects of the project are in order. However, integrating all the code has proven to be a bit too sophisticated for my skills. Despite spending weeks trying to resolve the issue myself, and even hiring a coder from Fiverr, the project still does not work as intended.
I am hopeful that there is a coding wizard here who could assist me in properly operating the 7 servo motors. Easy peasy. I would very appreciate some help with this.
So the pictures of the Project in progress:
All the 3D parts:
The whole contraption without box. It is quite tiny.
A pitcture of the switch rotating mechanism
Here I will paste the "most advanced" code so for and sum up the problems which I have currently:
/*
* Changelog:
* V0 - First Draft Code by Saeed
* V1 - Changes done by Nik:
- Changed some Servo Position names and Start and end Positions
- Excluded the LED Eyes function for now. I will just turn the LED on when DingDing gets out of the box to S7_OutPos
- "Deleted" the Cases. I will set them once I have some time. So lets work for now only with case 1. I made the example what should happen in Cas1
*V1.1
- I did it like this and this worked well with the switch (On/off Switch) and the code like:
...
Switch Pin 13;
...
void loop() {
switchState = digitalRead(switchPin);
if (switchState != lastSwitchState) {
if (switchState == HIGH) {
int mode = random(2);
...
And then going into different cases
Request for V2
Could
* V2 by Saeed
- Make a function "void MoveServoHuman (Servo, Position)" which then can be used to move a defined Servo to a certain Position)
- Somehow in ThinkerCad the "human Movement" of the Servos is not as intendet from me.
The Servo should start moving normal fast and getting slower at the very last bit of movement until reaching the final position.
*/
#include <Servo.h>
//Switch will workwith 1 GND pin and 1 Digital Pin , no need of VCC pin :)
#define SwitchPin 3 // Digital Pin for Switch set on PCB
#define ledPin 3 // DingDingLED Eyes
unsigned long previousMillis = 0;
int ledState = LOW;
int busy_flag=LOW;
//Servo Pins:
#define S1_PIN 12 // on PCB slot1
#define S2_PIN 5
#define S3_PIN 6
#define S4_PIN 7
#define S5_PIN 8
#define S6_PIN 9
#define S7_PIN 10
//Servo Objects:
Servo S1_SP; //Switch Pusher
Servo S2_LP; //Lid Pusher
Servo S3_FP; //Flag Pusher
Servo S4_ST; //Switch Turner (360° Servo)
Servo S5_SL; //Switch Level
Servo S6_IO; //Iris Opener
Servo S7_DP; //DingDing Pusher
//Start & End Servo Positions:
//Switch Pusher
int S1_StartPos = 130; //Arm In the box
int S1_MidPos = 100; //Arm lurking
int S1_EndPos = 52; //Arm pushed switch
// Lid Pusher
int S2_ClosePos = 30; // Lid Closed
int S2_OpenPos = 60; // Lid Open
//Flag Pusher
int S3_StartPos = 30; // Pos in the Box
int S3_FlagPos_L = 60;
int S3_FlagPos_R = 90;
// Switch Turner
int S4_Turn_R = 30; // Turn CW
int S4_Hold = 0; // No turn
int S4_Turn_L = -30; // Turn CCW
//Switch Level
int S5_UpPos = 30; //Switch Up
int S5_DownPos = 60; // Switch down
// Iris Opener
int S6_OpenPos = 30; // Iris open
int S6_ClosePos = 60; // Iris closed
// DingDing Pusher
int S7_InPos = 30; // DingDing In Box
int S7_OutPos = 60; //DingDing out of box
unsigned long startTime;
float movementProgress;
//---------------VOID SETUP---------------
void setup() {
S1_SP.attach(S1_PIN);
S2_LP.attach(S2_PIN);
S3_FP.attach(S3_PIN);
S4_ST.attach(S4_PIN);
S5_SL.attach(S5_PIN);
S6_IO.attach(S6_PIN);
S7_DP.attach(S7_PIN);
// Set all Servos on starting Position
S1_SP.write(S1_StartPos);
S3_FP.write(S3_StartPos);
S2_LP.write(S2_ClosePos);
S6_IO.write(S6_OpenPos); //Open Iris before Switch is up
S4_ST.write(S4_Hold);
S5_SL.write(S5_UpPos);
S7_DP.write(S7_InPos);
// Setup Debug Serial
Serial.begin(9600);
pinMode(SwitchPin, INPUT_PULLUP); // Set the SwitchPin as input with internal pull-up resistor
attachInterrupt(digitalPinToInterrupt(SwitchPin),Rand_Case_Switch, FALLING); // Attach the interrupt
startTime = millis();
}
//---------------VOID LOOP---------------
void loop() {
//DingDing_leds(500);
//Jitter();
Rand_Case_Switch();
}
//---------------FUNCTIONS---------------
// FUNCTION 1): For Check Servo Movment Debug
void servo_check(Servo &theServo, int angle)
{
theServo.write(angle);
}
// FUNCTION 2): For Rumbles the Lid in Idle
void Jitter() //
{
if (busy_flag == LOW) {
int pos = random(0, 180); // Random servo position
S2_LP.write(pos);
// Introduce jitter occasionally
if (random(0, 100) < 10) { // Adjust probability as needed
int originalPos = pos;
int jitterRange = 10; // Jitter range in degrees
int jitterTime = 500; // Jitter time in milliseconds
// Create a rumbling effect
for (int i = 0; i < 5; i++) { // Number of rumble cycles
int jitter = random(-jitterRange, jitterRange);
S2_LP.write(originalPos + jitter);
delay(jitterTime);
S2_LP.write(originalPos - jitter);
delay(jitterTime);
}
S2_LP.write(originalPos); // Return to the original position
}
}
}
/*
void DingDing_leds(int interval)
{
unsigned long currentMillis1 = millis();
if (currentMillis1 - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis1;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}
*/
// Choooses Case if switch is pushed
void Rand_Case_Switch()
{
busy_flag=HIGH;
int caseNumber = generateRandomCase();
switch (caseNumber) {
case 1:
// Code for Case 1
Serial.println("Case 1");
case1();
break;
case 2:
// Code for Case 2
Serial.println("Case 2");
case2();
break;
case 3:
// Code for Case 3
Serial.println("Case 3");
case3();
break;
case 4:
// Code for Case 4
Serial.println("Case 4");
case4();
break;
case 5:
// Code for Case 5
Serial.println("Case 5");
case5();
break;
case 6:
// Code for Case 6
Serial.println("Case 6");
case6();
break;
case 7:
// Code for Case 7
Serial.println("Case 7");
case7();
break;
default:
// Default case (shouldn't be reached)
Serial.println("Unknown case");
break;
}
}
// Function to set Probability of the Cases
int generateRandomCase() {
float probabilities[] = {0.8, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01}; // Adjust probabilities as needed
int cases[] = {1, 2, 3, 4, 5, 6, 7};
float randValue = random(100) / 100.0;
float cumulativeProb = 0.0;
for (int i = 0; i < sizeof(probabilities) / sizeof(float); i++) {
cumulativeProb += probabilities[i];
if (randValue < cumulativeProb) {
return cases[i];
}
}
// Fallback case (shouldn't be reached)
return cases[sizeof(cases) / sizeof(int) - 1];
}
// Servo Movement with an slowed down Endspeed
void MoveServoHuman(Servo &theServo, int Position) {
int initialPosition = theServo.read();
int targetPosition = constrain(Position, 0, 180); // Make sure the target position is within valid range
int currentPosition = initialPosition;
//Serial.write(theServo);
//Serial.write(Position);
int speed = 5; // Initial speed of movement
int minSpeed = 1; // Minimum speed to ensure smooth final positioning
while (currentPosition != targetPosition) {
if (currentPosition < targetPosition) {
currentPosition += speed;
if (currentPosition > targetPosition) {
currentPosition = targetPosition;
}
} else {
currentPosition -= speed;
if (currentPosition < targetPosition) {
currentPosition = targetPosition;
}
}
theServo.write(currentPosition);
// Gradually decrease speed as servo approaches final position
if (abs(targetPosition - currentPosition) < 10) {
speed = max(minSpeed, speed - 10); //-1 Start
}
delay(20); // Delay for smoother movement
}
}
//---------------CASES---------------
// CASE 1: Simple Pusher
void case1(){
MoveServoHuman (S1_SP, S1_EndPos); //-> The Arm pushes the Switch
delay(500); //--> Waits a few ms
MoveServoHuman (S1_SP, S1_StartPos); //--> Moves Arm back in
}
// Case is Same as Case 1 but the arm goes out and stays longer aka. firmer on the switch
void case2(){
S2_LP.write(S2_OpenPos); // Lid of the box opens (normal movement)
delay(100); // wait for some ms
MoveServoHuman (S7_DP, S7_OutPos); // Ding Ding comes out of box
delay(200); // wait for some ms
MoveServoHuman (S1_SP, S1_EndPos); //-> The Arm pushes the Switch
delay(2000); //--> Waits a bit more than case 2
MoveServoHuman (S1_SP, S1_StartPos); //--> Moves Arm back in
MoveServoHuman (S7_DP, S7_InPos); // Ding Ding goes back in box
delay(200); // wait for some ms
S2_LP.write(S2_ClosePos); // Lid of the box closes (normal movement)
}
//In this case the switch gets removed from the box and the iris closed
void case3(){
S5_SL.write(S5_DownPos); //Switch gets moved "down" or in the box with no special movement
delay(500);
MoveServoHuman (S6_IO, S6_ClosePos); // Iris gets closed smothley
delay(4000); // waits forever
// --> Then Case 4 inside the box (Turns switch)
MoveServoHuman (S6_IO, S6_OpenPos); // Iris gets opened again smothley
delay(500);
S5_SL.write(S5_UpPos); //Switch gets moved "up" agai
}
void case4(){
S4_ST.write(S4_Turn_R); //The switch gets turned clockwise
delay(1000); //Move Servo approx 1 sec until the Switch reached 180°
S4_ST.write(S4_Hold); //Stop Servo
busy_flag=LOW; //set back switch state ?! Or is it working like this?
}
/* My two problems here to code:
1) The Switch should always be mechanically turned alternating clockwise 180° and when this case occurs again counter clockwise (180°). So that the switch cabling doesn't get tangled up.
How does such a case look like?
Saeed's Response: Bro that's mechanical problem you can use brushes just like in DC motor to avoid the wires being rotated . It will not leave any effect at arduino it'll alsway consier as a switch as it is.
2) Normally (in other cases) the switch gets pushed by the User and bushed back by the machine.
In this "turning case" the switch gets pushed by the User but the machine only turns the switch phisicaly by 180° to solve the case.
So does the system know again that busy_flag state has changed?
Saeed's Response: Busy flag is just there for jitter function , you told that there must be an occasional jitter when device is not doing anything else. So
this flag tell about if device is busy in another function or not.
*/
//In this case the box gives up... or supposes like
void case5(){
S2_LP.write(S2_OpenPos); // Lid of the box opens (normal movement)
delay(100); // wait for some ms
MoveServoHuman (S7_DP, S7_OutPos); // Ding Ding comes out of box
delay(200); // wait for some ms¨
MoveServoHuman (S3_FP, S3_FlagPos_R); // Flag waving from right to left...
MoveServoHuman (S3_FP, S3_FlagPos_L); // Flag waving from right to left...
MoveServoHuman (S3_FP, S3_FlagPos_R); // Flag waving from right to left...
MoveServoHuman (S3_FP, S3_FlagPos_L); // Flag waving from right to left...
MoveServoHuman (S3_FP, S3_FlagPos_R); // Flag waving from right to left...
MoveServoHuman (S3_FP, S3_FlagPos_L); // Flag waving from right to left...
MoveServoHuman (S3_FP, S3_FlagPos_R); // Flag waving from right to left...
MoveServoHuman (S3_FP, S3_FlagPos_L); // Flag waving from right to left...
//--> There is a smarter way to do this waving flag?
// now suddenly the arm switches the switch and everything closes
MoveServoHuman (S3_FP, S3_StartPos); // -> Flag goes back in
MoveServoHuman (S1_SP, S1_EndPos); // -> The Arm pushes the Switch
delay(500); //--> Waits a few ms
MoveServoHuman (S1_SP, S1_StartPos); // --> Moves Arm back in
MoveServoHuman (S7_DP, S7_InPos); // Ding Ding goes back in box
delay(200); // wait for some ms
S2_LP.write(S2_ClosePos); // Lid of the box closes (normal movement)
}
void case6(){
//TBD by me
}
void case7(){
unsigned long currentTime = millis();
}
// Restore Servo Positions
void restoreservos() // This func. is used to restore servo positions to their start when intended
{
S1_SP.write(S1_StartPos);
S3_FP.write(S3_StartPos);
S2_LP.write(S2_ClosePos);
S6_IO.write(S6_OpenPos); //Open Iris before Switch is up
S4_ST.write(S4_Hold);
S5_SL.write(S5_UpPos);
S7_DP.write(S7_InPos);
}
So main problem right now:
The switch can physically be turned by the machine by 180° CW and CCW. Solving the problem of a flicked switch alternatively then pushing it back with the classic uslessbox arm.
So this code still doesn't solve the problem that the switch could be in each state (0 or 1) when I turn the machine on. So the code should always start with the momentarly state of the switch as default and react with a "Case" when the switch is trigered by the user.
Furthermore: In case 4 the switch gets physically turned on the box and so the whole reaction concept has to work when it's turned again.
Do you guys understand the problem?
2nd problem:
The function MoveServoHuman() should move the servos like James Bruton has showed in his video about how to move Servos more "humanly". Which means: the servos de-accelarate steadily but heavily before reachaing it's end position. This function the Fiverr Coder wrote seems not to work as intended even though I worked with the variables.
This is it for now Thank you guys in advance