Software Design

Remote Controlled Outlet Strip
High-Level Design
Hardware Design
Software Design

Daniel Warren

Heidi Ng

Just like the hardware section, doing a remote control project involves two separate pieces working together to achieve the goal.  The software on the transmitter functioned to read the buttons and safely get that data to the receiver.  The receiver's code interpreted the signal constantly coming in from the RF receiver, decoding a data byte when it exists, and running simple state machines to control the outlets.


This code serves to read the buttons when they change, send the data, and then be asleep until something happens again.  To this end, we use the external edge triggered interrupt INT2 coming from the NAND gate discussed in the hardware section.  If no buttons are pushed for 5 seconds, we turn off all peripherals (UART and ADC), enable the external interrupt, and put the MCU into power down sleep.  When it wakes up, we disable that external interrupt, re-enable peripherals, and transmit the state of the port.  The wake-up sequence is long enough from that step that the buttons are no longer bouncing.  We also preload the debounce state machine to have that value of the port, such that it doesn't transmit the same data twice.

Our heartbeat LED blinks with a 50% duty cycle, every second.  That indicates that the MCU is powered up, and executing code.  When it's off, either something is stuck, or it's powered down.  If pushing a button starts it up again, we can be pretty sure it was powered down, as well as watching the supply current change.  We also read the analog input every half a second, to ascertain the level of charge on the battery.  When it falls below 8V, we light the yellow LED, indicating the transmitter range may begin to suffer.  Below 7.75V, we light the red LED, indicating the MCU and transmitter will soon stop operating, and it's time for a new battery.  If a button is pushed and perhaps the NAND LED lights, but none of the MCU status LEDs do, it could be due to three main factors.  Either the MCU is stuck in an infinite loop somewhere, the MCU is broken, or the battery is dead.  Hopefully the user was paying attention to the yellow and red LEDs to notice if the problem is the battery.

While the MCU is awake, we check the button state every 30ms.  We use the same global debounce scheme we've been using for the last few labs in class.  When a button is pushed, we wait for it to stop bouncing, and then run the transmit routine.  That routine makes sure only one button was pressed, since that's part of our error correction scheme.  If that condition is satisfied, it sends out a stream of 0xaa bytes to synchronize the receiver's automatic gain control to the correct levels, and give it enough chances to resolve frame errors and find the actual start of the data byte.  We then send our start byte, data byte, and stop byte.  These three bytes compose our data packet.  The receiver will be constantly checking for it, to ensure it doesn't think data from someone else's project (or other stray noise) is a valid signal.  We don't use the transmit interrupt, since we want the operation to be blocking.  Transmitting at 1200bps is fast enough that we can push buttons in succession and not notice that the MCU was waiting for the data to transmit for most of the time.


This code is constantly watching the receive UART for a valid data packet, and when it gets one, updates the state machines controlling the relays.  As mentioned in the high-level design section, our error detection scheme isn't perfect, but is rather robust.  Through the combination of parity errors, frame errors, start and stop bytes, and one-hot encoding, we can determine if another signal interfered with our reception.  We cannot correct the error, but at least we can recover from bad data and not change the relays when that operation is not desired.  When good data is received, we light an LED for half a second to indicate so.  We also have a heartbeat LED with 10% duty cycle every second to indicate that code is correctly running.

The way the port is connected on the transmitter is as follows:

7 6 5 4 3 2 1 0
all off speed toggle 2nd toggle 4th all on mode toggle 1st toggle 3rd

We decided it was simplest to send that data byte directly as wired, and do the decoding at the receiving end.  So, when the data packet arrives, we decode the data byte according to that description.  We use the receive interrupt, since the receiver will constantly be sending data to the UART, and we wouldn't want to miss anything that might be our data.

We have a variable which stores the "active" state of the outlets.  Normally, the active lights are on, the inactive ones are off.  However, in some modes of operation, that is not so.  Depending on the speed chosen from {125, 250, 500, 1000, 2000, 5000} ms, the blink state machine updates itself at that interval.  The different modes are normal (as just described), blink active on/all off, blink all on/inactive off, alternate active/inactive, scroll active, and marquee all.  At any point in these states, the active set can be altered, which will change the pattern displayed.  It's rather simple to use, yet offers a good range of possibilities.

As mentioned in the hardware section, we had to reduce the baud rate to 1200bps because of losses in the transmission channel.  That speed was plenty fast for our purposes though.  We did get held up for quite a while by frame errors, however.  Regardless of the amount of 0xaa or 0xf0 or 0x0f or anything we sent, the frame errors would never resolve themselves completely.  There would be a varying amount of them before the data byte, but the data byte itself would always have a frame error.  We were able to monitor this by outputting the frame error to a pin, and watching the receiver output at the same time as that pin on the oscilloscope.  What we figured out eventually was that all the received bytes except for the data byte had an even number of 1's, while the data byte always had an odd number.  We were attempting to use odd parity, so all the other bytes' parity bits would be a high level, while the data byte's would be low.  However, the receive UART was not properly set, so it was not expecting parity.  It saw the parity bit as the stop bit, which is supposed to be high.  So even though parity was being transmitted, it wasn't interpreted as such.  Only the data byte would consistently have frame errors, since the stop bit wasn't a high level.  This error was caused by an idiosyncrasy of UBRRH and UCSRC, both of which we were using.  UBRRH we needed to slow transmission down to 1200bps, and UCSRC to enable parity checking and use two stop bits.  However, they share the same I/O address.  They way the MCU determines which one you meant to access is by the most significant bit -- 0 means UBRRH, 1 means UCSRC.  Somehow we missed this, and never were writing to UCSRC.  The importance of reading and rereading datasheets is second only to starting on the project early.  Even though we were using a different microcontroller, which should mean we have to be extra careful, this would have caused trouble with the Mega32 as well.

Home | High-Level Design | Hardware Design | Software Design | Results | Conclusions | Pictures | Appendices

 ECE 476 Final Project
Spring 2004 Daniel Warren and Heidi Ng
Problems? Questions?  Contact
Last updated: 05/03/04.