> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kode.diy/llms.txt
> Use this file to discover all available pages before exploring further.

# Inventor

> Conecta motores DC, un servomotor y sensores para crear tu propio robot.

**Módulo:** Basic <Icon icon="seedling" iconType="solid" />

# Características

Con este módulo puedes conectar **motores a tu Kode Dot y hacer robots, como por ejemplo un coche siguelineas.**

Puedes conectar lo siguiente:

* 2x motores DC o 1x motor paso a paso
* 1x servomotor
* 4x GPIOs para sensores
* 1x bus de I2C

No es necesario usar una fuente externa de alimentación ya que **todo se alimenta desde el Kode Dot.**

Los **motores DC tienen que ser de 5V y están limitados por hardware a una corriente máxima de 700mA por motor.** Así, como el Kode Dot puede suministrar hasta 2A, quedan **600mA para conectar un servomotor.**

Los **motores DC se controlan por PWM** y se puede medir la corriente que esta consumiendo cada motor para tener una estimación de su torque.

<Frame>
  <img width="400" src="https://mintcdn.com/kodediy/XTX2YJaXd-8SFLrS/images/external-modules/inventor/inventor.png?fit=max&auto=format&n=XTX2YJaXd-8SFLrS&q=85&s=4a90a58f228b847eaaa95ecab19c945a" data-path="images/external-modules/inventor/inventor.png" />
</Frame>

## Conexión con el Kode Dot

<Frame>
  <img src="https://mintcdn.com/kodediy/XTX2YJaXd-8SFLrS/images/external-modules/inventor/inventor-kodedot.png?fit=max&auto=format&n=XTX2YJaXd-8SFLrS&q=85&s=810f73467f639f637b36d21db39628b1" width="1920" height="1080" data-path="images/external-modules/inventor/inventor-kodedot.png" />
</Frame>

## Esquema de conexión

El driver que controla los motores está conectado de la siguiente manera:

| Driver  | ESP32-S3 |
| ------- | -------- |
| AIN1    | GPIO42   |
| AIN2    | GPIO41   |
| BIN1    | GPIO40   |
| BIN2    | GPIO39   |
| nFAULT  | GPIO38   |
| AIPROPI | GPIO37   |
| BIPROPI | GPIO36   |

<Tip> AIPROPI y BIPROPI sirven para medir la corriente que está consumiendo cada motor. Para más información consulta el datasheet del driver.</Tip>

## Ejemplo de código

Conecta **dos motores DC a los conectores** y con este código verás como primero aceleran, luego mantienen la velocidad máxima y finalmente desaceleran.

```cpp motors_test.ino lines icon="microchip" theme={null}
/**
 * DRV8411A + 2x DC + Servo + Lectura de corriente por IPROPI (ESP32-S3).
 * Muestra en Serial Plotter: IA(A) \t IB(A) \t ITRIP(A) mientras ejecuta secuencias de ambos motores y un servo.
 * Fórmulas IPROPI (datasheet): IPROPI(µA) = I_LS_total(A) * 200 µA/A; V_IPROPI = IPROPI * R_IPROPI;
 *                              I_motor = V_IPROPI / (R_IPROPI * 200e-6); I_TRIP = VREF / (R_IPROPI * 200e-6).
 */
/* ───────── KODE | docs.kode.diy ───────── */

#include <Arduino.h>
#include <ESP32Servo.h>

/* Pines DRV8411A */
constexpr int PIN_AIN1   = 42;
constexpr int PIN_AIN2   = 41;
constexpr int PIN_BIN1   = 39;
constexpr int PIN_BIN2   = 40;
constexpr int PIN_nFAULT = 3;

/* IPROPI -> ADC (canal A y canal B) */
constexpr int PIN_AIPROPI = 2;   /* Entrada ADC para A-IPROPI */
constexpr int PIN_BIPROPI = 1;   /* Entrada ADC para B-IPROPI */

/* Servo */
constexpr int PIN_SERVO = 13;
Servo servo;

/* Configuración del servo (microsegundos y ángulos) */
constexpr int SERVO_MIN_US = 500;
constexpr int SERVO_MAX_US = 2500;
constexpr int SERVO_CENTER = 90;
constexpr int SERVO_LEFT   = 0;
constexpr int SERVO_RIGHT  = 180;

/* Funciones auxiliares DRV8411A */
inline void motorA_coast()  { digitalWrite(PIN_AIN1, LOW);  digitalWrite(PIN_AIN2, LOW);  } /* Alta impedancia en ambas entradas */
inline void motorA_brake()  { digitalWrite(PIN_AIN1, HIGH); digitalWrite(PIN_AIN2, HIGH); } /* Freno por decaimiento rápido */
inline void motorA_fwd()    { digitalWrite(PIN_AIN1, HIGH); digitalWrite(PIN_AIN2, LOW);  } /* Motor A adelante */
inline void motorA_rev()    { digitalWrite(PIN_AIN1, LOW);  digitalWrite(PIN_AIN2, HIGH); } /* Motor A atrás */

inline void motorB_coast()  { digitalWrite(PIN_BIN1, LOW);  digitalWrite(PIN_BIN2, LOW);  } /* Alta impedancia en ambas entradas */
inline void motorB_brake()  { digitalWrite(PIN_BIN1, HIGH); digitalWrite(PIN_BIN2, HIGH); } /* Freno por decaimiento rápido */
inline void motorB_fwd()    { digitalWrite(PIN_BIN1, HIGH); digitalWrite(PIN_BIN2, LOW);  } /* Motor B adelante */
inline void motorB_rev()    { digitalWrite(PIN_BIN1, LOW);  digitalWrite(PIN_BIN2, HIGH); } /* Motor B atrás */

/* Tiempos */
constexpr uint32_t T_MOTOR_DIR   = 2000; /* 2 s por sentido */
constexpr uint32_t T_SERVO_TRAMO = 1000; /* 1 s por tramo */

/* Parámetros IPROPI (ajustar R al valor real usado) */
constexpr float VREF_V         = 3.3f;       /* VREF (V) */
constexpr float RIPROPI_OHMS   = 23700.0f;   /* Cambiar a 10000.0f si se usa 10 kΩ */
constexpr float AIPROPI_GAIN   = 200e-6f;    /* 200 µA/A (ganancia IPROPI) */

/* Configuración del ADC */
constexpr int   ADC_BITS       = 12;         /* Resolución ADC */
constexpr float ADC_VFULL      = 3.3f;       /* Con atenuación 11 dB, ~3.3 V de escala completa */

/* Máquinas de estado concurrentes */
enum class Phase { FWD, REV, DONE };
struct MotorSeq {
  bool enabled=false; Phase phase=Phase::FWD; uint32_t tEnd=0;
  void start() { enabled=true; phase=Phase::FWD; tEnd=millis()+T_MOTOR_DIR; }
};
struct ServoSeq {
  bool enabled=false; int step=0; uint32_t tEnd=0;
  void start() { enabled=true; step=0; servo.write(SERVO_RIGHT); tEnd=millis()+T_SERVO_TRAMO; }
};
MotorSeq seqA, seqB; ServoSeq seqS;

/* ADC -> voltios */
float adcVolts(int pin) {
  uint16_t raw = analogRead(pin);
  return (raw * ADC_VFULL) / ((1 << ADC_BITS) - 1);
}

/* Voltios IPROPI -> corriente del motor (A) */
float ipropiToCurrentA(float v_ipropi) {
  return v_ipropi / (RIPROPI_OHMS * AIPROPI_GAIN);
}

/* Muestreo periódico para Serial Plotter: IA, IB e ITRIP */
void sampleAndPrintCurrents() {
  float vA = adcVolts(PIN_AIPROPI);
  float vB = adcVolts(PIN_BIPROPI);
  float iA = ipropiToCurrentA(vA);
  float iB = ipropiToCurrentA(vB);
  float iTRIP = VREF_V / (RIPROPI_OHMS * AIPROPI_GAIN); /* Umbral de sobrecorriente (A) */

  /* Columnas separadas por tabuladores para Arduino Serial Plotter */
  Serial.print(iA, 4); Serial.print('\t');
  Serial.print(iB, 4); Serial.print('\t');
  Serial.println(iTRIP, 4);
}

/* Espera activa con muestreo periódico (~cada 20 ms) */
void waitWithSampling(uint32_t ms) {
  uint32_t t0 = millis(), tNext = 0;
  while ((uint32_t)(millis() - t0) < ms) {
    uint32_t now = millis();
    if ((int32_t)(now - tNext) >= 0) {
      sampleAndPrintCurrents();
      tNext = now + 20;
    }
    delay(1); /* Breve espera para ceder CPU */
  }
}

/* Verifica si /nFAULT está en bajo durante la sección indicada */
void checkFault(const char* tag) {
  if (digitalRead(PIN_nFAULT) == LOW) {
    Serial.print("[nFAULT] Falla detectada durante ");
    Serial.println(tag);
  }
}

void setup() {
  Serial.begin(115200);
  delay(100);

  pinMode(PIN_AIN1, OUTPUT);
  pinMode(PIN_AIN2, OUTPUT);
  pinMode(PIN_BIN1, OUTPUT);
  pinMode(PIN_BIN2, OUTPUT);
  pinMode(PIN_nFAULT, INPUT_PULLUP);

  motorA_coast();
  motorB_coast();

  /* ADC: 11 dB para abarcar hasta ~3.3 V y resolución de 12 bits */
  analogReadResolution(ADC_BITS);
  analogSetAttenuation(ADC_11db);

  /* Cabecera opcional para Serial Plotter */
  Serial.println("IA(A)\tIB(A)\tITRIP(A)");

  /* Configuración del servo (50 Hz, limitado a los pulsos definidos) */
  servo.setPeriodHertz(50);
  servo.attach(PIN_SERVO, SERVO_MIN_US, SERVO_MAX_US);
  servo.write(SERVO_CENTER);

  /* 1) Motor A adelante y atrás con muestreo */
  motorA_fwd();  waitWithSampling(T_MOTOR_DIR);
  motorA_rev();  waitWithSampling(T_MOTOR_DIR);
  motorA_brake(); delay(100); motorA_coast();
  checkFault("Secuencia 1 (Motor A)");

  /* 2) Motor B adelante y atrás con muestreo */
  motorB_fwd();  waitWithSampling(T_MOTOR_DIR);
  motorB_rev();  waitWithSampling(T_MOTOR_DIR);
  motorB_brake(); delay(100); motorB_coast();
  checkFault("Secuencia 2 (Motor B)");

  /* 3) Barrido del servo: +90°, -90° (1 s cada tramo) */
  servo.write(SERVO_RIGHT); waitWithSampling(T_SERVO_TRAMO);
  servo.write(SERVO_LEFT);  waitWithSampling(T_SERVO_TRAMO);
  servo.write(SERVO_CENTER);

  /* 4) Ejecución concurrente */
  seqA.start(); seqB.start(); seqS.start();
}

void loop() {
  /* Muestreo continuo para el Plotter (~50 Hz) */
  static uint32_t tNext = 0;
  uint32_t now = millis();
  if ((int32_t)(now - tNext) >= 0) {
    sampleAndPrintCurrents();
    tNext = now + 20;
  }

  /* Secuencia concurrente Motor A */
  if (seqA.enabled) {
    if (seqA.phase == Phase::FWD) {
      motorA_fwd();
      if ((int32_t)(now - seqA.tEnd) >= 0) {
        seqA.phase = Phase::REV;
        seqA.tEnd = now + T_MOTOR_DIR;
      }
    } else if (seqA.phase == Phase::REV) {
      motorA_rev();
      if ((int32_t)(now - seqA.tEnd) >= 0) {
        seqA.phase = Phase::DONE; motorA_brake(); delay(50); motorA_coast();
      }
    }
  }

  /* Secuencia concurrente Motor B */
  if (seqB.enabled) {
    if (seqB.phase == Phase::FWD) {
      motorB_fwd();
      if ((int32_t)(now - seqB.tEnd) >= 0) {
        seqB.phase = Phase::REV;
        seqB.tEnd = now + T_MOTOR_DIR;
      }
    } else if (seqB.phase == Phase::REV) {
      motorB_rev();
      if ((int32_t)(now - seqB.tEnd) >= 0) {
        seqB.phase = Phase::DONE; motorB_brake(); delay(50); motorB_coast();
      }
    }
  }

  /* Secuencia concurrente Servo (DERECHA -> IZQUIERDA -> CENTRO) */
  if (seqS.enabled) {
    if (seqS.step == 0) {
      if ((int32_t)(now - seqS.tEnd) >= 0) {
        servo.write(SERVO_LEFT);
        seqS.step = 1; seqS.tEnd = now + T_SERVO_TRAMO;
      }
    } else if (seqS.step == 1) {
      if ((int32_t)(now - seqS.tEnd) >= 0) {
        servo.write(SERVO_CENTER);
        seqS.step = 2;
      }
    }
  }

  /* Condición de fin: ambos motores terminados y servo centrado */
  if (seqA.phase == Phase::DONE && seqB.phase == Phase::DONE && seqS.step == 2) {
    checkFault("Secuencia 4 (concurrente)");
    seqA.enabled = seqB.enabled = false; seqS.enabled = false;
    motorA_coast(); motorB_coast();
    while (true) { delay(1000); } /* Mantener aquí tras finalizar */
  }
}
```

## Descarga de ejemplos

Puedes probar los códigos de ejemplo mediante el IDE de Arduino o el IDE de ESP-IDF o descargar los códigos en nuestro drive:

[Ejemplos de código del módulo Inventor](https://drive.google.com/drive/folders/1wom3hU-bjWmbpT9DnZFzFtpL3cr-7Qzg)
