2 Channel RC Safety Mux

One 3rd party feature I found essential when testing out my Self Driving RC Car was the "Fail Safe Mux" by DIY DronesIt allowed me to use a spare RC servo channel to remotely switch control between my RC receiver and my Arduino Mega-based autopilot. Acting as a control multiplexer" (MUX), or remote switch, it allowed me to take RC control at any time, such as when my autopilot code went off the deep end.  While I really like the DIY Drone product, and highly recommend it, it required rather a rat's nest of servo wiring to make all the connections it requires.  In addition, being designed more for aerial vehicles than for cars, it lacked a few features I thought might be useful.  So, this page describes my efforts to build my own version.

Theory of Operation

The basic principle of a safety MUX, or remote RC switch, is that it routes two sets of control inputs, one from the RC receiver and the other from the autopilot through a switch that can select which of these inputs to route to the car's throttle control and steering servo.  This switch, in turn, is controlled by a small micro controller chip that's been programmed to monitor a 3rd RC channel and interpret that channel's signal as a kind of binary switch.  That is, if servo pulse is detected on this channel and the width of the pulse if greater than the neutral position (>1.5 ms), the MUX switch is set to one position.  Or, if the width of the pulse is less than neutral (<1.5 ms), the switch is set to the other position.  Finally, if there is no pulse present, the switch would, ideally, default to the autopilot position so that control of the car could be relinquished by simply turning off the transmitter.

The DIY Drones product uses a 4 channel, 74LS157D IC as its multiplexor switch.  I used this same IC in my first design, but later decided that, since I only needed to switch 2 channels, I could simplify the design by using a pair of single channel, SN74LVC1G97DBVR ICs to simplify and reduce the size of the design.  The SN74LVC1G97DBVR is an interesting chip, as it's actually designed to perform many different logic functions, depending upon how it's wired into the circuit.  The SN74LVC1G97DBVR comes in an SOT-23-6 package, so it's a surface mount chip.  But, it's the same size as the ATTIny10 I planned to use for the switch micro controller, so this made for a nice, compact design.  The final schematic looks like this:

Click image for larger view

The ATTiny10 monitors the 3rd RC channel on pin 3 and then uses pin4 to output the control signal to the switch.  A low output on pin 4 selects the autopilot and a high output selects the RC receiver to control the car.  The code for the ATTIny10 looks like this:

.device ATtiny10

;             +====+

;  (PWMA) PB0 |*   | PB3 (RESET)

;         GND |    | Vcc

;  (PWMB) PB1 |    | PB2

;             +====+

; PB1:Pin 3 - Pulse In

; PB2:Pin 4 - Select Out (LOW selects CPU, HIGH selects Receiver)

;

; Note: neutral servo pulse width is 1500 us  

; available fuses: ckout, wdton, rstdisbl

.DSEG

 

.CSEG

.org 0

;Interrupt vector table

rjmp reset ; All Resets

reti ; External Interrupt Request 0

reti ; Pin Change Interrupt Request 0

reti ; Timer/Counter0 Input Capture

reti ; Timer/Counter0 Overflow

reti ; Timer/Counter0 Compare Match A

reti ; Timer/Counter0 Compare Match B

reti ; Analog Comparator

reti ; Watchdog Time-out

reti ; VCC Voltage Level Monitor

reti ; ADC Conversion Complete

reset:

; Set Stack Pointer (SP)

ldi r16, 0x00

out SPH, r16

ldi r16, 0x5F

out SPL, r16

; Set clock to 8MHz

ldi r16, 0xD8 ; Unprotect CLKPSR reg

out CCP, r16

ldi r16, 0 ; Divide by 1

out CLKPSR, r16

; Calibrate Oscillator

ldi r16, 0x58 ; Value varies from shhip to chip

out OSCCAL, r16

; PB1 is Input, all others Output

ldi r16, (1<<PINB3) | (1<<PINB2) | (0<<PINB1) | (1<<PINB0)

out DDRB, r16

; All Pullups Off

ldi r16, (0<<PINB3) | (0<<PINB2) | (0<<PINB1) | (0<<PINB0)

out PORTB, r16

loop:

rcall pulse_in

cpi r16, 0

brne has_pulse

; get here if no pulse detected (timeout)

cbi PORTB, PINB2 ; set to CPU

rjmp loop

;

has_pulse:

cpi r16, 15

brlo short_pulse ; branch if r16 < 15

long_pulse:

sbi PORTB, PINB2 ; select Receiver

rjmp loop

;

short_pulse:

cbi PORTB, PINB2 ; select CPU

rjmp loop

;

; Wait for pint to go HIGH, then measure length of pulse

; Return r16 as pulse width, else return r16 = 0 if timeout

pulse_in:

ldi r16, 0

ldi r17, 0

ldi r18, 10

pulse_wait:

sbic PINB, PINB1

rjmp pulse_time

inc r16

brne pulse_wait

inc r17

brne pulse_wait

dec r18

brne pulse_wait

ldi r16, 0 ; return r16 = 0 if timeout

ret ; timeout

pulse_time:

ldi r16,0

pulse_loop:

sbis PINB, PINB1 ; skip if PINB1 is HIGH

ret ; pulse ended

rcall delay100 ; delay 100 us

inc r16 ; inc time

rjmp pulse_loop

;

; Delay approx 100 us (using calibrated osc)

delay100:

inc r17

brne delay100

ret

Note: this code is designed to be compiled on my custom ATTiny10 assembler and assumes the on-chip oscillator has been calibrated.  However, the timing requirements are not all that precise and you should be able to adapt this design to many other micro controllers.

Safety Mux for my FrSky Telemetry Receiver

Here's a photo of revision 1 of my multiplexer design fitted to my FrSky D8R-II Plus, 8 channel telemetry receiver.  The mux PCB sits on top and uses a 90 degree adapter PCB to plug into the receiver.  The small board visible in front is a signal inverter used to send telemetry data back to the RC transmitter:

Click for larger view

Note: I later revised this design to use a pair of SN74LVC1G97DBVR chips to handle the multiplexing.  However, the version shown above is an earlier design that used a quad, two channel mux.  In the near future I plan to revise this board again to add in my ESC Protector circuit.