edge lab LIVE! starts Tuesday in Munich. Grab a workshop seat and take home the board you build on. Code FRIEND-HACKSTER saves 20%. () Learn more!

Nomadic infrastructure for urban microecosistems

How to create an autonomous mechanical organism inspired by walking structures that transports environmental connditions through the city

IntermediateWork in progress18 hours17
Nomadic infrastructure for urban microecosistems

Things used in this project

Hardware components

FireBeetle ESP32 IOT Microcontroller (Supports Wi-Fi & Bluetooth)
DFRobot FireBeetle ESP32 IOT Microcontroller (Supports Wi-Fi & Bluetooth)
For this project we use Barduino (made by Fab Lab Barcelona), but the module is ESP32S3
×1
SparkFun Soil Moisture Sensor (with Screw Terminals)
SparkFun Electronics SparkFun Soil Moisture Sensor (with Screw Terminals)
×1
Photo resistor
Photo resistor
×1
Gravity: DHT11 Temperature Humidity Sensor For Arduino
DFRobot Gravity: DHT11 Temperature Humidity Sensor For Arduino
×1
Resistor 10k ohm
Resistor 10k ohm
×1
Servomotor 360°
×2
M4 T-nuts
×8
nuts M4
×8
cap nuts M4
×2
Threaded rod M4 (10cm lenght)
×2
nuts M3
×28
M3 lock nuts
×93
screws M 3 × 12 mm
×36
screws M 3 × 16 mm
×48
screws M 3 × 20 mm
×36

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Laser cutter (generic)
Laser cutter (generic)

Story

Read more

Custom parts and enclosures

Theo Jansen mechanism pt.1

You can see the files for the original mechanism here: https://github.com/MakeMagazinDE/Strandbeest/tree/master

Theo Jansen mechanism pt.2

This is the files of the estructure
Video tutorial: https://www.youtube.com/watch?v=ZCO-I99kyqU

Cad File

Cad File pt.2

Schematics

Circuit diagram

Components list

Nomadic ecosistem schematics

The arduino needed for this project is Arduino esp32

Code

Nomadic-code.ino

C/C++
#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");
}

Credits

Antonio García Fernández
3 projects • 2 followers
Erandi Itzai Estrada Martínez
3 projects • 1 follower

Comments