Wireless Controlled Shutter Button for DSLR using Arduino and nRF24L01

Published  April 15, 2021   1
Wireless Controlled Shutter Button for DSLR

Who does not love photography! There are lots of subjects in this creative field and each one is unique, let's choose one. How much all of us love to see wild animals on National Geography or Discovery channels? Yes, I am talking about one of the widely cultivated genres of photography as well as one of the most dangerous photography subjects that is Wildlife Photography. In Wildlife Photography, there are situations where getting close to the subject may become life-threatening or dangerous, but at the same time, the photo needs to be captured. In such a case, a wired switch that could control the camera from a long distance will be beneficial in such cases. What if it goes wireless? It will definitely be of great benefit for the photographer.  

Here, in this project, we will build a wireless controlled shutter button for the DSLR. To do that, we will be using the nRF24L01–2.4GHz RF Transceiver module with Arduino UNO, so there is no need to be present with the DSLR camera, and it can be triggered far from the sight. We previously used nRF24L01 with Arduino to control the servo motor and create a Chat Room.

Components Required to Build Wireless Trigger for DSLR

Since this project deals with wireless control, a wireless receiver or transmitter is required. That is none other than NRF24L01 pairs. By using this with a microcontroller, data transfer can be done wirelessly. We used Arduino UNO and Arduino NANO for this one.

  1. Arduino UNO Board
  2. USB Cable with 5V Adapter or Power Bank - 2 Pcs
  3. Arduino NANO
  4. NRF24L01 - 2pcs
  5. 2.5mm JACK - Monotype.
  6. 5V converter (Requires if it is using 7.4V Lithium Battery)

DSLR Remote Trigger - Schematic Diagram

Schematic Diagram to build remote trigger for DSLR is shown below.

DSLR Remote Trigger Schematic Diagram

The Schematic is simple and is divided into two sections, nRF24L01 with Arduino to act as Receiver and nRF24L01 with Arduino to act as Transmitter. In the receiver section, the button is in a low state, thus a pull-down resistor is used. When the button is pressed, it will make the pin D4 as Low to High. This data will be transmitted via nRF24L01. On the other hand, the Receiver will receive the data and transfer this to the 2.5mm Jack. Both the transmitter and receiver circuit setup are shown below.

DSLR Remote Trigger

Remote Trigger for DSLR

Explanation of the Circuit and Working

DSLR supports shutter remote input where existing wired remotes can be connected to trigger the shutter. This can be seen in the image given below.

DSLR Trigger Pin

The connection of this pin can be seen in the image given below.

DSLR Shutter Pin

The tip is connected with the shutter button internally in the DSLR. It is in 3.3V level by using a pull-up resistor available inside of the 2.5mm Jack. Now, when this tip gets 0V or Low, the shutter button gets released. This is done using the Arduino NANO. Whenever the Arduino Nano receives the data via NRF24L01 that the switch in the transmitter is pressed, it immediately makes the TIP of the connector to Low.

On the other hand, there is a pull-up resistor used across the Arduino UNO. Whenever the switch is pressed, the input pin where the switch is connected gets low. The Arduino further transmits this data via nRF24L01 to the transmitter. Also, the button on the transmitter side uses a debounce for false triggering issues. Another important module is the NRF24L201. This is used as a pair. Below is the pin diagram of NRF24L01. 

NRF24L01 Module Pinout

It can be configured as a Receiver or as a Transmitter. It is a very low-cost RF transceiver module that uses 2.4GHz of RF range and could produce a very long range of data transmission from 50 feet to 200 feet of distance. To know how this can be configured, let's move into the code. To understand the work, kindly look into the code comments as well as the descriptions. Previously, we have dedicated an article on the communication process of NRF modules where we have mentioned all the necessary things to abort this module, you can check that out if you want to know more about these modules.

Arduino Code for Remote DSLR Trigger

There are two different codes used for this project, one for the Transmitter and the other one for the Receiver.

Code For the Transmitter:

The Libraries are included first.

//Include Libraries
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include "printf.h"
#include <ArduinoJson.h>

The address is set, This must be the same for both transmitter and receiver.

//address through which two modules communicate.
const byte address[6] = {0xe1, 0xe1, 0xe1, 0xe1, 0xe1};

A boolean is created where the trigger state will be changed from true to false or vice versa.

volatile bool isTrigger = false;

The next step is to start the NRF24L01 before running the actual application. Here, the range, data type, and state are also set. The nRF24L01 is set as a transmitter, and it is also set for the long-range distance with maximum RF Power. Also, put the address that was previously set.

void setup()
{
  Serial.begin(9600);
  printf_begin();
  Serial.println(F("\n\rRF24/examples/scanner/"));
  // Setup and configure rf radio
  radio.begin();
  radio.setAutoAck(false);
  //set the address
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN); //set as: RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
  radio.setDataRate(RF24_2MBPS); //set as: F24_250KBPS, F24_1MBPS, F24_2MBPS ==>250KBPS = longest range
  radio.setChannel(115); //sets channel from 2.4 to 2.524 GHz in 1 MHz increments 2.483.5 GHz is normal legal limit
  radio.setCRCLength(RF24_CRC_8);
  //  radio.printDetails();
  //Set module as transmitter
  radio.stopListening();

After setting the Transmitter nRF24L01, it is time to set the button input that will be pressed by the user during the trigger. The pin is set as input mode.

  pinMode(SHUTT_REMOTE_TRIGGER_PIN, INPUT);   // sets the digital pin "SHUTT_TRIGGER_PIN" as output
  //  attachInterrupt(digitalPinToInterrupt(SHUTT_REMOTE_TRIGGER_PIN), shutter_remote_trigger, LOW);
  //  attachInterrupt(digitalPinToInterrupt(SHUTT_REMOTE_TRIGGER_PIN), shutter_remote_trigger, HIGH  );
}
void shutter_remote_trigger() {
  isTrigger = true;
}
// Variables will change:
int ledState = HIGH; // the current state of the output pin
int buttonState;   // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin
// the following variables are unsigned longs because the time, measured in
// milliseconds will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers

In the loop, the button state is constantly monitored and whenever it is pressed, a JSON is sent to the receiver.

void loop()
{
 // read the state of the switch into a local variable:
  int reading = digitalRead(SHUTT_REMOTE_TRIGGER_PIN);
  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:
  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
                // reset the debouncing timer
                lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
                // whatever the reading is at, it's been there for longer than the debounce
                // delay, so take it as the actual current state:
                // if the button state has changed:
                if (reading != buttonState) {
                buttonState = reading;
                // only toggle the LED if the new button state is LOW
                if (buttonState == LOW) {
                isTrigger = true;
                }
                }
  }
  // save the reading. Next time through the loop, it'll be the lastButtonState:
  lastButtonState = reading;
  if (true == isTrigger) {
                // Allocate the JSON document
                //
                // Inside the brackets, 200 is the RAM allocated to this document.
                // Don't forget to change this value to match your requirement.
                // Use arduinojson.org/v6/assistant to compute the capacity.
                StaticJsonDocument<50> doc;
                doc[SHUTT_TRIGGER_JSON_KEY] = SHUTT_TRIGGER_ACTIVE;
                //Send message to receiver
                char send_dt[32] = {0};
                String output;
                //serializeJson(doc, Serial);
                serializeJson(doc, send_dt);
                Serial.println(send_dt);
                radio.write(&send_dt, sizeof(send_dt));
                isTrigger = false;
  }
  delay(10);
}

Code For the Receiver:

This code is similar to the transmitter, but after receiving the bit, it provides data for the trigger.

The Libraries are included first.

//Include Libraries
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include "printf.h"
#include <ArduinoJson.h>

The address is set, as discussed before, this address must be the same for both transmitter and receiver. It is given previously in the transmitter address section as well.

//address through which two modules communicate.
const byte address[6] = {0xe1, 0xe1, 0xe1, 0xe1, 0xe1};

The next step is to start the nRF24L01 before running the actual application. Here, the range, data type, and the state are also set the same as the transmitter section, but here it is set as a receiver instead of a transmitter. It is also set for the long-range distance with maximum RF Power.

It also puts the address that was previously set.

void setup()
{
  Serial.begin(9600);
  printf_begin();
  //Serial.println(F("\n\rRF24/examples/scanner/"));
  // Setup and configure rf radio
  radio.begin();
  radio.setAutoAck(false);
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN); //set as: RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
  radio.setDataRate(RF24_2MBPS); //set as: F24_250KBPS, F24_1MBPS, F24_2MBPS ==>250KBPS = longest range
  radio.setChannel(115); //sets channel from 2.4 to 2.524 GHz in 1 MHz increments 2.483.5 GHz is normal legal limit
  radio.setCRCLength(RF24_CRC_8);
  // Get into standby mode
  radio.startListening();

After setting the Receiver nRF24L01, it is time to put the logic to set the shutter pin low if the JSON is received that has the information of the button being pressed.

void loop()
{
  digitalWrite(SHUTT_TRIGGER_PIN, HIGH);
  //Read the data if available in buffer
  if (radio.available())
  {
                // Allocate the JSON document
                //
                // Inside the brackets, 200 is the capacity of the memory pool in bytes.
                // Don't forget to change this value to match your JSON document.
                // Use arduinojson.org/v6/assistant to compute the capacity.
                StaticJsonDocument<50> doc;
                char recv_dt[32] = {0};
                radio.read(&recv_dt, sizeof(recv_dt));
                Serial.println(recv_dt);
                // Deserialize the JSON document
                DeserializationError error = deserializeJson(doc, recv_dt);
                // Test if parsing succeeds.
                if (error) {
                Serial.print(F("deserializeJson() failed: "));
                Serial.println(error.f_str());
                //return;
                } else {
                // Fetch values.
                //
                // Most of the time, you can rely on the implicit casts.
                // In other case, you can do doc["time"].as<long>();
                char s_trig_stat = doc[SHUTT_TRIGGER_JSON_KEY];
                if (SHUTT_TRIGGER_ACTIVE == s_trig_stat) {
                Serial.println("Set Shutter pin Low");
                digitalWrite(SHUTT_TRIGGER_PIN, LOW); // sets the digital pin "SHUTT_TRIGGER_PIN" on
                digitalWrite(LED_BUILTIN, HIGH);
                delay(250);  // waits for a 250 millisecond
                }
                }
                digitalWrite(LED_BUILTIN, LOW);
  }
}

Testing

To test the circuit, the transmitter and receiver circuit is constructed properly. A 2.5mm mono jack is used with the DSLR and tested for the circuitry operations.

Below are the images of the setup. 

Wireless Trigger for DSLR

To check the full testing and working, please watch the video given below. And if you have any questions regarding this project, feel free to comment down below.

Code

Transmitter Code:

//Include Libraries
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include "printf.h"
#include <ArduinoJson.h>
//create an RF24 object
RF24 radio(9, 8);  // CE, CSN
//address through which two modules communicate.
const byte address[6] = {0xe1, 0xe1, 0xe1, 0xe1, 0xe1};
//DSLR Shutter Trigger Operation's variable
#define SHUTT_TRIGGER_JSON_KEY  "s_trig"
#define SHUTT_TRIGGER_ACTIVE 100
#define SHUTT_TRIGGER_INACTIVE 0
#define SHUTT_REMOTE_TRIGGER_PIN 4
volatile bool isTrigger = false;
void setup()
{
  Serial.begin(9600);
  printf_begin();
  Serial.println(F("\n\rRF24/examples/scanner/"));
  // Setup and configure rf radio
  radio.begin();
  radio.setAutoAck(false);
  //set the address
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN); //set as: RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
  radio.setDataRate(RF24_2MBPS); //set as: F24_250KBPS, F24_1MBPS, F24_2MBPS ==>250KBPS = longest range
  radio.setChannel(115); //sets channel from 2.4 to 2.524 GHz in 1 MHz increments 2.483.5 GHz is normal legal limit
  radio.setCRCLength(RF24_CRC_8);
  //  radio.printDetails();
  //Set module as transmitter
  radio.stopListening();
  pinMode(SHUTT_REMOTE_TRIGGER_PIN, INPUT);      // sets the digital pin "SHUTT_TRIGGER_PIN" as output
  //  attachInterrupt(digitalPinToInterrupt(SHUTT_REMOTE_TRIGGER_PIN), shutter_remote_trigger, LOW);
  //  attachInterrupt(digitalPinToInterrupt(SHUTT_REMOTE_TRIGGER_PIN), shutter_remote_trigger, HIGH  );
}
void shutter_remote_trigger() {
  isTrigger = true;
}
// Variables will change:
int ledState = HIGH;               // the current state of the output pin
int buttonState;            // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin

// the following variables are unsigned longs because the time, measured in
// milliseconds will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;  // the debounce time; increase if the output flickers
void loop()
{
  // read the state of the switch into a local variable:
  int reading = digitalRead(SHUTT_REMOTE_TRIGGER_PIN);
  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:
  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
            // reset the debouncing timer
            lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
            // whatever the reading is at, it's been there for longer than the debounce
            // delay, so take it as the actual current state:

            // if the button state has changed:
            if (reading != buttonState) {
            buttonState = reading;
            // only toggle the LED if the new button state is LOW
            if (buttonState == LOW) {
            isTrigger = true;
            }
            }
  }
  // save the reading. Next time through the loop, it'll be the lastButtonState:
  lastButtonState = reading;
  if (true == isTrigger) {
            // Allocate the JSON document
            //
            // Inside the brackets, 200 is the RAM allocated to this document.
            // Don't forget to change this value to match your requirement.
            // Use arduinojson.org/v6/assistant to compute the capacity.
            StaticJsonDocument<50> doc;
            doc[SHUTT_TRIGGER_JSON_KEY] = SHUTT_TRIGGER_ACTIVE;
            //Send message to receiver
            char send_dt[32] = {0};
            String output;
            //serializeJson(doc, Serial);
            serializeJson(doc, send_dt);
            Serial.println(send_dt);
            radio.write(&send_dt, sizeof(send_dt));
            isTrigger = false;
  }
  delay(10);
}
Receiver Code -
//Include Libraries
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include "printf.h"
#include <ArduinoJson.h>
//create an RF24 object
RF24 radio(9, 8);  // CE, CSN
//address through which two modules communicate.
const byte address[6] = {0xe1, 0xe1, 0xe1, 0xe1, 0xe1};
//DSLR Shutter Trigger Operation's variable
#define SHUTT_TRIGGER_JSON_KEY  "s_trig"
#define SHUTT_TRIGGER_ACTIVE 100
#define SHUTT_TRIGGER_INACTIVE 0
#define SHUTT_TRIGGER_PIN 4
void setup()
{
  Serial.begin(9600);
  printf_begin();
  //Serial.println(F("\n\rRF24/examples/scanner/"));
  // Setup and configure rf radio
  radio.begin();
  radio.setAutoAck(false);
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN); //set as: RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
  radio.setDataRate(RF24_2MBPS); //set as: F24_250KBPS, F24_1MBPS, F24_2MBPS ==>250KBPS = longest range
  radio.setChannel(115); //sets channel from 2.4 to 2.524 GHz in 1 MHz increments 2.483.5 GHz is normal legal limit
  radio.setCRCLength(RF24_CRC_8);
  // Get into standby mode
  radio.startListening();
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(SHUTT_TRIGGER_PIN, OUTPUT);       // sets the digital pin "SHUTT_TRIGGER_PIN" as output
  digitalWrite(SHUTT_TRIGGER_PIN, HIGH);  // sets the digital pin "SHUTT_TRIGGER_PIN" off
}
void loop()
{
  digitalWrite(SHUTT_TRIGGER_PIN, HIGH);
  //Read the data if available in buffer
  if (radio.available())
  {
            // Allocate the JSON document
            //
            // Inside the brackets, 200 is the capacity of the memory pool in bytes.
            // Don't forget to change this value to match your JSON document.
            // Use arduinojson.org/v6/assistant to compute the capacity.
            StaticJsonDocument<50> doc;
            char recv_dt[32] = {0};
            radio.read(&recv_dt, sizeof(recv_dt));
            Serial.println(recv_dt);
            // Deserialize the JSON document
            DeserializationError error = deserializeJson(doc, recv_dt);
            // Test if parsing succeeds.
            if (error) {
            Serial.print(F("deserializeJson() failed: "));
            Serial.println(error.f_str());
            //return;
            } else {
            // Fetch values.
            //
            // Most of the time, you can rely on the implicit casts.
            // In other case, you can do doc["time"].as<long>();
            char s_trig_stat = doc[SHUTT_TRIGGER_JSON_KEY];
            if (SHUTT_TRIGGER_ACTIVE == s_trig_stat) {
            Serial.println("Set Shutter pin Low");
            digitalWrite(SHUTT_TRIGGER_PIN, LOW); // sets the digital pin "SHUTT_TRIGGER_PIN" on
            digitalWrite(LED_BUILTIN, HIGH);
            delay(250);                  // waits for a 250 millisecond
            }
            }
            digitalWrite(LED_BUILTIN, LOW);
  }
}

Video

Have any question realated to this Article?

Ask Our Community Members

Comments