Plateforme webcam ESP32

L'idée d'assembler une plateforme mobile avec une webcam à bord est apparue presque spontanément. Je voulais quelque chose comme une caméra IP dans mon humble arsenal domotique. Et ici, ce n'est pas tant une question de prix ou de qualité, mais plutôt une expérience créative. Divers articles de bricolage et projets comme celui-ci ont été utilisés comme source d'inspiration.



La structure assemblée ressemble à ceci



image



Composants



La base est une plate-forme robotique mobile à deux étages Châssis de voiture 2WD Mini Kit



image



Dimensions de la plate-forme: 135 mm x 135 mm x 80 mm L'



entraînement est composé de deux roues motrices standard avec une boîte de vitesses et un moteur à courant continu avec des disques matriciels pour capteurs de vitesse:



  • courant nominal: 250mA max. à une tension de 3,6 V
  • couple 800 g / cm (à une tension de 6 V)
  • tension d'alimentation: 6 - 8 V
  • vitesse à vide: 170 tr / min (à 3,6 V)
  • rapport de démultiplication: 1: 48
  • les essieux sortent des deux côtés
  • diamètre de l'axe: 5 mm
  • dimensions: 64x20x20 mm
  • poids: 26g




Le module MX1508 a été sélectionné comme pilote de moteur.Vous

pouvez en savoir plus sur le module ici



image



Paramètres techniques:



  • Tension d'alimentation: 2 - 10 V
  • Pilote de travail par canal: 1,5 A (courant de crête 2,5 A, pas plus de 10 secondes)
  • Entrée logique: 5V
  • Dimensions: 24,7 x 21 x 0,5 mm




Pour le mouvement horizontal et vertical de la caméra IP, les servomoteurs SG90 2kg populaires ont été sélectionnés.Les



image



spécifications suivantes sont présentées sur le site Web du fabricant:



  • Poids: 9g
  • Dimension: 23 × 12.2x29mm
  • Couple de décrochage: 1,8 kg / cm (4,8 v)
  • Type d'engrenage: ensemble d'engrenages POM
  • Vitesse de fonctionnement: 0.1sec / 60degree (4.8v)
  • Tension de fonctionnement: 4,8 v
  • Plage de température: 0 ℃ _ 55 ℃
  • Largeur de bande morte: 1us
  • Alimentation: via un adaptateur externe
  • longueur du fil servo: 25 cm
  • Prise servo: JR (pour JR et Futaba)




Un kit de support FPV a été choisi pour la webcam



image



Description du support dans la boutique en ligne: «FPV vous permettra d'orienter votre caméra FPV dans 3 plans. Une connexion et une opération simples vous permettront d'assembler et de connecter rapidement la plate-forme au contrôleur ou au contrôleur de vol. Utilisé avec le mini servo EMAX 9g ES08A ou les servos SG90 (avec quelques modifications). "

«Avec quelques modifications» - il faut en tenir compte, l'ensemble a dû être modifié avec un fichier au sens littéral. Mais pour un bricolage à 3 $, ce n'est pratiquement rien. Certains se sont plaints que même la révision n'a pas aidé et que les servos ne correspondaient pas à la taille, dans mon cas, à toutes les règles. Deux diapositives SG90 sont utilisées pour déplacer la caméra horizontalement et verticalement. L'option de concevoir et d'imprimer sur une imprimante 3D a également été envisagée, mais s'est jusqu'à présent arrêtée à ce titulaire.



Caméra IP basée sur ESP32 CAM



image



Comme décrit: « Le sous-système I2S de l'ESP32 fournit également un bus haute vitesse connecté directement à la RAM pour un accès direct à la mémoire. En termes simples, vous pouvez configurer le sous-système ESP32 I2S pour envoyer ou recevoir des données parallèles sous contrôle matériel. »

Ceux. vous pouvez configurer l'interface I2S ESP32 pour envoyer ou recevoir des données parallèles sous contrôle matériel, qui est implémenté pour connecter la caméra. Cette carte est développée par Seeed Studio, ici le prix est de 9,90 $, mais dans nos magasins de radio, ils se vendent 8 $, apparemment, non seulement Seeed Studio peut les produire.



Données techniques:



  • Le plus petit module SoC BT Wi-Fi 802.11b / g / n
  • CPU 32 bits basse consommation, peut également servir le processeur d'application
  • Vitesse d'horloge jusqu'à 160 MHz , Puissance de calcul sommaire jusqu'à 600 DMIPS
  • SRAM 520 Ko intégrée, 4MPSRAM externe
  • Prend en charge UART / SPI / I2C / PWM / ADC / DAC
  • Prise en charge des caméras OV2640 et OV7670, lampe Flash intégrée.
  • Télécharger l'image de soutien WiFI
  • Carte de soutien TF
  • Prend en charge plusieurs modes de sommeil.
  • Lwip et FreeRTOS intégrés
  • Prend en charge le mode de fonctionnement STA / AP / STA + AP
  • Prise en charge de la technologie Smart Config / AirKiss
  • Prise en charge des mises à niveau du micrologiciel local et distant du port série (FOTA)




Source de pouvoir



La plate-forme n'a pas été contrôlée à partir d'une alimentation autonome pendant une longue période sans recharge. Par conséquent, un module d'alimentation 2A 18650 avec une sortie USB avec un emplacement a été choisi comme source.



image



Caractéristiques:

  • Type de batterie: 18650 Li-Ion (pas de protection)
  • Tension du chargeur: 5V à 8V
  • Tensions de sortie:
  • 3V - directement de la batterie à travers le dispositif de protection
  • 5V - via un convertisseur boost.
  • Courant de sortie maximal:
  • Sortie 3V - 1A
  • Sortie 5V - 2A
  • Courant de charge maximal: 1A
  • Type de connecteur d'entrée: micro-USB
  • Type de connecteur de sortie: USB-A
  • Consommateurs 5V - contre les surcharges et les courts-circuits
  • Dimensions:
  • Circuit imprimé: 29,5 x 99,5 x 19 mm
  • Appareil entier: 30 x 116 x 20 mm




ESP-WROOM-32 a été sélectionné comme contrôleur principal,



image



j'ai décrit plus en détail les caractéristiques de l'ESP32. Voici les caractéristiques de base du module:

  • Microprocesseur double cœur Xtensa LX6 32 bits jusqu'à 240 MHz
  • Mémoire flash: 4 Mo
  • Communication sans fil Wi-Fi 802.11b / g / n jusqu'à 150 Mb / s, Bluetooth 4.2 BR / EDR / BLE
  • Prise en charge des modes STA / AP / STA + AP, pile TCP / IP intégrée
  • GPIO 32 (UART, SPI, I2C, interfaces I2S, PWM, contrôleurs SD, tactile capacitif, ADC, DAC et plus
  • Alimentation: via connecteur microUSB (convertisseur CP2102) ou sorties
  • Pas de broche: 2,54 mm (peut être inséré dans la planche à pain)
  • Taille de la planche: 5,2 x 2,8 cm




Deux encodeurs optiques "Noname" sont utilisés comme capteurs de vitesse pour compter les impulsions de rotation des disques matriciels du moteur-roue.



image



Caractéristiques:

  • Tension d'alimentation: 3,3 V - 5 V
  • Largeur de rainure du capteur: 6 mm;
  • Type de sortie: analogique et numérique
  • Indicateur: état de la sortie




Le HC-SR04 télémétriques ultrasonique populaire a été utilisé pour mesurer la distance.



image



Caractéristiques:

  • Tension d'alimentation: 5 V
  • Consommation en mode silencieux: 2mA
  • Consommation en fonctionnement: 15 mA
  • Gamme de distance: 2-400 cm
  • Angle de vision effectif: 15
  • Angle de vision de travail: 30 °




Implémentations logicielles



La première étape consistait à connaître et à faire clignoter le module ESP32 CAM.

La description de l'utilisation du module est présentée sur Harba, ici, ici et sur d'autres ressources.

La plupart des articles décrivent un processus de clignotement simple utilisant l'IDE Arduino. Dans la plupart des cas, cela suffit, et au début, cette option était également très bien.



image



Dans les magasins de radio, les modules ESP32-CAM sont vendus avec une caméra OV2640, donc un petit changement doit être apporté au croquis:



// 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




Et spécifiez également le SSID et le mot de passe du point d'accès Wi-Fi



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




L'une des conditions pour qu'une caméra Web fonctionne dans mon cas est la possibilité de transmettre un flux vidéo via le serveur proxy Keenetic. J'utilise un routeur domestique Keenetik Viva. Le service KeenDNS fournit le nom de domaine à la ressource Web d'accueil. Mais à ma grande surprise, la première tentative a échoué. En essayant d'accéder à distance via Internet, j'ai reçu l'erreur "Les champs d'en-tête sont trop longs pour que le serveur puisse les interpréter". Avec ce problème que j'ai rencontré pour la première fois. La solution à ce problème est de changer la configuration CONFIG_HTTPD_MAX_REQ_HDR_LEN, par exemple



#define CONFIG_HTTPD_MAX_REQ_HDR_LEN 2048




Dans le cas de l'Arduino IDE ESP32, les modules sont déjà compilés et présentés comme des bibliothèques statiques, qui se trouvent dans Windows le long du chemin -% userprofile% \ AppData \ Local \ Arduino15 \ packages \ esp32 \ hardware \ esp32 \ 1.0.4 \ tools \ sdk \

Just changer le paramètre dans l'en-tête ne fera rien.

Autrement dit, pour changer la configuration, nous devons recompiler les bibliothèques ESP-IDF.

La solution était de cloner le projet github.com/espressif/esp-who . Dans le répertoire avec des exemples, nous trouvons le projet camera_web_server, changeons le paramètre de la longueur maximale de l'en-tête, et n'oubliez pas non plus de spécifier les paramètres de connexion Wi-Fi



image



Pour que le projet se compile, nous avons dû installer une autre case à cocher - Support array 'rtc_gpio_desc' pour ESP32



image



Une fois la compilation et le chargement du projet réussis, accédez à l'adresse IP correspondante dans le navigateur et accédez à la page avec l'interface de notre caméra Web.



image



L'interface est similaire aux exemples Arduino, mais certaines fonctionnalités ont été ajoutées.



J'ai apporté de petites modifications au fichier original app_httpd.c pour contrôler le signal de la broche GPIO_NUM_2 à partir de l'interface Web. Bien que la description du module mentionne l'utilisation de broches pour les besoins d'une carte SD, mais je ne l'utilise pas, je peux donc utiliser ces broches.



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);
    	}




Pour le contrôle à distance, j'ai compensé un panneau Node-Red simple qui fonctionne sur un Raspberry pi.



image



Nous avons réussi à intégrer l'image du flux vidéo dans le nœud de modèle:



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




Un point est important ici: il est nécessaire d'intégrer http, dans le cas de https il y aura des problèmes avec la Content-Security-Policy. Si des problèmes surviennent dans ce cas, vous pouvez essayer d'ajouter des en-têtes:



<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>




Pour contrôler la broche GPIO_NUM_2 du module ESP32-CAM, après avoir apporté des modifications au firmware, la requête http GET suivante doit être effectuée:



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




Sur l'interface du panneau, il s'agit du commutateur de réveil, dans le thread de travail, il ressemble à ceci



image



où la fonction de requête:



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




Paramètres du nœud de requête http: d'



image



autres paramètres et états sont transmis via MQTT



Connectivité Wi-Fi et MQTT



Je vais donner des exemples utilisant le framework Arduino, puisque j'ai également expérimenté dessus. Mais à la fin, j'ai une application fonctionnelle sur ESP-IDF.



#Include <WiFi.h> en-tête



Fonction de connexion Wi-Fi pour le cadre 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 fonction contient une boucle pour trois itérations, car échoue souvent à se connecter au premier essai, puis attend indéfiniment l'état WL_CONNECTED. Peut-être que vous pouvez toujours le résoudre d'une autre manière, mais cela fonctionne comme ça.



La connexion au MQTT pour le framework Arduino se fait à l'aide de la bibliothèque github.com/knolleary/pubsubclient.git .



Pour utiliser la bibliothèque, vous devez inclure l'en-tête #include <PubSubClient.h>



Fonction de connexion 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;
}






Tout d'abord, nous vérifions que nous sommes connectés au Wi-Fi, puis nous nous connectons au courtier client.setServer (mqtt_server, 1883);



Et définissez la fonction de rappel client.setCallback (callback);



Fonction de rappel 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 cas de connexion réussie, abonnez-vous aux rubriques



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




Il y a eu quelques cas de chute de connexion MQTT, donc une vérification a été ajoutée à la tâche d'interrogation périodique.



Tâche d'interrogation périodique
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 exemple de connexion au Wi-Fi et MQTT à l'aide d'ESP-IDF a été décrit dans l' article précédent. Dans le



cas de l'utilisation d'ESP-IDF, il n'y a eu aucun problème lors de la connexion au Wi-Fi et à MQTT. Une nuance lors du traitement des données d'une rubrique MQTT dans la fonction esp_err_t mqtt_event_handler (événement esp_mqtt_event_handle_t): lorsque le type d'événement est MQTT_EVENT_DATA, vous devez prendre en compte les paramètres event-> topic_len et event-> data_lenbage et prendre le nom de la rubrique et les données exactement de la longueur appropriée. Pour ce faire, nous pouvons créer des tableaux de tampons ou allouer dynamiquement de la mémoire (puis la libérer), et copier les données, par exemple



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




L'envoi de données à une rubrique se fait à l'aide de la fonction esp_mqtt_client_publish



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




Traitement des données du capteur à ultrasons HC-SR04



HC-SR04 est un capteur bon marché et populaire pour la conception de dispositifs à microcontrôleur. Comme d'habitude, il y a beaucoup de matériel sur Internet à ce sujet: ici et ici. La description peut également être consultée ici, et une courte fiche technique ici.

En bref, pour commencer à mesurer la distance, vous devez appliquer un signal haut d'une durée de 10 μs à la broche Trig. Cela initie le capteur à transmettre 8 cycles d'une impulsion ultrasonore de 40 kHz et à attendre l'impulsion ultrasonore réfléchie. Lorsque le transducteur détecte un signal ultrasonore du récepteur, il règle la sortie d'écho à un niveau élevé et retardé d'une période (largeur) proportionnelle à la distance. Pour calculer la distance, vous devez calculer la formule:



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



340 m / s - la vitesse de propagation du son dans l'air.



image



Dans le cadre Arduino, la fonction pulseIn vous permet de connaître la largeur d'impulsion en μs.

Pour ESP-IDF, il existe un projet de bibliothèque de composants ESP-IDF , qui a également un composant ultrasonique pour HC-SR04.



Exemple de code
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;
}






Il y a une explication de l'algorithme dans les commentaires. La durée de l'impulsion est mesurée dans la boucle while tandis que le niveau du signal est élevé sur la broche Echo (après // obtenu un écho, mesure) après quoi la distance est mesurée



*distance = (time - echo_start) / ROUNDTRIP


Le coefficient pour obtenir la distance en centimètres ROUNDTRIP = 58.



Dans le cadre Arduino, cela semble encore plus facile



Exemple de code
#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;
}






Il y a eu une tentative d'utilisation de la bibliothèque ultrasonique ESP-IDF pour le projet ESP32 Arduino, mais ce cas fonctionne jusqu'au premier problème de capteur. Pourquoi, il n'a pas été possible de savoir exactement. Un problème de capteur est une erreur de calcul périodique des impulsions et de fausses lectures, dans les chiffres calculés, il ressemble à une distance de plus de 20 000 cm. Sur les forums, ils écrivent que cela est dû à un capteur de mauvaise qualité (copie chinoise).



Mesure de la vitesse avec des capteurs optiques



Le module optique de lecture des impulsions est basé sur un comparateur LM393 et ​​un capteur à fente. Conçu pour être utilisé avec des disques raster qui s'adaptent sur l'arbre d'une boîte de vitesses ou d'un moteur électrique.



Comme d'habitude, il existe déjà des articles sur ce sujet: digitrode.ru , mirrobo.ru et arduino-kit.ru .



Circuit capteur:



image



Dans le cadre Arduino, nous calculons la vitesse comme suit:

- définissons la variable (structure) du compteur, par exemple

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;




Ensuite, dans la fonction de configuration, nous devons enregistrer la broche d'entrée et l'interrompre



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




Ensuite, le nombre de tours par minute est calculé



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




Exemple de code
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);
   }
}






Dans ESP-IDF, vous pouvez utiliser le compteur matériel PCNT, qui a été décrit dans l' article précédent , à cet effet .



Exemple de code de la tâche en cours de traitement
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);
}
}






Contrôle PWM



Vous pouvez en savoir plus sur le contrôle des servo-variateurs sur Arduino sur developer.alexanderklimov , wiki.amperka.ru .

Comme indiqué dans la source ci-dessus: "Un servo est un mécanisme avec un moteur électrique qui peut tourner à un angle donné et maintenir sa position actuelle." En pratique, nous avons affaire à une modulation de largeur d'impulsion, où l'angle de rotation de l'actionneur dépend de la largeur d'impulsion du signal.



image



Pour ESP32 sur un framework Arduino, vous pouvez utiliser la librairie ESP32Servo



Pour cela, on connecte l'en-tête



#include <ESP32Servo.h>




Créer un objet



Servo servo_horisontal;




Nous indiquons la broche de sortie



 servo_horisontal.attach(SERVO_CAM_HOR_PIN);




Après cela, nous pouvons noter la valeur requise du montant de rotation



servo_horisontal.write(value);




Le contrôle PWM pour d'autres types de périphériques sur le framework Arduino est effectué à l'aide de la bibliothèque esp32-hal-ledc.h Les

microcontrôleurs ESP32 ne prennent pas en charge la fonction standard Arduino analogWrite () pour PWM. A leur place, des fonctions sont

fournies : ledcSetup (channel, freq, resolution_bits) - le canal, la fréquence et la résolution sont

indiqués ledcAttachPin (GPIO, channel) - le port et le canal sont indiqués

ledcWrite (channel, dutycycle) - le canal et le cycle de service du signal PWM sont indiqués

Des exemples peuvent être vus

Comment Comme son nom l'indique, les fonctions étaient à l'origine conçues pour contrôler les modules LED, mais elles sont également utilisées à d'autres fins.



Dans le cadre ESP-IDF, le servo variateur est contrôlé de la même manière que la commande du commutateur à l'aide du module MCPWM, comme décrit dans l'article précédent. Un exemple de commande de servomoteur MCPWM peut être consulté ici



Exemple de code
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
        }
    }
}






Ceux. nous devons initialiser le module en utilisant la fonction mcpwm_init (MCPWM_UNIT_0, MCPWM_TIMER_0, & pwm_config);

Et puis définissez la valeur d'angle

mcpwm_set_duty_in_us (MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, angle);



Des exemples d'utilisation du module MCPWM pour différents types de lecteurs peuvent être trouvés sur github .

Un exemple de commande de moteur à balais a également été présenté dans l' article précédent .



Il est à noter qu'une telle plateforme est un non holonomique à contrôle différentielsystème. Les moteurs ont des performances variables, vous devez donc définir un décalage logiciel pour l'un d'entre eux afin d'assurer une vitesse uniforme. Vous pouvez vous familiariser avec la théorie sur robotosha.ru robotosha.ru/robotics/robot-motion.html . Pour un contrôle optimal des motoréducteurs, un algorithme PID avec retour sous forme de capteurs optiques est utilisé. La description de l'algorithme est présentée ici et ici .

Une description des équations de mouvement, ainsi que des algorithmes de contrôle, dépasse le cadre de cet article. La cinématique différentielle n'est pas encore implémentée dans le code.



Modes de sommeil



Selon la documentation , ainsi que la description dans l' article, l' ESP32 peut basculer entre différents modes d'alimentation:

  • Mode actif
  • Mode veille du modem
  • Mode veille légère
  • Mode sommeil profond
  • Mode veille prolongée




Le tableau montre les différences de consommation de courant dans les différents modes.



image



J'ai utilisé le mode Deep Sleep en l'absence de signal haut sur la broche GPIO_NUM_13



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




En l'absence d'influence externe, j'ai tiré l'entrée 10k avec une résistance à 3,3 V, bien que cela soit possible dans le logiciel. Et dans la tâche d'interrogation périodique, je vérifie l'état du signal d'entrée



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




Cela complètera la description. Un exemple pratique d'utilisation de modules ESP32 avec divers périphériques a été présenté. Certaines questions d'implémentation logicielle et de comparaison des approches ESP-IDF et Arduino sont également abordées.



All Articles