Sonntag, 1. Januar 2012

Triangle control Arduino, PC, Camera.


В советские времена нас учили, что кадры решают все. Может быть. Довелось мне поработать и там и тут и могу с уверенностью сказать, что кадры это только ступеньки к успеху. Все решает управление. В этой статье постараюсь рассказать как шаг за шагом  сделать универсальный пульт дистанционного управления для камеры, позволяющий менять настройки камеры. 

Шаг первый - ИК пульт.
 
Вы случайно под новый год не решили поменять телевизор или выбросить старый DVD проигрыватель. Если нет, то жаль, так бы вам очень пригодился дистанционный пульт. У меня таких накопилось штук восемь. Но это не все, чем полезна старая электроника. Вооружившись отверткой можно вытащить ИК приемник.  Приемник обычно размещаются под передней крышкой прибора.

Подойдет любой IR приемник TSOP17, TSOP13, SM3374, TK19 и им подобные. После того, как приемник найден, собираем маленькую схему на Arduino и приступаем к изучению пульта управления.

Пульт желательно использовать не самый новый, но и не самый старый, чтоб было максимум рабочих кодов. Для сканирования нужно загрузить библиотеку IRremote. Создать Sketch для Arduino IR_code_to_232.pde


#include <IRremote.h>

int RECV_PIN = 2;
int STATUS_PIN = 13;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{
Serial.begin(115200);
irrecv.enableIRIn(); // Start the receiver
}

int codeType = -1; // The type of code

void loop() {
if (irrecv.decode(&results)) {
codeType = results.decode_type;
if (codeType != UNKNOWN) {
if (results.value != REPEAT) {
Serial.println(results.value, HEX);
}
}
irrecv.resume();  
}
}
и загрузить код в Arduino. Открыть терминал и, нажимая кнопки на пульте управления, записать код для каждой кнопки. Для ленивых как я. :) открываем командное окно



Start > «cmd», перейти в рабочую директорию, туда же скопировать программу RS232read.7z

> RS232read.exe 7 115200 > rs232.txt.

Где 7 - номер COM порта, 115200 - скорость, rs232.txt файл куда сохраняются принимаемые данные. После этого открыть rs232.txt и скопировать коды в Sketch  должно получиться вот так.

#define button1 0xFF906F
#define button2 0xFFB847
#define button3 0xFFF807
#define button4 0xFFB04F
#define button5 0xFF9867
#define button6 0xFFD827
#define button7 0xFF8877
#define button8 0xFFA857
#define button9 0xFFE817
Теперь можно запрограммировать кнопку «1» на спуск камеры.

IR_Shutter.pde


#include <IRremote.h>

#define button1 0xFF906F
#define button2 0xFFB847
#define button3 0xFFF807
#define button4 0xFFB04F
#define button5 0xFF9867
#define button6 0xFFD827
#define button7 0xFF8877
#define button8 0xFFA857
#define button9 0xFFE817

const int Shot  = 11;
const int Focus  = 12;

int RECV_PIN = 2;
int STATUS_PIN = 13;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{
Serial.begin(115200);
irrecv.enableIRIn(); // Start the receiver
pinMode(STATUS_PIN, OUTPUT);
pinMode(Shot, OUTPUT);
pinMode(Focus, OUTPUT); 
}

int codeType = -1; // The type of code

void loop() {
digitalWrite(STATUS_PIN, LOW );
if (irrecv.decode(&results)) {
codeType = results.decode_type;
if (codeType != UNKNOWN) {
if (results.value != REPEAT) {
if (results.value == button1) {
digitalWrite(Focus, HIGH);
digitalWrite(Shot, HIGH);
delay(1000);                  // waits for a second
digitalWrite(Focus, LOW);
digitalWrite(Shot, LOW); 
}
if (results.value == button2) {
Serial.println("Button 2");
}
if (results.value == button3) {
Serial.println("Button 3");
}
if (results.value == button4) {
Serial.println("Button 4");
}
if (results.value == button5) {
Serial.println("Button 5");
}
if (results.value == button6) {
Serial.println("Button 6");
}
if (results.value == button7) {
Serial.println("Button 7");
}
if (results.value == button8) {
Serial.println("Button 8");
}
if (results.value == button9) {
Serial.println("Shutter");
}
//Serial.println(results.value, HEX);
//irrecv.resume(); // Receive the next value
}
}
digitalWrite(STATUS_PIN, HIGH);  
irrecv.resume();  
}
}

Шаг второй - Windows Image Acquisition (WIA).

Почему WIA? Потому что позволяет управлять камерой, не углубляясь в  Nikon SKD или Canon SDK. Свободно распространяется Microsoft, управлять можно из VBScript. Чтобы лучше понять, что может WIA, запустите пару скриптов из командной строки. Camera_Properties.wsf



' before!!!! run cmd as Administrator and "cscript.exe //H:cscript" 
<job>
<reference object="wia.DeviceManager" />
<object id="DevMan" progid="Wia.DeviceManager" />
<object id="dlg" progid="WIA.CommonDialog" />
<script language="VBScript">

' Copyright 2011 All Rights Reserved
' Use and distribution for non-commercial purposes permitted
' http://karu2003.blogspot.com/
Option Explicit

Dim WiaDev 'As wia.Device
Dim WiaItm 'As wia.Item

Dim dev 'As Device
Dim i 'As Integer
Dim s 'As String
Dim p 'As Property

Set dev = dlg.ShowSelectDevice

For Each p In dev.Properties
s = p.Name '& "(" & p.PropertyID & ")"
'MsgBox s
WScript.echo s
Next

</script>
</job>


получим список всех Properties (свойства) камеры.
Display_Detailed_Property.wsf 


' before!!!! run cmd as Administrator and "cscript.exe //H:cscript" 
<job>
<reference object="wia.DeviceManager" />
<object id="DevMan" progid="Wia.DeviceManager" />
<object id="dlg" progid="WIA.CommonDialog" />
<script language="VBScript">

' Copyright 2011 All Rights Reserved
' Use and distribution for non-commercial purposes permitted
' http://karu2003.blogspot.com/

Option Explicit

Dim WiaDev 'As wia.Device
Dim WiaItm 'As wia.Item

Dim dev 'As Device
Dim p 'As Property
Dim s 'As String
Dim i 'As Integer

Set dev = dlg.ShowSelectDevice

For Each p In dev.Properties
s = p.Name & "(" & p.PropertyID & ") = "
If p.IsVector Then
s = s & "[vector of data]"
Else
s = s & p.Value
If p.SubType <> UnspecifiedSubType Then       
If p.Value <> p.SubTypeDefault Then
s = s & "(Default = " & p.SubTypeDefault & ")"
End If
End If
End If

If p.IsReadOnly then
s= s & " [READ ONLY]"
else
Select Case p.SubType
Case FlagSubType
s = s & " [ valid flags include:"
For i = 1 To p.SubTypeValues.Count
s = s & p.SubTypeValues(i)
If i <> p.SubTypeValues.Count Then
s = s & ", "
End If
Next
s = s & " ]"
Case ListSubType
s = s & " [ valid values include:"
For i = 1 To p.SubTypeValues.Count
s = s & p.SubTypeValues(i)
If i <> p.SubTypeValues.Count Then
s = s & ", "
End If
Next
s = s & " ]"
Case RangeSubType
s = s & " [ valid values in the range from " & _
p.SubTypeMin & " to " & p.SubTypeMax & _
" in increments of " & p.SubTypeStep & " ]"
Case Else 'UnspecifiedSubType
End Select
End If

'MsgBox s
WScript.echo s
Next

</script>
</job>


получим список всех Properties и все возможные value (значение). Обратите внимание на два свойства "F Number" - диафрагма и "Exposure Time" — выдержка, значения этих свойств отличаются от привычных фотографу. Чтобы установить выдержку в 1 секунду нужно отправить в камеру значение 10000, для диафрагмы F8 нужно отправить значение 800. Диафрагму нужно умножить на 100. С выдержкой искомое значение получается несколько сложнее. Чтобы установить выдержку 1/160 нужно 10000/160 и отбросить дробную часть. Получим 62. 

(1,"6400");
(2,"4000");
(3,"3200");
(4,"2500");
(5,"2000");
(6,"1600");
(8,"1250");
(10,"1000");
(12,"800");
(13,"750");
(15,"640");
(20,"500");
(25,"400");
(28,"350");
(31,"320");
(40,"250");
(50,"200");
(55,"180");
(62,"160");
(80,"125");
(100,"100");
(111,"90");
(125,"80");
(166,"60");
(200,"50");
(222,"45");
(250,"40");
(333,"30");
(400,"25");
(500,"20");
(666,"15");
(769,"13");
(1000,"10");
(1250,"8");
(1666,"6");
(2000,"5");
(2500,"4");
(3333,"3");
(4000,"2.5");
(5000,"2");
(6250,"1.6");
(6666,"1.5");
(7692,"1.3");
(10000,"1\"");
(13000,"1.3\"");
(15000,"1.5\"");
(16000,"1.6\"");
(20000,"2\"");
(25000,"2.5\"");
(30000,"3\"");
(40000,"4\"");
(50000,"5\"");
(60000,"6\"");
(80000,"8\"");
(100000,"10\"");
(130000,"13\"");
(150000,"15\"");
(200000,"20\"");
(250000,"25\"");
(300000,"30\"");
Для примера из командной строки запустите скрипт 160f8.wsf


' before!!!! run cmd as Administrator and "cscript.exe //H:cscript" 
<job>
<reference object="wia.DeviceManager" />
<object id="DevMan" progid="Wia.DeviceManager" />
<object id="dlg" progid="WIA.CommonDialog" />
<script language="VBScript">

' Copyright 2011 All Rights Reserved
' Use and distribution for non-commercial purposes permitted
' http://karu2003.blogspot.com/

Option Explicit

Dim WiaDev 'As wia.Device

Set WiaDev = dlg.ShowSelectDevice
WiaDev.properties("Exposure Time").value = 62 
WiaDev.properties("F Number").value = 800 

</script>
</job>


и камера поменяет экспозицию на 1/160 F8. Очень важный момент: количество свойств и их значения могут меняться от камеры к камере и они могут быть только для чтения. Так что, перед тем, как изменять какие-либо свойства, убедитесь, что они доступны для записи.

Шаг три — Arduino > WIA > Camera.

Первым делом организуем срабатывание затвора по пути Arduino > PC > Camera. Для этого загрузим Sketch IR_Shutter.pde где на код кнопки «1» камера срабатывать на прямую от Arduino, а на код кнопки «9» Arduino будет отсылать в компьютер «Shutter». Со стороны компьютера из командной строки запустим скрипт  Shutter.wsf


' before!!!! run cmd as Administrator and "cscript.exe //H:cscript" 
<job>
<reference object="wia.DeviceManager" />
<object id="DevMan" progid="Wia.DeviceManager" />
<object id="dlg" progid="WIA.CommonDialog" />
<script language="VBScript">

' Copyright 2011 All Rights Reserved
' Use and distribution for non-commercial purposes permitted
' http://karu2003.blogspot.com/

Option Explicit

Dim WiaDev 'As wia.Device
Const ForReading = 1
dim fso
dim com
dim s
Dim strPortName' As String

strPortName = GetComPort

Set fso = CreateObject("Scripting.FileSystemObject")
Set com = fso.OpenTextFile(strPortName & ":115200,N,8,1", ForReading)

Set WiaDev = dlg.ShowSelectDevice

WScript.echo "Shutter Camera Control with Arduino Script 1.0 "
WScript.echo "Copyright 2011 All Rights Reserved"
WScript.echo "Use and distribution for non-commercial purposes permitted"
WScript.echo "http://karu2003.blogspot.com/"
Wscript.echo "Controlling  " & WiaDev.Properties("Description").Value

' set raw
WiaDev.Properties("Format").Value = "{B96B3CA9-0728-11D3-9D7B-0000F81EF32E}"
Wscript.echo "Shooting in RAW Format"
' set jpg
'WiaDev.Properties("Format").Value = "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}"
'Wscript.echo "Shooting in JPG Format"

WScript.echo "Start to read data from " & strPortName
WScript.echo "Press Ctrl-Break to exit"

Do While 1 
Do While com.AtEndOfStream <> True
WScript.Sleep(100)
s = com.ReadLine
'WScript.echo (s)
If s = "Shutter" Then
WiaDev.ExecuteCommand (wiaCommandTakePicture)
WScript.echo (s)
End If
Loop
Loop

com.Close()

'************************************************************************
Function GetComPort()
Dim strComputer
Dim objWMIService
Dim colItems
Dim objItem
Dim objRgx 'As RegExp
Dim objRegMatches 'As MatchCollection
Dim strDevName

GetComPort = ""

strComputer = "."
Set objWMIService = GetObject( _
"winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery _
("Select * from Win32_PnPEntity")
For Each objItem In colItems
If ("FTSER2K" = objItem.Service) And ("FTDI" = objItem.Manufacturer) Then

set objRgx = CreateObject("vbScript.RegExp")

strDevName = objItem.Name
objRgx.Pattern = "COM[0-9]+"
Set objRegMatches = objRgx.Execute(strDevName)
If objRegMatches.Count = 1 Then
GetComPort = objRegMatches.Item(0).Value
Else
End If
End If
Next
End Function
' ********************************************************************

</script>
</job>
Теперь у нас есть две возможности электорального спуска затвора и все с одного пульта управления. Немного изменив код, можно добавить сохранение снятого кадра прямо на компьютер. Но об этом в другой раз. Информацию, как это сделать, можно найти на DIYPhotoBits.com. Теперь осталось изменить пару строчек исходного кода в скрипте и Arduino и получится универсальный командный пульт. Но перед этим стоит разобраться, какие свойства камеры можно изменять. Для своих фото экспериментов - макро, HDRi, предметки мне хватает шести основных:

("White Balance").value
("F Number").value
("Exposure Time").value
("Exposure Mode").value
("Exposure Index").value
("Exposure Compensation").value

IR_WIAComander.wsf


' before!!!! run cmd as Administrator and "cscript.exe //H:cscript" 
<job>
<reference object="wia.DeviceManager" />
<object id="DevMan" progid="Wia.DeviceManager" />
<object id="dlg" progid="WIA.CommonDialog" />
<script language="VBScript">

' Copyright 2011 All Rights Reserved
' Use and distribution for non-commercial purposes permitted
' http://karu2003.blogspot.com/

Option Explicit

Dim WiaDev 'As wia.Device
Const ForReading = 1
dim fso
dim com
dim s
Dim strPortName' As String
Dim k

strPortName = GetComPort

Set fso = CreateObject("Scripting.FileSystemObject")
Set com = fso.OpenTextFile(strPortName & ":115200,N,8,1", ForReading)

Set WiaDev = dlg.ShowSelectDevice

WScript.echo "Shutter Camera Control with Arduino Script 1.0 "
WScript.echo "Copyright 2011 All Rights Reserved"
WScript.echo "Use and distribution for non-commercial purposes permitted"
WScript.echo "http://karu2003.blogspot.com/"
Wscript.echo "Controlling  " & WiaDev.Properties("Description").Value

' set raw
WiaDev.Properties("Format").Value = "{B96B3CA9-0728-11D3-9D7B-0000F81EF32E}"
Wscript.echo "Shooting in RAW Format"
' set jpg
'WiaDev.Properties("Format").Value = "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}"
'Wscript.echo "Shooting in JPG Format"

WScript.echo "Start to read data from " & strPortName
WScript.echo "Press Ctrl-Break to exit"

Do While 1 
Do While com.AtEndOfStream <> True
s = com.ReadLine
k = Len(s)
'WScript.echo (s)
If  s = "Shutter" Then
WiaDev.ExecuteCommand (wiaCommandTakePicture)
WScript.echo (s)
End If

If Left(s,2) = "WB" Then
WiaDev.properties("White Balance").value = CDbl(Mid(s,3,k-2))
End If

If Left(s,2) = "FN" Then
WiaDev.properties("F Number").value =  CDbl(Mid(s,3,k-2))
End If

If Left(s,2) = "ET" Then
WiaDev.properties("Exposure Time").value = CDbl(Mid(s,3,k-2))
End If

If Left(s,2) = "EM" Then
WiaDev.properties("Exposure Mode").value = CDbl(Mid(s,3,k-2))
End If

If Left(s,2) = "EI" Then
WiaDev.properties("Exposure Index").value = CDbl(Mid(s,3,k-2))
End If

If Left(s,2) = "EC" Then
WiaDev.properties("Exposure Compensation").value = CDbl(Mid(s,3,k-2))
End If    

WScript.Sleep(100)
Loop
Loop

com.Close()

'************************************************************************
Function GetComPort()
Dim strComputer
Dim objWMIService
Dim colItems
Dim objItem
Dim objRgx 'As RegExp
Dim objRegMatches 'As MatchCollection
Dim strDevName


GetComPort = ""

strComputer = "."
Set objWMIService = GetObject( _
"winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery _
("Select * from Win32_PnPEntity")
For Each objItem In colItems
If ("FTSER2K" = objItem.Service) And ("FTDI" = objItem.Manufacturer) Then

set objRgx = CreateObject("vbScript.RegExp")

strDevName = objItem.Name
objRgx.Pattern = "COM[0-9]+"
Set objRegMatches = objRgx.Execute(strDevName)
If objRegMatches.Count = 1 Then
GetComPort = objRegMatches.Item(0).Value
Else
End If
End If
Next
End Function
' ********************************************************************
</script>
</job>
IR_WIAComander.pde
#include <IRremote.h>
//WIA Format
//("White Balance").value =
//("F Number").value = 
//("Exposure Time").value =
//("Exposure Mode").value =
//("Exposure Index").value =
//("Exposure Compensation").value =

const char* WB = "WB"; //"White Balance";
const char* FN = "FN"; //"F Number";
const char* ET = "ET"; //"Exposure Time";
const char* EM = "EM"; //"Exposure Mode";
const char* EI = "EI"; //"Exposure Index";
const char* EC = "EC"; //"Exposure Compensation";

#define button1 0xFF906F
#define button2 0xFFB847
#define button3 0xFFF807
#define button4 0xFFB04F
#define button5 0xFF9867
#define button6 0xFFD827
#define button7 0xFF8877
#define button8 0xFFA857
#define button9 0xFFE817

const int Shot  = 11;
const int Focus  = 12;

int RECV_PIN = 2;
int STATUS_PIN = 13;
String sProperties;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{
Serial.begin(115200);
irrecv.enableIRIn(); // Start the receiver
pinMode(STATUS_PIN, OUTPUT);
pinMode(Shot, OUTPUT);
pinMode(Focus, OUTPUT); 
}

int codeType = -1; // The type of code

void loop() {
digitalWrite(STATUS_PIN, LOW );
if (irrecv.decode(&results)) {
codeType = results.decode_type;
if (codeType != UNKNOWN) {
if (results.value != REPEAT) {
if (results.value == button1) {
digitalWrite(Focus, HIGH);
digitalWrite(Shot, HIGH);
delay(1000);                  // waits for a second
digitalWrite(Focus, LOW);
digitalWrite(Shot, LOW); 
}
if (results.value == button2) {
sProperties = FN;
sProperties = sProperties + "800";
Serial.println(sProperties);
delay(100);
sProperties = ET;
sProperties = sProperties + "62";
Serial.println(sProperties);

}
if (results.value == button3) {
sProperties = FN;
sProperties = sProperties + "400";
Serial.println(sProperties);
delay(100);
sProperties = ET;
sProperties = sProperties + "166";
Serial.println(sProperties);
}
if (results.value == button4) {
Serial.println("Button 4");
}
if (results.value == button5) {
Serial.println("Button 5");
}
if (results.value == button6) {
Serial.println("Button 6");
}
if (results.value == button7) {
Serial.println("Button 7");
}
if (results.value == button8) {
Serial.println("Button 8");
}
if (results.value == button9) {
Serial.println("Shutter");
}
//Serial.println(results.value, HEX);
//irrecv.resume(); // Receive the next value
}
}
digitalWrite(STATUS_PIN, HIGH);  
irrecv.resume();  
}
}
Вот и все, что нужно. Дальше каждый расширяет код под Arduino под свои потребности.

Чтобы не работать постоянно, из командной строки написал программу  IR_WIAComander.7z . По логике работы программа ничем не отличается от скрипта , добавлено несколько проверок на подключение камеры и Arduino.

Но, чтобы программа работала, в системе нужно зарегистрировать библиотеку работы с  COM Port.   

1. загрузить MSCOMM32.ocx
2. copy MSCOMM32.ocx в C:\Windows\System32\
3. открыть cmd как администратор
4. > regsvr32 C:\Windows\System32\MSCOMM32.ocx
5. загрузить vbctrls.zip
6. добавить лицензию в регистер.

Надеюсь, что описал не очень запутано.
Удачи в эксперементах.

© Andrew Buckin.

Shutterstock Dreamstime

Fotostream http://www.flickr.com

TODO:
Multiple FTDI Chip.
Detect Arduino Bootloader
Send Properties to Arduino.