esp32-cam публикует изображение в mqtt
Я хочу опубликовать захват изображения через ESP32-CAM в MQTT. Имейте следующий код:
#include "WiFi.h"
#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "soc/soc.h" // Отключить проблемы с затемнением
#include "soc/rtc_cntl_reg.h" // Отключить проблемы с затемнением
#include "driver/rtc_io.h"
#include <StringArray.h>
#include <PubSubClient.h>
#include <base64.h>
#include <libb64/cencode.h>
// Замените учетными данными вашей сети
const char* ssid = "####";
const char* password = "####";
// Добавьте IP-адрес вашего MQTT-брокера, например:
const char* mqtt_server = "###.cloudmqtt.com";
const int mqtt_port = 11073;
const char* mqtt_user = "###";
const char* mqtt_password = "###";
#define SLEEP_DELAY 10000 //Задержка 10 секунд
#define FILE_PHOTO "/photo.jpg"
// контакты модуля камеры OV2640 (CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
//#определить ESP32_CLIENT_ID = WiFi.macAddress()
//const char* esp_client_id = WiFi.macAddress()
WiFiClient mqttClient;
PubSubClient client(mqttClient);
const int LED_BUILTIN = 4;
void setup_camera() {
// Отключаем «детектор затемнения»
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
// Модуль камеры OV2640
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
if (psramFound()) {
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Инициализация камеры
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
ESP.restart();
}
}
// Сделать снимок и сохранить его в SPIFFS
void capturePhoto( void ) {
camera_fb_t * fb = NULL; // указатель
bool ok = 0; // логическое значение, указывающее, правильно ли сделан снимок
// Делаем фото камерой
Serial.println("Taking a photo...");
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
return;
}
Serial.print("Heap Size : ");
Serial.println(ESP.getFreeHeap());
//Serial.println(fb->format);
Serial.print("fb len : ");
Serial.println(fb->len);
Serial.print("base64 encode expected len : ");
Serial.println(base64_encode_expected_len(fb->len) + 1);
String base64image = base64::encode(fb->buf, fb->len);
Serial.print("base64 image : ");
Serial.println(base64image);
esp_camera_fb_return(fb);
}
void setup_wifi() {
delay(10);
// Начнем с подключения к сети WiFi
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address : ");
Serial.println(WiFi.localIP());
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
setup_camera();
// инициализируем цифровой вывод LED_BUILTIN как выход.
pinMode(LED_BUILTIN, OUTPUT);
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
}
// функция цикла запускается снова и снова навсегда
void loop() {
Serial.println("PSRAM found: " + String(psramFound()));
digitalWrite(LED_BUILTIN, HIGH); // включаем светодиод (HIGH - уровень напряжения)
delay(1000); // ждем секунду
digitalWrite(LED_BUILTIN, LOW); // выключаем светодиод, понижая напряжение
delay(1000); // ждем секунду
capturePhoto();
if (!client.connected()) {
reconnect();
}
client.loop();
delay(SLEEP_DELAY);
}
Это выводит на консоль
Connecting to ####
...
WiFi connected
IP address : 192.168.1.247
PSRAM found: 1
Taking a photo...
Heap Size : 187548
fb len : 135966
base64 encode expected len : 181289
base64 image : -FAIL-
Обновление от 15 февраля:
Я попробовал следующий код
void setup() {
Serial.begin(115200);
}
// функция цикла запускается снова и снова навсегда
void loop() {
Serial.println("PSRAM found: " + String(psramFound()));
Serial.print("Total heap: ");
Serial.println(ESP.getHeapSize());
Serial.print("Free heap: ");
Serial.println(ESP.getFreeHeap());
Serial.print("Total PSRAM: ");
Serial.println(ESP.getPsramSize());
Serial.print("Free PSRAM: ");
Serial.println(ESP.getFreePsram());
}
который напечатал
PSRAM found: 1
Total heap: 378748
Free heap: 352836
Total PSRAM: 4194252
Free PSRAM: 4194252
Это настройки платы
Плата: AI Thinker ESP32 CAM
Последний пакет платы:
Я пробовал использовать следующие настройки платы
Плата: Модуль разработки ESP32
Скорость загрузки : 921600
Частота ЦП : 240 МГц
Частота вспышки: 80 МГц
Режим вспышки : QIO
Размер флэш-памяти: 4 МБ
Схема разделов: по умолчанию 4 МБ с spiffs
Основной уровень отладки: подробный
PSRAM: включено
что также дает мне следующий вывод
[D][esp32-hal-psram.c:47] psramInit(): PSRAM enabled
Connecting to ###
[D][WiFiGeneric.cpp:337] _eventCallback(): Event: 0 - WIFI_READY
[D][WiFiGeneric.cpp:337] _eventCallback(): Event: 2 - STA_START
[D][WiFiGeneric.cpp:337] _eventCallback(): Event: 4 - STA_CONNECTED
[D][WiFiGeneric.cpp:337] _eventCallback(): Event: 7 - STA_GOT_IP
[D][WiFiGeneric.cpp:381] _eventCallback(): STA IP: 192.168.1.247, MASK: 255.255.255.0, GW: 192.168.1.1
.
WiFi connected
IP address : 192.168.1.247
PSRAM found: 1
Taking a photo...
Heap Size : 187260
fb len : 101157
base64 encode expected len : 134877
base64 image : -FAIL-
Из этого вывода видно, что PSRAM включен. Как я могу использовать его для изображения в кодировке base64?
Также пытался добавить byte* psdRamBuffer = (byte*)ps_malloc(500000);
, упомянутый в https://thingpulse.com/esp32-how-to-use-psram/, но не помогло.
У меня есть ESP32-CAM
Обновление:
Следующее работает, но происходит сбой для framesize_t разрешения_ =FRAMESIZE_QXGA;
, поэтому я не могу захватить 2-мегапиксельное изображение.
#include "WiFi.h"
#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "soc/soc.h" // Отключить проблемы с затемнением
#include "soc/rtc_cntl_reg.h" // Отключить проблемы с затемнением
#include "driver/rtc_io.h"
#include "SPIFFS.h"
#include "base64.h"
#include <PubSubClient.h>
// Замените учетными данными вашей сети
const char* ssid = "xxxxx";
const char* password = "xxxxx";
// Добавьте IP-адрес вашего MQTT-брокера, например:
const char* mqtt_server = "xxxx.cloudmqtt.com";
const int mqtt_port = 1883;
const char* mqtt_user = "xxxx";
const char* mqtt_password = "xxxxx";
//название темы
const char* mqtt_TopicName = "/devices/esp32/data";
framesize_t resolution_ = FRAMESIZE_QVGA;
//используем эту задержку 1000==1 секунда
#define SLEEP_DELAY 60000 //Задержка 60 секунд
#define FILE_PHOTO "/photo.jpg"
// контакты модуля камеры OV2640 (CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
//#определить ESP32_CLIENT_ID = WiFi.macAddress()
//const char* esp_client_id = WiFi.macAddress()
WiFiClient mqttClient;
PubSubClient client(mqttClient);
const int LED_BUILTIN = 4;
void setup_camera() {
// Отключаем «детектор затемнения»
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
// Модуль камеры OV2640
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
if (psramFound()) {
config.frame_size = resolution_ ;// FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 1;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 2;
}
// Инициализация камеры
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
ESP.restart();
}
}
void publishTelemetryFromFile() {
File file = SPIFFS.open("/b64image.txt", FILE_READ);
if (!file) {
Serial.println("There was an error opening the file for read");
return;
} else {
Serial.println(String(file.size())+ "Byte");
}
char* data = (char*)heap_caps_malloc(file.size()+1, MALLOC_CAP_8BIT);
if (data == NULL)
Serial.println("Can not malloc memory");
int i=0;
//пока(файл.доступен()){
for (i=0;i<file.size();i++){
data[i] = file.read();
}
delay(10);
//client.publish_P(mqtt_TopicName,"qwertyuiopasdfghjkl;zxcvbnm,", true);
Serial.print( "Published to MQTT " + String(mqtt_server) + " server.." );
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
boolean Status=client.publish_P( mqtt_TopicName, (const uint8_t*)data, file.size(), true);
Serial.println(String(Status? "Successfully":"Error") );
free(data);
file.close();
}
void capturePhoto( void ) {
// Получить фреймбуфер камеры
camera_fb_t * fb = NULL;
uint8_t* _jpg_buf = NULL;
esp_err_t res = ESP_OK;
size_t frame_size = 0;
Serial.print("Capturing Image ..");
digitalWrite(LED_BUILTIN, HIGH); // включаем светодиод (HIGH - уровень напряжения)
delay(1000); // ждем секунду
fb = esp_camera_fb_get();
digitalWrite(LED_BUILTIN, LOW); // выключаем светодиод, понижая напряжение
delay(1000); // ждем секунду
if (!fb) {
Serial.println("Camera capture failed");
res = ESP_FAIL;
} else {
Serial.println("Done!");
Serial.println(String("Size of the image...")+String(fb->len));
{
if(fb->format != PIXFORMAT_JPEG){
Serial.println("Compressing");
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &frame_size);
esp_camera_fb_return(fb);
fb = NULL;
if(!jpeg_converted){
Serial.println("JPEG compression failed");
res = ESP_FAIL;
}
} else {
frame_size = fb->len;
_jpg_buf = fb->buf;
Serial.print("Size of the base64 encoded image...");
my_base64_encode(_jpg_buf,fb->len,String("Sat Mar 28 11:47:01 EDT 2020") );
esp_camera_fb_return(fb);
publishTelemetryFromFile();
}
}
}
if (res != ESP_OK) {
// ESP_LOGW(TAG, "Сбой захвата камеры с ошибкой = %d", err);
return;
}
}
void setup_wifi() {
delay(10);
// Начнем с подключения к сети WiFi
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address : ");
Serial.println(WiFi.localIP());
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void setup() {
Serial.begin(9600);
//byte* psdRamBuffer = (byte*)ps_malloc(500000);
setup_camera();
// инициализируем цифровой вывод LED_BUILTIN как выход.
pinMode(LED_BUILTIN, OUTPUT);
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
}
// функция цикла запускается снова и снова навсегда
void loop() {
Serial.println("PSRAM found: " + String(psramFound()));
capturePhoto();
if (!client.connected()) {
reconnect();
}
client.loop();
delay(SLEEP_DELAY);
}
@roy, 👍1
Обсуждение2 ответа
Как вы можете видеть здесь, определение возвращаемого значения для esp_camera_fb_get()
равно
typedef struct {
uint8_t * buf; /*!< Pointer to the pixel data */
size_t len; /*!< Length of the buffer in bytes */
size_t width; /*!< Width of the buffer in pixels */
size_t height; /*!< Height of the buffer in pixels */
pixformat_t format; /*!< Format of the pixel data */
} camera_fb_t;
..
camera_fb_t* esp_camera_fb_get();
..
typedef enum {
PIXFORMAT_RGB565, // 2BPP/RGB565
PIXFORMAT_YUV422, // 2BPP/YUV422
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
PIXFORMAT_JPEG, // JPEG/COMPRESSED
PIXFORMAT_RGB888, // 3BPP/RGB888
PIXFORMAT_RAW, // RAW
PIXFORMAT_RGB444, // 3BP2P/RGB444
PIXFORMAT_RGB555, // 3BP2P/RGB555
} pixformat_t;
Это означает, что fb->buf
и bf->len
содержат необработанные данные в формате, заданном pixformat_t
. Кроме того, это не «строка», это необработанные байты, которые вы все равно можете отлично закодировать в base64.
Итак, для библиотеки base64
static String encode(const uint8_t * data, size_t length);
он уже принимает правильный тип данных, и вы можете это сделать
fb = esp_camera_fb_get();
..
//будет размещено в куче. Занимает около 4/3 входного размера, поэтому в основном это удваивает ваши требования к памяти.
String imgDataB64 = base64::encode(fb->buf, fb->len);
//добавляем к объекту JSON ширину, высоту и формат метаданных, чтобы его можно было декодировать
Вы должны вывести значение fb->format
, чтобы проверить формат данных, и добавить это, а также ширину & информацию о высоте, чтобы изображение могло быть построено с другой стороны. Остерегайтесь высоких требований к памяти, поскольку кодировка base64 в основном создает новый буфер для хранения его представления base64. Это можно оптимизировать, записывая данные кадрового буфера в изначально больший буфер, который затем преобразуется на месте. Но это нужно изменить на уровне драйвера образа.
Я обновил вопрос с несколько рабочим кодом., @roy
Если у вас все еще не работает, попробуйте приведенный ниже код, у меня он работает:
int image_buf_size = 4000 * 1000;
uint8_t *image = (uint8_t *)ps_calloc(image_buf_size, sizeof(char));
size_t length=fb->len;
size_t olen;
Serial.print("length is");
Serial.println(length);
int err1= mbedtls_base64_encode(image, image_buf_size, &olen, fb->buf, length);
Serial.println(err1);
String img((const __FlashStringHelper*) image);
Serial.println(img);
//msg1=img;
Я получил ошибку: 'mbedtls_base64_encode' не был объявлен в этой области
int err1= mbedtls_base64_encode(image, image_buf_size, &olen, fb->buf, длина);
, @roy
Включите исходный файл base64 C и исходный файл заголовка C в свой скетч, как это указано на [ссылка](https://tls.mbed.org/base64-source-code), @shiv
#include "mbedtls/base64.h", @Mitja Gustin
- esp32 Stack canary watchpoint срабатывает
- ESP32S v1.1 NodeMCU vs ESP32 DevKitV1
- Публиковать данные json в mqtt
- Не удалось подключиться к брокеру MQTT через esp8266/32 и pub/sub client
- Как назначить точке доступа ESP8266 имя хоста, которое может быть разрешено станциями?
- Adafruit esp32 Feather не удалось скомпилировать
- Взаимодействие ESP32-CAM с CCS811+HDC1080+BMP280
- ESP32 WROOM 32 Проблемы с SPI EMF
Комментарии не для расширенного обсуждения; этот разговор был [перемещен в чат](https://chat.stackexchange.com/rooms/104812/discussion-on-question-by-roy-esp32-cam-publish-image-to-mqtt)., @VE7JRO