Plataforma de cámara web ESP32

La idea de montar una plataforma móvil con una cámara web a bordo apareció casi de forma espontánea. Quería algo así como una cámara IP en mi humilde arsenal de automatización del hogar. Y aquí ni siquiera se trata tanto de una cuestión de precio o de calidad, sino que se puede llamar un experimento creativo. Varios artículos de bricolaje y proyectos como este se utilizaron como inspiración.



La estructura ensamblada se ve así



imagen



Componentes



La base es una plataforma robótica móvil de dos pisos Car Chassis 2WD Mini Kit



imagen



Dimensiones de la plataforma: 135 mm x 135 mm x 80 mm La



unidad es dos ruedas de motor estándar con una caja de cambios y un motor de CC con discos de trama para sensores de velocidad:



  • corriente nominal: 250mA máx. a una tensión de 3,6 V
  • torque 800 g / cm (a voltaje de 6V)
  • tensión de alimentación: 6 - 8 V
  • velocidad sin carga: 170 rpm (a 3,6 V)
  • relación de transmisión: 1:48
  • los ejes salen por ambos lados
  • diámetro del eje: 5 mm
  • dimensiones: 64x20x20 mm
  • peso: 26g




El módulo MX1508 fue seleccionado como controlador del motor .

Puede leer sobre el módulo aquí



imagen



Parámetros técnicos:



  • Voltaje de suministro: 2 - 10 V
  • Controlador de trabajo por canal: 1,5 A (corriente máxima de 2,5 A, no más de 10 segundos)
  • Entrada lógica: 5 V
  • Dimensiones: 24,7 x 21 x 0,5 mm




Para el movimiento horizontal y vertical de la cámara IP, se han seleccionado los populares servomotores SG90 de 2 kg. La



imagen



siguiente especificación se presenta en el sitio web del fabricante:



  • Peso: 9g
  • Dimensión: 23 × 12.2x29 mm
  • Par de bloqueo: 1,8 kg / cm (4,8 v)
  • Tipo de engranaje: conjunto de engranajes POM
  • Velocidad de funcionamiento: 0,1 segundos / 60 grados (4,8 v)
  • Voltaje de funcionamiento: 4,8 v
  • Rango de temperatura: 0 ℃ _ 55 ℃
  • Ancho de banda muerta: 1us
  • Fuente de alimentación: mediante adaptador externo
  • longitud del cable servo: 25 cm
  • Enchufe servo: JR (se adapta a JR y Futaba)




Se eligió un Kit de Soporte FPV para la webcam



imagen



Descripción del soporte en la tienda online: “FPV te permitirá orientar tu cámara FPV en 3 planos. La conexión y operación simples le permitirán ensamblar y conectar rápidamente la plataforma a un controlador o controlador de vuelo. Se utiliza junto con EMAX 9g ES08A Mini Servo o SG90 servos (con algunas modificaciones) ".

"Con algunas modificaciones" - debe tenerse en cuenta, el conjunto tuvo que ser modificado con un archivo en el sentido literal. Pero para un bricolaje por $ 3, eso es prácticamente nada. Algunos se quejaron de que incluso la revisión no ayudó, y los servos no encajaron en tamaño, en mi caso, todas las reglas. Se utilizan dos diapositivas SG90 para mover la cámara horizontal y verticalmente. También se consideró la opción de diseñar e imprimir en una impresora 3D, pero hasta ahora me detuve en este soporte.



Cámara IP basada en ESP32 CAM



imagen



Como se describe: “ El subsistema I2S en el ESP32 también proporciona un bus de alta velocidad conectado directamente a la RAM para acceso directo a la memoria. En pocas palabras, puede configurar el subsistema ESP32 I2S para enviar o recibir datos en paralelo bajo control de hardware ".

Aquellos. Puede configurar la interfaz I2S ESP32 para enviar o recibir datos en paralelo bajo control de hardware, que se implementa para conectar la cámara. Esta placa es desarrollada por Seeed Studio, el precio aquí es de $ 9,90, pero en nuestras tiendas de radios se venden por $ 8, aparentemente no solo Seeed Studio puede producirlas.



Datos técnicos:



  • El módulo SoC Wi-Fi BT 802.11b / g / n más pequeño
  • CPU de 32 bits de baja potencia, también puede servir al procesador de aplicaciones
  • Velocidad de reloj de hasta 160 MHz, potencia de cálculo resumida de hasta 600 DMIPS
  • SRAM de 520 KB incorporado, 4MPSRAM externo
  • Soporta UART / SPI / I2C / PWM / ADC / DAC
  • Admite cámaras OV2640 y OV7670, lámpara de flash incorporada.
  • Carga de imágenes de soporte WiFI
  • Soporte de tarjeta TF
  • Admite múltiples modos de suspensión.
  • Lwip y FreeRTOS integrados
  • Admite el modo de operación STA / AP / STA + AP
  • Admite la tecnología Smart Config / AirKiss
  • Soporte para actualizaciones de firmware locales y remotas del puerto serie (FOTA)




Fuente de poder



La plataforma no se controló desde la fuente de alimentación autónoma durante mucho tiempo sin recargar. Por lo tanto, se eligió como fuente un módulo de fuente de alimentación 2A 18650 con una salida USB con una ranura.



imagen



Especificaciones:

  • Tipo de batería: 18650 Li-Ion (sin protección)
  • Voltaje del cargador: 5 V a 8 V
  • Voltajes de salida:
  • 3V - directamente desde la batería a través del dispositivo de protección
  • 5V - a través de un convertidor elevador.
  • Corriente máxima de salida:
  • Salida 3V - 1A
  • Salida 5V - 2A
  • Corriente de carga máxima: 1A
  • Tipo de conector de entrada: micro-USB
  • Tipo de conector de salida: USB-A
  • Consumidores de 5 V: por sobrecarga y cortocircuito
  • Dimensiones:
  • PCB: 29,5 x 99,5 x 19 mm
  • Dispositivo completo: 30 x 116 x 20 mm




Se seleccionó ESP-WROOM-32 como controlador principal.



imagen



Anteriormente describí las características del ESP32 con más detalle. Estas son las características básicas del módulo:

  • Microprocesador Xtensa LX6 de doble núcleo de 32 bits hasta 240 MHz
  • Memoria flash: 4 MB
  • Comunicación inalámbrica Wi-Fi 802.11b / g / n hasta 150 Mb / s, Bluetooth 4.2 BR / EDR / BLE
  • Admite modos STA / AP / STA + AP, pila TCP / IP incorporada
  • GPIO 32 (UART, SPI, I2C, interfaces I2S, PWM, controladores SD, toque capacitivo, ADC, DAC y más
  • Fuente de alimentación: mediante conector microUSB (convertidor CP2102) o salidas
  • Paso del pin: 2,54 mm (se puede insertar en la placa de pruebas)
  • Tamaño del tablero: 5,2 x 2,8 cm




Dos codificadores ópticos "Noname" se utilizan como sensores de velocidad para el recuento de los impulsos de rotación de los discos de trama del motor-rueda.



imagen



Características:

  • Voltaje de suministro: 3,3 V - 5 V
  • Ancho de la ranura del sensor: 6 mm;
  • Tipo de salida: analógica y digital
  • Indicador: estado de salida




El populares ultrasónica del telémetro HC-SR04 se utilizó para medir la distancia.



imagen



Características:

  • Voltaje de suministro: 5 V
  • Consumo en modo silencioso: 2mA
  • Consumo en funcionamiento: 15 mA
  • Rango de distancia: 2-400 cm
  • Ángulo de visión efectivo: 15
  • Ángulo de visión de trabajo: 30 °




Implementaciones de software



El primer paso fue conocer y flashear el módulo CAM ESP32.

La descripción de cómo trabajar con el módulo se presenta en Harba, aquí, aquí y en otros recursos.

La mayoría de los artículos describen un proceso de flasheo simple usando el IDE de Arduino. En la mayoría de los casos, esto es suficiente, y al principio esta opción también me pareció bien.



imagen



En las tiendas de radio, los módulos ESP32-CAM se venden con una cámara OV2640, por lo que es necesario realizar un pequeño cambio en el boceto:



// Select camera model
//#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_ESP_EYE
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE
#define CAMERA_MODEL_AI_THINKER




Y también especifique el SSID y la contraseña para el punto de acceso Wi-Fi



const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";




Una de las condiciones para que una cámara web funcione en mi caso es la capacidad de transmitir un flujo de video a través del servidor proxy Keenetic. Estoy usando un enrutador doméstico Keenetik Viva. El servicio KeenDNS proporciona el nombre de dominio al recurso web de inicio. Pero para mi sorpresa, el primer intento falló. Al intentar acceder de forma remota a través de Internet, recibí el error "Los campos de encabezado son demasiado largos para que el servidor los interprete". Con este problema que encontré por primera vez. La solución a este problema es cambiar la configuración CONFIG_HTTPD_MAX_REQ_HDR_LEN, por ejemplo



#define CONFIG_HTTPD_MAX_REQ_HDR_LEN 2048




En el caso del Arduino IDE ESP32, los módulos ya están compilados y presentados como bibliotecas estáticas, que se encuentran en Windows a lo largo de la ruta -% userprofile% \ AppData \ Local \ Arduino15 \ packages \ esp32 \ hardware \ esp32 \ 1.0.4 \ tools \ sdk \

Just cambiar el parámetro en el encabezado no hará nada.

Es decir, para cambiar la configuración, necesitamos recompilar las bibliotecas ESP-IDF.

La solución fue clonar el proyecto github.com/espressif/esp-who . En el directorio con ejemplos, encontramos el proyecto camera_web_server, cambiamos el parámetro de la longitud máxima del encabezado, y tampoco olvidemos especificar la configuración de la conexión Wi-



imagen



Fi.Para que el proyecto se compile, tuvimos que instalar otra casilla de verificación - Support array 'rtc_gpio_desc' para ESP32



imagen



Después de la compilación y carga exitosa del proyecto, vaya a la dirección IP correspondiente en el navegador y acceda a la página con la interfaz de nuestra cámara web.



imagen



La interfaz es similar a los ejemplos de Arduino, pero se han agregado algunas funciones.



Hice pequeños cambios en el archivo app_httpd.c original para controlar la señal del pin GPIO_NUM_2 desde la interfaz web. Aunque la descripción del módulo dice sobre el uso de pines para las necesidades de una tarjeta SD, pero yo no lo uso, entonces puedo usar estos pines.



void app_httpd_main()
{
	gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);

static esp_err_t cmd_handler(httpd_req_t *req)
{
.......
// 736
else if(!strcmp(variable, "gpio2")) {
    		if (val == 0)
                gpio_set_level(GPIO_NUM_2, 0);
            else
                gpio_set_level(GPIO_NUM_2, 1);
    	}




Para el control remoto, compensé un panel sencillo en Node-Red que se ejecuta en una Raspberry pi.



imagen



Logramos incrustar la imagen de la secuencia de video en el nodo de la plantilla:



<iframe 
    src="http://192.168.1.61"
    width="300" height="300">
</iframe>




Un punto es importante aquí: es necesario incrustar http, en el caso de https habrá problemas con la Política de seguridad de contenido. Si surgen problemas en este caso, puede intentar agregar encabezados:



<script>
    var meta = document.createElement('meta');
    meta.httpEquiv = "Content-Security-Policy";
    meta.content = "default-src * 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src * 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * 'unsafe-inline';";
document.getElementsByTagName('head')[0].appendChild(meta);
</script>




Para controlar el pin GPIO_NUM_2 del módulo ESP32-CAM, después de realizar cambios en el firmware, se debe realizar la siguiente solicitud http GET:



http://192.168.1.61/control?var=gpio2&val=1 // 0




En la interfaz del panel, este es el interruptor de activación, en el hilo del trabajador se ve así



imagen



donde la función de solicitud:



var newMsg = {}
var i = msg.payload ? 1 : 0;
newMsg.query = "control?var=gpio2&val=" + i
node.send(newMsg)




Configuración del nodo de solicitud http:



imagen



otros parámetros y estados se transmiten a través de MQTT



Conectividad Wi-Fi y MQTT



Daré ejemplos usando el marco Arduino, ya que también experimenté con él. Pero al final, tengo una aplicación que funciona en ESP-IDF.



#Incluir encabezado <WiFi.h>



Función de conexión Wi-Fi para el marco Arduino
void setup_wifi()
{
  Serial.println("Starting connecting WiFi.");
  delay(1000);
  for (int8_t i = 0; i < 3; i++)
  {
    WiFi.begin(ssid, password);
    uint32_t start = millis();
    while (WiFi.status() != WL_CONNECTED && ((millis() - start) < 4000))
    {
      Serial.print(".");
      delay(500);
    }
    if (WiFi.status() == WL_CONNECTED)
    {
      Serial.println("WiFi connected");
      Serial.println("IP address: ");
      Serial.println(WiFi.localIP());
      return;
    }
    else
    {
      Serial.println("Connecting Failed");
      //WiFi.reconnect(); // this reconnects the AP so the user can stay on the page
    }
  }
}






La función contiene un bucle para tres iteraciones, ya que a menudo no se conecta en el primer intento y luego espera interminablemente el estado WL_CONNECTED. Tal vez todavía puedas resolverlo de otra manera, pero funciona así.



La conexión a MQTT para el marco de Arduino se realiza mediante la biblioteca github.com/knolleary/pubsubclient.git .



Para usar la biblioteca, debe incluir el encabezado #include <PubSubClient.h>



Función de conexión MQTT
bool setup_mqtt()
{
  if (WiFi.status() == WL_CONNECTED)
  {
    if (!client.connected())
    {
      client.setServer(mqtt_server, 1883);
      client.setCallback(callback);
    }
    Serial.print("Connecting to MQTT server ");
    Serial.print(mqtt_server);
    Serial.println("...");
    String clientId = "ESP32_car_client";
    if (client.connect(clientId.c_str()))
    {
      Serial.println("Connected to MQTT server ");
      //subscribing to topics
      client.subscribe("esp32/car/#");
      client.subscribe("esp32/camera/#");
      return true;
    }
    else
    {
      Serial.println("Could not connect to MQTT server");
      return false;
    }
  }
  return false;
}






Primero, comprobamos que estamos conectados a Wi-Fi, luego nos conectamos al broker client.setServer (mqtt_server, 1883);



Y configure la función de devolución de llamada client.setCallback (callback);



Función de devolución de llamada MQTT
void callback(char *topic, byte *payload, unsigned int length)
{
  Serial.println("Message arrived ");
  memset(payload_buf, 0, 10);
  for (int i = 0; i < length; i++)
  {
    payload_buf[i] = (char)payload[i];
  }

  command_t mqtt_command = {
      .topic = topic,
      .message = payload_buf};
  xQueueSend(messageQueue, (void *)&mqtt_command, 0);
}






En caso de conexión exitosa, suscríbase a temas



client.subscribe("esp32/car/#");
client.subscribe("esp32/camera/#");




Ha habido casos de fallas de conexión MQTT, por lo que se agregó una verificación a la tarea de sondeo periódico.



Tarea de sondeo periódico
void pollingTask(void *parameter)
{
  int32_t start = 0;

  while (true) {
    if (!client.connected()) {
      long now = millis();
      if (now - start > 5000) {
        start = now;
        // Attempt to reconnect
        if (setup_mqtt()) {
          start = 0;
        }
      }
    }
    else {
      client.loop();
      int val = digitalRead(WAKEUP_PIN);
      if (val == LOW) {
        Serial.println("Going to sleep now");
        esp_deep_sleep_start();
      }
    }
    vTaskDelay(100 / portTICK_PERIOD_MS);
  }
  vTaskDelete(NULL);
}






Un ejemplo de conexión a Wi-FI y MQTT usando ESP-IDF se describió en el artículo anterior . En el



caso de usar ESP-IDF, no hubo fallas al conectarse a Wi-Fi y MQTT. Un matiz al procesar datos de un tema MQTT en la función esp_err_t mqtt_event_handler (evento esp_mqtt_event_handle_t): cuando el tipo de evento es MQTT_EVENT_DATA, debe tener en cuenta los parámetros event-> topic_len y event-> data_len y, de lo contrario, obtendremos el nombre del tema y los datos de la longitud adecuada. Para hacer esto, podemos crear matrices de búfer o asignar memoria dinámicamente (luego liberarla) y copiar los datos, por ejemplo



strncpy(topic, event->topic, event->topic_len);
strncpy(data, event->data, event→data_len);




El envío de datos a un tema se realiza mediante la función esp_mqtt_client_publish



esp_mqtt_client_publish(client, topics[i], topic_buff[i], 0,0,0);




Procesamiento de datos del sensor ultrasónico HC-SR04



HC-SR04 es un sensor barato y popular para diseñar dispositivos con microcontroladores. Como de costumbre, hay mucho material en Internet sobre este tema: aquí y aquí. La descripción también se puede ver aquí, y una breve hoja de datos aquí.

En resumen, para comenzar a medir la distancia, debe aplicar una señal alta con una duración de 10 μs al pin Trig. Esto inicia el sensor para transmitir 8 ciclos de un pulso ultrasónico de 40 kHz y esperar el pulso ultrasónico reflejado. Cuando el transductor detecta una señal ultrasónica del receptor, establece la salida de eco alta y retardada por un período (ancho) proporcional a la distancia. Para calcular la distancia, debe calcular la fórmula:



distance = duration * 340 / = duration * 0.034 /,



340 m / s: la velocidad de propagación del sonido en el aire.



imagen



En el marco de Arduino, la función pulseIn le permite averiguar el ancho de pulso en μs.

Para ESP-IDF hay un proyecto de biblioteca de componentes ESP-IDF , que también tiene un componente ultrasónico para HC-SR04.



Código de muestra
esp_err_t ultrasonic_measure_cm(const ultrasonic_sensor_t *dev, uint32_t max_distance, uint32_t *distance)
{
    CHECK_ARG(dev && distance);

    PORT_ENTER_CRITICAL;

    // Ping: Low for 2..4 us, then high 10 us
    CHECK(gpio_set_level(dev->trigger_pin, 0));
    ets_delay_us(TRIGGER_LOW_DELAY);
    CHECK(gpio_set_level(dev->trigger_pin, 1));
    ets_delay_us(TRIGGER_HIGH_DELAY);
    CHECK(gpio_set_level(dev->trigger_pin, 0));

    // Previous ping isn't ended
    if (gpio_get_level(dev->echo_pin))
        RETURN_CRITICAL(ESP_ERR_ULTRASONIC_PING);

    // Wait for echo
    int64_t start = esp_timer_get_time();
    while (!gpio_get_level(dev->echo_pin))
    {
        if (timeout_expired(start, PING_TIMEOUT))
            RETURN_CRITICAL(ESP_ERR_ULTRASONIC_PING_TIMEOUT);
    }

    // got echo, measuring
    int64_t echo_start = esp_timer_get_time();
    int64_t time = echo_start;
    int64_t meas_timeout = echo_start + max_distance * ROUNDTRIP;
    while (gpio_get_level(dev->echo_pin))
    {
        time = esp_timer_get_time();
        if (timeout_expired(echo_start, meas_timeout))
            RETURN_CRITICAL(ESP_ERR_ULTRASONIC_ECHO_TIMEOUT);
    }
    PORT_EXIT_CRITICAL;

    *distance = (time - echo_start) / ROUNDTRIP;

    return ESP_OK;
}






Hay una explicación del algoritmo en los comentarios. La duración del pulso se mide en el bucle while mientras que el nivel de señal es alto en el pin Echo (después de // obtuve eco, midiendo) después de lo cual se mide la distancia



*distance = (time - echo_start) / ROUNDTRIP


El coeficiente para obtener la distancia en centímetros ROUNDTRIP = 58.



En el marco de Arduino parece aún más fácil



Código de muestra
#include "ultrasonic.h"

portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
#define PORT_ENTER_CRITICAL portENTER_CRITICAL(&mux)
#define PORT_EXIT_CRITICAL portEXIT_CRITICAL(&mux)

Ultrasonic::Ultrasonic() {
  pinMode(GPIO_NUM_33, OUTPUT);
  pinMode(GPIO_NUM_26, INPUT);
}

uint32_t Ultrasonic::calculateDistance() {
    PORT_ENTER_CRITICAL;
    digitalWrite(GPIO_NUM_33, LOW);
    delayMicroseconds(2);
    digitalWrite(GPIO_NUM_33, HIGH);
    delayMicroseconds(10);
    digitalWrite(GPIO_NUM_33, LOW);
    duration = pulseIn(GPIO_NUM_26, HIGH);
    PORT_EXIT_CRITICAL;
    distance = duration / 58;
    return distance;
}

uint32_t Ultrasonic::getDistance() {
    return distance;
}






Hubo un intento de usar la biblioteca ultrasónica ESP-IDF para el proyecto ESP32 Arduino, pero este caso funciona hasta la primera falla del sensor. Por qué es así, no fue posible averiguarlo exactamente. Un fallo del sensor es un error de cálculo periódico en pulsos y lecturas falsas, en las cifras calculadas parece una distancia de más de 20.000 cm. En los foros escriben que esto se debe a un sensor de baja calidad (copia china).



Medición de velocidad con sensores ópticos



El módulo óptico para la lectura de pulsos se basa en un comparador LM393 y un sensor de ranura. Diseñado para usar con discos de trama que encajan sobre el eje de una caja de cambios o motor eléctrico.



Como de costumbre, ya hay artículos sobre este tema: digitrode.ru , mirrobo.ru y arduino-kit.ru .



Circuito del sensor:



imagen



en el marco de Arduino, calculamos la velocidad de la siguiente manera:

- definir la variable (estructura) del contador, por ejemplo

typedef struct {
  int encoder_pin = ENCODER_PIN; // pulse output from the module
  unsigned int rpm = 0; // rpm reading
  volatile byte pulses = 0; // number of pulses
  unsigned long timeold = 0;
  unsigned int pulsesperturn = 20;
} pulse_t;




Luego, en la función de configuración, debemos registrar el pin de entrada e interrumpirlo



pinMode(pulse_struct.encoder_pin, INPUT);
attachInterrupt(pulse_struct.encoder_pin, counter, FALLING);




A continuación, se calcula el número de revoluciones por minuto.



pulse_struct.rpm = 
        (60 * 1000 / pulse_struct.pulsesperturn )/ 
        (1000)* pulse_struct.pulses;




Código de muestra
void pulseTask(void *parameters) {
  sensor_data_t data;
  data.sensor = OPTICAL_SENSOR;
  portBASE_TYPE xStatus;

   while (true) {
      //Don't process interrupts during calculations
      detachInterrupt(0);
      pulse_struct.rpm = 
        (60 * 1000 / pulse_struct.pulsesperturn )/ 
        (1000)* pulse_struct.pulses;
      pulse_struct.pulses = 0;
      data.value = pulse_struct.rpm;
      //Restart the interrupt processing
      attachInterrupt(0, counter, FALLING);
      Serial.print("optical: ");
      Serial.println(data.value);
     //Sending data to sensors queue
    xStatus = xQueueSend(sensorQueue, (void *)&data, 0);
    if( xStatus != pdPASS ) {
     printf("Could not send optical to the queue.\r\n");
    }
    taskYIELD();
    vTaskDelay(1000 / portTICK_PERIOD_MS);
   }
}






En ESP-IDF, puede utilizar el contador de hardware PCNT, que se describió en el artículo anterior , para este propósito .



Código de muestra de la tarea que se está procesando
typedef struct {
      uint16_t delay; //delay im ms
      int pin;
      int ctrl_pin;
      pcnt_channel_t channel;
      pcnt_unit_t unit;
      int16_t count;
} speed_sensor_params_t;

void pulseTask(void *parameters) {
  sensor_data_t data_1;
  sensor_data_t data_2;
  data_1.sensor = OPTICAL_SENSOR_1;
  data_2.sensor = OPTICAL_SENSOR_2;
  portBASE_TYPE xStatus;

  speed_sensor_params_t params_1 = {
      .delay = 100,
      .pin = ENCODER_1_PIN,
      .ctrl_pin = GPIO_NUM_0,
      .channel = PCNT_CHANNEL_0,
      .unit = PCNT_UNIT_0,
      .count = 0,
  };
    ESP_ERROR_CHECK(init_speed_sensor(&params_1));

    speed_sensor_params_t params_2 = {
      .delay = 100,
      .pin = ENCODER_2_PIN,
      .ctrl_pin = GPIO_NUM_1,
      .channel = PCNT_CHANNEL_0,
      .unit = PCNT_UNIT_1,
      .count = 0,
  };
    ESP_ERROR_CHECK(init_speed_sensor(&params_2));

    while(true) {
        data_1.value = calculateRpm(&params_1);
        data_2.value = calculateRpm(&params_2);
        sensor_array[OPTICAL_SENSOR_1] = data_1.value;
        sensor_array[OPTICAL_SENSOR_2] = data_2.value;
        printf("speed 1 = %d\n", data_1.value);
        printf("speed 2 = %d\n", data_2.value);
        xStatus = xQueueSend(sensorQueue, (void *)&data_1, 0);
        xStatus = xQueueSend(sensorQueue, (void *)&data_2, 0);
        if( xStatus != pdPASS ) {
        printf("Could not send optical to the queue.\r\n");
        }
        vTaskDelay(100 / portTICK_PERIOD_MS);
}
}






Control PWM



Puede leer sobre el control de servoaccionamientos en Arduino en developer.alexanderklimov , wiki.amperka.ru .

Como se indica en la fuente anterior: "Un servo es un mecanismo con un motor eléctrico que puede girar en un ángulo determinado y mantener su posición actual". En la práctica, estamos tratando con modulación por ancho de pulso, donde el ángulo de rotación del actuador depende del ancho de pulso de la señal.



imagen



Para ESP32 en un marco Arduino, puede usar la biblioteca ESP32Servo



Para esto, conectamos el encabezado



#include <ESP32Servo.h>




Crea un objeto



Servo servo_horisontal;




Indicamos el pin de salida



 servo_horisontal.attach(SERVO_CAM_HOR_PIN);




Después de eso, podemos anotar el valor requerido de la cantidad de rotación



servo_horisontal.write(value);




El control PWM para otros tipos de dispositivos en el marco Arduino se realiza mediante la biblioteca esp32-hal-ledc.h. Los

microcontroladores ESP32 no admiten la función estándar Arduino analogWrite () para PWM. En lugar de ellos, se

proporcionan funciones: ledcSetup (canal, frecuencia, resolución_bits) - se

indican el canal, la frecuencia y la resolución ledcAttachPin (GPIO, canal) - se indican el puerto y el canal

ledcWrite (canal, ciclo de trabajo) - se indican el canal y ciclo de trabajo de la señal PWM Se

pueden ver ejemplos

Cómo Como sugiere el nombre, las funciones se diseñaron originalmente para controlar módulos LED, pero también se utilizan para otros fines.



En el marco ESP-IDF, el servodrive se controla de la misma manera que el control del conmutador utilizando el módulo MCPWM, como se describe en el artículo anterior. Un ejemplo de control de motores servo MCPWM se puede ver aquí



Código de muestra
static uint32_t servo_per_degree_init(uint32_t degree_of_rotation)
{
    uint32_t cal_pulsewidth = 0;
    cal_pulsewidth = (SERVO_MIN_PULSEWIDTH + (((SERVO_MAX_PULSEWIDTH -          SERVO_MIN_PULSEWIDTH) * (degree_of_rotation)) / (SERVO_MAX_DEGREE)));
    return cal_pulsewidth;
}

void mcpwm_example_servo_control(void *arg)
{
    uint32_t angle, count;
    //1. mcpwm gpio initialization
    mcpwm_example_gpio_initialize();

    //2. initial mcpwm configuration
    printf("Configuring Initial Parameters of mcpwm......\n");
    mcpwm_config_t pwm_config;
    pwm_config.frequency = 50;    //frequency = 50Hz, i.e. for every servo motor time period should be 20ms
    pwm_config.cmpr_a = 0;    //duty cycle of PWMxA = 0
    pwm_config.cmpr_b = 0;    //duty cycle of PWMxb = 0
    pwm_config.counter_mode = MCPWM_UP_COUNTER;
    pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
    mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config);    //Configure PWM0A & PWM0B with above settings
    while (1) {
        for (count = 0; count < SERVO_MAX_DEGREE; count++) {
            printf("Angle of rotation: %d\n", count);
            angle = servo_per_degree_init(count);
            printf("pulse width: %dus\n", angle);
            mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, angle);
            vTaskDelay(10);     //Add delay, since it takes time for servo to rotate, generally 100ms/60degree rotation at 5V
        }
    }
}






Aquellos. necesitamos inicializar el módulo usando la función mcpwm_init (MCPWM_UNIT_0, MCPWM_TIMER_0, & pwm_config);

Y luego establezca el valor del ángulo

mcpwm_set_duty_in_us (MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, angle);



Se pueden encontrar ejemplos del uso del módulo MCPWM para diferentes tipos de unidades en github .

En el artículo anterior también se presentó un ejemplo de control de motor cepillado .



Cabe señalar que dicha plataforma es un sistema no holonómico controlado diferencialmente .sistema. Los motores tienen variabilidad en el rendimiento, por lo que debe establecer una compensación de software para uno de ellos para garantizar una velocidad uniforme. Puede familiarizarse con la teoría en robotosha.ru robotosha.ru/robotics/robot-motion.html . Para un control óptimo de los motorreductores, se utiliza un algoritmo PID con retroalimentación en forma de sensores ópticos. La descripción del algoritmo se presenta aquí y aquí .

Una descripción de las ecuaciones de movimiento, así como de los algoritmos de control, está fuera del alcance de este artículo. La cinemática diferencial aún no está implementada en el código.



Modos de reposo



Según la documentación , así como la descripción en el artículo, el ESP32 puede cambiar entre diferentes modos de energía:

  • Modo activo
  • Modo de suspensión del módem
  • Modo de suspensión ligera
  • Modo de sueño profundo
  • Modo de hibernación




La tabla muestra las diferencias en el consumo actual en diferentes modos.



imagen



Activé el modo de suspensión profunda en ausencia de una señal alta en el pin GPIO_NUM_13



gpio_set_direction(WAKEUP_PIN, GPIO_MODE_INPUT);
esp_sleep_enable_ext0_wakeup(WAKEUP_PIN,1); //1 = High, 0 = Low




En ausencia de influencia externa, elevé la entrada de 10k con una resistencia a 3.3 V, aunque es posible en el software. Y en la tarea de sondeo periódico, verifico el estado de la señal de entrada.



if(!gpio_get_level(WAKEUP_PIN)) {
         printf("Going to sleep now\n");
        esp_deep_sleep_start();
    }




Esto completará la descripción. Se mostró un ejemplo práctico del uso de módulos ESP32 con varios periféricos. También se abordan algunos problemas de implementación de software y comparación de enfoques ESP-IDF y Arduino.



All Articles