Plataforma de webcam ESP32

A ideia de montar uma plataforma móvel com uma câmera web a bordo surgiu quase que espontaneamente. Eu queria algo como uma câmera IP em meu humilde arsenal de automação residencial. E aqui não é tanto uma questão de preço ou qualidade, mas pode ser chamado de experimento criativo. Vários artigos e projetos de DIY como este foram usados ​​como inspiração.



A estrutura montada se parece com isto



imagem



Componentes



A base é uma plataforma robótica móvel de dois decks Car Chassis 2WD Mini Kit



imagem



Dimensões da plataforma: 135 mm x 135 mm x 80 mm O



acionamento consiste em duas rodas motorizadas padrão com uma caixa de câmbio e um motor DC com discos raster para sensores de velocidade:



  • corrente nominal: 250mA máx. a uma tensão de 3,6 V
  • torque de 800 g / cm (na tensão de 6 V)
  • tensão de alimentação: 6 - 8 V
  • velocidade sem carga: 170 rpm (a 3,6 V)
  • relação de engrenagem: 1: 48
  • eixos saem em ambos os lados
  • diâmetro do eixo: 5 mm
  • dimensões: 64x20x20 mm
  • peso: 26g




O módulo MX1508 foi selecionado como o driver do motor. Você

pode ler sobre o módulo aqui



imagem



Parâmetros técnicos:



  • Tensão de alimentação: 2 - 10 V
  • Driver de trabalho por canal: 1,5 A (corrente de pico 2,5 A, não mais do que 10 segundos)
  • Entrada lógica: 5V
  • Dimensões: 24,7 x 21 x 0,5 mm




Para o movimento horizontal e vertical da câmera IP, os populares servo motores SG90 de 2 kg foram selecionados. A



imagem



seguinte especificação é apresentada no site do fabricante:



  • Peso: 9g
  • Dimensão: 23 × 12,2x29 mm
  • Torque de perda: 1,8 kg / cm (4,8 V)
  • Tipo de engrenagem: conjunto de engrenagem POM
  • Velocidade de operação: 0,1 seg / 60 graus (4,8 V)
  • Tensão de operação: 4,8v
  • Faixa de temperatura: 0 ℃ _ 55 ℃
  • Largura da banda morta: 1us
  • Fonte de alimentação: através de adaptador externo
  • comprimento do fio do servo: 25 cm
  • Plug Servo: JR (se encaixa JR e Futaba)




Foi escolhido um Kit de Suporte FPV para a webcam



imagem



Descrição do suporte na loja online: “O FPV permite orientar sua câmera FPV em 3 planos. A conexão e a operação simples permitirão que você monte e conecte rapidamente a plataforma ao controlador ou controlador de vôo. Usado em conjunto com EMAX 9g ES08A Mini Servo ou servos SG90 (com algumas modificações). "

“Com algumas modificações” - deve-se levar em consideração, o conjunto teve que ser modificado com um arquivo no sentido literal. Mas para um DIY por US $ 3, isso não é quase nada. Alguns reclamaram que nem mesmo a revisão ajudou, e os servos não se encaixaram em tamanho, no meu caso, todas as regras. Dois slides SG90 são usados ​​para mover a câmera horizontal e verticalmente. A opção de projetar e imprimir em uma impressora 3D também foi considerada, mas até agora eu parei neste suporte.



Câmera IP baseada em ESP32 CAM



imagem



Conforme descrito: “ O subsistema I2S no ESP32 também fornece um barramento de alta velocidade conectado diretamente à RAM para acesso direto à memória. Simplificando, você pode configurar o subsistema ESP32 I2S para enviar ou receber dados paralelos sob controle de hardware. ”

Essa. você pode configurar a interface I2S ESP32 para enviar ou receber dados paralelos sob controle de hardware, que é implementado para conectar a câmera. Esta placa é desenvolvida pela Seeed Studio, aqui o preço é de $ 9,90, mas em nossas lojas de rádios ela é vendida por $ 8, aparentemente não apenas a Seeed Studio pode produzi-la.



Dados técnicos:



  • O menor módulo 802.11b / g / n Wi-Fi BT SoC
  • CPU de 32 bits de baixa potência, também pode servir ao processador do aplicativo
  • Velocidade de clock de até 160 MHz , Potência de computação resumida de até 600 DMIPS
  • SRAM de 520 KB integrado, 4MPSRAM externo
  • Suporta UART / SPI / I2C / PWM / ADC / DAC
  • Suporta câmeras OV2640 e OV7670, lâmpada Flash embutida.
  • Suporte para upload de imagem por WiFI
  • Suporte cartão TF
  • Suporta vários modos de suspensão.
  • Lwip e FreeRTOS incorporados
  • Suporta modo de operação STA / AP / STA + AP
  • Suporte à tecnologia Smart Config / AirKiss
  • Suporte para atualizações de firmware local e remoto de porta serial (FOTA)




Fonte de poder



A plataforma não foi controlada por fonte de alimentação autônoma por muito tempo sem recarga. Portanto, um módulo de fonte de alimentação 2A 18650 com uma saída USB com um slot foi escolhido como a fonte.



imagem



Especificações:

  • Tipo de bateria: 18650 Li-Ion (sem proteção)
  • Tensão do carregador: 5V a 8V
  • Tensões de saída:
  • 3V - diretamente da bateria através do dispositivo de proteção
  • 5V - através de um conversor boost.
  • Corrente máxima de saída:
  • Saída 3V - 1A
  • Saída 5V - 2A
  • Corrente máxima de carga: 1A
  • Tipo de conector de entrada: micro-USB
  • Tipo de conector de saída: USB-A
  • Consumidores 5V - de sobrecarga e curto-circuito
  • Dimensões:
  • PCB: 29,5 x 99,5 x 19 mm
  • Dispositivo completo: 30 x 116 x 20 mm




O ESP-WROOM-32 foi selecionado como o controlador principal.



imagem



Anteriormente, descrevi as características do ESP32 com mais detalhes. Aqui estão as características básicas do módulo:

  • Microprocessador dual-core Xtensa LX6 de 32 bits até 240 MHz
  • Memória Flash: 4 MB
  • Comunicação sem fio Wi-Fi 802.11b / g / n até 150 Mb / s, Bluetooth 4.2 BR / EDR / BLE
  • Suporta modos STA / AP / STA + AP, pilha TCP / IP embutida
  • GPIO 32 (interfaces UART, SPI, I2C, I2S, PWM, controladores SD, toque capacitivo, ADC, DAC e mais
  • Fonte de alimentação: via conector microUSB (conversor CP2102) ou saídas
  • Distância do pino: 2,54 mm (pode ser inserido na placa de ensaio)
  • Tamanho da placa: 5,2 x 2,8 cm




Dois leitores ópticos "Noname" são usados como sensores de velocidade para a contagem dos impulsos de rotação dos discos de varredura do motor-roda.



imagem



Características:

  • Tensão de alimentação: 3,3 V - 5 V
  • Largura da ranhura do sensor: 6 mm;
  • Tipo de saída: analógica e digital
  • Indicador: status de saída




O popular, ultra-som rangefinder HC-SR04 foi utilizado para medir a distância.



imagem



Características:

  • Tensão de alimentação: 5V
  • Consumo no modo silencioso: 2mA
  • Consumo durante a operação: 15 mA
  • Faixa de distância: 2-400 cm
  • Ângulo de visão efetivo: 15
  • Ângulo de visão de trabalho: 30 °




Implementações de software



O primeiro passo foi conhecer e atualizar o módulo ESP32 CAM.

A descrição de como trabalhar com o módulo é apresentada no Harba, aqui, aqui e em outros recursos.

A maioria dos artigos descreve um processo de flashing simples usando o IDE do Arduino. Na maioria dos casos, isso é o suficiente e, no início, essa opção também funcionava.



imagem



Em lojas de rádio, os módulos ESP32-CAM são vendidos com uma câmera OV2640, portanto, uma pequena mudança precisa ser feita no esboço:



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




E também especificar o SSID e a senha para o ponto de acesso Wi-Fi



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




Uma das condições para que uma câmera da web funcione no meu caso é a capacidade de transmitir um fluxo de vídeo através do servidor proxy Keenetic. Estou usando um roteador doméstico Keenetik Viva. O serviço KeenDNS fornece o nome de domínio para o recurso da web inicial. Mas, para minha surpresa, a primeira tentativa falhou. Ao tentar acessar remotamente pela Internet, recebi o erro "Os campos do cabeçalho são muito longos para o servidor interpretar". Com este problema que encontrei pela primeira vez. A solução para este problema é alterar a configuração CONFIG_HTTPD_MAX_REQ_HDR_LEN, por exemplo



#define CONFIG_HTTPD_MAX_REQ_HDR_LEN 2048




No caso do Arduino IDE ESP32, os módulos já estão compilados e apresentados como bibliotecas estáticas, que estão localizadas no Windows ao longo do caminho -% userprofile% \ AppData \ Local \ Arduino15 \ packages \ esp32 \ hardware \ esp32 \ 1.0.4 \ tools \ sdk \

Just alterar o parâmetro no cabeçalho não fará nada.

Ou seja, para alterar a configuração, precisamos recompilar as bibliotecas ESP-IDF.

A solução foi clonar o projeto github.com/espressif/esp-who . No diretório com exemplos, encontramos o projeto camera_web_server, alteramos o parâmetro do comprimento máximo do cabeçalho e também não se esqueça de especificar as configurações de conexão Wi-Fi.



imagem



Para que o projeto seja compilado, tivemos que instalar outra caixa de seleção - Matriz de suporte 'rtc_gpio_desc' para ESP32



imagem



Após a compilação e carregamento bem-sucedidos do projeto, vá para o endereço IP correspondente no navegador e acesse a página com a interface de nossa câmera web.



imagem



A interface é semelhante aos exemplos do Arduino, mas algumas funcionalidades foram adicionadas.



Fiz pequenas alterações no arquivo app_httpd.c original para controlar o sinal de pino GPIO_NUM_2 da interface da web. Embora a descrição do módulo fale sobre o uso de pinos para as necessidades de um cartão SD, mas eu não uso, então posso usar esses pinos.



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 o controle remoto, criei um painel Node-Red simples que roda em um Raspberry pi.



imagem



Conseguimos incorporar a imagem do stream de vídeo ao nó do modelo:



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




Um ponto é importante aqui: é necessário inserir http, no caso de https haverá problemas com a Política de Segurança de Conteúdo. Se surgirem problemas neste caso, você pode tentar adicionar cabeçalhos:



<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 o pino GPIO_NUM_2 do módulo ESP32-CAM, após fazer alterações no firmware, a seguinte solicitação http GET deve ser realizada:



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




Na interface do painel, esta é a chave de ativação, no thread de trabalho é semelhante a



imagem



onde a função de solicitação:



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




Configurações do nó de solicitação http:



imagem



Outros parâmetros e status são transmitidos via MQTT



Conectividade Wi-Fi e MQTT



Vou dar exemplos usando a estrutura do Arduino, já que também experimentei. Mas, no final, tenho um aplicativo funcionando no ESP-IDF.



#Include cabeçalho <WiFi.h>



Função de conexão Wi-Fi para a estrutura 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
    }
  }
}






A função contém um loop para três iterações, uma vez que frequentemente falha ao se conectar na primeira tentativa e, em seguida, espera indefinidamente pelo status WL_CONNECTED. Talvez você ainda consiga resolver de outra forma, mas funciona assim.



A conexão com o MQTT para a estrutura do Arduino é feita usando a biblioteca github.com/knolleary/pubsubclient.git .



Para usar a biblioteca, você precisa incluir o cabeçalho #include <PubSubClient.h>



Função de conexão 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;
}






Primeiro, verificamos se estamos conectados ao Wi-Fi, depois nos conectamos ao broker client.setServer (mqtt_server, 1883);



E defina a função de retorno de chamada client.setCallback (callback);



Função de retorno de chamada 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);
}






Em caso de conexão bem-sucedida, inscreva-se nos tópicos



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




Houve alguns casos de queda de conexão MQTT, portanto, uma verificação foi incluída na tarefa de pesquisa periódica.



Tarefa de sondagem periódica
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);
}






Um exemplo de conexão a Wi-FI e MQTT usando ESP-IDF foi descrito no artigo anterior. No



caso de usar ESP-IDF, não houve falhas ao conectar-se a Wi-Fi e MQTT. Uma nuance ao processar dados de um tópico MQTT na função esp_err_t mqtt_event_handler (evento esp_mqtt_event_handle_t): quando o tipo de evento é MQTT_EVENT_DATA, você deve levar em conta os parâmetros event-> topic_t e event-> data_len e pegar o nome do tópico e os dados exatamente com o comprimento apropriado. Para fazer isso, podemos criar matrizes de buffer ou alocar memória dinamicamente (em seguida, liberá-la) e copiar os dados, por exemplo



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




O envio de dados para um tópico é feito usando a função esp_mqtt_client_publish



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




Processamento de dados do sensor ultrassônico HC-SR04



HC-SR04 é um sensor barato e popular para projetar dispositivos microcontroladores. Como de costume, há muito material sobre esse assunto na rede: aqui e aqui. A descrição também pode ser vista aqui, e uma pequena folha de dados aqui.

Resumindo, para começar a medir a distância, você precisa aplicar um sinal alto com duração de 10 μs ao pino de disparo. Isso inicia o sensor para transmitir 8 ciclos de um pulso ultrassônico de 40 kHz e aguardar o pulso ultrassônico refletido. Quando o transdutor detecta um sinal ultrassônico do receptor, ele define a saída do eco alto e atrasada por um período (largura) proporcional à distância. Para calcular a distância, você precisa calcular a fórmula:



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



340 m / s - a velocidade de propagação do som no ar.



imagem



Na estrutura do Arduino, a função pulseIn permite descobrir a largura do pulso em μs.

Para ESP-IDF, há um projeto de biblioteca de componentes ESP-IDF , que também tem um componente ultrassônico para HC-SR04.



Código de amostra
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;
}






Há uma explicação do algoritmo nos comentários. A duração do pulso é medida no loop while enquanto o nível do sinal é alto no pino Echo (após // obter eco, medição) após o qual a distância é medida



*distance = (time - echo_start) / ROUNDTRIP


O coeficiente para obter a distância em centímetros ROUNDTRIP = 58.



No framework do Arduino, parece ainda mais fácil



Código de amostra
#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;
}






Houve uma tentativa de usar a biblioteca ultrassônica ESP-IDF para o projeto ESP32 Arduino, mas este caso funciona até a primeira falha do sensor. Por quê, não foi possível saber exatamente. Uma falha de sensor é um erro de cálculo periódico em pulsos e a emissão de leituras falsas, nos números calculados parece uma distância de mais de 20.000 cm. Nos fóruns, eles escrevem que isso é devido a um sensor de má qualidade (cópia chinesa).



Medição de velocidade com sensores ópticos



O módulo óptico para leitura de pulsos é baseado em um comparador LM393 e um sensor de slot. Projetado para uso com discos raster que se encaixam no eixo de uma caixa de engrenagens ou motor elétrico.



Como de costume, já existem artigos sobre esse assunto: digitrode.ru , mirrobo.ru e arduino-kit.ru .



Circuito do sensor:



imagem



No framework do Arduino, calculamos a velocidade da seguinte maneira:

- definir a variável (estrutura) do contador, por exemplo

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;




Então, na função de configuração, devemos registrar o pino de entrada e interrompê-lo.



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




Em seguida, o número de revoluções por minuto é calculado



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




Código de amostra
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);
   }
}






No ESP-IDF, você pode usar o contador de hardware PCNT, que foi descrito no artigo anterior , para essa finalidade .



Código de amostra da tarefa que está sendo processada
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);
}
}






Controle PWM



Você pode ler sobre o controle de servo drives no Arduino em developer.alexanderklimov , wiki.amperka.ru .

Como afirmado na fonte acima: "Um servo é um mecanismo com um motor elétrico que pode girar em um determinado ângulo e manter sua posição atual." Na prática, estamos lidando com modulação por largura de pulso, onde o ângulo de rotação do atuador depende da largura de pulso do sinal.



imagem



Para ESP32 em uma estrutura Arduino, você pode usar a biblioteca ESP32Servo.



Para isso, conectamos o cabeçalho



#include <ESP32Servo.h>




Crie um objeto



Servo servo_horisontal;




Nós indicamos o pino de saída



 servo_horisontal.attach(SERVO_CAM_HOR_PIN);




Depois disso, podemos escrever o valor necessário da quantidade de rotação



servo_horisontal.write(value);




O controle PWM para outros tipos de dispositivos na estrutura do Arduino é feito usando a biblioteca esp32-hal-ledc.h Os

microcontroladores ESP32 não oferecem suporte à função analógica padrão do Arduino () para PWM. Em vez deles, as funções são

fornecidas : ledcSetup (canal, freq, resolution_bits) - indica o canal, a frequência e a resolução

ledcAttachPin (GPIO, canal) - indica a porta e o canal

ledcWrite (canal, ciclo de trabalho) - indica o canal e o ciclo de trabalho do sinal PWM.

Exemplos podem ser vistos

como Como o nome sugere, as funções foram originalmente projetadas para controlar módulos de LED, mas também são usadas para outros fins.



Na estrutura ESP-IDF, o servoacionamento é controlado da mesma forma que o controle do comutador usando o módulo MCPWM, conforme descrito no artigo anterior. Um exemplo de controle de servo motor MCPWM pode ser visto aqui



Código de amostra
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
        }
    }
}






Essa. precisamos inicializar o módulo usando a função mcpwm_init (MCPWM_UNIT_0, MCPWM_TIMER_0, & pwm_config);

Em seguida, defina o valor do ângulo

mcpwm_set_duty_in_us (MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, ângulo);



Exemplos de uso do módulo MCPWM para diferentes tipos de unidade podem ser encontrados no github .

Um exemplo de controle de motor com escova também foi apresentado no artigo anterior .



Deve-se notar que tal plataforma é um sistema não holonômico controlado diferencialmentesistema. Os motores têm variabilidade no desempenho, então você deve definir um deslocamento de software para um deles para garantir uma velocidade uniforme. Você pode se familiarizar com a teoria em robotosha.ru robotosha.ru/robotics/robot-motion.html . Para o controle ideal dos motoredutores, é utilizado um algoritmo PID com feedback na forma de sensores ópticos. A descrição do algoritmo é apresentada aqui e aqui .

Uma descrição das equações de movimento, bem como dos algoritmos de controle, está além do escopo deste artigo. A cinemática diferencial ainda não foi implementada no código.



Modos de dormir



De acordo com a documentação , bem como a descrição no artigo, o ESP32 pode alternar entre diferentes modos de energia:

  • Modo ativo
  • Modo de espera do modem
  • Modo de suspensão leve
  • Modo de sono profundo
  • Modo de hibernação




A tabela mostra as diferenças no consumo atual em diferentes modos.



imagem



Usei o modo Sono profundo na ausência de um sinal alto no pino GPIO_NUM_13



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




Na ausência de influência externa, aumentei a entrada de 10k com um resistor para 3,3 V, embora seja possível no software. E na tarefa de pesquisa periódica, eu verifico o estado do sinal de entrada



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




Isso irá completar a descrição. Um exemplo prático do uso de módulos ESP32 com vários periféricos foi mostrado. Algumas questões de implementação de software e comparação de abordagens ESP-IDF e Arduino também são abordadas.



All Articles