|
1 |
| -### cpx-ir-shutter-remote v1.0 |
| 1 | +### cpx-ir-shutter-remote v1.1 |
2 | 2 | ### Circuit Playground Express (CPX) shutter remote using infrared for Sony Cameras
|
3 |
| -### TODO - describe in more detail |
4 | 3 |
|
5 | 4 | ### copy this file to CPX as code.py
|
6 | 5 |
|
|
26 | 25 | ### OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
27 | 26 | ### SOFTWARE.
|
28 | 27 |
|
| 28 | + |
| 29 | +### This uses the Circuit Playground Express with its onboard infrared LED |
| 30 | +### to send the shutter release codes |
| 31 | + |
| 32 | +### If the switch is to the left then the shutter fires when the left button is pressed |
| 33 | +### together with a brief flash of Neopixels. |
| 34 | +### The right button can be used to toggle the use of NeoPixels. |
| 35 | + |
| 36 | +### If the switch is to the right then the CPX functions as an intervalometer |
| 37 | +### with the shutter being fired on a timer after each interval |
| 38 | +### The default interval is thirty seconds and this can be changed |
| 39 | +### by reduced with the left button and increased with the right button |
| 40 | + |
29 | 41 | import time
|
30 | 42 |
|
31 | 43 | import pulseio
|
|
34 | 46 | from adafruit_circuitplayground import cp
|
35 | 47 |
|
36 | 48 |
|
37 |
| -debug = 1 |
38 |
| - |
39 |
| -def d_print(level, *args, **kwargs): |
40 |
| - """A simple conditional print for debugging based on global debug level.""" |
41 |
| - if not isinstance(level, int): |
42 |
| - print(level, *args, **kwargs) |
43 |
| - elif debug >= level: |
44 |
| - print(*args, **kwargs) |
45 |
| - |
46 |
| - |
47 |
| -### 40kHz modulation (for Sony) |
48 |
| -### with 20% duty cycle (13107 out of 65535) |
| 49 | +### 40kHz modulation (for Sony) with 20% duty cycle |
49 | 50 | CARRIER_IRFREQ_SONY = 40 * 1000
|
50 | 51 | ir_carrier_pwm = pulseio.PWMOut(board.IR_TX,
|
51 | 52 | frequency=CARRIER_IRFREQ_SONY,
|
52 |
| - duty_cycle=13107) |
| 53 | + duty_cycle=round(20 / 100 * 65535)) |
53 | 54 | ir_pulseout = pulseio.PulseOut(ir_carrier_pwm)
|
54 | 55 |
|
55 |
| - |
56 | 56 | ### Sony timing values (in us) based on the ones in
|
57 | 57 | ### https://github.com/z3t0/Arduino-IRremote/blob/master/src/ir_Sony.cpp
|
58 | 58 | ### Disabling the addition of trail value is required to make this work
|
59 |
| -### trail=0 does not work |
| 59 | +### trail=0 did not work |
60 | 60 | ir_encoder = adafruit_irremote.GenericTransmit(header=[2400, 600],
|
61 | 61 | one= [1200, 600],
|
62 | 62 | zero= [600, 600],
|
63 | 63 | trail=None)
|
64 | 64 |
|
65 |
| -SHUTTER_CMD_COLOUR = (16, 0, 0) |
| 65 | +def fire_shutter(): |
| 66 | + """Send infrared code to fire the shutter. |
| 67 | + This is a code used by Sony cameras.""" |
| 68 | + ir_encoder.transmit(ir_pulseout, [0xB4, 0xB8, 0xF0], |
| 69 | + repeat=2, delay=0.005, nbits=20) |
| 70 | + |
| 71 | + |
| 72 | +SHUTTER_CMD_COLOUR = (8, 0, 0) |
| 73 | +IMPENDING_COLOUR = (7, 4, 0) |
66 | 74 | BLACK = (0, 0, 0)
|
67 | 75 |
|
68 | 76 | S_TO_NS = 1000 * 1000 * 1000
|
69 | 77 | ADJ_NS = 20 * 1000 * 1000
|
| 78 | +IMPENDING_NS = 2 * S_TO_NS |
70 | 79 | intervals = [5, 10, 15, 20, 25, 30, 60,
|
71 | 80 | 120, 180, 240, 300, 600, 1800, 3600]
|
72 | 81 | interval_idx = intervals.index(30) ### default is 30 seconds
|
73 | 82 | intervalometer = False
|
74 | 83 | last_cmd_ns = None
|
75 | 84 | first_cmd_ns = None
|
| 85 | +pixel_indication = True |
| 86 | +impending = False |
76 | 87 |
|
77 |
| -###encoder.transmit(pulseout, [0x12, 0xB8, 0xF0], nbits=20) ### start video |
78 | 88 | while True:
|
79 |
| - if cp.switch: ### switch to left |
| 89 | + ### CPX switch to left |
| 90 | + if cp.switch: |
80 | 91 | intervalometer = False
|
81 | 92 | if cp.button_a: ### left button_a
|
82 |
| - cp.pixels.fill(SHUTTER_CMD_COLOUR) |
| 93 | + if pixel_indication: |
| 94 | + cp.pixels.fill(SHUTTER_CMD_COLOUR) |
83 | 95 | last_cmd_ns = time.monotonic_ns()
|
84 |
| - ir_encoder.transmit(ir_pulseout, [0xB4, 0xB8, 0xF0], |
85 |
| - repeat=3, delay=0.005, nbits=20) ### shutter |
| 96 | + fire_shutter() |
86 | 97 | if first_cmd_ns is None:
|
87 | 98 | first_cmd_ns = last_cmd_ns
|
88 | 99 | print("Manual", "shutter release at", (last_cmd_ns - first_cmd_ns) / S_TO_NS)
|
89 |
| - cp.pixels.fill(BLACK) |
| 100 | + if pixel_indication: |
| 101 | + cp.pixels.fill(BLACK) |
90 | 102 | while cp.button_a:
|
91 | 103 | pass ### wait for button release
|
92 |
| - |
93 |
| - else: ### switch to right |
| 104 | + |
| 105 | + elif cp.button_b: |
| 106 | + pixel_indication = not pixel_indication |
| 107 | + while cp.button_b: |
| 108 | + pass ### wait for button release |
| 109 | + |
| 110 | + ### CPX switch to right |
| 111 | + else: |
| 112 | + if not intervalometer: |
| 113 | + last_cmd_ns = time.monotonic_ns() |
| 114 | + intervalometer = True |
| 115 | + |
94 | 116 | if cp.button_a and interval_idx > 0:
|
95 | 117 | interval_idx -= 1
|
96 | 118 | while cp.button_a:
|
97 | 119 | pass ### wait for button release
|
| 120 | + |
98 | 121 | elif cp.button_b and interval_idx < len(intervals) - 1:
|
99 | 122 | interval_idx += 1
|
100 | 123 | while cp.button_b:
|
101 | 124 | pass ### wait for button release
|
102 |
| - |
| 125 | + |
| 126 | + ### If enough time has elapsed fire the shutter |
| 127 | + ### or show the impending colour on NeoPixels |
103 | 128 | now_ns = time.monotonic_ns()
|
104 |
| - if (not intervalometer |
105 |
| - or now_ns - last_cmd_ns >= intervals[interval_idx] * S_TO_NS - ADJ_NS): |
106 |
| - intervalometer = True |
107 |
| - |
108 |
| - cp.pixels.fill(SHUTTER_CMD_COLOUR) |
| 129 | + interval_ns = intervals[interval_idx] * S_TO_NS |
| 130 | + if now_ns - last_cmd_ns >= interval_ns - ADJ_NS: |
| 131 | + if pixel_indication: |
| 132 | + cp.pixels.fill(SHUTTER_CMD_COLOUR) |
109 | 133 | last_cmd_ns = time.monotonic_ns()
|
110 |
| - ir_encoder.transmit(ir_pulseout, [0xB4, 0xB8, 0xF0], |
111 |
| - repeat=3, delay=0.005, nbits=20) ### shutter |
| 134 | + fire_shutter() |
112 | 135 | if first_cmd_ns is None:
|
113 | 136 | first_cmd_ns = last_cmd_ns
|
114 | 137 | print("Timer", "shutter release at", (last_cmd_ns - first_cmd_ns) / S_TO_NS)
|
| 138 | + if pixel_indication: |
| 139 | + cp.pixels.fill(BLACK) |
| 140 | + impending = False |
| 141 | + |
| 142 | + elif (pixel_indication |
| 143 | + and not impending |
| 144 | + and now_ns - last_cmd_ns >= interval_ns - IMPENDING_NS): |
| 145 | + cp.pixels.fill(IMPENDING_COLOUR) |
| 146 | + time.sleep(0.1) |
115 | 147 | cp.pixels.fill(BLACK)
|
| 148 | + impending = True |
0 commit comments