Saturday 1 April 2017

Data Encoder/Decoder HT12E/HT12D

Data Encoder / Decoder
HT12E / HT12D

Introduction

I recently came across an old article I had kept from the Radio Society of Great Britain magazine (Radcomm) May 1998 by G6AWT, entitled 'A Versatile Remote Control System', in which a pair of encoding and decoding processors named the HT12E and HT12D had been used to provide remote control functions for radio equipment.

I wondered whether these processors were still available - they are! I began to think about how I could use this technology with the current range of micro-controllers such as the Arduino and the Raspberry Pi, together with other potential uses. I also thought it would be a great opportunity to fully investigate the operation of the two processors to help me better understand how I might use these in the future.


HT12E & HT12D description and operation
These devices are made by HOLTEK and have common applications in car and garage door controllers, smoke and fire alarms, burglar and car alarms, security systems etc .

The encoder (HT12E) is capable of encoding information comprising 12 bits in total; 8 address bits and 4 data bits. Each address/data bit can be set to either High or Low logic states. The output from the encoder can be used in various ways; for example to feed a Radio Frequency or infrared transmitter , or it could be directly connected to a decoding processor.  Eight address bits allows for one encoder / transmitter combination to use 1 of 256 addresses, each one capable of sending 1 of 16 different data values. This address/data combination allows the possibilities of using one encoder to control many remote receivers with HT12D decoders. The outputs from the HT12D decoder can be used to switch connected logic circuitry or other attached devices via suitable interfaces.

HT12E Encoder circuit

The basic circuit arrangements for the encoder:


The address to use is selected by connecting one or more pins A0 - A7 to 0 volts. If left 'open', that is, not connected to 0 volts, the address will be all '1's or logic HIGH on each pin. The same process is applied to the data bits. If left open, these data bits are at logic 1 or HIGH. OSC1 is the on board oscillator input pin, OSC2 is the oscillator output pin. These two pins are connected together via resistor R, in this case its value is 1Mohm. When the XMIT button is pressed to take pin 14 to 0 volts, the address and data bit stream appears at pin 17 DOUT. The data stream comprises one synchronising bit followed by 8 address bits and 4 data bits. The oscilloscope trace below shows one complete cycle comprising the synchronising bit, 8 address bits and 4 data bits. All address bits and data bits are at logic '1'. In this example the HT12E is powered at 3.3v.


If pin 14 (Transmit Enable) is permanently taken to ground (0v), the encoder continuously transmits the data stream. If a short enable pulse is applied to pin 14, in this case 5mS, one complete transmit cycle is produced comprising 4 cycles, see trace below:


Selecting data pin AD8 and taking this to ground (0v) produces the following data stream:



Selecting address pin A0 and taking this to ground (0v) while AD8 is also at 0v, produces the following data stream:



HT12D Decoder circuit

The basic circuit arrangements for the decoder:



The address to use is selected by connecting one or more pins A0 - A7 to 0 volts. If left 'open', that is, not connected to 0 volts, the address will be all '1's or logic HIGH on each pin. For correct operation between the HT12E and HT12D, the address values must be the same. OSC1 is the on board oscillator input pin, OSC2 is the oscillator output pin. These two pins are connected together via resistor R, in this case its value is 33kohm. The data sheets for both HT12D and HT12E provide details of suitable resistor values. When a data stream from the HT12E encoder is applied to pin 14, DIN, the decoder compares the input data three times with the locally selected address. If no errors are present the input data codes are decoded and latch transferred to the data output pins D8 - D11. The data levels remain 'latched' until a different data set is received. These output pins can be connected to other logic circuitry or external devices via suitable interfaces, as they are unable to provide high current drive. The VT (Valid Transmission pin) pin 17 goes high to indicate a valid transmission and in the circuit above, the LED is switched on. In this example the HT12D is powered at 3.3v.


Using simple 433Mhz Transmitter and Receiver for communications between HT12E & HT12D

Having proved correct operation between the HT12E and HT12D by directly connecting the DOUT to the DIN, I investigated using the basic 433Mhz transmitter and receiver combination in the image below. Transmitter on the left, receiver on the right.


I had little success with these units without any external antennas, so I investigated some options. I took the design for a simple wound coil antenna from an 'Instructables' article. http://www.instructables.com/id/433-MHz-Coil-loaded-antenna/

With these antennas attached during some experimentation I managed to get a reliable communications link of approximately 5 metres. I didn't spend any time trying to optimise the antennas, but early results were encouraging.

Connect the DOUT from the HT12E to the data input on the transmitter module, and apply +3.3v power and ground to the transmitter. The output from the 433Mhz receiver module is connected via an inverter. During inactive periods, the receiver output can be at +3.3v so when a data stream is received, the first positive going synchronising pulse does not get correctly presented to the HT12D and therefore the first cycle would be discounted leaving only 3 cycles (the minimum) for checking. Also, I noted that the output from the 433Mhz receiver is an inverse of the data stream. Using a BC337 NPN transistor as an emitter follower provides a simple inverting function drawing very small amounts of current. Basic circuits below:



Examining the output directly from the HT12E DOUT pin and the BC337 Emitter/R4 junction (HT12D input) confirms the data stream has been correctly received over the 433Mhz communications link. 




In the following trace, address bit A0 is set LOW. Note you can see a very small time lag between the top trace HT12E Data out and the BC337 inverter output (HT12D Data input)  due to the time taken to pass through the 433Mhz TX/RX link.


Using the Raspberry Pi to control

I further experimented with the Raspberry Pi to set the HT12E data pins (AD8 - AD11) using the GPIO pins. Additionally, I used another GPIO pin to act as the trigger for the HT12E. I haven't drawn any circuits for this as the interfacing is straight forward as both the Raspberry Pi and the HT12E operate at 3.3v.
The 4 Raspberry Pi GPIO pins needed to set the HT12E data bits can be directly connected to the HT12E. The GPIO pin acting as the trigger function needs connecting via an inverter circuit. This is to ensure that when the Raspberry Pi GPIO pin is taken 'HIGH' a corresponding 'LOW' is present at the HT12E trigger pin.

I have included a simple Python3 script below, which prompts the user to enter a number between 0 and 15, and using Binary Coded Decimal, sets the AD8 - AD11 pins to convey the user entered number correctly. I have experimented further with this, connecting LEDs to the HT12D decoder data pins to ensure the transmitted and received data is presented correctly. I also used the HT12D data output pins connected to a TTL IC 7447 (BCD to 7-Segment display driver) and a 7-segment display. I have referenced this before in an earlier blog and the direct link is: (http://www.electronics-tutorials.ws/counter/bcd-counter-circuit.html).

Using the Python script below (GPIO_HT12E.py), when a number is entered (0-9) the 7-Segment display shows the number. It could be said that this is a very convoluted way to use this technology but it was really useful to experiment and prove that it was possible.

I can see many uses for the HT12E/D combination, especially when used with either the Raspberry Pi or Arduino. The transmission of the data stream can be over an RF link or direct connection using 2-core cable. With the possibilities of using multiple decoders (HT12D) all 'listening' on their individually configured addresses it is possible for the Raspberry Pi or Arduino to control both the address values and control the use of the data pins. It would be possible to control literally hundreds of external devices this way. To break out the HT12D 4 data output pins further, it is possible to connect these to a 4 to 16 decoder such as the MC14514 / 74HC4514 to get 16 individual output pins. Just one HT12E/D combination could control up to 16 different devices, ideal for something like a model railway layout controlling signals, points, lights etc.


Python3 script:

# Python v3.4.2 console based script
# Set Raspberry Pi GPIO ports Low depending on user input
# User enters number between 0 and 15, GPIO ports 22, 23, 24 & 25
# are set as BCD representations of the decimal input.
# GPIO ports are weighted 0, 2, 4, 8 [GPIO 22, 23, 24, 25]
# Number 99 entered quits the script
# GPIO ports connect to AD8-11 ports on HT12E encoder chip
# user input is BCD coded & used to set AD8-11 ports
# GPIO port 17 is used to send trigger pulse to activate the HT12E for 1 complete cycle


import RPi.GPIO as GPIO # import GPIO module
import time

GPIO.setwarnings(False) # disable warning messages
GPIO.setmode(GPIO.BCM)  # use chip GPIO pin numbers

GPIO.setup(17, GPIO.OUT)
GPIO.output(17, GPIO.LOW)
GPIO.setup(22, GPIO.OUT)
GPIO.output(22, GPIO.HIGH)
GPIO.setup(23, GPIO.OUT)
GPIO.output(23, GPIO.HIGH)
GPIO.setup(24, GPIO.OUT)
GPIO.output(24, GPIO.HIGH)
GPIO.setup(25, GPIO.OUT)
GPIO.output(25, GPIO.HIGH)

def One():
    GPIO.output(22, GPIO.LOW)

def Two():
    GPIO.output(23, GPIO.LOW)

def Four():
    GPIO.output(24, GPIO.LOW)

def Eight():
    GPIO.output(25, GPIO.LOW)

def ClearAll():
    GPIO.output(22, GPIO.HIGH)
    GPIO.output(23, GPIO.HIGH)
    GPIO.output(24, GPIO.HIGH)
    GPIO.output(25, GPIO.HIGH)

def Trigger():
    GPIO.output(17, GPIO.HIGH)
    time.sleep(0.005)
    GPIO.output(17, GPIO.LOW)

try:
    while True:
        print ("Setting GPIO ports Decimal to BCD")
        print ("Enter number between 0 and 15 [99 quits]")
        print ("Warning! supplied parameters are not validated")
        decimal = input(">_ ") # get number as a string
        print
        dec_number = int(decimal) # convert string to integer
        if dec_number != 99:
            ClearAll() # set A8-11 ports to '0000'
            Trigger() # send trigger pulse
            time.sleep(0.1)
            if dec_number == 0:
                ClearAll()
                Trigger()
            if dec_number == 1:
                One()
                Trigger()
            if dec_number == 2:
                Two()
                Trigger()
            if dec_number == 3:
                One()
                Two()
                Trigger()
            if dec_number == 4:
                Four()
                Trigger()
            if dec_number == 5:
                One()
                Four()
                Trigger()
            if dec_number == 6:
                Two()
                Four()
                Trigger()
            if dec_number == 7:
                One()
                Two()
                Four()
                Trigger()
            if dec_number == 8:
                Eight()
                Trigger()
            if dec_number == 9:
                One()
                Eight()
                Trigger()
            if dec_number == 10:
                Two()
                Eight()
                Trigger()
            if dec_number == 11:
                One()
                Two()
                Eight()
                Trigger()
            if dec_number == 12:
                Four()
                Eight()
                Trigger()
            if dec_number == 13:
                One()
                Four()
                Eight()
                Trigger()
            if dec_number == 14:
                Two()
                Four()
                Eight()
                Trigger()
            if dec_number == 15:
                One()
                Two()
                Four()
                Eight()
                Trigger()
        else: # user exit
            print ("Exiting..")
            GPIO.cleanup()
            break

except KeyboardInterrupt: # catch the Ctrl-C
    print ("Exit..")
except: # catch other errors
    print ("Error..Exiting")
finally: # clean up on exit
    GPIO.cleanup()


Python3 script GPIO_HT12E.py (Google Drive)