How to transmit video from esp32 cam to display wirelessly

My question is how do I transmit video from an esp32 cam to display wirelessly using nrf24l01 + PA + LNA?

The transmitter will be the esp32 cam connected to the esp8266

The screen will be a 1.8-inch TFT screen, connected to an Arduino Nano with the nrf24l01 + PA + LNA being the receiver.

Is there a way to convert the video into numbers and then send the numbers to the receiver and decode them to display them?

Also, I don't care about the fps a lot. I just want it to transmit the video like 1 image per second would be more than enough.
Thanks.

1 Like

The NRF24L01 is far too slow to allow transmission of video.

The nRF24L01+ PA + LNA communicates over a 4-pin SPI (Serial Peripheral Interface) with a maximum data rate of 10Mbps.
Isn't that enough?
also wouldn't every frame be less than 1.5 Mbps? It will have a lot of time to transmit it because 1 frame per second is enough.
also, I don't care about the fps a lot. I just want it to transmit the video like 1 image per second would be more than enough.

I have brainstormed with chat GPT for about 2 hours now, I came up with this:
this is only the transmitter which is the encoder.

/*
  so those are the connections:
  ESP32-CAM to ESP8266:
  3.3V to 3.3V
  GND to GND
  TX (ESP32) to RX (ESP8266)
  RX (ESP32) to TX (ESP8266)

  ESP8266 to NRF24L01+:
  3.3V to VCC
  GND to GND
  D7 (GPIO13) to CE
  D8 (GPIO15) to CSN
  D5 (GPIO14) to SCK
  D6 (GPIO12) to MISO
  D7 (GPIO13) to MOSI
*/
#include <Arduino.h>
#include <JPEGEncoder.h>
#include <NRF24L01.h>
#include <RF24.h>
#include <esp_camera.h>

// Define the camera pins for ESP32-CAM
const int cameraXCLK = 0; // Not used by ESP32-CAM
const int cameraPCLK = 21; // Pixel clock
const int cameraHREF = 22; // Horizontal reference
const int cameraVSYNC = 26; // Vertical sync
const int cameraSDA = 13;   // I2C SDA
const int cameraSCL = 14;   // I2C SCL

// Define the UART pins for communication between ESP32-CAM and ESP8266
const int esp32TxPin = 17; // Transmit from ESP32
const int esp32RxPin = 16; // Receive on ESP32

// Define the NRF24L01+ pins for ESP8266
const int nrfCE = 5;  // GPIO5 for CE
const int nrfCSN = 16; // GPIO16 for CSN

// Create an instance of the JPEGEncoder
JPEGEncoder encoder;

// Create an instance of the NRF24L01+ driver for ESP8266
RF24 radio(nrfCE, nrfCSN);

void setup() {
  Serial.begin(115200);

  // Setup camera
  setupCamera();

  // Setup NRF24L01+
  setupNRF24L01();

  // Initialize the encoder
  encoder.setup(80); // Quality factor (adjust as needed)

  // Initialize UART for communication between ESP32-CAM and ESP8266
  Serial1.begin(115200, SERIAL_8N1, esp32RxPin, esp32TxPin);
}

void loop() {
  // Capture image
  uint8_t *imageBuffer = captureImage();

  // Compress image to JPEG
  encoder.encode(imageBuffer, 160, 120); // Adjust dimensions as needed

  // Get compressed JPEG data
  uint8_t *jpegData = encoder.getData();
  size_t jpegSize = encoder.getSize();

  // Send JPEG data via NRF24L01+ to ESP8266
  sendViaNRF24L01(jpegData, jpegSize);

  delay(1000); // Adjust delay based on desired frame rate
}

void setupCamera() {
  // Camera configuration
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = 5;
  config.pin_d1 = 18;
  config.pin_d2 = 19;
  config.pin_d3 = 21;
  config.pin_d4 = 36;
  config.pin_d5 = 39;
  config.pin_d6 = 34;
  config.pin_d7 = 35;
  config.pin_xclk = 0;
  config.pin_pclk = cameraPCLK;
  config.pin_vsync = cameraVSYNC;
  config.pin_href = cameraHREF;
  config.pin_sscb_sda = cameraSDA;
  config.pin_sscb_scl = cameraSCL;
  config.pin_pwdn = 32;
  config.pin_reset = -1; // Set to -1 if not used
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_QQVGA; // Adjust frame size as needed

  // Initialize camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    while (1);
  }

  // Disable extra frame buffer to save memory
  sensor_t *s = esp_camera_sensor_get();
  s->set_framesize(s, FRAMESIZE_QQVGA); // Adjust frame size as needed
  s->set_vflip(s, true); // Flip vertically if needed

  Serial.println("Camera setup complete");
}

void setupNRF24L01() {
  // Initialize NRF24L01+ for ESP8266
  radio.begin();
  radio.openWritingPipe(0xABCDABCD01LL); // Set the pipe address (must match the receiver)
}

uint8_t *captureImage() {
  // Capture image from camera
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    return nullptr;
  }

  return fb->buf;
}

void sendViaNRF24L01(uint8_t *data, size_t size) {
  // Send data via NRF24L01+ to ESP8266
  radio.write(data, size);
}

The data rate of the Arduino talking to the NRF24 is not the point. The NRF24 has a max air rate of 2Mbs only.

Circa 1 image per second is possible with LoRa modules thats about the limit.

LoRa modules have an FLRC mode which runs at about the same air rate as an NRF24 and an 800 x 600 image can be transfered with that in just under 1 second.

Not sure a NRF24 would be capable of the same rate, the max packet size it can use is 32 bytes, whilst the LoRa device can use 127 byte packets in FLRC mode and 255 bytes in LoRa mode. The larger packets do reduce the overhead of the required send and acknowledge system quite a bit.

Can you use JPEGencoder to do the job? Also, can you check my code?

/*
  so those are the connections:
  ESP32-CAM to ESP8266:
  3.3V to 3.3V
  GND to GND
  TX (ESP32) to RX (ESP8266)
  RX (ESP32) to TX (ESP8266)

  ESP8266 to NRF24L01+:
  3.3V to VCC
  GND to GND
  D7 (GPIO13) to CE
  D8 (GPIO15) to CSN
  D5 (GPIO14) to SCK
  D6 (GPIO12) to MISO
  D7 (GPIO13) to MOSI
*/
#include <Arduino.h>
#include <JPEGEncoder.h>
#include <NRF24L01.h>
#include <RF24.h>
#include <esp_camera.h>

// Define the camera pins for ESP32-CAM
const int cameraXCLK = 0; // Not used by ESP32-CAM
const int cameraPCLK = 21; // Pixel clock
const int cameraHREF = 22; // Horizontal reference
const int cameraVSYNC = 26; // Vertical sync
const int cameraSDA = 13;   // I2C SDA
const int cameraSCL = 14;   // I2C SCL

// Define the UART pins for communication between ESP32-CAM and ESP8266
const int esp32TxPin = 17; // Transmit from ESP32
const int esp32RxPin = 16; // Receive on ESP32

// Define the NRF24L01+ pins for ESP8266
const int nrfCE = 5;  // GPIO5 for CE
const int nrfCSN = 16; // GPIO16 for CSN

// Create an instance of the JPEGEncoder
JPEGEncoder encoder;

// Create an instance of the NRF24L01+ driver for ESP8266
RF24 radio(nrfCE, nrfCSN);

void setup() {
  Serial.begin(115200);

  // Setup camera
  setupCamera();

  // Setup NRF24L01+
  setupNRF24L01();

  // Initialize the encoder
  encoder.setup(80); // Quality factor (adjust as needed)

  // Initialize UART for communication between ESP32-CAM and ESP8266
  Serial1.begin(115200, SERIAL_8N1, esp32RxPin, esp32TxPin);
}

void loop() {
  // Capture image
  uint8_t *imageBuffer = captureImage();

  // Compress image to JPEG
  encoder.encode(imageBuffer, 160, 120); // Adjust dimensions as needed

  // Get compressed JPEG data
  uint8_t *jpegData = encoder.getData();
  size_t jpegSize = encoder.getSize();

  // Send JPEG data via NRF24L01+ to ESP8266
  sendViaNRF24L01(jpegData, jpegSize);

  delay(1000); // Adjust delay based on desired frame rate
}

void setupCamera() {
  // Camera configuration
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = 5;
  config.pin_d1 = 18;
  config.pin_d2 = 19;
  config.pin_d3 = 21;
  config.pin_d4 = 36;
  config.pin_d5 = 39;
  config.pin_d6 = 34;
  config.pin_d7 = 35;
  config.pin_xclk = 0;
  config.pin_pclk = cameraPCLK;
  config.pin_vsync = cameraVSYNC;
  config.pin_href = cameraHREF;
  config.pin_sscb_sda = cameraSDA;
  config.pin_sscb_scl = cameraSCL;
  config.pin_pwdn = 32;
  config.pin_reset = -1; // Set to -1 if not used
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_QQVGA; // Adjust frame size as needed

  // Initialize camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    while (1);
  }

  // Disable extra frame buffer to save memory
  sensor_t *s = esp_camera_sensor_get();
  s->set_framesize(s, FRAMESIZE_QQVGA); // Adjust frame size as needed
  s->set_vflip(s, true); // Flip vertically if needed

  Serial.println("Camera setup complete");
}

void setupNRF24L01() {
  // Initialize NRF24L01+ for ESP8266
  radio.begin();
  radio.openWritingPipe(0xABCDABCD01LL); // Set the pipe address (must match the receiver)
}

uint8_t *captureImage() {
  // Capture image from camera
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    return nullptr;
  }

  return fb->buf;
}

void sendViaNRF24L01(uint8_t *data, size_t size) {
  // Send data via NRF24L01+ to ESP8266
  radio.write(data, size);
}

If this code is good I will proceed to make the screen code (the decoder)

Maybe you missunderstand the complexity of the issue.

To send an image you have to break the memory buffer, that is the image, lets say its 64Kbyte, into a series of 2,000+ individual packets.

Then send each packet in turn, check the data received is correct and in the right sequence, and then reassemble all the received packets into an image.

That will send one packet, max 32 bytes.

how am I gonna do it, also update, i am gonna use esp8266 to transmit the video.
could this work:

const int MAX_PACKET_SIZE = 32;

void sendViaNRF24L01(uint8_t *data, size_t size) {
  // Calculate the number of packets needed
  int numPackets = (size + MAX_PACKET_SIZE - 1) / MAX_PACKET_SIZE;

  // Send each packet
  for (int i = 0; i < numPackets; i++) {
    // Calculate the start and end indices for the current packet
    int startIdx = i * MAX_PACKET_SIZE;
    int endIdx = min((i + 1) * MAX_PACKET_SIZE, size);

    // Calculate the size of the current packet
    int packetSize = endIdx - startIdx;

    // Send the current packet
    radio.write(&data[startIdx], packetSize);

    // Add a delay if needed to avoid overwhelming the receiver
    delay(1);
  }
}

// Function to receive a complete image
void receiveImage() {
  // Assuming you have a function to receive NRF24L01+ packets
  // and store them in a buffer (e.g., receivedData)

  // Create a buffer to store the complete image
  uint8_t *completeImage = new uint8_t[IMAGE_SIZE];

  // Loop to receive packets and reassemble the image
  for (int i = 0; i < numPackets; i++) {
    // Receive the current packet
    // Assuming you have a receive function, modify accordingly
    radio.read(&receivedData[i * MAX_PACKET_SIZE], MAX_PACKET_SIZE);

    // Add a delay if needed to avoid overwhelming the sender
    delay(1);
  }

  // Now you have the complete image in the completeImage buffer
}

chat GPT made this btw.

Good luck then.

Problem is that you want code that works in the real world.

1 Like

Several options, I can think of;

  1. Spend a year or two learning how to code in Arduino.

  2. Find an example of the complete code you want for NRF24.

  3. Pop across to the paid consultancy forum and ask in there.

  4. Use the LoRa code, that works.

Incidently the ESP32CAM is a far from ideal choice for projects of this type, the ESP32 is fairly short of spare IO pins.

The ESP32S3 is better choice in my view, I have connected an OV2640 camera to one of those and transferred images via a connected LoRa device. Code is simpler than for an ESP32CAM itself.

Or there is the SEEED XIAO ESP32S3 sense board, had image transfers working on that too.

what do you mean by "Problem is that you want code that works in the real world"
I am trying my best, i just asked chat GPT to do this because i had no idea what to do bruv.

I can't find an example of the complete code you want for NRF24.
Could you send me a link to the "paid consultancy forum"?
Just tell me if the code is good, I want to then start working on the receiver, nrf24l01 + pa + lna will work with me because I want like 1 fps.

Search for 'Jobs and Paid Consultancy'

https://forum.arduino.cc/categories

so if I pay you, you would help me?

I dont do paid work.

Do you want this topic moving to the paid category? I can do that for you.

If you are willing to pay for this project, just buy a complete setup that transmits and receives video. Well under USD 100 these days.

Not sure why the LoRa solution is not used.

The 2.4Ghz LoRa modules are only about £5 each and provide a working solution. You can also use the UHF LoRa modules if you want range into the kms and transfer speed is not such an issue.

Paying someone to write equivalent code for the NR24 (even if its possible) will cost vastly more than the purchase price of a couple of LoRa modules or other ready made solutions........

Okay, I will purchase a LoRa module. What camera should I use?

No thanks, @srnet told me to get a LoRa module which will be cheaper than paying someone to write me a code for the project that was in my mind.

1 Like