Making an ultra-compact, FOB-sized controller for wireless power points.
I’ve got a few remote controlled power points around the house (controlled by an Arduino which is triggered by a smartphone app), but I wanted something small I could put in my pocket, and know that I could turn the lights on with, even if the WiFi was off. A PIC microcontroller seemed to be the perfect choice for a simple task like this.
Ten or fifteen years ago, before Arduino was even thought of, PIC microcontrollers (from the Microchip Company) were a common choice for hobbyists. The tools to write, compile and upload a project were cheap and accessible (compared to the professional alternatives). PICs have evolved, but many of the older PIC microcontrollers are still available, so my comment above about being perfect for a simple task refers to these older models as many of the new PICs have features like USB or similar to the AVR microcontrollers in many Arduino boards.
Anyway, I needed to make a simple 433MHz remote control, and I wondered if I could pull it all together using the older PIC ICs and tools, and how they would compare with some of the newer PIC tools.
THE BROAD OVERVIEW
The actual build itself is quite straightforward, requiring only four components including the battery. This was quite necessary, as my aim was to fit the project into a tiny keyfob enclosure to make a device I could carry around in my pocket. In the end, a small amount of surgery was needed, but since it’s still pocket-sized, I’m going to call that a win.
The big hurdle was writing the code to do what I wanted (send out a 433MHz command), and then being able to upload that to the PIC. In the end, I found I was using a similar setup to what I was using about 10 years ago. The code is created in any old text editor – these days it is Wordpad but way back, it was the DOS Edit utility. Compilation is done by another DOS program, and uploading via a piece of hardware called a PICkit 2 (no inbuilt uploader and USB-serial converter like Arduino), and its own specific application. There’s the MPLABX software, which support newer PIC devices (and most of the old ones), but I found that the new software offered way more features and options than I needed for this project, so much, in fact, that it made it more difficult.
There’s a bit of a jump from Arduino and its one-click compile and upload with syntax highlighting, but I would recommend using some of these tools if you want to get a good understanding of how microcontrollers actually work – both from a user and a hardware level.
HOW IT WORKS
There actually isn’t much to the project itself, once it’s finished, since the components involved are a PIC 12F675 microcontroller, a 3V battery, a tactile switch, and a 433MHz transmitter module. Like many projects, the magic is in the code and for the most part, the code is designed to emulate the signal from a 433MHz remote control; in this case the code that is emitted is the same as the A-on and A-off buttons.
Of course, it might seem that with only one button, we only get one function, but in fact, this circuit can output both the on and off codes for one channel.
The way this happens is that every time the microcontroller starts up, it reads a value from EEPROM, checks it, and then writes back a different value. Then, next time the button is pressed, the other code is sent and the value is set back to its original value. This way, it can output different signals with one button. Since there are only two options (on or off), a second press will give the desired result if the first doesn’t.
As part of the miniaturisation, a small battery was chosen, and to make the battery last as long as possible, the circuit is only powered when the button is pressed. The button isn’t even an input to the microcontroller, but simply turns it on when pressed. The microcontroller outputs a few repeats of the digital pattern, and then goes into sleep mode, saving even more power if the button is held down.
The digital pattern is the most complicated part of the code; fortunately, I have decoded the signal from these remotes previously. It is a pattern of 35 bits, which includes a 20 bit address, a 4 bit command, an 8 bit checksum, and 3 trailing bits, which are always equivalent to a “0” bit. It’s also possible to “record” the signal by opening up the original remote and probing the right trace on the PCB.
Being a digital signal, the input to the 433MHz module is either on or off (which results in the 433MHz carrier wave being on or off), but the on and off don’t correspond to “1” and “0”. A “1” is sent by the digital signal being on for (about) 800us, and then off for 320us, while a “0” is on for 320us and off for 800us.
So all the microcontroller has to do is output the 35 bits in order, then wait, and repeat the cycle a few times in case there is interference. A similar system is used in IR remote controls, and it is called “pulse width encoding”.
You can almost see the bit pattern in the waveform – the 1s have a wide peak, while the 0s have a narrow peak – the three 0s at the end are obvious.
THE BUILD
Parts Required: | Jaycar | Altronics | |
---|---|---|---|
1 x Wireless Remote Powerpoint | MS6148 | - | |
1 x Keyfob Enclosure | HB5605 | - | |
1 x CR2032 Battery with Solder Leads | SB1762 | - | |
1 x Tactile Switch | SP0601 | S1120 | |
1 x PIC 12F675 | ZZ8603 | - | |
1 x 433MHz Module | ZW3100 | Z6900 | |
1 x Red LED (Optional) | ZD0100 | Z0700 | |
1 x 100Ω Resistor (Optional) | RR0548 | R7534 |
Of course, a separate PIC programmer is needed to program the microcontroller. I found it best to test everything on a breadboard first before soldering it into the enclosure. Although Microchip has stopped selling the PICkit 2, they appear to have put the circuit and software online. People are also making their own versions, and there are other programmers available such as the newer PICkit 3. See the code section later, for my experience in getting a programmer working.
I set up the following on a breadboard to test the code as I developed it.
You’ll see there are two wires that don’t seem to go anywhere – the grey one on the right of the PICkit 2, which is a spare output from the PICkit 2 and not needed for programming 12F675s. The other is the green wire from the 433MHz module, which is the antenna.
The PICkit 2 has a lot of features and when programming isn’t occurring, the pins can be controlled to give inputs to the circuit for testing; so I didn’t even need the tactile switch for the testing stage. I did however, add an LED and resistor to get a visual confirmation that the output was doing something.
The important things to note are that pins 4, 6 and 7 are only used for programming the PIC, and aren’t used in the final project. Pin 5, known internally as GP2 is what is connected to the LED and 433MHz module’s data pin, and this is what controls the emitted signal. The 12F675 can make use of an external crystal oscillator, but with so few pins and little space available, I’ve stuck to using its internal 4MHz oscillator.
The next step was to make some working code, and get the breadboard circuit working before committing to gluing and soldering everything in place.
THE CODE
For those who are familiar with Arduino, the assembly language I am using to code this project might come as a bit of a shock. Assembly language isn’t so much a language as a half-human readable version of the machine code that gets loaded as the firmware on a microcontroller (or even microprocessor or computer). As such, there is no single “assembly language”, but different versions for different processors. For mid-range PICs like the 12F675 and even the much-loved 16F84 and its 16F88 successor, each machine code instruction is a 14-bit word, and most lines of the source code correspond one-to-one with a 14-bit machine code instruction.
Because I’m using assembly language doesn’t mean that there aren’t compilers out there for higher level languages like “C”. Microchip provides the MPLABX IDE and XC8 compiler (the 12F675 is considered an 8 bit PIC because it can only work with 8 bits of data at a time), and I did try downloading and using these. I found that the MPLABX project system was overkill for what I was try to do, and it was taking minutes at a time to compile a basic project, where my trusty old DOS program took a few seconds (even on an old 486). If I had a more complex project in mind, perhaps I would try harder to get MPLABX working, but it just didn’t suit this project.
My first step was to get a version of the Arduino “blink sketch” working to show that all parts of my toolchain (editing, compiling and uploading) were working. The catch is that assembly language doesn’t even have a “delay” function, so I had to build this from scratch. This was important in the final project, as I need to send out precisely timed pulses on the 433MHz module.
Knowing that the 4MHz internal clock executes one million instructions per second (these PICs take four clock cycles to execute one instruction), meant that for a one-second delay has to sit idle for one million instructions. Given that counting to one million in binary takes 20 bits, and we are working on an 8 bit machine, it’s not as simple as counting to one million either. Sixteen bit and higher operations are possible, but they have to be built up from eight bit building blocks.
The first step is to build a block of code that delays for 16 cycles:
delay2 ;255 cycles of 16 cycles delay
movlw 0ffh
movwf DEL
delayn ;delay 16x DEL cycles
nop 1
//**** 2-12 OMITTED FOR CLARITY ****//
nop ;13
decfsz DEL,f
goto delayn ;15,16 => 16 x const cycles
return
The “nop” command is easiest to understand – it just means “no operation”, and does nothing for one clock cycle. It’s very useful during a delay, but with only 1k of program memory, we don’t have room for one million of these!
The label “delayn” in the first column is referred to in the “goto” instruction later, so that this is a loop. If you count the instructions up to the “goto”, you’ll see there are only 15, but this becomes 16 cycles because the PIC takes two cycles to execute a jump or branch. The “decfsz” instruction decodes as “decrement file and skip if zero”, which is our way out of this loop, once the DEL value has been decreased to zero.
The first two instructions decode as “move literal to w” and “move w to file”. In this case “w” is the “working register” of the PIC, and a “file” is a file register, or more simply, a memory location in RAM. The “literal” is simply the constant 0xFF or 255, and the file is the DEL variable. By setting the “DEL” variable to a number, we can control the number of times we do the loop. In this case, start at 255 (the highest an 8-bit number can be), and the loop proceeds 255 times, taking up 4080 cycles, or 4.0
8ms.Of course, I can’t just assume that the “DEL” variable is available to me, I had to declare it at the start of the program like this:
BD equ 020h
DEL equ 021h
This is another type of instruction that doesn’t actually create any code, but simply tells the compiler to use the alias “DEL” for address 0x21 in RAM. Note that the value in “DEL” is not initialised – if I need it to be a certain value, I need to do that separately. It’s also up to me to be sure that the address 0x21 is available for me to use, which I have to refer to the datasheet to be sure of.
To expand my 4.08ms loop into something longer, I add this code to the start of the loop code above:
bigdel ;big delay of 255 small delays
movlw 0ffh
movwf BD
l3
call delay2
decfsz BD,f
goto l3
return
This gives another loop of 255 cycles using “BD” as the counter around the inner loop, making a total of approximately 1.04s of delay (there is a bit of extra overhead in the outer loop which I haven’t counted).
The two other things we need to do for a “blink” program is to turn the pin output on and off to blink the LED. There are two registers to change, one to say that the pin is an output rather than an input, and another register to say whether the pin is set to a high or low level.
Another tricky issue with most PICs – the control registers are often spread across two “banks” of memory, so there is another register which needs to be changed to make sure you’re working in the right bank:
bsf RP0 ;bank 1
clrf TRISIO ;GPIO is output
bcf RP0 ;bank 0
bcf GP2 ;pin 5 low
The first line sets the bank select bit, selecting bank 1, while the second clears the “TRIS” register (short for “tri-state register”, i.e. input, output high or output low), which makes all pins outputs, after which we clear the bank select bit, going back to bank 0, and then clearing the GP2 bit makes sure that the output is low and the LED is off. If we attempted to access the TRIS register while in bank 0 would actually access the GP register, as they are both at address 5 (on a 12F675) in their respective banks.
Even the register names are not known to the compiler, but are provided in a separate “include” file, which is specific to the microcontroller I am using. In this case, it isn’t much more than a series of “equ” directives, creating aliases for the various register numbers and allowing a more memorable name to be used.
The actual code that actually causes the blinking is this:
loop
bsf GP2 ;output high
call bigdel ;wait
bcf GP2 ;output low
call bigdel ;wait
goto loop ;and around again
The code that actually runs the remote (instead of just blinking an LED) is not much different, except there is a specific sequence of delays between the highs and lows. The code outputs the pattern a few times and then executes the “sleep” command to go into low power mode.
Now that is creating and editing our source code file. Next we need to compile the source code into a HEX file, which can be uploaded to the PIC. As I noted above, I am using a tool that I have had for a long time. The program is an old DOS based program called AF84, and it was included in the very first PIC programmer kit I bought. There was also an uploader program in this package, but since it was designed to work with a parallel printer port, it isn’t much use now. The compiler still works though, but I have to run it on my older 32 bit laptop. It might be possible to run it on an emulator like DOSbox if you have a 64 bit PC.
I even wrote myself a short batch file called “make.bat” just to avoid having to type in all the parameters every time:
af84 /hex %1.pic %1.hex
Anyway, compiling the ‘blink.pic’ file is as simple as running:
make blink
Assuming there are no errors, this will output a file called blink.hex in the same folder. If there are compiler errors, then they are displayed to help with debugging.
Parallel ports disappeared a long time ago, so I did get myself a PICkit2 many years ago, and the software is still available for download. The main window has a lot of information, including program memory contents and EEPROM contents (which can be edited manually).
If the window doesn’t display “Midrange/Standard Configuration”, click Device Family>Midrange>Standard to set this.
The upload process is as simple as clicking File>Import Hex, and choosing the file, and then clicking “Write”. The circuit can be powered for testing by clicking “On”, under VDD PICkit 2.
That is the upload process. After this I tweaked the program to provide the right pulses for the 433MHz module, and once I was satisfied that the breadboard circuit was correctly operating the remote power point, I moved on to final assembly.
ASSEMBLY
The final circuit is quite simple – the hard part is squeezing it into the case!
When it came to the final version, I started by laying out the components around the tactile switch. This is critical, as it needs to line up with the back of the button on the case. Note how the legs are bent to bring them clear of the case, which makes soldering easier. You might also be able to see the pencil lines I used to line up the switch. I did this by measuring the distances from the edge of the case.
I glued the tactile switch in place, and then started soldering components together. I figured I’d be able to fit the PIC to the right of the battery, which would allow it to be soldered directly. I bent the pins on the PIC down to reduce their height and give them a bit more reach. I had to remember that the pin locations are different when it’s upside-down, so I double-checked every step.
Then the PIC and battery are put in place and soldered to the switch. These connections are easy as there are no wires needed. I haven’t glued anything else down yet, in case it needs to be adjusted.
Then I added the 433 MHz module, noting that I couldn’t fit it completely inside the case without doing some trimming. I had two choices: either cut out the post for the screw to go into and glue the case together, or make a slot at the edge of the case to allow the 433MHz module to protrude slightly. Ultimately, I made a slot in the case.
The orange wires are positive connections, and the black wire is negative. Using the extra terminals on the switch for the positive allowed me to connect three wires without having to hold them all in the same place while trying to solder them.
The last two wires are the data from pin 5 on the PIC to the 433MHz module (yellow) and an antenna (white). I found I had to trim the antenna slightly after fitting, so it would not block the catch behind the battery.
The final step here is to trim the case slightly so that it will close. I used a set of pliers as a very rough “nibbler”, and then straightened out the edge with a hobby knife.
Finally, the lid is screwed in place and the final test is done to make sure it all works.
I made one small adjustment after this – the button needed to be pushed down a fair way to work, so I glued a small piece of plastic onto the back of the button to space it out, and now the button doesn’t need to be pushed so far.
WHERE TO NEXT?
I’m pretty happy with how the remote ended up, although next time I might use an ATTiny85, and program it with the Arduino IDE, using another Arduino as ISP programmer. C programming is often more familiar, and it's fairly straight forward to achieve a good result and fast development time, even with a raw chip.
I hope it gives you some idea of the capabilities of the PIC microcontrollers. One advantage of using assembly language over C is that you have a lot more control when precision timing is needed, whereas even different versions of the Arduino IDE can generate different code, which in turn will change the timing of the code.
One project I did many years ago with a PIC was generating a composite TV signal, which needed to have regular pulses every 64us for the horizontal sync, amongst other things.
I’m more interested now in seeing what PICs can offer, and I plan to spend more time learning how MPLABX works, as this will be much more powerful than the ancient compiler I have used in this project. Although this was sufficient for a basic project, MPLABX offers integration of a C compiler and uploading via the PICkit 2. Although the PICkit 2 appears to be unavailable from Microchip, they do have a PICkit 3, which I am yet to try.
While I was searching for my PICkit 2, I purchased another PIC programmer called a “K150” from an online store. I did get it working, but it was a lot of trouble. The program seemed to work well, but it appears some of these have a counterfeit PL2303 USB-serial converter chip which just doesn’t work under Windows 10. Given that it is just a serial interface, I’ve already started building an Arduino-based PIC programmer, which should work under Windows 10. One of the interesting parts of programming PICs is that they need to generate about 13V to put the PIC into programming mode (if you put your multimeter on pin 4 of the 12F675 during programming, you’ll see this). One of the challenges of the Arduino based PIC programmer will be generating and switching 13V from a 5V USB supply.
So far I’ve got the Arduino PIC programmer programming a 16F84A using the program from the K150 kit, and it evens works under Windows 10. I just need to do a bit more work on the 12F675, but this should be fairly straightforward.
For those who don’t want to wait to build an Arduino-based programmer, I would recommend getting a PICkit 3 and using the MPLABX IDE for uploading, supported by Microchip, and would also be recommended for coding in languages other than assembler.
Ultimately I found my PICkit 2, and if nothing else it is easy to use; it even has a logic probe function, which is what I used to capture the waveform in the “How it works” section.
For others, this project should be adaptable to any system that needs to transmit one or two simple codes on a 433MHz frequency. Perhaps a supercap in place of the battery will allow the 433MHz module to fit fully inside the case. It would probably make a great IR remote, given that IR LEDs are quite small. If the 12F675 is too big, Microchip also offers the 6 pin 10F206.