Wednesday, 29 August 2018

How to make your own Olympus RM-1 compatible remote

Some history


Back in 2006, after a lot of research and experimenting I managed to crack the way Olympus controls the E-500 using the RM-1 IR remote control. This was my first digital camera related work and did it for fun. Recently I received some questions about it, so since it seems that the idea still interests some people I decided to post this article again. Olympus no longer makes any DSLR and I have no idea if this works with the more modern mirrorless cameras, so this is history. So whatever you make of this is up to you. I will not be able to provide any support, other than I know that the code worked very reliably and well with the remotes I made.

About the IR remote


The remote has five buttons but the picture shows a three button version. I managed to crack the function of all five. I don’t think any deep explanation is necessary, any person with a basic knowledge of microcontroller programming can implement the code in any type of microcontroller. The code is primarily written for the 12F629 PIC made by Microchip (which I believe is also out of production now) but it is easy to translate to other microcontrollers if you have enough programming knowledge and can read and understand assembly language.



Technical functional description


Modulation is 40 kHz symmetrical pulse modulation.
Each command starts with 3.8 ms 40kHz pulse train followed by 4ms Low state on GP5 and finished off with 550us 40kHz pulse train.

1 = 1500us GP5 Low state followed by 500us 40kHz pulse train.
0 = 500us GP5 Low state followed by 500us 40kHz pulse train.

After each button press there is a 500ms button repeat delay.

The Olympus button control codes are:

Fire button: 0110 0001 1101 1100 1000 0000 0111 1111
W button:    0110 0001 1101 1100 0100 0000 1011 1111
T button:    0110 0001 1101 1100 1100 0000 0011 1111
- button:    0110 0001 1101 1100 0010 0000 1101 1111
+ button:    0110 0001 1101 1100 1010 0000 0101 1111


The hardware


The hardware is very simple, it contains only of three resistors, one transistor driving the IR LED and a Microchip 12F629 PIC microcontroller and five buttons. The source code below is a fully working code example for the 12F629.

Click on the image to the right to see the schematics.





The picture shows a three button pocket version. With 4.5 V (3x1.5 hearing aid batteries) the range is about 12-15 meters outdoors. My remote has been working very well since May 2006, used it for as long as I used Olympus cameras.




 

 

The Source code


The source code is also very simple. It is written in assembly language, so just copy the code below and paste it into an assembler and compile it, or modify if you wish to modify it to your purpose.

;----------------------------------------------------------------
;
;    Emulation of Olympus RM-1 IR remote control using PIC 12F629.
;
;    The PIC GPIO 5 controls an IR-LED via one BC557 and a 2.7Ohm
;    resistor in series.
;
;    Olympus control codes are:
;

;    Button        GPIO    Code
;
;    Fire          0        0110 0001 1101 1100 1000 0000 0111 1111
;    W             1        0110 0001 1101 1100 0100 0000 1011 1111
;    T             2        0110 0001 1101 1100 1100 0000 0011 1111
;    -             3        0110 0001 1101 1100 0010 0000 1101 1111
;    +             4        0110 0001 1101 1100 1010 0000 0101 1111
;
;    Modulation is 40 kHz symetrical pulse modulation.
;
;    Each command starts with 3.8 ms 40kHz pulse train followed by
;    4ms Low state on GP5 and finished off with 550us 40kHz pulse train.
;
;    1 = 1500us GP5 Low state followed by 500us 40kHz pulse train.
;    0 = 500us GP5 Low state followed by 500us 40kHz pulse train.
;
;    After each button press there is a 500ms button repeat delay.
;
;
    title        "Olyremote-1"

    include     <p12f629.inc>
    __config    _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT & _BODEN_OFF

    errorlevel    -302    ; No bank selection messages

;----------------------------------------------------------------
;
;            REGISTER DEFINITION
;
;----------------------------------------------------------------

DELAY_H        equ    0x20
DELAY_L        equ    0x21
IR_LENGTH    equ    0x22

;----------------------------------------------------------------
;
;            CODE
;
;----------------------------------------------------------------

    org     0x0000            ; RESET VECTOR
               
;----------------------------------------------------------------
;
;    Initialize PIC
;
initialize_pic

    bsf        STATUS,RP0        ; Sel Bank 1
    call    0x3ff            ; Get OSCAL value
    movwf    OSCCAL            ; write to OSCCAL register

    bcf        OPTION_REG,7    ; Enable weak pullups
    movlw    b'00010111'        ; on GPIO 0-4
    movwf    WPU

    movlw    b'00001000'        ; Enable port change interrupt
    movwf    INTCON

    bcf     STATUS,RP0        ; Select bank 0
    clrf    GPIO            ; Init GPIO
    movlw    0x07            ; set GP2 to digital
    movwf    CMCON
   
    bsf        STATUS,RP0
    movlw    b'00011111'           
    movwf    TRISIO            ; Set I/O 5 as outputs

    movlw    0x1f            ; Enable port change IRQ on GPIO 0-4
    movwf    IOC

    bcf     STATUS,RP0        ; Select bank 0
    bcf        GPIO,5            ; Set GPIO 5 Low

;----------------------------------------------------------------
;                                    
led_main_loop

    bcf        INTCON,GPIF        ; Clear interrupt flag
    bcf        GPIO,5            ; Set GPIO 5 Low

    sleep                    ; and go to sleep to save power
;
;    Wake up from sleep
;    Check which button generated wake-up
;
Test_W
    MOVF    GPIO,W            ; Read GPIO and test for W button
    ANDLW    b'00000001'        ; Mask for the W switch
    BTFSS    STATUS,Z        ; Check ZERO first
    goto    Test_F
    call    Exec_W
    goto    led_main_loop
   
Test_F
    MOVF    GPIO,W            ; Read GPIO and test for Fire button
    ANDLW    b'00000010'        ; Mask for the Fire switch
    BTFSS    STATUS,Z        ; Check ZERO
    goto    Test_T            ; Not Z
    call    Fire
    goto    led_main_loop

Test_T
    MOVF    GPIO,W            ; Read GPIO and test for Fire button
    ANDLW    b'00000100'        ; Mask for the Fire switch
    BTFSS    STATUS,Z        ; Check ZERO
    goto    Test_M            ; Not Z
    call    Exec_T
    goto    led_main_loop

Test_M
    MOVF    GPIO,W            ; Read GPIO and test for Fire button
    ANDLW    b'00001000'        ; Mask for the Fire switch
    BTFSS    STATUS,Z        ; Check ZERO
    goto    Test_P            ; Not Z
    call    Exec_M
    goto    led_main_loop

Test_P
    MOVF    GPIO,W            ; Read GPIO and test for Fire button
    ANDLW    b'00010000'        ; Mask for the Fire switch
    BTFSS    STATUS,Z        ; Check ZERO
    goto    led_main_loop    ; Not Z
    call    Exec_P
    goto    led_main_loop

;----------------------------------------------------------------
;
;        SUBROUTINES
;
;----------------------------------------------------------------

;----------------------------------------------------------------
;
;    The FIRE button was pressed, execute control sequence
;
;    Fire = 0110 0001 1101 1100 1000 0000 0111 1111
;
Fire
    call    Send_Header

    call    Send_One
    call    Send_Null
    call    Send_Null
    call    Send_Null

    call    Send_Null
    call    Send_Null
    call    Send_Null
    call    Send_Null

    call    Send_Null
    call    Send_One
    call    Send_One
    call    Send_One

    call    Send_One
    call    Send_One
    call    Send_One
    call    Send_One

    movlw    .250
    call    DELAY_X_msec        ; 250ms delay before return
    movlw    .250
    call    DELAY_X_msec        ; 250ms delay before return
    return

;----------------------------------------------------------------
;
;    The W button was pressed, execute control sequence
;
;    W = 0110 0001 1101 1100 0100 0000 1011 1111
;
Exec_W
    call    Send_Header

    call    Send_Null
    call    Send_One
    call    Send_Null
    call    Send_Null

    call    Send_Null
    call    Send_Null
    call    Send_Null
    call    Send_Null

    call    Send_One
    call    Send_Null
    call    Send_One
    call    Send_One

    call    Send_One
    call    Send_One
    call    Send_One
    call    Send_One

    movlw    .250
    call    DELAY_X_msec        ; 250ms delay before return
    movlw    .250
    call    DELAY_X_msec        ; 250ms delay before return
    return

;----------------------------------------------------------------
;
;    The T button was pressed, execute control sequence
;
;    T = 0110 0001 1101 1100 1100 0000 0011 1111
;
Exec_T
    call    Send_Header

    call    Send_One
    call    Send_One
    call    Send_Null
    call    Send_Null

    call    Send_Null
    call    Send_Null
    call    Send_Null
    call    Send_Null

    call    Send_Null
    call    Send_Null
    call    Send_One
    call    Send_One

    call    Send_One
    call    Send_One
    call    Send_One
    call    Send_One

    movlw    .250
    call    DELAY_X_msec        ; 250ms delay before return
    movlw    .250
    call    DELAY_X_msec        ; 250ms delay before return
    return

;----------------------------------------------------------------
;
;    The - button was pressed, execute control sequence
;
;    - = 0110 0001 1101 1100 0010 0000 1101 1111
;
Exec_M
    call    Send_Header

    call    Send_Null
    call    Send_Null
    call    Send_One
    call    Send_Null

    call    Send_Null
    call    Send_Null
    call    Send_Null
    call    Send_Null

    call    Send_One
    call    Send_One
    call    Send_Null
    call    Send_One

    call    Send_One
    call    Send_One
    call    Send_One
    call    Send_One

    movlw    .250
    call    DELAY_X_msec        ; 250ms delay before return
    movlw    .250
    call    DELAY_X_msec        ; 250ms delay before return
    return

;----------------------------------------------------------------
;
;    The + button was pressed, execute control sequence
;
;    + = 0110 0001 1101 1100 1010 0000 0101 1111
;
Exec_P
    call    Send_Header

    call    Send_One
    call    Send_Null
    call    Send_One
    call    Send_Null

    call    Send_Null
    call    Send_Null
    call    Send_Null
    call    Send_Null

    call    Send_Null
    call    Send_One
    call    Send_Null
    call    Send_One

    call    Send_One
    call    Send_One
    call    Send_One
    call    Send_One

    movlw    .250
    call    DELAY_X_msec        ; 250ms delay before return
    movlw    .250
    call    DELAY_X_msec        ; 250ms delay before return
    return

;----------------------------------------------------------------
;
;    Send_Header:    Remote codes must starts with this sequence.
;
Send_Header
    movlw    .120                ; Pulse 8ms
    call    Send_IR_Pulse
    movlw    .200
    call    Send_IR_Pulse
    movlw    .4                    ; Space 4ms
    call    DELAY_X_msec
    movlw    .22                    ; Pulse 550us
    call    Send_IR_Pulse

    call    Send_Null
    call    Send_One
    call    Send_One
    call    Send_Null

    call    Send_Null
    call    Send_Null
    call    Send_Null
    call    Send_One

    call    Send_One
    call    Send_One
    call    Send_Null
    call    Send_One

    call    Send_One
    call    Send_One
    call    Send_Null
    call    Send_Null

    return

;----------------------------------------------------------------
;
;    Send_One:   Sends 1677us low signal followed by pulse
;                modulated by 40kHz (25us) symetrical pulses sent
;                for 559us. Corresponds to Olympus IR code "1"
Send_One
    call    Delay_1500us        ; Space 1677us
    movlw    .22
    call    Send_IR_Pulse
    return

;----------------------------------------------------------------
;
;    Send_Null:    Sends 559us low signal followed by pulse
;                modulated by 40kHz (25us) symetrical pulses sent
;                for 559us. Corresponds Olympus IR code "0"
Send_Null
    call    Delay_500us            ; Space 559us
    movlw    .22                    ; Pulse
    call    Send_IR_Pulse
    return

;----------------------------------------------------------------
;
;    Send_IR_Pulse will send a 50% duty cycle pulse until
;    IR_LENGTH is zero. Puls sent on GPIO 5
;
Send_IR_Pulse
    movwf    IR_LENGTH    ; Save number of IR cycles
IR_Pulse                ; loop
    movlw    .3            ; set this to 3 to get 40kHz modulation
    movwf    DELAY_L
    bsf        GPIO,5        ; Set GPIO 5 High
High_20
    decfsz    DELAY_L,F    ; This gives about 12 us High
    goto    High_20
    nop                    ; set this two nops to get 40kHz modulation
    nop
    movlw    .2            ; set this to 2 to get 40kHz modulation
    movwf    DELAY_L
    bcf        GPIO,5        ; Set GPIO 5 Low
Low_20
    decfsz    DELAY_L,F    ; This gives about 12 us Low
    goto    Low_20
    nop
    decfsz    IR_LENGTH,F    ; Decrement IR cycle counter
    goto    IR_Pulse    ; and loop if not zero
    nop
    return

;----------------------------------------------------------------
;
;     Delay routines for correct output pulse width
;
;    This delay is used for output pulse width control
;
DELAY_X_msec                ; Call here with W = X msec delay
    MOVWF    DELAY_H

DELAY_1msec
    MOVLW    .250            ; 1 msec delay loop counter
    MOVWF    DELAY_L
   
DELAY_1ms                    ; 1 msec delay loop
    NOP
    DECFSZ    DELAY_L, F
    GOTO    DELAY_1ms

    DECFSZ    DELAY_H, F        ; Loop counter 1 msec delay loop
    GOTO    DELAY_1msec        ; until counter is zero
    RETLW    0

;----------------------------------------------------------------
;
Delay_1500us

    call    Delay_500us
    call    Delay_500us

;----------------------------------------------------------------
;
Delay_500us
    movlw    .122
    MOVWF    DELAY_L
    call    DELAY_4us
    nop
    nop
    return

;----------------------------------------------------------------
;
DELAY_4us                    ; 4usec delay loop.
    NOP
    DECFSZ    DELAY_L, F
    GOTO    DELAY_4us
    return
   
    end


And finally...


For those of you who only are interested in the HEX code, here it is:
 
:020000040000FA
:100000008316FF23900081131730950008308B0072
:10001000831285010730990083161F3085001F3039
:100020009600831285120B108512630005080139B2
:10003000031D1C284A20132805080239031D222805
:100040003420132805080439031D282860201328AC
:1000500005080839031D2E287620132805081039B5
:10006000031D13288C201328A220BB20BF20BF20F3
:10007000BF20BF20BF20BF20BF20BF20BB20BB2090
:10008000BB20BB20BB20BB20BB20FA30D520FA30E0
:10009000D5200800A220BF20BB20BF20BF20BF204A
:1000A000BF20BF20BF20BB20BF20BB20BB20BB2068
:1000B000BB20BB20BB20FA30D520FA30D520080069
:1000C000A220BB20BB20BF20BF20BF20BF20BF205D
:1000D000BF20BF20BF20BB20BB20BB20BB20BB203C
:1000E000BB20FA30D520FA30D5200800A220BF204E
:1000F000BF20BB20BF20BF20BF20BF20BF20BB2010
:10010000BB20BF20BB20BB20BB20BB20BB20FA30C4
:10011000D520FA30D5200800A220BB20BF20BB206C
:10012000BF20BF20BF20BF20BF20BF20BB20BF20DB
:10013000BB20BB20BB20BB20BB20FA30D520FA302F
:10014000D52008007830C320C830C3200430D52023
:100150001630C320BF20BB20BB20BF20BF20BF2044
:10016000BF20BB20BB20BB20BF20BB20BB20BB20AF
:10017000BF20BF200800DE201630C3200800E0208A
:100180001630C3200800A2000330A1008516A10B81
:10019000C728000000000230A1008512A10BCE2864
:1001A0000000A20BC42800000800A000FA30A10043
:1001B0000000A10BD828A00BD6280034E020E020B6
:1001C0007A30A100E6200000000008000000A10B2A
:0401D000E628080015
:02400E00843FED:00000001FF

Good luck. I hope some of you will still find this old code useful.