Go Down

Topic: Camera Control (Read 7895 times) previous topic - next topic

rickpbush

Hi, please help me, I think I've implemented Pelco D on my mega but it don't work and I don't know why :(
I think it has something to do with the way I'm hadling bytes, I'm a bit lost I'm afraid. The hall effect joystick is working well and to design but just no talkie to the cam?

The MAX485 wiring



The Code

Code: [Select]

/*
The general idea is to have a hall effect joystick with X, Y and Z axis.
The Z axis is the zoom.

The joystick feeds into three analog inputs on the arduino.
Each axis has the following attributes :

1. AzisZero Range - this is the range between which we can assume the joystick is centered and not being used
2. AxisMax - this is the value which represents the joystick being moved to its maximum extent in the 'up/right/in' direction for Y, X and Z respectively
3. AxisMin - this is the value which represents the joystick being moved to its maximum extent in the 'down/left/out' direction for Y, X and Z respectively

The basic principal to which we will work is as follows :

We will calculate the direction to move based on whether the joystick is being moved left, right, up or down. If the joystick is no
at its centered position we can assume we need to send a 'move camera' command. The speed should be calculated based on how far between
AxisZero and AxisMaz/Min the stick is positioned.

Example.

AxisXZero Range = 520 - 530
AxisXMax        = 1000      (Far Right)
AxisXMin        = 0         (Far Left)

AxisYZero Range = 520 - 530
AxisYMax        = 1000      (Top)
AxisYMin        = 0         (Bottom)

Stick AxisXCurrent = 650
Stick AxisYCurrent = 400

This would mean we are asking the camera to move RIGHT (because 650 is greater than AxisXZero maximum of 530)
and DOWN (because 400 is less than AxisYZero minimum of 520).

The speed value the camera is expecting is between $00 (stop) to $3F (high speed). This gives us decimal 0-63.
The stick has an X axis movement range for travelling RIGHT of 530-1000, giving us 470 incements.
We can map the 470 increments to our 63 using the arduino function 'map(value, fromLow, fromHigh, toLow, toHigh)'
which re-maps a number from one range to another.

Actual Position in the right range = 650 - 530, = 120

map(120, 0, 470, 0, 63) = whatever!


*/
//set some Pelco command vars
int PelcoMaxSpeed = 63;
byte CurrCameraAddress = 02;
byte SynchByte = 00;
byte PanSpeed = 00;
byte TiltSpeed = 00;
byte ZoomSpeed = 00;

/*
command 1
  7  Sense
  6  Reserved
  5  Reserved
  4  Auto/Manual Scan
  3  Camera On/Off
  2  Iris Closed
  1  Iris Open
  0  Focus Near
*/
byte command1 = B00000000;

/*
command 2
  7  Focus Far
  6  Zoom Wide
  5  Zoom Tele
  4  Down
  3  Up
  2  Left
  1  Right
  0  Always 0
*/
byte command2 = B00000000;

//set our analog read pins
int XAxisPin = 7;                       
int XAxisVal = 0;

int YAxisPin = 1;                       
int YAxisVal = 0;

int ZAxisPin = 0;                       
int ZAxisVal = 0;           

//set our axis zero ranges
int XAxisZeroMax = 520;
int XAxisZeroMin = 510;

int YAxisZeroMax = 525;
int YAxisZeroMin = 515;

int ZAxisZeroMax = 530;
int ZAxisZeroMin = 520;

//set our axis maximums
int XAxisMax = 910;
int YAxisMax = 1020;
int ZAxisMax = 1020;

//set our axis minimums
int XAxisMin = 120;
int YAxisMin = 10;
int ZAxisMin = 0;

//set up some working vars so we don't need to calculate them every loop itteration
int rightRange = 0;
int leftRange = 0;
int upRange = 0;
int downRange = 0;
int zoomInRange = 0;
int zoomOutRange = 0;

int MAX485 = 3;

void setup()
{
  rightRange = XAxisMax - XAxisZeroMax;
  leftRange = XAxisZeroMin - XAxisMin;
  upRange = YAxisMax - YAxisZeroMax;
  downRange = YAxisZeroMin - YAxisMin;
  zoomInRange = ZAxisMax - ZAxisZeroMax;
  zoomOutRange = ZAxisZeroMin - ZAxisMin;

  Serial.begin(9600);          //  setup serial for debug
  Serial3.begin(2400);         //  setup serial for commands
 
  pinMode(MAX485, OUTPUT);
}

void loop()
{
  //reset the command
  command2 = B00000000;
 
  //read the stick values
  XAxisVal = analogRead(XAxisPin);
  YAxisVal = analogRead(YAxisPin);
  ZAxisVal = analogRead(ZAxisPin); 
 
  //X Axis
    //are we within the zero range
    if(XAxisVal < XAxisZeroMin || XAxisVal > XAxisZeroMax)
    {
      //we are not zerod, we need to send out a Pan command
      //deturmine if it will be left or right
      if(XAxisVal > XAxisZeroMax)
      {
        //we are going right, calculate the speed
        int rightVal = XAxisVal - XAxisZeroMax;
        PanSpeed = map(rightVal, 0, rightRange, 0, PelcoMaxSpeed);
       
        //set the move right bit of the command
        command2 = command2 | B00000010;
       
        //debug
        Serial.print("Move Right");
        Serial.println(PanSpeed);
      }
      else
      {
        //we are going left
        int leftVal = XAxisZeroMin - XAxisVal;
        PanSpeed = map(leftVal, 0, leftRange, 0, PelcoMaxSpeed);
       
        //set the move left bit of the command
        command2 = command2 | B00000100;
       
        //debug
        Serial.print("Move Left");
        Serial.println(PanSpeed);
      }
    }

//Y Axis
    //are we within the zero range
    if(YAxisVal < YAxisZeroMin || YAxisVal > YAxisZeroMax)
    {
      //we are not zerod, we need to send out a Pan command
      //deturmine if it will be left or right
      if(YAxisVal > YAxisZeroMax)
      {
        //we are going up, calculate the speed
        int upVal = YAxisVal - YAxisZeroMax;
        TiltSpeed = map(upVal, 0, upRange, 0, PelcoMaxSpeed);
       
        //set the move right bit of the command
        command2 = command2 | B00001000;
       
        //debug
        Serial.print("Move Up");
        Serial.println(TiltSpeed);
      }
      else
      {
        //we are going left
        int downVal = YAxisZeroMin - YAxisVal;
        TiltSpeed = map(downVal, 0, downRange, 0, PelcoMaxSpeed);
       
        //set the move left bit of the command
        command2 = command2 | B00010000;
       
        //debug
        Serial.print("Move Down");
        Serial.println(TiltSpeed);
      }
    }

//Z Axis
    //are we within the zero range
    if(ZAxisVal < ZAxisZeroMin || ZAxisVal > ZAxisZeroMax)
    {
      //we are not zerod, we need to send out a zoom command
      //deturmine if it will be tele or wide
      if(ZAxisVal > ZAxisZeroMax)
      {
        //we are going tele, calculate the speed
        int teleVal = ZAxisVal - ZAxisZeroMax;
        ZoomSpeed = map(teleVal, 0, zoomInRange, 0, PelcoMaxSpeed);
       
        //set the move right bit of the command
        command2 = command2 | B00100000;
       
        //debug
        Serial.print("Zoom In");
        Serial.println(ZoomSpeed);
      }
      else
      {
        //we are going left
        int wideVal = ZAxisZeroMin - ZAxisVal;
        ZoomSpeed = map(wideVal, 0, zoomOutRange, 0, PelcoMaxSpeed);
       
        //set the move left bit of the command
        command2 = command2 | B01000000;
       
        //debug
        Serial.print("Zoom Out");
        Serial.println(ZoomSpeed);
      }
    }
   
   
  //set the MAX485 to send data
  digitalWrite(MAX485, HIGH);
  /*
  SEND SPECIAL ZOOM SPEED COMMAND
 
  Command        W3 | W4 | W5 | W6
  --------------------------------------
  Set Preset     00 | 03 | 00 | 01 to 20
  Clear Preset   00 | 05 | 00 | 01 to 20
  Go To Preset   00 | 07 | 00 | 01 to 20
  Set Zoom Speed 00 | 25 | 00 | 00 to 03
  */
 
  Serial3.write((byte)SynchByte); // Synch Byte
  Serial3.write((byte)CurrCameraAddress); //Camera Address
  Serial3.write((byte)00);
  Serial3.write((byte)25);
  Serial3.write((byte)00);
  Serial3.write((byte)ZoomSpeed);
  byte checkSum = ((byte)SynchByte + (byte)CurrCameraAddress + (byte)00 + (byte)25 + (byte)00 + (byte)ZoomSpeed)%256;
  Serial3.write((byte)checkSum); //check sum is the sum of bytes (excluding the synchronization byte) modulo 256

  /*
  SEND PTZ COMMAND
 
  Byte 1 - Synch Byte
  Byte 2 - Address
  Byte 3 - Command 1
  Byte 4 - Command 2
  Byte 5 - Data 1/Pan Speed
  Byte 6 - Data 2/Tilt Speed
  Byte 7 - Check Sum
  */
  Serial3.write((byte)SynchByte); // Synch Byte
  Serial3.write((byte)CurrCameraAddress); //Camera Address
  Serial3.write((byte)command1); 
  Serial3.write((byte)command2);
  Serial3.write((byte)PanSpeed); //Pan Speed
  Serial3.write((byte)TiltSpeed); //Tilt Speed
 
  checkSum = ((byte)SynchByte + (byte)CurrCameraAddress + (byte)command1 + (byte)command2 + (byte)PanSpeed + (byte)TiltSpeed)%256;
  Serial3.write((byte)checkSum); //check sum is the sum of bytes (excluding the synchronization byte) modulo 256
   
  //set the MAX485 to STOP send data
  digitalWrite(MAX485, LOW);
 
  delay(500);
}


AWOL

What does your debug output tell you?

rickpbush

Hi,the debug output from the code to serial(0) reports the extent the joystick has been moved and in which direction. That code is working fine it's just placing the commands onto serial 3 that is failing for some reason

PaulS

Unplug the joystick. It clearly has nothing to do with the issue. Do the commands without the joystick attached work? If so, then you have one problem. If not, you have something wired incorrectly, or the commands are wrong. We need to know which problem you have, in order to help you.
The art of getting good answers lies in asking good questions.

rickpbush

Ok, great, I have removed the stick and boiled my code down to what is below but still no joy, I'm suspecting there is a problem with my hardware, specifically the max485, I have a 100ohm resistor across pins 6+7 as well as an led to show data output. When I remove the camera from the output of the max485, the led blinks when it should, reconnect the camera wires, no blinking.

Code: [Select]

byte CurrCameraAddress = 0x01;
byte SynchByte = 0x00;
byte PanSpeed = 0x10;
byte TiltSpeed = 0x10;

int MAX485 = 3;

byte command1 = 0x00;
byte command2 = 0x02;  //should go right

void setup()
{
  Serial3.begin(2400);         //  setup serial for commands
  pinMode(MAX485, OUTPUT);
}

void loop()
{
  digitalWrite(MAX485, HIGH);
  delay(1);
  Serial3.flush();
 
  byte checkSum = ((byte)CurrCameraAddress + (byte)command1 + (byte)command2 + (byte)PanSpeed + (byte)TiltSpeed)%256;
  Serial3.write((byte)SynchByte); // Synch Byte
  Serial3.write((byte)CurrCameraAddress); //Camera Address
  Serial3.write((byte)command1); 
  Serial3.write((byte)command2);
  Serial3.write((byte)PanSpeed); //Pan Speed
  Serial3.write((byte)TiltSpeed); //Tilt Speed
  Serial3.write((byte)checkSum); //check sum is the sum of bytes (excluding the synchronization byte) modulo 256
   
  //set the MAX485 to STOP send data
  digitalWrite(MAX485, LOW);
}

PaulS

Code: [Select]
  Serial3.flush();
You haven't sent anything to Serial3 yet. Why do you need to wait until all pending output has been sent?

The art of getting good answers lies in asking good questions.

rickpbush

My mistake, I've removed the flush but still no joy

gilhad

#7
Apr 05, 2014, 03:11 pm Last Edit: Apr 05, 2014, 03:14 pm by gilhad Reason: 1
I do not know, but should not you flush just after you send everything, but before you cut it off? Just asking.

Edit: and if leds blinks without camera attached, but not blinks with the camera, are you sure you have enought power for the camera? there is relatively low limit, how much can a pin offer in terms of current.

rickpbush

Oh my gosh I've done it, it was the synch, it needed to be 0xFF, I had it set to 0x00 !!!!

Thanks for your help guys :)

rickpbush

Ok, my next issue. Having completed the code to generate a byte array which represents a Pelco command, I now want to use C# to UDP that byte array to an UNO I have up stairs. So, no probs with the c# code :

Code: [Select]

Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress serverAddr = IPAddress.Parse("192.168.1.111");
IPEndPoint endPoint = new IPEndPoint(serverAddr, 4567);
byte check = (0x02 + 0x00 + 0x00 + 0x00 + 0x00) % 256;
byte[] send_buffer = {0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, check};
sock.SendTo(send_buffer, endPoint);


but I'm having a heck of a time getting it out the other end intact.

My UDP server code ON THE uno looks like this

Code: [Select]

#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008

...


char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
int packetSize = Udp.parsePacket();
  if(packetSize)
  {
    digitalWrite(MAX485, HIGH);
    delay(50);
    while(Udp.available() != 0)
    {
      Serial.print((int)Udp.read());
      Serial.print(",");
      //Serial.write((byte)Udp.read()); THIS WILL PUT THE BYTE ONTO THE PELCO SERIAL WIRES, NOT USED IN    THIS TEST
    }
    Serial.println("");
    delay(50);
    digitalWrite(MAX485, LOW);
}


Ok, so the general idea is that I'm sat in the office and have a live feed from the cam on my PC screen. On the same PC I have a mega with attatched joystick issuing Peloc commands to my c# app via usb. The c# app takes the commands, sends them to the uno running the UDP server whos only job is to grab the from the UDP packet and send the out the serial port to the camera.

To test the c#->udp server connection I have created the c# code above, problem is that when I instruct the uno to bung the packet data onto a serial monitor it is not consistent with what I've sent with my test c# code.

So, c# is udping 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, check, which equates to 255,2,0,0,0,0,0,2, sometimes I see this in the uno serial monitor, sometimes not. the following is a screen grab from the serial monitor :

255,2,255,0,255,0,254,
255,254,0,255,0,255,2,
255,2,0,0,0,0,2,
255,2,0,0,0,0,2,
255,2,0,0,0,0,2,
255,254,0,0,0,0,2,-1,-1,-1,-1,-1,-1,-1,-1 ...........on and on and on and, you get the idea :P

Please could you suggest what ideotic mistake I am making :)

PaulS

Quote
whos only job is to grab the from the UDP packet

Grab the what?

Code: [Select]
int packetSize = Udp.parsePacket();
  if(packetSize)

packetSize isn't a boolean. Don't use it like one.

Code: [Select]
      Serial.print((int)Udp.read());
      Serial.print(",");
      //Serial.write((byte)Udp.read()); THIS WILL PUT THE BYTE ONTO THE PELCO SERIAL WIRES, NOT USED IN    THIS TEST

No, it won't. You've read a char from the packet, and sent it to the serial port;. Reading another byte, and writing it to the serial port will NOT get it to the camera!

Quote
but I'm having a heck of a time getting it out the other end intact.

You should probably start with getting the packet size printing correctly, first.

Code: [Select]
sock.SendTo(send_buffer, endPoint);
How is this supposed to know how big send_buffer is?
The art of getting good answers lies in asking good questions.

rickpbush

Hi, I worked out the issue, the SD card needed to be turned of by setting pin 4 high.

whos only job is to grab the COMMAND PACKET DATA from the UDP packet (sorry, I'm rubbish)

PacketSize is used like that on the Arduino playground ethernet example page, see here http://arduino.cc/en/Reference/EthernetUDPRead. Being as :

ParsePacket (from the EthernetUdp.h file) // Returns the size of the packet in bytes, or 0 if no packets are available

In any case I've taken your advise as it alowed me to screen for only packets of correct size.

I have also amended the packetBuffer var to be of type byte and so that now works, I know I know, it's size is not 7, should I make it so?

Also, on your advice I changed the c# code to this to let it know the size of the data it's sending.

sock.SendTo(send_buffer, 7, SocketFlags.None, endPoint);

Anyway, below(bottom) is my amended code and it works. The problem I have now is my lack of understanding of either UDP in general or specifically the EthernetUDP arduino implementation of it. I know UDP is a post and forget protocol which for my purpose is fine as I'm just generating pelco commands from a joystick and throwing them at the camera controller, it doesn't matter if some are lost along the way. Problem is lag or speed. It is very laggy, move the joystick and maybe sometime next week the camers will respond.

I've tried to think about this, maybe I'm trowing too many commands per second at the UNO, maybe they are overflowing the recieve buffer on the UNO, I don't really want a recieve buffer, I want it real time.

My understanding goes like this:

Man A has lots of balls, Man B has a basket.

A throws balls into Bs basket and B removes them from the basket.

If A throws balls too quickly the basket will overflow before B has a chance to remove them.

What happens to the overflowed balls?

On that basis, should I be using UDP or is there a better way to do what I'm trying to?

I'd like to say I appretiate the help offered and am thankful for the time you guys have spend reading my woes. I am finding the solutions myself alot of the time but it really helps to be mind dumping here and getting feedback, thank you so much. Regards, Rick.

Code: [Select]

#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008

int MAX485 = 3;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //this mac
IPAddress ip(192, 168, 1, 111); //this IP
unsigned int localPort = 4567;      // local port to listen on

// buffers for receiving and sending data
byte packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,

EthernetUDP Udp;

void setup() {
  // start the Ethernet and UDP:
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);
  pinMode(MAX485, OUTPUT);
 
  //turn off SD card
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);
   
  Serial.begin(2400);
}

void loop() {
  // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if(packetSize == 7)
  {   
    // read the packet into packetBufffer
    Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE);
   
    //only for valid Pelco commands, ie sync byte 0xff
    if(packetBuffer[0] == 0xFF)
    {
      digitalWrite(MAX485, HIGH);
      delay(100);
     
      byte checkSum = ((byte)packetBuffer[1] + (byte)packetBuffer[2] + (byte)packetBuffer[3] + (byte)packetBuffer[4] + (byte)packetBuffer[5])%256;
      Serial.write((byte)packetBuffer[0]); // Synch Byte
      Serial.write((byte)packetBuffer[1]); //Camera Address
      Serial.write((byte)packetBuffer[2]); 
      Serial.write((byte)packetBuffer[3]);
      Serial.write((byte)packetBuffer[4]); //Pan Speed
      Serial.write((byte)packetBuffer[5]); //Tilt Speed
      Serial.write((byte)checkSum); //check sum is the sum of bytes (excluding the synchronization byte) modulo 256
   
      Udp.flush();// Finish reading the current packet
     
      delay(100);
      digitalWrite(MAX485, LOW);
   
    }
    else
      Udp.flush();
  }
  else
  {
    Udp.flush();
  }
}


PaulS

Quote
What happens to the overflowed balls?

What happens to a radio stations broadcast after I turn my radio off? It's still happening, but it's accomplishing nothing as far as my radio is concerned. The packets are lost.

Are you using UDP in broadcast mode? In general, broadcast mode is much slower than "Hey, you. Yes, you!" mode.

Code: [Select]
      delay(100);
I guess your need-for-speed isn't all that strong.

Code: [Select]
      delay(100);
I guess your need-for-speed isn't all that strong.

Code: [Select]
      Serial.write((byte)packetBuffer[0]); // Synch Byte
      Serial.write((byte)packetBuffer[1]); //Camera Address
      Serial.write((byte)packetBuffer[2]); 
      Serial.write((byte)packetBuffer[3]);
      Serial.write((byte)packetBuffer[4]); //Pan Speed
      Serial.write((byte)packetBuffer[5]); //Tilt Speed

That's one way.

Code: [Select]
      Serial.write((byte)packetBuffer, 6);
is another.
The art of getting good answers lies in asking good questions.

rickpbush

Thanks mate your awsome, I was looking at that ugly six liner and thinking there must be a way to lay the byte array down in one line :)

I also thought about the delays, I don't want them and try to avoid delay wherever possible but it didnt seem to work without them, I'll try and remove them and see if it still works. Keep you posted :)

rickpbush

Ok, so it turns out that the delays were needed to give the max485 pin time to go high before writing data to the max485. I have now coded it so that the max485 pin is always off except when a packet is recieved, it is then turned high and stays high but turned low after 5 seconds of not receiving packets. This UDP to pelco server is now working lickedy spit :)

Once again, thanks for all your help and pushing me in the right direction, next I have ordered an OLED screen and a wifi module, I plan to make a wifi UDP remote control box to send Pelco over UDP to the UDP/Pelco server, so i have a project box with a hall effect joustick, a wifi module, an OLED screen and some buttons and a rotary encoder. I will design a menu system which will allow me to use the rotary encoder to navigate menu options on the screen to choose a wifi network, save preset positions etc. No doubt I will run into issues so stay alert, I may well beg for your assistance and show my ignorance in a later post :)

Just in case anyone reading this thread is wondering what the heck is this guy tring to achieve with this project, I have draw this pretty picture :) I want to control my camera server using a remote joystick. The camera server can be viewed via a browser in any room in the house with a pc in it, sad I know but it keeps me entertained :)



Thanks again for your help, Rick.

Go Up