Este repositorio contiene código, documentación y otras cosas relacionadas con el proyecto del auto de juguete amarillo que hice.
También creé la aplicación móvil Flutter para controlar el auto de juguete, consulte el repositorio de YellowToyCarApp.
El hardware consta de:
El software consta de:
/
o /index
o /index.html
→ Sitio web presentado para que el usuario controle el automóvil.
/status
→ Estado básico, incluida la hora, el estado de las luces y los motores y otros datos de diagnóstico.
{
"uptime" : 123456 , // Microseconds passed from device boot.
"time" : "2023-01-12T23:49:03.348+0100" , // Device time, synced using SNTP.
"rssi" : - 67 , // Signal strength of AP the device is connected to, or 0 if not connected.
/* With `?details=1` querystring parameter, extended response is provided. */
"stations" : [ "a1:b2:c3:d4:e5:f6" ] , // list of stations currently connected to our AP
}
/config
→ Punto final para solicitudes de configuración (JSON GET/POST API)
{
/* Control & config for motors and lights */
"control" : {
/* Other */
"timeout" : 2000 , // Time in milliseconds counted from last control request/packet, after which movement should stop for safety reason
/* Input values */
"mainLight" : 1 ,
"otherLight" : 1 ,
"left" : 12.3 , // The motors duty cycle are floats as percents,
"right" : 12.3 , // i.e. 12.3 means 12.3% duty cycle.
/* Calibration */
"calibrate" : {
"left" : 0.95 , // Inputs will be multiplied by calibration values before outputting PWM signal.
"right" : 1.05 ,
"frequency" : 100 , // Frequency to be used by PWMs
}
} ,
/* Networking related. Some things are not implemented, including: DNS and DHCP leases */
"network" : {
"mode" : "ap" , // for Access Point or "sta" for station mode, or "nat" (to make it work like router)
"fallback" : 10000 , // duration after should fallback to hosting AP if cannot connect as station
"dns1" : "1.1.1.1" ,
"dns2" : "1.0.0.1" ,
"sta" : {
"ssid" : "YellowToyCar" ,
"psk" : "AAaa11!!" ,
"static" : 0 , // 1 if static IP is to be used in STA mode
"ip" : "192.168.4.1" ,
"mask" : 24 , // as number or IP
"gateway" : "192.168.4.1"
} ,
"ap" : {
"ssid" : "YellowToyCar" ,
"psk" : "AAaa11!!" ,
"channel" : 0 , // channel to use for AP, 0 for automatic
"hidden" : 0 ,
"ip" : "192.168.4.1" ,
"mask" : 24 , // as number or IP
"gateway" : "192.168.4.1" ,
"dhcp" : {
"enabled" : 1 ,
"lease" : [ "192.168.4.1" , "192.168.4.20" ] ,
}
} ,
"sntp" : {
"pool" : "pl.pool.ntp.org" ,
"tz" : "CET-1CEST,M3.5.0,M10.5.0/3" ,
"interval" : 3600000
}
} ,
/* Camera settings. See this project or `esp32_camera` library sources for details. */
"camera" : {
"framesize" : 13 ,
"pixformat" : 4 ,
"quality" : 12 ,
"bpc" : 0 ,
"wpc" : 1 ,
"hmirror" : 0 ,
"vflip" : 0 ,
"contrast" : 0 ,
"brightness" : 0 ,
"sharpness" : 0 ,
"denoise" : 0 ,
"gain_ceiling" : 0 ,
"agc" : 1 ,
"agc_gain" : 0 ,
"aec" : 1 ,
"aec2" : 0 ,
"ae_level" : 0 ,
"aec_value" : 168 ,
"awb" : 1 ,
"awb_gain" : 1 ,
"wb_mode" : 0 ,
"dcw" : 1 ,
"raw_gma" : 1 ,
"lenc" : 1 ,
"special" : 0
}
}
Devuelve JSON de la configuración actual, si no cambia nada.
192.168.4.1
por ahora, ya que la configuración de DHCP está codificada en algunos valores predeterminados. /capture
→ Captura de fotogramas desde la cámara del coche.
:81/stream
→ Se transmiten cuadros continuos desde la cámara del automóvil usando MJPEG que explota un tipo de contenido especial: multipart/x-mixed-replace
que informa al cliente que reemplace la imagen si es necesario. Se utiliza un servidor HTTP independiente (de ahí el puerto no estándar 81), ya que es la forma más sencilla de enviar continuamente partes (siguientes fotogramas) en esta única solicitud interminable.
La aplicación espera paquetes UDP en el puerto 83.
Octeto | 0 | 1 | 2 | 3 | |
---|---|---|---|---|---|
Octeto | bits | 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15 | 16 17 18 19 20 21 22 23 | 24 25 26 27 28 29 30 31 |
0 | 0 | (UDP) Puerto de origen | (UDP) Puerto de destino | ||
4 | 32 | (UDP) Longitud | (UDP) Suma de comprobación | ||
8 | 64 | Tipo de paquete (siempre 1) | Banderas (ver tabla a continuación) | Servicio del motor izquierdo | Servicio del motor derecho |
Poco | Mascarilla | Descripción |
---|---|---|
0 | 0b00000001 | Luz principal (LED blanco brillante externo) |
1 | 0b00000010 | Otra luz (LED rojo pequeño interno) |
2 | 0b00000100 | Reservado |
3 | 0b00001000 | Reservado |
4 | 0b00010000 | Reservado |
5 | 0b00100000 | Reservado |
6 | 0b01000000 | Dirección del motor izquierdo |
7 | 0b10000000 | Dirección derecha del motor |
0
) significa hacia adelante, el bit establecido ( 1
) significa hacia atrás. Octeto | 0 | 1 | 2 | 3 | |
---|---|---|---|---|---|
Octeto | bits | 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15 | 16 17 18 19 20 21 22 23 | 24 25 26 27 28 29 30 31 |
0 | 0 | (UDP) Puerto de origen | (UDP) Puerto de destino | ||
4 | 32 | (UDP) Longitud | (UDP) Suma de comprobación | ||
8 | 64 | Tipo de paquete: 2 | Banderas (ver más abajo) | Tiempo (en milisegundos) para suavizar la mezcla hacia los valores motores objetivo | |
12 | 96 | Servicio del motor izquierdo, porcentaje como flotación (es decir, 63.8f equivale al 63,3 % del ciclo de trabajo) | |||
16 | 128 | Servicio del motor derecho, porcentaje como flotación (es decir, 63.8f equivale al 63,3 % del ciclo de trabajo) |
Se desarrollaron algunos scripts para facilitar el desarrollo y el uso.
$ python . s cripts c onfig.py --help
usage: config.py [-h] [--status] [--status-only] [--config-file PATH] [--wifi-mode {ap,sta,apsta,nat,null}] [--ip IP] [--read-only] [--restart [RESTART]]
This script allows to send & retrieve config from the car.
optional arguments:
-h, --help show this help message and exit
--status Request status before sending/requesting config.
--status-only Only request status.
--config-file PATH JSON file to be send as config.
--wifi-mode {ap,sta,apsta,nat,null}
Overwrite WiFi mode from config.
--ip IP, --address IP
IP of the device. Defaults to the one used for AP mode from new config or 192.168.4.1.
--read-only If set, only reads the request (GET request instead POST).
--restart [TIMEOUT] Requests for restart after updating config/retrieving the config.
$ python . s cripts c ontrol.py --help
usage: control.py [-h] [--ip IP] [--port PORT] [--interval INTERVAL] [--dry-run] [--show-packets] [--short-packet-type] [--no-blink] [--max-speed VALUE] [--min-speed VALUE] [--acceleration VALUE]
This script allows to control the car by continuously reading keyboard inputs and sending packets.
optional arguments:
-h, --help show this help message and exit
--ip IP, --address IP
IP of the device. Default: 192.168.4.1
--port PORT Port of UDP control server. Default: 83
--interval INTERVAL Interval between control packets in milliseconds. Default: 100
--dry-run Performs dry-run for testing.
--show-packets Show sent packets (like in dry run).
--short-packet-type Uses short packet type instead long.
--no-blink Prevents default behaviour of constant status led blinking.
Driving model:
--max-speed VALUE Initial maximal speed. From 0.0 for still to 1.0 for full.
--min-speed VALUE Minimal speed to drive motor. Used to avoid motor noises and damage.
--acceleration VALUE Initial acceleration per second.
Note: The 'keyboard' library were used (requires sudo under Linux), and it hooks work also out of focus, which is benefit and issue at the same time, so please care.
Controls:
WASD (or arrows) keys to move; QE to rotate;
F to toggle main light; R to toggle the other light;
Space to stop (immediately, uses both UDP and HTTP);
V to toggle between vectorized (smoothed) and raw mode;
+/- to modify acceleration; [/] to modify max speed;
Shift to temporary uncap speed; ESC to exit.
Nombre amigable | Nombre | Afinidad | Prioridad | Archivo fuente | Descripción |
---|---|---|---|---|---|
Tareas de PCI | ipcx * | Todo* | 0 | (interno) | Las tareas de IPC se utilizan para implementar la función de llamada entre procesadores. |
Principal | main | CPU0 | 1 | main.cpp | Inicializa todo, inicia otras tareas y luego lleva la lógica en segundo plano. |
Transmisión de cámara | httpd | CPU0 | 5 | camera.cpp | |
LwIP | ? | ||||
Wi-Fi | CPU0 | ||||
Eventos | ? | ||||
Tareas inactivas | ipcx * | Todo* | 24 | (interno) | Tareas inactivas creadas para (y ancladas a) cada CPU. |
* - Algunas tareas funcionan en varias CPU, como tareas independientes.
struct
. Vea la discusión aquí. Como solución, descubrí que es más fácil de usar strncpy
, que se integra/optimiza._binary_src_
al acceder a las etiquetas de inicio/final de los bloques de datos incrustados (como en la macro GENERATE_HTTPD_HANDLER_FOR_EMBEDDED_FILE
), no es cierto. Los documentos parecen desactualizados o no válidos en algunas áreas, al menos para esp-idf
. Sin embargo, encontré una solución : use board_build.embed_files
en platformio.ini
y también EMBED_FILES
en CMakeLists.txt
. En el código, use _binary_
, sin la parte src_
.snake_case
mezclado con camelCase
porque usamos bibliotecas C de ESP-IDF y algunas partes las usan mucho. Es aún más feo montar en un solo camello en medio de serpientes.ESP_LOGV
y ESP_LOGD
para un solo archivo, por lo que redefino esas macros a ESP_LOGI
como solución alternativa.esp32-camera
que utiliza el proyecto tiene algunos problemas extraños, aquí hay algunos:camera.py
, que incluye la tarea de enviarlo vía WiFi:string_view
s, como en el código relacionado con config/JSON. Recientemente tuve un problema con el hecho de que strlen
no era seguro...vTaskList
/ uxTaskGetSystemState
Kconfig
para mantener allí las funciones opcionales, incluida alguna depuración. Consulte también https://esp32tutorials.com/esp32-static-fixed-ip-address-esp-idf/esp32-camera
fb_size
cuando use JPEG para permitir que funcione el tamaño más pequeño de 96x96. Tener un mínimo de 2048 parece funcionar, parece recomendable usar más por si acaso. (problema en github).xclk_freq_hz = 10'000'000,
¿para camera_config_t
? 10 MHz podrían ser mejores que 20 MHz, consulte espressif/esp32-camera#15COM8_AGC_EN
en las definiciones de los registros de la cámara desactivadas en 1?constexpr
rápida y C++ para la función IP 4