donutsorelse
Published © GPL3+

Easter Eggs that Hide Themselves

A simple robot that gives easter eggs the ability to run off and hide themselves.

BeginnerFull instructions provided2 hours604
Easter Eggs that Hide Themselves

Things used in this project

Hardware components

Romeo BLE - Arduino Robot Control Board with Bluetooth 4.0
DFRobot Romeo BLE - Arduino Robot Control Board with Bluetooth 4.0
×1
DC motor (generic)
×2
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Custom parts and enclosures

eggbigger_good_topright_6w0ZhAsYRL.stl

Sketchfab still processing.

eggbigger_good_topleft_D6C6nZiohI.stl

Sketchfab still processing.

eggbigger_good_bottomright_2N0kw5b9PU.stl

Sketchfab still processing.

eggbigger_good_bottomleft_qFHMlh3SZX.stl

Sketchfab still processing.

Code

self_hiding_egg.ino

Arduino
Full code for the self hiding egg robot
enum State { CRUISE, EVADE, RUN, IDLE };
const char *stateName[] = { "CRUISE", "EVADE", "RUN", "IDLE" };

/* ------------- detection window ----------------------------------------- */
const int PERSON_NEAR_LOW  = 30;   // cm  1ft
const int PERSON_NEAR_HIGH = 174;  //183 cm  6ft

/* ------------- pins (Romeo) --------------------------------------------- */
const int M1_EN = 5,  M1_IN1 = 4;   // left wheel
const int M2_EN = 6,  M2_IN1 = 7;   // right wheel
const int TRIG  = 2,  ECHO   = 3;

/* ------------- behaviour parameters ------------------------------------- */
const int  CRUISE_PWM        = 180;
const int  RUN_PWM           = 255;
const unsigned long EVADE_BACK_MS = 800;
const unsigned long EVADE_SPIN_MS = 700;
const unsigned long MIN_RUN_MS    = 2000UL;   // 2s
const unsigned long MAX_RUN_MS    = 10000UL;  // 10s
const unsigned long JITTER_START_MS = 5000UL; // add wiggles after 5s
const int           JITTER_DELTA   = 60;      // PWM change for wiggle
const unsigned long LOG_INTERVAL   = 200UL;   // ms
const unsigned long GIVE_UP_TIME   = 60000UL; // idle after 60s inactivity

/* ------------- globals --------------------------------------------------- */
State         state             = CRUISE;
unsigned long stateStartMS      = 0;
unsigned long lastDetectionMS   = 0;
unsigned long lastLogMS         = 0;
unsigned long runDurationMS     = 0;  // random sprint length

/* ------------- helpers --------------------------------------------------- */
void motor(int en, int in, int pwm, bool fwd) {
  digitalWrite(in, fwd ? HIGH : LOW);
  analogWrite(en, pwm);
}
void wheels(int lPWM, int rPWM) {
  motor(M1_EN, M1_IN1, abs(lPWM), lPWM >= 0);
  motor(M2_EN, M2_IN1, abs(rPWM), rPWM >= 0);
}
long pingCM() {
  digitalWrite(TRIG, LOW);  delayMicroseconds(2);
  digitalWrite(TRIG, HIGH); delayMicroseconds(10);
  digitalWrite(TRIG, LOW);
  long us = pulseIn(ECHO, HIGH, 30000);            // 30ms = 5m
  if (us == 0) return -1;
  return (us * 34L) / 2000;                        // cm
}
void logLine(long dist, int lPWM, int rPWM) {
  unsigned long now = millis();
  if (now - lastLogMS < LOG_INTERVAL && state != IDLE) return;
  lastLogMS = now;

  unsigned long inState = now - stateStartMS;
  long toIdle = (state == IDLE) ? 0
               : (long)GIVE_UP_TIME - (long)(now - lastDetectionMS);
  long toRunEnd = (state == RUN)
                ? (long)runDurationMS - (long)inState : 0;

  Serial.print(now);              Serial.print(',');
  Serial.print(stateName[state]); Serial.print(',');
  Serial.print(dist);             Serial.print(',');
  Serial.print(lPWM);             Serial.print(',');
  Serial.print(rPWM);             Serial.print(',');
  Serial.print(inState/1000);     Serial.print(',');
  Serial.print(toIdle);           Serial.print(',');
  Serial.println(toRunEnd);
}
void changeState(State s) {
  state = s;
  stateStartMS = millis();
  logLine(-1, 0, 0);
}

/* ------------- setup ----------------------------------------------------- */
void setup() {
  pinMode(TRIG, OUTPUT);  pinMode(ECHO, INPUT);
  pinMode(M1_IN1, OUTPUT); pinMode(M2_IN1, OUTPUT);
  pinMode(M1_EN, OUTPUT);  pinMode(M2_EN, OUTPUT);

  Serial.begin(115200);
  Serial.println(
    "ms,state,dist,leftPWM,rightPWM,secInState,msToIdle,msToRunEnd");
  lastDetectionMS = millis();
  stateStartMS    = millis();
}

/* ------------- main loop ------------------------------------------------- */
void loop() {
  unsigned long now = millis();
  long d = pingCM();                              // -1 = no reading
  bool personSeen = (d >= PERSON_NEAR_LOW && d <= PERSON_NEAR_HIGH);

  /* idle timeout for active states */
  if (state != IDLE && (now - lastDetectionMS) > GIVE_UP_TIME) {
    wheels(0, 0);
    changeState(IDLE);
  }

  switch (state) {

  /* ---------- CRUISE ---------- */
  case CRUISE:
    wheels(CRUISE_PWM, CRUISE_PWM);
    logLine(d, CRUISE_PWM, CRUISE_PWM);

    if (personSeen) {
      lastDetectionMS = now;
      changeState(EVADE);
    }
    break;

  /* ---------- EVADE (reverse + spin) ---------- */
  case EVADE: {
      wheels(-RUN_PWM, -RUN_PWM);                 // back up
      logLine(d, -RUN_PWM, -RUN_PWM);
      delay(EVADE_BACK_MS);

      bool left = random(0, 2);                   // spin random dir.
      wheels(left ? -RUN_PWM : RUN_PWM,
             left ?  RUN_PWM : -RUN_PWM);
      logLine(d,
              left ? -RUN_PWM : RUN_PWM,
              left ?  RUN_PWM : -RUN_PWM);
      delay(EVADE_SPIN_MS);

      /* decide sprint length (210s) */
      runDurationMS = random(MIN_RUN_MS, MAX_RUN_MS + 1UL);
      changeState(RUN);
    }
    break;

  /* ---------- RUN (sprint away) ---------- */
  case RUN: {
      unsigned long elapsed = now - stateStartMS;

      /* steering: straight first 5s, then add wiggles */
      int lPWM = RUN_PWM;
      int rPWM = RUN_PWM;
      if (elapsed > JITTER_START_MS) {
        int delta = random(-JITTER_DELTA, JITTER_DELTA + 1);
        lPWM = RUN_PWM + delta;
        rPWM = RUN_PWM - delta;
        lPWM = constrain(lPWM, 0, 255);
        rPWM = constrain(rPWM, 0, 255);
      }
      wheels(lPWM, rPWM);
      logLine(d, lPWM, rPWM);

      if (elapsed >= runDurationMS) {
        wheels(0, 0);
        changeState(IDLE);
      }
    }
    break;

  /* ---------- IDLE ---------- */
  case IDLE:
    wheels(0, 0);
    logLine(d, 0, 0);

    if (personSeen) {                            // wake up
      lastDetectionMS = now;
      changeState(EVADE);
    }
    delay(250);
    break;
  }
}

Credits

donutsorelse
21 projects • 21 followers
I make different stuff every week of all kinds. Usually I make funny yet useful inventions.
Contact

Comments

Please log in or sign up to comment.