Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 2 | ||||
| × | 8 | ||||
| × | 8 | ||||
| × | 2 | ||||
| × | 2 | ||||
| × | 28 | ||||
| × | 93 | ||||
| × | 36 | ||||
| × | 48 | ||||
| × | 36 | ||||
Software apps and online services | ||||||
![]() |
| |||||
Hand tools and fabrication machines | ||||||
![]() |
| |||||
How to create an autonomous mechanical organism inspired by walking structures that transports environmental conditions (shade, humidity, and shelter) to enable the survival of insects and other organisms in hostile urban environments. It moves because urban hostility is not static. Heat, shade, dryness, and exposure shift constantly in the city and most non-human species lack the ability to escape them. Mobility becomes a survival strategy.
We downloaded the files for the Theo Jansen mechanism for the moving structure and cut them using the laser cutter.
We assembled the mechanism, 12 legs in total. We had to buy a lot of screws from the hardware store, and it was tricky to tighten the nuts just enough to allow movement without making the structure unstable.
Instructions to assemble the legs can be seen here, just be careful with the correct direction of the pieces, since the left side is the mirror of the right side.
With the Arduino, we prepared the most important part of the prototype: 3 sensors in total, one humidity sensor, one photosensitive sensor, and one temperature sensor. This way, we tell the two servo motors when they need to move to search for a more suitable habitat for the organisms in the micro-ecosystem.
We assembled the motors with the structure’s legs.
We designed a terrarium to hold the soil, moss, and worms. We laser-cut it entirely in MDF, and then assembled it together with the structure, Arduino, and batteries.
#include <ESP32Servo.h>
#include "DHTesp.h"
// Pines
#define MOISTURE_PIN 1
#define LDR_PIN 2
#define DHT_PIN 9
#define SERVO1_PIN 6 // Patas izquierdas
#define SERVO2_PIN 7 // Patas derechas
// Objetos
Servo servo1;
Servo servo2;
DHTesp dht;
// Constantes para servos de rotacin continua
#define STOP_SPEED 90 // Velocidad para detener el servo
#define WALK_SPEED 180 // Velocidad para caminar (ajusta 0 o 180 segn direccin deseada)
// Estados
enum HabitatState {
STABLE,
TOO_SUNNY,
TOO_HOT,
TOO_COLD,
TOO_DARK,
TOO_WET, // Solo basado en soil moisture
TOO_DRY, // Solo basado en soil moisture
WALKING // Caminata activa
};
HabitatState currentState = STABLE;
// Tiempos
const unsigned long STRESS_DETECTION_TIME = 5000; // 5 segundos detectando estrs
const unsigned long WALKING_TIME = 10000; // 10 segundos caminando
const unsigned long READING_INTERVAL = 5000; // Lecturas cada 5 segundos en estable
unsigned long lastReadingTime = 0;
unsigned long walkingStartTime = 0;
unsigned long stressDetectionStart = 0;
// Umbrales ambientales
const int LIGHT_SUNNY = 3000; // Demasiada luz solar
const int LIGHT_DARK = 700; // Muy poca luz
const float TEMP_HOT = 26.0; // Temperatura muy alta
const float TEMP_COLD = 17.0; // Temperatura muy baja
const int MOISTURE_WET = 1500; // Suelo muy hmedo (TOO_WET)
const int MOISTURE_DRY = 800; // Suelo muy seco (TOO_DRY)
// Variables para almacenar lecturas
int lastMoisture = 0;
int lastLight = 0;
float lastTemperature = 0;
float lastHumidity = 0;
// Variables para condiciones actuales
bool isTooSunny = false;
bool isTooHot = false;
bool isTooCold = false;
bool isTooDark = false;
bool isTooWet = false;
bool isTooDry = false;
// Bandera para iniciar caminata solo una vez al entrar al estado
bool walkingStarted = false;
// ===============================
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("========================================");
Serial.println("Autonomous Mobile Habitat");
Serial.println("========================================");
Serial.println("Mode: Read every 5s -> Walk 10s -> Read every 5s");
Serial.println("Sensors: LDR (light), DHT11 (temp), Soil Moisture");
Serial.println("Servos: Continuous rotation (speed control)");
Serial.println("----------------------------------------");
servo1.attach(SERVO1_PIN);
servo2.attach(SERVO2_PIN);
dht.setup(DHT_PIN, DHTesp::DHT11);
analogReadResolution(12);
// Detener servos al iniciar
servo1.write(STOP_SPEED);
servo2.write(STOP_SPEED);
lastReadingTime = millis();
stressDetectionStart = 0;
}
// ===============================
void loop() {
unsigned long now = millis();
// ===== MQUINA DE ESTADOS =====
switch (currentState) {
case STABLE:
handleStableState(now);
break;
case TOO_SUNNY:
case TOO_HOT:
case TOO_COLD:
case TOO_DARK:
case TOO_WET:
case TOO_DRY:
handleStressDetection(now);
break;
case WALKING:
handleWalkingState(now);
break;
}
// Pequeo delay para evitar sobrecarga
delay(10);
}
// Estado ESTABLE
void handleStableState(unsigned long now) {
// Lecturas cada 5 segundos
if (now - lastReadingTime >= READING_INTERVAL) {
readSensors();
lastReadingTime = now;
// Evaluar condiciones individuales
evaluateConditions();
// Determinar tipo de estrs si existe (prioridad en orden)
if (isTooSunny) {
printBadConditions("TOO SUNNY", "LDR SENSOR");
currentState = TOO_SUNNY;
stressDetectionStart = now;
walkingStarted = false; // Resetear bandera para prximo paseo
}
else if (isTooHot) {
printBadConditions("TOO HOT", "TEMPERATURE SENSOR");
currentState = TOO_HOT;
stressDetectionStart = now;
walkingStarted = false;
}
else if (isTooCold) {
printBadConditions("TOO COLD", "TEMPERATURE SENSOR");
currentState = TOO_COLD;
stressDetectionStart = now;
walkingStarted = false;
}
else if (isTooDark) {
printBadConditions("TOO DARK", "LDR SENSOR");
currentState = TOO_DARK;
stressDetectionStart = now;
walkingStarted = false;
}
else if (isTooWet) {
printBadConditions("TOO WET", "SOIL MOISTURE SENSOR");
currentState = TOO_WET;
stressDetectionStart = now;
walkingStarted = false;
}
else if (isTooDry) {
printBadConditions("TOO DRY", "SOIL MOISTURE SENSOR");
currentState = TOO_DRY;
stressDetectionStart = now;
walkingStarted = false;
}
else {
Serial.println("\n ALL CONDITIONS ARE OPTIMAL ");
printAllSensorValues();
}
}
// Mantener servos detenidos
servo1.write(STOP_SPEED);
servo2.write(STOP_SPEED);
}
// Deteccin de ESTRS (5 segundos)
void handleStressDetection(unsigned long now) {
// Mostrar progreso de deteccin
unsigned long elapsed = now - stressDetectionStart;
int secondsElapsed = elapsed / 1000;
int totalSeconds = STRESS_DETECTION_TIME / 1000;
// Solo imprimir cada segundo
static int lastPrintedSecond = -1;
if (secondsElapsed != lastPrintedSecond) {
Serial.print("Stress detection: ");
Serial.print(secondsElapsed);
Serial.print("s / ");
Serial.print(totalSeconds);
Serial.println("s");
lastPrintedSecond = secondsElapsed;
}
// Si pasaron 5 segundos de estrs, empezar a caminar
if (elapsed >= STRESS_DETECTION_TIME) {
Serial.println("STARTING 10s WALK");
currentState = WALKING;
walkingStartTime = now;
}
// Mantener servos detenidos durante la deteccin
servo1.write(STOP_SPEED);
servo2.write(STOP_SPEED);
}
// Estado CAMINANDO (10 segundos)
void handleWalkingState(unsigned long now) {
// Al entrar al estado, arrancar los servos una sola vez
if (!walkingStarted) {
servo1.write(WALK_SPEED);
servo2.write(WALK_SPEED);
walkingStarted = true;
Serial.println("Walking started - continuous rotation");
}
// Controlar tiempo de caminata
unsigned long walkElapsed = now - walkingStartTime;
int secondsElapsed = walkElapsed / 1000;
int totalSeconds = WALKING_TIME / 1000;
// Mostrar progreso de caminata cada segundo (opcional)
static int lastWalkSecond = -1;
if (secondsElapsed != lastWalkSecond) {
Serial.print("Walking: ");
Serial.print(secondsElapsed);
Serial.print("s / ");
Serial.print(totalSeconds);
Serial.println("s");
lastWalkSecond = secondsElapsed;
}
// Si pasaron 10 segundos, volver a estado estable
if (walkElapsed >= WALKING_TIME) {
Serial.println("WALKING COMPLETE - RETURNING TO STABLE");
stopWalking();
currentState = STABLE;
lastReadingTime = now; // Reiniciar timer de lecturas
stressDetectionStart = 0;
walkingStarted = false;
return;
}
}
// Lectura de sensores
void readSensors() {
Serial.println("READING SENSORS...");
lastMoisture = analogRead(MOISTURE_PIN);
lastLight = analogRead(LDR_PIN);
TempAndHumidity data = dht.getTempAndHumidity();
lastTemperature = data.temperature;
lastHumidity = data.humidity; // Solo para monitoreo, no para estrs
}
// Evaluar condiciones ambientales individualmente
void evaluateConditions() {
// Reiniciar todas las condiciones
isTooSunny = false;
isTooHot = false;
isTooCold = false;
isTooDark = false;
isTooWet = false;
isTooDry = false;
// Evaluar luz (LDR) - 2 casos
if (lastLight > LIGHT_SUNNY) {
isTooSunny = true;
} else if (lastLight < LIGHT_DARK) {
isTooDark = true;
}
// Evaluar temperatura (DHT11) - 2 casos
if (lastTemperature > TEMP_HOT) {
isTooHot = true;
} else if (lastTemperature < TEMP_COLD) {
isTooCold = true;
}
// Evaluar humedad del suelo (Soil Moisture) - 2 casos
if (lastMoisture > MOISTURE_WET) {
isTooWet = true;
} else if (lastMoisture < MOISTURE_DRY) {
isTooDry = true;
}
}
// Imprimir valores de TODOS los sensores
void printAllSensorValues() {
Serial.println("CURRENT SENSOR VALUES:");
// Temperatura
Serial.print(" Temperature: ");
Serial.print(lastTemperature);
Serial.print("C (OK: ");
Serial.print(TEMP_COLD);
Serial.print("-");
Serial.print(TEMP_HOT);
Serial.print("C) - ");
if (lastTemperature > TEMP_HOT) {
Serial.println("TOO HOT!");
} else if (lastTemperature < TEMP_COLD) {
Serial.println("TOO COLD!");
} else {
Serial.println("OK");
}
// Luz
Serial.print(" Light Level: ");
Serial.print(lastLight);
Serial.print(" (OK: ");
Serial.print(LIGHT_DARK);
Serial.print("-");
Serial.print(LIGHT_SUNNY);
Serial.print(") - ");
if (lastLight > LIGHT_SUNNY) {
Serial.println("TOO SUNNY!");
} else if (lastLight < LIGHT_DARK) {
Serial.println("TOO DARK!");
} else {
Serial.println("OK");
}
// Humedad del suelo
Serial.print(" Soil Moisture: ");
Serial.print(lastMoisture);
Serial.print(" (OK: ");
Serial.print(MOISTURE_DRY);
Serial.print("-");
Serial.print(MOISTURE_WET);
Serial.print(") - ");
if (lastMoisture > MOISTURE_WET) {
Serial.println("TOO WET!");
} else if (lastMoisture < MOISTURE_DRY) {
Serial.println("TOO DRY!");
} else {
Serial.println("OK");
}
// Humedad ambiental (solo informativa)
Serial.print(" Air Humidity: ");
Serial.print(lastHumidity);
Serial.println("% (monitoring only)");
Serial.println("----------------------------------------");
}
// Imprimir condiciones MALAS especficas
void printBadConditions(const char* conditionName, const char* sensorType) {
Serial.println("\nBAD CONDITIONS DETECTED");
Serial.print("CONDITION: ");
Serial.println(conditionName);
Serial.print("SENSOR: ");
Serial.println(sensorType);
Serial.println("PROBLEMATIC SENSOR VALUES:");
if (strcmp(conditionName, "TOO SUNNY") == 0) {
Serial.print(" Light: ");
Serial.print(lastLight);
Serial.print(" > ");
Serial.println(LIGHT_SUNNY);
}
else if (strcmp(conditionName, "TOO HOT") == 0) {
Serial.print(" Temperature: ");
Serial.print(lastTemperature);
Serial.print("C > ");
Serial.print(TEMP_HOT);
Serial.println("C");
}
else if (strcmp(conditionName, "TOO COLD") == 0) {
Serial.print(" Temperature: ");
Serial.print(lastTemperature);
Serial.print("C < ");
Serial.print(TEMP_COLD);
Serial.println("C");
}
else if (strcmp(conditionName, "TOO DARK") == 0) {
Serial.print(" Light: ");
Serial.print(lastLight);
Serial.print(" < ");
Serial.println(LIGHT_DARK);
}
else if (strcmp(conditionName, "TOO WET") == 0) {
Serial.print(" Soil Moisture: ");
Serial.print(lastMoisture);
Serial.print(" > ");
Serial.println(MOISTURE_WET);
}
else if (strcmp(conditionName, "TOO DRY") == 0) {
Serial.print(" Soil Moisture: ");
Serial.print(lastMoisture);
Serial.print(" < ");
Serial.println(MOISTURE_DRY);
}
printAllSensorValues();
}
// Detener servos (inmediato para rotacin continua)
void stopWalking() {
servo1.write(STOP_SPEED);
servo2.write(STOP_SPEED);
Serial.println("Servos stopped at neutral (90)");
Serial.println("----------------------------------------\n");
}









_l4kqqZGT7A.png)
Comments