Nikon D50 Remote Camera Trigger - Introduction

by MikeW254 in Circuits > Cameras

1944 Views, 2 Favorites, 0 Comments

Nikon D50 Remote Camera Trigger - Introduction

DSC_0105.JPG

This Instructable may be of limited use in its current form but may inspire someone to have a go at their own version.

I'm a keen wildlife photographer and lucky enough to have lots of wild birds visiting my garden feeder. I wanted to take some close ups of the birds but they are understandably nervous about having anyone around. My camera is a Nikon D50 Digital SLR - an old model I know but I'm still very happy with it. It has a remote trigger but it is Infra Red which limits the range and the positioning of the sensor makes it impossible to take images with the camera facing away from you.

The Parts

Camera Trigger.jpg

The hardware for the project is fairly simple:

1 x Arduino Nano

1 x PIR Sensor

1 x IR Transmitter LED

1 x 433MHz Receiver

1 x 433MHz Transmitter

Some electrical wire

9V battery (PP3)

A box to house it all.

Some sticky-backed velcro to mount it to the camera mount

Electronics

The Arduino was wired up as shown in the circuit diagram. The battery needs to be connected to the Vin pin of the Arduino - this will accept 6V - 20V unregulated DC on this input and will use the internal voltage regulator to power itself. It will also provide 5V out which is used to power the 433 MHz Receiver and PIR sensor. Be careful with what you connect to this pin - the general concensus is that it will provide up to around 900mA in this configuration but I'm not sure ihow well it is protected against over-current or shorts.

The 433MHz Receiver is an RFM83 module which should draw a maximum current of around 3mA. The PIR sensor is a cheap PIR module like this one https://learn.adafruit.com/pir-passive-infrared-pr... The current draw is very low ~100uA so the Nano should have no problem with both it and the Receiver.

The only other item I added was a power switch on the 9V battery line so I didn't have to keep openeing the box and disconnecting it when I had finished

The Transmitter

This is one area where you may have to experiment. I used a Medion 433 MHz transmitter that came with a computer I bought from Aldi. It controls a DVB-T card fitted into the PC and had more buttons than I needed for the purpose but this allowed me to add different functions to the camera trigger. I realise that this is one area where you may have problems building this project but there are plenty of alternatives around - the only issue would be establishing the different codes that each type produces.

The Software - Part 1

There were 2 main challenges with the coding:

  1. Decoding the 433 MHz receiver output
  2. Encoding the IR pulses out to the camera - This required some fairly accurate timing from the Arduino

The Nano was just able to do this. I could have had it trigger when it received any 433 MHz signal above a particular threshold however I have 2 other systems that all work on the same frequency - a weather station and a power consumption monitor. I didn't want the camera triggering just because it had picked up something from those devices. This meant that it had to look for a specific pulse train ( or trains as I was to discover) from the receiver. After a week or so of playing around with this wondering why it didn't work consistently I found that the Medion transmitter sent 2 different pulse trains alternately for each button

I set out to just provide a remote trigger that would give me extended distance and camera orientation compared to using the IR trigger that Nikon produces. Once I had solved the main problem of decoding the receiver output I realised I could add some other functions:

  • PIR sensor. This would enable the system to work as a camera trap, triggered by an Infra Red source (i.e. an animal or person) that moved into the field of the PIR sensor.
  • Delays between exposure. This would allow time-lapse photographs to be taken of a slow-moving subject. Unfortunately I didn't code this function in this project. I couldn't find a way of having relatively accurate timing with the Arduino interrupts enabled and turning them off meant I couldn't turn the time lapse function off using the remote control

The Software - Source Code

const int TOTAL_COUNT = 44;
const int IP_433_pin = 3;
const int IR_led = 2;
const int IR_sensor_pin = 4;
const int HIGH_LVL = 1;
const int LOW_LVL = 0;
  bool start_count;
  bool process;
  bool IRFlg, IRPic;
  int data[TOTAL_COUNT] = {};
  int idx;
  unsigned long start_time;
  boolean old_state,first;
  volatile boolean state;
  unsigned long duration;
  bool DlFlg;
  int DL;
//*********************************************************************
void setup() {
  Serial.begin(9600);
  pinMode(IP_433_pin, INPUT);
  pinMode(IR_led, OUTPUT);
  pinMode(IR_sensor_pin, INPUT);
  Serial.println ("Running...");
  start_time = micros();
  start_count = false;
  state = true;
  old_state = true;
  idx = 0;
  first = true;
  IRFlg = false;
  IRPic = false;
  DlFlg = false;
  attachInterrupt(digitalPinToInterrupt(IP_433_pin), Input433, CHANGE);
}
//*********************************************************************
void Input433(){
  state = digitalRead(IP_433_pin);
}
//*******************************************************************
void modulate(int pulses){
  for (int idx = 0; idx < pulses; idx ++)
  {
    digitalWrite(IR_led, HIGH);
    delayMicroseconds(8);
    digitalWrite(IR_led, LOW);
    delayMicroseconds(8);
  }
}
//*******************************************************************
void Tx(){
  modulate(80);//Send leader (80 cycles of 38.4k)
  delay(27);//Delay is in milliseconds
  delayMicroseconds(830);//Gives a total delay of 27830us
  modulate(16);//2nd pulse (16 cycles)
  delayMicroseconds(1580);//64 cycle pause
  modulate(16);//3rd pulse (16 cycles)
  delayMicroseconds(3580);//128 cycle pause
  modulate(16);//4th pulse (16 cycles)
}  
//*******************************************************************
void takePicture(){
  Tx();
  delay(62);
  delayMicroseconds(500);
  Tx();
}
//*******************************************************************
void loop() {
    if (IRFlg)
    {
      //If IR is selected and a L-H transition has taken place - take picture
      if (digitalRead(IR_sensor_pin) && (!IRPic))
      {
        IRPic = true;  
        takePicture();
      }
      if (!digitalRead(IR_sensor_pin) && IRPic)
      {
        IRPic = false;  
      }
    }
   
    if (!first)
      duration = abs(micros() - start_time);
    else
      {
        duration = 0;
        start_time = micros();
        first = false;
      }
    if (!state && old_state) //Check for a high to low transition
    {
        old_state = state;
        start_time = micros();
        if ((duration > 2700) && (duration < 3000))//Look for starting 'mark' of a data block
        {
          start_count = true;
        }
      
        if (start_count && (duration > 500))
        {
          data[idx] = duration;
          idx++;
        }
     }
    if (state && !old_state)  //Check for a low to high transition
    {
      
      old_state = state;
      start_time = micros();
      if (start_count && (duration > 500))
      {
        data[idx] = duration;
        idx++;
      }
    }
    if (idx >= TOTAL_COUNT)
    {
      start_count = false;
      process = true;
    }
    if (process)
    {
      noInterrupts();
      idx = 0;
      process = false;
      for (int idx1 = 0; idx1 < TOTAL_COUNT; idx1++)
      {
        Serial.println(data[idx1]); 
      }
      String op = "";
      for (int idx1 = 1; idx1 < TOTAL_COUNT - 1; idx1++)
      {
        op += data[idx1] / 500;
      }
      interrupts();
      //See what key(s) were pressed
      if (op == "313131313111113131111111313131311111111111" || op == "311131313111113131311111313131311111111111")//'OK' Button pressed
      {
        takePicture();
      }
      if (op == "311131311111113111311111113131113111111111" || op == "313131311111113111111111113131113111111111")//'1' Button pressed
      {
        if (!IRFlg)
        {
          IRFlg = true;
        }
        else
        {
          IRFlg = false;
        }
      }

      Serial.println(op); 
    
      Serial.println ("Processing complete"); 
    }
}

The Software - Initialisation

So here's what the code does:

const int TOTAL_COUNT = 44;
const int IP_433_pin = 3;<br>const int IR_led = 2;
const int IR_sensor_pin = 4;
const int HIGH_LVL = 1;
const int LOW_LVL = 0; 

Sets up and initialises a number of constants. TOTAL_COUNT is the number of pulses expected from the Medion Transmitter.

  • Digital Pin 2 is the output to the IR LED
  • Digital Pin 3 is the input from the 433 MHz receiver module
  • Digital Pin 4 is the input from the PIR Sensor
bool start_count;<br>  bool process;
  bool IRFlg, IRPic;
  int data[TOTAL_COUNT] = {};
  int idx;
  unsigned long start_time;
  boolean old_state,first;
  volatile boolean state;
  unsigned long duration;
  bool DlFlg;
  int DL;

Variables used in the code

//*********************************************************************<br>void setup() {
  Serial.begin(9600);
  pinMode(IP_433_pin, INPUT);
  pinMode(IR_led, OUTPUT);
  pinMode(IR_sensor_pin, INPUT);
  Serial.println ("Running...");
  start_time = micros();
  start_count = false;
  state = true;
  old_state = true;
  idx = 0;
  first = true;
  IRFlg = false;
  IRPic = false;
  DlFlg = false;
  attachInterrupt(digitalPinToInterrupt(IP_433_pin), Input433, CHANGE);
}
//*********************************************************************

The set up function - This sets up the Nano pins as inputs or outputs, initalises the variables and sets an interrupt on pin 3 so that the Input433 function is called if the Nano detects a change (high to low or low to high) on that pin

//*********************************************************************<br>void Input433(){
  state = digitalRead(IP_433_pin);
}
//*******************************************************************

The Input433 function sets the state equal to the new state of pin 3 - e.g. high or low

The Software - Infra Red Camera Trigger Output

//*******************************************************************
void modulate(int pulses){
  for (int idx = 0; idx < pulses; idx ++)
  {
    digitalWrite(IR_led, HIGH);
    delayMicroseconds(8);
    digitalWrite(IR_led, LOW);
    delayMicroseconds(8);
  }
}
//*******************************************************************

The modulate function is used to drive the IR LED that outputs pulses to the camera IR sensor - as you can see the pulse length and gap is quite small ~ 8 microseconds

//*******************************************************************
void Tx(){
  modulate(80);//Send leader (80 cycles of 38.4k)
  delay(27);//Delay is in milliseconds
  delayMicroseconds(830);//Gives a total delay of 27830us
  modulate(16);//2nd pulse (16 cycles)
  delayMicroseconds(1580);//64 cycle pause
  modulate(16);//3rd pulse (16 cycles)
  delayMicroseconds(3580);//128 cycle pause
  modulate(16);//4th pulse (16 cycles)
}  
//*******************************************************************
void takePicture(){
  Tx();
  delay(62);
  delayMicroseconds(500);
  Tx();
}
//*******************************************************************

The takePicture function initiates a photograph. The Nikon D50 IR trigger requires a pulse train consisting of:

  • 80 cycles (header)
  • 27 millisecond delay
  • 16 pulses
  • 1.58 millisecond delay
  • 16 pulses
  • 3.58 millisecond delay
  • 16 pulses

This is repeated a second time after a 62.5 millisecond delay. The pulses are sent at around 38.4 kHz

The Software - Main Loop

//*******************************************************************
void loop() {
    if (IRFlg)
    {
      //If IR is selected and a L-H transition has taken place - take picture
      if (digitalRead(IR_sensor_pin) && (!IRPic))
      {
        IRPic = true;  
        takePicture();
      }
      if (!digitalRead(IR_sensor_pin) && IRPic)
      {
        IRPic = false;  
      }
    }
  
    if (!first)
      duration = abs(micros() - start_time);
    else
      {
        duration = 0;
        start_time = micros();
        first = false;
      }
    if (!state && old_state) //Check for a high to low transition
    {
        old_state = state;
        start_time = micros();
        if ((duration > 2700) && (duration < 3000))//Look for starting 'mark' of a data block
        {
          start_count = true;
        }
      
        if (start_count && (duration > 500))
        {
          data[idx] = duration;
          idx++;
        }
     }
    if (state && !old_state)  //Check for a low to high transition
    {
      
      old_state = state;
      start_time = micros();
      if (start_count && (duration > 500))
      {
        data[idx] = duration;
        idx++;
      }
    }
    if (idx >= TOTAL_COUNT)
    {
      start_count = false;
      process = true;
    }
    if (process)
    {
      noInterrupts();
      idx = 0;
      process = false;
      for (int idx1 = 0; idx1 < TOTAL_COUNT; idx1++)
      {
        Serial.println(data[idx1]); 
      }
      String op = "";
      for (int idx1 = 1; idx1 < TOTAL_COUNT - 1; idx1++)
      {
        op += data[idx1] / 500;
      }
      interrupts();
      //See what key(s) were pressed
      if (op == "313131313111113131111111313131311111111111" || op == "311131313111113131311111313131311111111111")//'OK' Button pressed
      {
        takePicture();
      }
      if (op == "311131311111113111311111113131113111111111" || op == "313131311111113111111111113131113111111111")//'1' Button pressed
      {
        if (!IRFlg)
        {
          IRFlg = true;
        }
        else
        {
          IRFlg = false;
        }
      }

      Serial.println(op); 
    
      Serial.println ("Processing complete"); 
    }
}

The first 'If' statement check if we are in PIR mode and if we have received a PIR pulse - if both are true we take a picture. We can only set an interrupt on one pin and this is set for the 433 Input pin so we poll the PIR pin to see if there is a pulse

The 'if (!first)' line just gets rid of the initial pulse we receive - The Nano takes a little while settling into its stride and the first timed pulse is measured as being longer than it actually is so we discard it.

We then have 2 blocks - one checking for high to low transitions and the other checking for low to high. The time between each is measured and stored in an array.

The start of a pulse train from the Medion transmitter is indicated by a High pulse of between 2.7 and 3 milliseconds. We flag that we have received this and start counting subsequent pulses received. Once we have received 44 of these that is the end of the pulse train.

We then process the received data by dividing each pulse by 500 which is the length of the Medion data pulse in microseconds. As it uses Manchester encoding we end up with data being represented by either a pulse or 3 pulse length = 500 or 1500 microseconds. Dividing by 500 gives us a value of 1 or 3 which is encoded into a string as a '1' or '3'.

The string is then compared with the measured value for individual buttons on the Medion Transmitter. I used the 'OK' button to take an instant picture and the '1' key to turn the PIR function on or off.

Assembly and Use

camera with LED.jpg
DSC_0107.JPG

Assembly

I'm afraid I didn't take any images of the assembly part of the project however there was nothing difficult. I fitted everything into a small plastic food container. I cut a whole in the lid and hot glued the PIR sensor so it points out and 3 small holes for the aerial lead, leads for the IR LED and one for the power switch.

The aerial needs to be 17.3 cm for a 433 MHz 1/4 wavelength.

On the back of the box and near the end of the LED lead I stuck some sticky backed velcro hooks and put the corresponding loops on the camera tripod and in a position where the LED can be seen by the IR sensor on the camera.

The battery is mounted in the box and I wrapped it in some funky foam to insulate it both electrically and thermally.

Use

I had considered adding an LED to show that power is switched on but the LED on the Nano does that job well enough.

I have used the system with the camera around 30 meters away. There are occasions where it doesn't trigger but I think it is because of interference from the other 433 MHz systems I have.

On the whole the system works well though the Nikon D50 is probably not the best camera for this type of thing:

  • There is a maximum of 15 minutes in trigger mode between pictures . You have to take an image every 10 minutes or so even without a subject in frame just to keep the camera in trigger mode
  • The shutter is extremely noisy and slow to the extent that it will scare birds away as soon as the shutter starts to move and I often just get the tip of a wing or tail in shot as a result
  • The auto focus is also quite slow and noisy so I set it on manual and pre-focus it on an area I expect the birds to be.

The PIR sensor is a lot more directional than I expected so I need to come up with a way of pointing it in the same direction as the camera lens - possibly by re-mounting the system and sensor in an old flash gun casing and mounting it on the hot shoe at the top of the camera. This would mean that I don't need to velcro it to the tripod

Conclusion

As I mentioned at the start, this is a very hardware specific project but I hope it will inspire someone to build their own system. Thinking this concept through a bit further I can see similar systems being used as range extenders for remote controls for any home automation system. The output from the Arduino can be fed to another 433 MHz Transmitter instead of the IR LED I use.

There are lots of resources on the Internet on how to decode the Manchester code that most 433 MHz On-Off Keying (OOK) systems use. There are instructions on how to make an oscilloscope using your sound card to help decode the pulse trains and quite a few code examples on decoding them using RPi's or Arduinos