Alien Planet Exploration Rescue and Recovery

Stanford ME 218c Spring 2013 Project

James Bensson, David Choy, Jason Gonzalez, Craig Western


Task

Project Task

Create a robot and controller to navigate a dark, unfamiliar terrain to find and retrieve a desired object.

Full project description

Operation Description

A robot (ROAMER) will be created to extract a group of miners trapped "underground". The ROAMER will be controlled by a user through a POD. Lights and a camera feed are provided by a SPECIAL, which also responds with the battery status. The mission ends when the miners are successfully retrieved back to the ROAMER drop zone, or when the SPECIAL's battery, and with it the camera feed, has died.

The trapped miners are in a capsule (below right) that resides in an airlock (below left). Each ROAMER must find these airlocks, open a hatch to gain access to the miners' capsule. Then enter the airlock and drag the capsule out of the airlock and back to the extraction point (starting location).

The Planet

The planet is dark with only a single light source to charge the SPECIAL's battery. The surface contains a mixture of hard terrain along with regions of soft compressible material, both of which the ROAMER must be able to navigate over.

SPECIAL

The SPECIAL contains a camera, which provides a video feed to the operator of the POD for navigation purposes. Only this view is available to the operator and as the battery level drops, the video feed quality drops accordingly and intermittently drops out. The SPECIAL also contains three bright lights to illuminate the area in front of the ROAMER. These lights can be independently controlled and their use causes the battery life to drop more rapidly.


Function Description

Project Specifications

Design Motivation

The motivation for the design came from our ability to successfully complete the mission. We wanted large wheels to be able to provide enough clearance on the underside of the ROAMER when traveling over the compressible material. In order to drag the capsule and open the hatch, a gripper was needed to manipulate the accessible tabs on the capsule and hatch. Finally, in order to be able to see when dragging the capsule, the SPECIAL needed to be rotated to face the rear while driving.

Design Implementation

ROAMER

Movement

Goal: Drive over multiple types of terrain.

Wheels: 4" wheels were used to gain enough clearance once the robot settled on the compressible surface.

Gripper

Goal: Be able to grab the airlock hatch and capsule.

4 bar linkage: Designed to translate small motions to large motions at the actuator in a lateral direction, a symmetic set of links were used to pinch at a point about six inches past the robot perimeter.

Door Lock Motor: A combination of a DC motor with limit stops and a worm gear, this linear actuator provided the forward and back motion to actuate the gripper.

SPECIAL Position

Goal: Be able to see both in front and behind ROAMER.

Servo: Using a "Lazy-Susan" type of design, the SPECIAL sat upon a holder that was actuated by a servo. Given the right command, the servo was set to rotate 180 degrees to display the other view to the operator.

POD

Continuous Operation

The POD must be battery operated and have enough battery capacity for eight hours of continuous operation.

A 1700mAh battery was used, which for 8 hours continous operation, allowed for 212.5mA to be drawn on average by the POD.

Max Current Draw of Components

Total average current draw = 68.91mA

This allows for over 24 hours of continous operation with the battery chosen.

Communications Protocol

As a class, a member from each group met together to decide how every POD should communicate to each ROAMER.

Protocol Documentation

Schematics

POD

Driving Input

Two motors were used as generators to determine the speed to be sent to the drive motors on the ROAMER. When the wheels were spun by the operator of the POD, back EMF was generated linearly proportional to the speed the wheels were spun. Since the back EMF was both positive and negative depending on the direction of the wheel's spin, an op-amp was used as a buffer to keep one side of the motor sitting at 1.8V, while the other side was a simple voltage divider that would change as back EMF was generated. This signal was amplified through the op-amp and run through a low pass filter with a cutoff frequency of about 0.7 Hz. This frequency was chosen to keep essentially only the DC component that was produced by the user spinning the wheel. The output was measured with the A/D converter on the PIC and sent as an analog value according to the communication protocol.

Microcontroller Line Distribution

This logic-level distribution board contained a single PIC16F690 that connected to the various inputs and outputs. The low-dropout logic-level 5V regulator was placed on this board. There was a second PIC located on the XBee radio board that controlled comminication between the PIC controlling the inputs and the XBee radio subsystem.

This schematic shows each Molex connector that was dedicated to carrying the signals, and if necessary, power and ground, to each sensor/input. This schematic also shows a graphical representation of the pin assignments.

ROAMER Select

With up to three ROAMERs being active at once, this switch allows for selecting which number the ROAMER thinks it is. This was done by having a single pole, triple throw switch. Each line has a different voltage and by reading the voltage with the analog converter and setting thresholds, the ROAMER number could be determined.

SPECIAL Position Control

A rotary potentiometer was used to map rotation of a dial to rotation of the SPECIAL.

Active Connection LED

The green LED was turned on when the POD was connected to a ROAMER. The LED was off while disconnected or attempting to connect.

Gripper Input

Due to the possibility of using an analog value to control the gripper, a force-sensitive resistor was placed under foam to create a larger travel distance. As the user pushed down harder, the resistance dropped, causing an increase in voltage read, and a higher analog value sent to the ROAMER.

Connect Button

In order to initiate the connection with a ROAMER, a button was placed on the side to send a connection request to the ROAMER selected. Once connected, the same button doubled as a disconnect button.

Switches

In order to turn on the lights of the SPECIAL and actuate the second gripper actuation, a single pole, single throw switch was used. The switches had markings denoting whether each switch was on or off, and served as feedback to the user on what the state of each switch was.

ROAMER

Motors

The TLE-5206 H-bridge was used to reduce the amount of loss through the H-bridge. The drive motors had to overcome a lot of force to climb across the compressible terrain and climb up onto the airlock hatch to enter to reach the miners' capsule, so those motors were run off of 14.4V. The gripper motor had a much lower resistance and only 7.2V was needed to generate the necessary force at the gripper actuator.

Power Distribution

Initially built to reduce noise, the logic power was separated by drawing power from a separate batteries from the motors. Since the gripper drew only 7.2V, while the drive motors used the full 14.4V, a wire was pulled off the lower battery as well. This drove both the gripper and was also fed into a 5V regulator to give power to the servo on the ROAMER.

Microcontroller Line Distribution

Most pins of the E128 were pulled directly out to the necessary locations such as to the H-bridge or to the servo. This schematic illustrates which pins were pulled out to these actuators.

SPECIAL

One requirement of the project was that the PIC that talked to the SPECIAL had to be written in assembly. Because the SPECIAL was a slave over SPI communication, this PIC became the master and talked with the E128 over SPI as well to relay the SPECIAL information.

Drawings

ROAMER

Original Design

The original ROAMER design attempted to be as simple as possible. The design consisted mainly of two tiers to support the three main functionalities of the ROAMER: driving, gripping, and moving the SPECIAL. Just below the first tier, the drive capabilities consisted of two wheels with ping pong ball casters supporting the robot on either side. Below the second tier of the ROAMER, the gripper was mounted to be at the specific height of the airlock handle. The SPECIAL was mounted on a servo above the second tier.

Final Design

The final ROAMER design is consistent with the original design. Each tier was laser cut from 1/4" duron and has a length and width of 12". The height of the ROAMER, including the mounted SPECIAL is roughly 11". A power switch and a ROAMER select switch were placed on the top tier of the ROAMER. Below is a brief description of the three main functionalities of the ROAMER: driving, gripping, and positioning the SPECIAL.

Driving

The drive train for each of the two ROAMER wheels consists of the following parts connected in series: drive motor, spider-coupler, shaft, wheel hub, and wheel. Three laser cut duron pieces where placed along the drive train to support the drive motor and keep whole drive train connected to the ROAMER's bottom tier. The duron pieces were connected to the main ROAMER body using screws and L-brackets as shown below.

Gripping

The gripper, just below the second tier of the ROAMER, consists of the following parts connected in series: a linear actuator and duron-cut gripper arms. When the linear actuator is fully extended, the movable gripper joints are forced outward and the gripper opens. Conversely, when the linear actuator contracts, the movable gripper joints are pulled inward towards each other and the gripper closes.

SPECIAL Positioning

The SPECIAL, located just above the second tier of the ROAMER, sits on a duron cut circle. A servo was connected at the center of the circle to turn the SPECIAL around a full 180 degrees.

Front View

Side View

POD

Original Design

The original design for the POD also was constructed with simplicity as the main goal. The design mainly consists of a box with a diagonal face where most of the ROAMER control elements are found. To control the ROAMER drive, the POD has wheels. The user can control the right and left wheels of the ROAMER by spinning the corresponding wheels on the POD at different speeds. To actuate the ROAMER gripper, a physical handle was attached to the POD for the user to grip. Finally, the SPECIAL position control was implemented in the original POD design using a rotary potentiometer.

Final Design

The final POD design differs from the original design in that it is more compact. The goal was to have the user be able to walk around while controlling the ROAMER. This led to a sleeker, more compact design. The final design consists of a PVC handle that is connected to a cube of side length 6.5". Below, is a description of the functionality that is present on each face of the POD.

Front Face

The front face of the POD has four features. A blue button allows the user to attempt to connect this POD to a specific ROAMER. The green LED above it lets the user know when a successful connection has been made. The three pronged switch to the right of the blue button allows the user to determine which of the three ROAMERs he wants to control. Finally the switch on the far right of the front face is a kill power switch for the POD.

Bottom Face

The bottom face of the POD contains the two wheels that control the ROAMER drive. The speed and direction at which each wheel is spun controls the speed and direction at which the corresponding ROAMER wheel drives.

Left Face

The left face of the POD contains a circular potentiometer. The position of the potentiometer controls the position of the SPECIAL on the ROAMER.

Right Face

The right face of the POD has supports two functionalities. The servo and arrow on the right side of the face give the user an indication of the SPECIAL's battery power on the ROAMER. The three buttons to the right of the servo control which lights on the SPECIAL should be turned on or off.

Top Face/Gripper

The top face of the POD allows for the gripper control. The user may control the gripper in two ways. A switch on the top end of the PVC pipe gives a digital value for the gripper. A force sensor is also present on top of the PVC pipe to implement analog control for the gripper. Depending on how each ROAMER implements gripping determines what each sensor actually controls.

Front View

Side View


Pseudo-code

Communication was implemented using a total of four separate microcontrollers and two XBee radios, along with the SPECIAL that was provided as a part of the project. Commands for the ROAMER originated at the POD brain PIC, where they were sent via SPI to the POD radio PIC. Here the packet necessary for communication with the XBee was constructed and sent to the XBee through asynchronous transmission. The XBee radio on the ROAMER side would receive the packet and forward information to the E128 ROAMER brain located on the ROAMER for command interpretation and implementation. Before sending a status update back to the POD brain through the same communication channels, the ROAMER brain would request an update from the SPECIAL PIC communicating with the SPECIAL, simultaneously exchanging any commands to be forwarded to the SPECIAL itself. The SPECIAL PIC served as a master, communicating via SPI with the ROAMER Brain and the SPECIAL as slaves.

Software Design and Implementation Details

POD Brain

Timers:
TIMEOUT_TIMER (0.2 sec)

Initialization:

Hardware initialization
Initialize timer

Initialize command array:
commandArray[0] = 0x0B (length)
commandArray[1]  = 0x01 (API ID)
commandArray[2]  = 0x01 (frame ID)
commandArray[3]  = 0x21 (ROAMER address hi)
commandArray[4]  = 0x84 (ROAMER address lo)
commandArray[5]  = 0x00 (command options)
Start data array:
commandArray[6]  = 0x00
commandArray[7]  = 0x00
commandArray[8]  = 0x00
commandArray[9]  = 0x00
commandArray[10] = 0x00
commandArray[11]  = 0x00

Initialize counters, disconnect flag, ROAMER select
Initialize PWM for servo
Set connect LED low
Initialize state to idleDisconnected

SWITCH ( CurrentState)
CASE idleDisconnected:
    IF event is CONNECT_BUTTON_PRESSED and ROAMER is disconnected
        disconFlag = 0
        Prepare array for transmitting broadcast:
        Set address bytes to 0xFF
        Clear data array
        Save ROAMER select input
        IF reading is greater than 0xC0
            Set ROAMER number in data array to 3
        ELSE IF reading is less than 0x40
            Set ROAMER number in data array to 2
        ELSE
            Set ROAMER number in data array to 2
        END IF
        Lower SS_LINE
        Read SSPBUF to clear flag
        Clear interrupt flag
        Write first command array byte to SSPBUF
        Increment bytes sent counter
        Reset TIMEOUT_TIMER for 0.2 sec
        Set currentState to Broadcasting
    END IF
CASE broadcasting:
    IF event is SPI_RECEIVE_FLAG_HI
        IF SPIBytesSent less than SPI_BYTES_TO_SEND
            Save SSPBUF contents to status array
            Write next byte of command array contents to SSPBUF
            Increment SPIBytesSent
        ELSE
            Save SSPBUF contents to status array
            Reset SPIBytesSent counter
            Raise SS LINE
            Post event PACKET_RECEIVED
            CurrentState = processingByte
        END IF
    ELSE IF event is TIMEOUT_TIMER
        Lower SS LINE
        Read SSPBUF to clear flag
        Write first command array byte to SPI
        Increment SPIBytesSent
        Reset TIMEOUT_TIMER
    END IF
CASE processingByte:
    IF event is PACKET_RECEIVED
        IF statusArray[1] is zero
            IF noReplyCount  is less than 4
                Increment noReplyCount
                Set currentState to broadcasting
            ELSE
                Reset noReplyCount counter
                Set disconFlag to 1
                Set currentState to idleDisconnected
            END IF
        ELSE IF statusArray[6]  is 0x04 (connect accepted)
            CONNECTION_LED on
            Save incoming ROAMER address
            Reset noReplyCount counter
            Set currentState to connectedWaitingToTransmit
        END IF
    END IF
CASE connectedWaitingToTransmit:
    IF event is TIMEOUT_TIMER
        Build data array
        Lower SS LINE
        Read SSPBUF to clear flag
        Write commandArray[0] to SSPBUF
        Increment SPIBytesSent counter
        Reset TIMEOUT_TIMER to 0.2 sec
        Set currentState to Transmitting
    ELSE IF event is CONNECT_BUTTON_PRESSED
        Set disconFlag to 1
        Clear noReplyCount counter
        Set currentState to waitingToSendDisconnect
    END IF
CASE Transmitting:
    IF event is SPI_RECEIVE_FLAG_HI
        IF SPIBytesSent less than SPI_BYTES_TO_SEND
            Save SSPBUF contents to status array
            Write next byte of command array contents to SSPBUF
            Increment SPIBytesSent
        ELSE
            Save SSPBUF contents to status array
            Reset SPIBytesSent counter
            Raise SS LINE
            IF disconFlag is 1
                Turn CONNECTION_LED off
                Set battery charge indicator to zero
                Set currentState to pauseBeforeDisconnect
            ELSE
                Post event PACKET_RECEIVED
                currentState = processingStatusByte
            END IF
        END IF
    END IF
CASE processingStatusByte:
    IF event is PACKET_RECEIVED
        IF statusArray[1] is zero
            IF noReplyCount is less than 4
                Increment noReplyCount
                Set currentState to connectedWaitingToTransmit
            ELSE
                Turn off CONNECTION_LED
                Reset noReplyCount counter
                Set disconFlag to 1
                Reset TIMEOUT_TIMER for 0.2 sec
                currentState = pauseBeforeDisconnect
            END IF
        ELSE (good packet received)
            Set PWM for servo according to appropriate status array byte
            Reset noReplyCount counter
            Set currentState to connectedWaitingToTransmit
        END IF
    ELSE IF event is CONNECT_BUTTON_PRESSED
        Set disconFlag to 1
        Set currentState to waitingToSendDisconnect
    END IF
CASE waitingToSendDisconnect:
    IF event is TIMEOUT_TIMER
        Build command array to send "disconnect request"
        Lower SS LINE
        Read SSPBUF to clear flag
        Write first command array byte to SSPBUF
        Increment SPIBytesSent
        Reset TIMEOUT_TIMER to 0.2 sec
        Set currentState to Transmitting
    END IF
CASE pauseBeforeDisconnect:
    IF event is TIMEOUT_TIMER
        Set currentState to idleDisconnected
        Reset noReplyCount counter
        Reset SPIBytesSent counter
    END IF
END

Function buildArray():
    Set commandArray[6] to 0x02
    Set commandArray[7] to AD input for left wheel control
    Set commandArray[8] to AD input for right wheel control
    Set commandArray[9] to gripper analog/ digital inputs in appropriate bits
    Set commandArray[10] to AD input for camera control
    Set commandArray[11] to hi/lo bits for LED digital control

Interrupt Response Routine:
    IF SPI receive flag is high
        Clear interrupt flag
        Post SPI_RECEIVE_FLAG_HI event
    END IF


Event Checker for CONNECT_BUTTON:
    Save current pin state
    IF currentPinState is high and different from last time
        Post CONNECT_BUTTON_PRESSED event
    END IF
    Save currentPinState to lastPinState

POD Radio

Initialization Routine:
Initialize hardware in testMain
Initialize command array
Initialize status array
Initialize variables, counters, checksums
Set currentState to waitingForSPI
Load contents of first status array byte into SPI buffer

SWITCH (currentState)
CASE waitingForSPI:
    IF event is PACKET_RECEIVED
        Clear TXIF flag
        Write first command byte into transfer register
        Increment asyncBytesWritten counter
        Set currentState to sendingCommandPacket
    END IF
CASE sendingCommandPacket
    IF event is ASYNC_TX_EMPTY
        IF asyncBytesWritten is not ASYNC_BYTES_TO_SEND
            Write next command byte to transfer register
            Increment asyncBytesWritten counter
        ELSE
            Write checksum to transfer register
            Clear overflow flag
            Clear receive flag
            Clear status array incoming buffer
            Write first byte of status array to SPI register
            Clear command checksum
            currentState = waitingForResponse
        END IF
    END IF
CASE waitingForResponse:
    IF event is ASYNC_BYTE_RECEIVED
        IF RX Reg contents equal 7E
            Set currentState to waitingForMSB
        ELSE
            Do nothing
        END IF
    END IF
CASE waitingForMSB:
    IF event is ASYNC_BYTE_RECEIVED
        Clear receive register
        Set currentState to waitingForLSB
    END IF
CASE waitingForLSB:
    IF event is ASYNC_BYTE_RECEIVED
        Set bytesToRead equal to RX Reg contents
        IF bytesToRead is longer than expected, return to waitingForResponse
        Save bytesToRead to status array incoming buffer
        Set status checksum to 0
        Set currentState to suckingPacket
    END IF
CASE suckingPacket:
    IF event is ASYNC_BYTE_RECEIVED
        IF bytesReadCount is not bytesToRead
            Save receive register contents to status array incoming buffer
            Increment checksum
            Increment bytesReadCount counter
        ELSE IF FF - RX Reg contents does not equal statusChecksum
            clear status array incoming buffer
            currentState = waitingForSPI
        ELSE IF status array incoming buffer is radio ack 0x89
            Clear status array incoming buffer
            currentState = waitingForResponse
        ELSE IF status array incoming buffer does not contain status update
            Clear status array incoming buffer
            currentState = waitingForResponse
        ELSE
            Transfer status array incoming buffer to status array
            Write first byte to SPI buffer 
            Clear command checksum
            currentState = waitingForSPI
        END IF
    END IF

Interrupt Service Routine:
    IF SSPIF flag is 1
        Put state machine back in waitingForSPI in order to respond
        Clear interrupt flag
        IF SPI_BYTES_TO_WRITE is not SPIBytesReceived
            Save contents of buffer to command array
            Write next byte in status array to SPI register
            Increment SPIBytesReceived counter
        ELSE
            Save contents of buffer to command array
            Write first byte of status array to SPI register
            Clear SPIBytesReceived counter
            Post SPI_PACKET_RECEIVED event to state machine
        END IF
    END IF

ROAMER E128 State Machine

Events: ASYNC_BYTE_RECEIVED, SPI_BUFFER_FULL, ASYNC_TX_EMPTY, RECEIVE_TIMEOUT, DISCONNECT_TIMEOUT

States: waitingForResponse, waitingForMSB, waitingForLSB, suckingPacket, transmittingSPI, sendingStatusPacket

#define SPI_BYTES_TO_WRITE 4
#define ASYNCH_BYTES_TO_SEND 13

Initialize variables:
currentState
asyncBytesToRead
asyncBytesReadCount
commandArray[11]
commandChecksum
SPIBytesWritten
statusArray[13]
statusChecksum
asyncBytesWritten
targetAddHi
targetAddLo
frameID
lastCameraCommand
connectionStatus

Initialization Routine:
//Registers and Variables
    Initialize statusArray to intial values
    Set necessary variables to 0 (checksums, BytesRead/Written, connectionStatus)
    Initialize SPI to be slave with receive interrupt enabled
    Initialize SCI to 9600 baud and Mode 3
    Initialize ES_Timers to 1ms rate
    Send commands to set motors to default positions
    Set initial state to waitingForResponse

State Machine:
Switch (currentState)
Case waitingForResponse:
If event is ASYNC_BYTE_RECEIVED
        If RX Reg contents equal 7E
            Start Receive Byte Timer
            Set currentState to waitingForMSB
        Else
            // Do nothing
        End if
    Else If event is ES_TIMEOUT from disconnect timer
        Set connectionStatus to 0 (disconnected)
        Stop Disconnect Timer
        Send commands to set motors to default positions
    End if

Case waitingForMSB:
    If event is ASYNC_BYTE_RECEIVED
        Reset Receive Byte Timer
        Set currentState to waitingForLSB
    Else if event is RECEIVE_TIMEOUT
        Clear BytesReadCount
        Clear commandArray
        Stop Receive Byte Timer
        CurrentState = waitingForResponse 
    End if

Case waitingForLSB:
    If event is ASYNC_BYTE_RECEIVED
        Set asyncBytesToRead = RX Reg contents 
        If asyncBytesToRead is greater than 0x0B
            Set currentState to waitingForResponse
            break
        Set bytesReadCount = 0
        Set commandChecksum = 0
        Reset Receive Byte Timer
        Set currentState to suckingPacket
    Else if event is RECEIVE_TIMEOUT
        Clear BytesReadCount
        Clear commandArray
        Stop Receive Byte Timer
        CurrentState = waitingForResponse
    End if

Case suckingPacket:
    If event is ASYNC_BYTE_RECEIVED
        If bytesReadCount is not bytesToRead
            commandArray[bytesReadCount] = RX Reg contents
            commandChecksum += commandArray[bytesReadCount]
            Increment bytesReadCount
        Else if FF - RX Reg contents does not equal commandChecksum
            currentState = waitingForResponse
        Else if commandArray[0] = 0x89
            // Response from POD radio - good or bad
            currentState = waitingForResponse
        Else
            // Write first byte
            SSPBUF = commandArray[0] 
            SPIBytesWritten = 1 // Already wrote byte 1
            statusChecksum = 0
            currentState = transmittingSPI
        End if
    Else if event is RECEIVE_TIMEOUT
        Save lastCameraCommand
        Stop Receive Byte Timer
        Call function to handle incoming message
    End if

Case transmittingSPI:
    If event is SPI_BUFFER_FULL
        If SPI_BYTES_TO_WRITE is not SPIBytesWritten
            statusArray[SPIBytesWritten+7] = SSPBUF
            SSPBUF = 0
            Increment SPIBytesWritten
            Lower DataRequestLine
        Else
            statusArray[SPIBytesWritten+7] = SSPBUF
            For Loop to calculate statusChecksum 
            Read TXIF flag // to clear
            TXREG = statusArray[0]
            asyncBytesWritten = 1
            currentState = sendingStatusPacket
        End if
    End if

Case sendingStatusPacket
    If event is ASYNC_TX_EMPTY
        If asyncBytesWritten is not ASYNC_BYTES_TO_SEND
            TXREG = statusArray[asyncBytesWritten]
            Increment asyncBytesWritten
        Else
            TXREG = FF - statusChecksum
            Clear overflow flag OERR
            Clear receive flag RCIF
            currentState = waitingForResponse
        End if
    End if
 

Public Functions:
    Retreive Command
        Allow RoamerService to request specific elements from commandArrray
    Get LowerAddressByte
        Allow RoamerService to request the lower address byte of the target POD

Private Functions:
    HandleIncomingMessage:
        If API ID is 0x81
            If Broadcast Message &&
            If currently disconnected &&
            If message is a Connect Request &&
            If message addressed to current RoamerNumber
                Set ConnectionStatus to 1 (Connected)
                Save TargetAddressHi & Lo
                Set statusArray to send connectAccepted 
                Start Disconnect Timer    
                Begin SPI Sequence
            Else if Direct Message &&
            If currently connected &&
            If message is Disconnect Request &&
            If message is coming from current targetAddress
                Set ConnectionStatus to 0 (Disconnected)
                Set statusArray to send disconnectAccepted 
                Stop Disconnect Timer    
                Begin SPI Sequence
                Send commands to set motors to default
            Else if Direct Message &&
            If current connected &&
            If message is a command from POD &&
            If message is coming from current targetAddress
                Set statusArray to send roamerStatus
                Reset Disconnect Timer    
                Begin SPI Sequence
                Post Command Event to RoamerService
            Else
                currentState = waitingForResponse
            End If
        End If

    BeginSPI:
        Set currentState to transmittingSPI
        If SPI Transmit Empty Flag is set
            Write SPECIAL light state byte to SPI
            SPIBytesWritten = 1
            statusChecksum = 0
            Raise Data Request Line
        End IF

    MotorStop:
        Set default Motor Values
            Set LeftMotor to full stop
            Set RightMotor to full stop
            Set Gripper to OPEN
            Set SPECIAL to default (forward)
        Post Command Event to RoamerService

    SPI Interrupt Response:
        If SPIBufferFullFlag set
            Post SPI_BUFFER_FULL event to RoamerRadioSM
        End


EventCheckers:
    Check SCIReceiveFlag
        If set Post ASYNC_BYTE_RECEIVED
    Check ACITransmitEmptyFlag
        If set Post ASYNC_TX_EMPTY

ROAMER E128 Service

Events: INPUT_SET, ES_INIT, ES_TIMEOUT

Initialize variables:
MyRoamerNumber

Initialization Routine:
//Registers and Variables
    Initialize RoamerSelect Pin to Analog Input
    Initialize PWM on Left and Right Wheel Motors
    Set Initial Duty Cycle to 0
    Initialize PWM on Gripper Motor
    Post ES_INIT Event to Service
    Initialize Servo Library on SPECIAL Servo Pin
    Determine Roamer Number


Service:
If event is INPUT_SET
    Set Left and Right Motor (ControMotors)
    Set Gripper Motor (ControlGripper)
    Set Special Servo (ControlSPECIAL)
If event is ES_INIT
    Set Gripper to OPEN
    Start Gripper Timer
If event is ES_TIMEOUT and param is Gripper Timer
    Set Gripper PWM to 0
    Stop Gripper Timer


Public Functions:
    RetreiveRoamerNumber
        Allow RoamerRadioSM to retrieve RoamerSelect switch state

Private Functions:
    ControlMotors
        Retrieve Left and Right Motor commands from RoamerRadioSM
        If Special is facing backwards
            Invert Motor command
        If TargetAddressLo is own POD (0x84)
            Map Commands Linearly:
            UpperThreshold - 0xFF --> 50-100% Duty Cycle (Polarity Low)
            LowerThreshold - 0x00 --> 50-100% Duty Cycle (Polarity High)
            Purpose of thresholds to provide a deadband around 0x80 (Full Stop)
        Else 
            Map Commands to square power function:
            UpperThreshold - 0xFF --> 50-100% Duty Cycle (Polarity Low)
            LowerThreshold - 0x00 --> 50-100% Duty Cycle (Polarity High)
            Purpose of thresholds to provide a deadband around 0x80 (Full Stop)

    ControlSpecial
        Retrieve SPECIAL command from RoamerRadioSM
        If command between 0x40 and 0xC0
            Special Forward
        Else
            Special Backward

    ControlGripper
        Retrieve Gripper Command from RoamerRadioSM
        If command MSB is set
            Set Gripper Polarity Low
            Set Gripper Timer
        Else
            Set Gripper Polarity High

    DetermineRoamerNumber
        Analog Read RoamerSelect Pin
        Map Upper Third to 1
        Map Lower Third to 2
        Map Middle Third to 3

SPECIAL PIC

Initialization:
Initialize internal oscillator
Initialize SPI
Set pins to digital I/O
Initialize two SS output lines
Initialize Timer 1
Clear/ initialize variables and set state to queryingBattery
Set Timer 1

WHILE (true)
    IF currentState is queryingBattery and timer is timed out
        IF no bytes have been transferred
            Lower SPECIAL SS line
            Clear BF bit
            Write 0x3F to SSPBUF
            Increment battBytesTransferred counter
        ELSE IF BF is set
            IF battBytesTransferred does not equal BATT_BYTES_TO_TRANSFER
                Clear BF bit
                IF battBytesTransferred = 3
                    Save SSPBUF to battCharge
                END IF 
                Write 0x00 to SSPBUF
                Increment battBytesTransferred
            ELSE
                Clear BF bit
                Save SSPBUF to battChargingState
                Set battBytesTransferred to 0
                Raise SPECIAL SS line
                Clear timer flag and reset timer
                Set currentState to querying LEDs
            END IF
        END IF
    ELSE IF currentState is queryingLEDs and timer is timed out
        IF no bytes have been transferred
            Lower SPECIAL SS line
            Clear BF bit
            Write lastLEDCMD to SSPBUF
            Increment LEDBytesTransferred
        ELSE IF BF is set
            IF LEDBytesTransferred does not equal LED_BYTES_TO_TRANSFER
                Clear BF bit
                Write 0x00 to SSPBUF
                Increment battBytesTransferred
            ELSE
                Clear BF bit
                Save SSPBUF to LEDStatus
                Set LEDBytesTransferred to 0
                Raise SPECIAL SS line
                Clear timer flag and reset timer
                Set currentState to updatingBrain
            END IF
        END IF
    ELSE IF currentState is updatingBrain and timer is timed out
        IF BRAIN_TRANSFER_REQ_LINE is high (Note: will be set low by brain ASAP
            after transfer begins)
                Set brainTransferIPFlag = 1;
        END IF
        IF brainTransferIPFlag is 1
            IF brainBytesTransferred = 0
                Lower BRAIN SS line
                Clear BF bit
                Write 0x00 to SSPBUF
                Increment brainBytesTransferred
           ELSE IF BF is set
               IF brainBytesTransferred = 1
                    Clear BF bit
                    Save SSPBUF to LastLEDCMD
                    Write battCharge to SSPBUF
                    Increment brainBytesTransferred
                ELSE IF brainBytesTransferred = 2
                    Clear BF bit
                    Write battChargingState to SSPBUF
                    Increment brainBytesTransferred
                ELSE IF brainBytesTransferred = 3
                    Clear BF bit
                    Write LEDStatus to SSPBUF
                    Increment brainBytesTransferred
                ELSE
                    Clear BF bit
                    Set brainBytesTransferred to 0
                    Raise BRAIN SS line
                    Set brainTransferIPFlag = 0
                    Clear timer flag and reset timer
                    Set CurrentState to queryingBattery
                END IF
            END IF
        END IF
    END IF
END

Code & Header Listings

Headers

POD Brain

ADPic.h
void AD_Init(void);
unsigned char AD_Read(unsigned char channel);
Const.h
// definition of constants

#include <htc.h>

// define timer rate
#define _200_MS 5

// define I/O ports and direction ports

#define SPI_FLAG_PORT SSPSTAT
#define SPI_FLAG_MASK BIT0HI

#define GRIPPER_INPUT_DIGITAL 3
#define GRIPPER_INPUT_DIGITAL_PORT PORTA
#define GRIPPER_INPUT_DIGITAL_MASK BIT3HI

#define BATTERY_LIFE_INDICATOR 5
#define BATTERY_LIFE_INDICATOR_PORT PORTC
#define BATTERY_LIFE_INDICATOR_MASK BIT5HI

#define ACTIVE_CONNECTION_INDICATOR 4
#define ACTIVE_CONNECTION_INDICATOR_PORT PORTC
#define ACTIVE_CONNECTION_INDICATOR_MASK BIT4HI

#define SLAVE_SELECT 6
#define SLAVE_SELECT_PORT PORTC
#define SLAVE_SELECT_MASK BIT6HI

#define SERIAL_DATA_OUT 7
#define SERIAL_DATA_OUT_PORT PORTC
#define SERIAL_DATA_OUT_MASK BIT7HI

#define CONNECT_BUTTON 7
#define CONNECT_BUTTON_PORT PORTB
#define CONNECT_BUTTON_MASK BIT7HI

#define SERIAL_CLOCK 6
#define SERIAL_CLOCK_PORT PORTB
#define SERIAL_CLOCK_MASK BIT6HI

#define SERIAL_DATA_IN 4
#define SERIAL_DATA_IN_PORT PORTB
#define SERIAL_DATA_IN_MASK BIT4HI

#define LIGHT_SWITCH_2 2
#define LIGHT_SWITCH_2_PORT PORTC
#define LIGHT_SWITCH_2_MASK BIT2HI

#define LIGHT_SWITCH_1 1
#define LIGHT_SWITCH_1_PORT PORTC
#define LIGHT_SWITCH_1_MASK BIT1HI

#define LIGHT_SWITCH_0 0
#define LIGHT_SWITCH_0_PORT PORTC
#define LIGHT_SWITCH_0_MASK BIT0HI

// AD channel definitions

#define LEFT_WHEEL_CONTROL_CHANNEL 0

#define RIGHT_WHEEL_CONTROL_CHANNEL 1

#define ROAMER_SELECT_CHANNEL 2

#define GRIPPER_CONTROL_ANALOG_CHANNEL 3

#define SPECIAL_POSITION_CONTROL_CHANNEL 4
ES_Configure.h
/****************************************************************************
 Module
     ES_Configure.h
 Description
     This file contains macro definitions that are edited by the user to
     adapt the Events and Services framework to a particular application.
 Notes

 History
 When           Who     What/Why
 -------------- ---     --------
 6/2/13         cmw      implementation of 218C project
 01/15/12 10:03 jec      started coding
*****************************************************************************/

#ifndef CONFIGURE_H
#define CONFIGURE_H

/****************************************************************************/
// The maximum number of services sets an upper bound on the number of
// services that the framework will handle. Reasonable values are 8 and 16
// HOWEVER: at this time only a value of 8 is supported.
#define MAX_NUM_SERVICES 8

/****************************************************************************/
// This macro determines that nuber of services that are *actually* used in
// a particular application. It will vary in value from 1 to MAX_NUM_SERVICES
#define NUM_SERVICES 1

/****************************************************************************/
// These are the definitions for Service 0, the lowest priority service
// every Events and Services application must have a Service 0. Further
// services are added in numeric sequence (1,2,3,...) with increasing
// priorities
// the header file with the public fuction prototypes
#define SERV_0_HEADER "PodBrainSM.h"
// the name of the Init function
#define SERV_0_INIT InitPodBrainSM
// the name of the run function
#define SERV_0_RUN RunPodBrainSM
// How big should this service's Queue be?
#define SERV_0_QUEUE_SIZE 3

/****************************************************************************/
// The following sections are used to define the parameters for each of the
// services. You only need to fill out as many as the number of services
// defined by NUM_SERVICES
/****************************************************************************/
// These are the definitions for Service 1
#if NUM_SERVICES > 1
// the header file with the public fuction prototypes
#define SERV_1_HEADER "TestService.h"
// the name of the Init function
#define SERV_1_INIT TestServiceInit
// the name of the run function
#define SERV_1_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_1_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 2
#if NUM_SERVICES > 2
// the header file with the public fuction prototypes
#define SERV_2_HEADER "TestService.h"
// the name of the Init function
#define SERV_2_INIT TestServiceInit
// the name of the run function
#define SERV_2_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_2_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 3
#if NUM_SERVICES > 3
// the header file with the public fuction prototypes
#define SERV_3_HEADER "TestService.h"
// the name of the Init function
#define SERV_3_INIT TestServiceInit
// the name of the run function
#define SERV_3_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_3_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 4
#if NUM_SERVICES > 4
// the header file with the public fuction prototypes
#define SERV_4_HEADER "TestService.h"
// the name of the Init function
#define SERV_4_INIT TestServiceInit
// the name of the run function
#define SERV_4_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_4_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 5
#if NUM_SERVICES > 5
// the header file with the public fuction prototypes
#define SERV_5_HEADER "TestService.h"
// the name of the Init function
#define SERV_5_INIT TestServiceInit
// the name of the run function
#define SERV_5_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_5_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 6
#if NUM_SERVICES > 6
// the header file with the public fuction prototypes
#define SERV_6_HEADER "TestService.h"
// the name of the Init function
#define SERV_6_INIT TestServiceInit
// the name of the run function
#define SERV_6_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_6_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 7
#if NUM_SERVICES > 7
// the header file with the public fuction prototypes
#define SERV_7_HEADER "TestService.h"
// the name of the Init function
#define SERV_7_INIT TestServiceInit
// the name of the run function
#define SERV_7_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_7_QUEUE_SIZE 3
#endif

/****************************************************************************/
// the name of the posting function that you want executed when a new
// keystroke is detected.
// The default initialization distributes keystrokes to all state machines
// DO NOT USE ED'S CHECK SERIAL PORT FUNCTION
//#define POST_KEY_FUNC ES_PostAll

/****************************************************************************/
// Name/define the events of interest
// Universal events occupy the lowest entries, followed by user-defined events
typedef enum {  ES_NO_EVENT = 0,
                ES_ERROR,  /* used to indicate an error from the service */
                ES_INIT,   /* used to transition from initial pseudo-state */
                //ES_NEW_KEY, /* signals a new key received from terminal */
                ES_TIMEOUT, /* signals that the timer has expired */
                /* User-defined events start here */
                CONNECT_BUTTON_PRESSED,
                SPI_RECEIVE_FLAG_HI,
                PACKET_RECEIVED
             } ES_EventTyp_t ;

/****************************************************************************/
// These are the definitions for the Distribution lists. Each definition
// should be a comma seperated list of post functions to indicate which
// services are on that distribution list.
#define NUM_DIST_LISTS 0
#if NUM_DIST_LISTS > 0
#define DIST_LIST0 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 1
#define DIST_LIST1 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 2
#define DIST_LIST2 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 3
#define DIST_LIST3 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 4
#define DIST_LIST4 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 5
#define DIST_LIST5 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 6
#define DIST_LIST6 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 7
#define DIST_LIST7 PostTemplateFSM
#endif

/****************************************************************************/
// This are the name of the Event checking funcion header file.
#define EVENT_CHECK_HEADER "EventCheckers.h"

/****************************************************************************/
// This is the list of event checking functions
#define EVENT_CHECK_LIST checkConnectButton

/****************************************************************************/
// These are the definitions for the post functions to be executed when the
// correspnding timer expires. All 8 must be defined. If you are not using
// a timers, then you can use TIMER_UNUSED
#define TIMER_UNUSED ((pPostFunc)0)
#define TIMER0_RESP_FUNC PostPodBrainSM
#define TIMER1_RESP_FUNC TIMER_UNUSED
#define TIMER2_RESP_FUNC TIMER_UNUSED
#define TIMER3_RESP_FUNC TIMER_UNUSED
#define TIMER4_RESP_FUNC TIMER_UNUSED
#define TIMER5_RESP_FUNC TIMER_UNUSED
#define TIMER6_RESP_FUNC TIMER_UNUSED
#define TIMER7_RESP_FUNC TIMER_UNUSED

/****************************************************************************/
// Give the timer numbers symbolc names to make it easier to move them
// to different timers if the need arises. Keep these definitons close to the
// definitions for the response functions to make it easire to check that
// the timer number matches where the timer event will be routed

#define TIMEOUT_TIMER 0

#endif /* CONFIGURE_H */
EventCheckers.h
#include "bitdefs.h"

// prototypes for test event checkers

boolean checkConnectButton(void);
boolean checkSPIFlag(void);
PODBrainSM.h
/****************************************************************************

  Header file for POD Brain State Machine
  based on the Gen2 Events and Services Framework

 ****************************************************************************/

#ifndef PodBrainSM_H
#define PodBrainSM_H

// Event Definitions
#include "ES_Configure.h"
#include "ES_Types.h"

// typedefs for the states
// State definitions for use with the query function
typedef enum { idleDisconnected, broadcasting,
               processingByte, connectedWaitingToTransmit, transmitting,
               processingStatusByte, pauseBeforeDisconnect,
               waitingToSendDisconnect
             } PodBrainSMState_t ;


// Public Function Prototypes

boolean InitPodBrainSM(uint8_t Priority);
boolean PostPodBrainSM(ES_Event ThisEvent);
ES_Event RunPodBrainSM(ES_Event ThisEvent);
PodBrainSMState_t QueryPodBrainSM(void);


#endif /* PodBrainSM_H */
ServoPic.h
void Servo_Init(void);
void Servo_Set(unsigned char value);

POD Radio

Const.h
// constant definitions

#include <htc.h>
#include "BITDEFS.h"

#define INPUT_PIN_MASK BIT0HI
#define INPUT_PORT PORTA

// define extra debugging LED ports/ pins

#define STATE_OUTPUT_PIN1 RC1
#define STATE_OUTPUT_PIN2 RC2

#define STATE_OUTPUT_DIRN1 TRISC1
#define STATE_OUTPUT_DIRN2 TRISC2

// definitions for SPI

#define SPI_FLAG_PORT SSPSTAT
#define SPI_FLAG_MASK BIT0HI

// baud rate given 8MHz oscillator

#define BAUD9_6K 0xCF;
ES_Configure.h
/****************************************************************************
 Module
     ES_Configure.h
 Description
     This file contains macro definitions that are edited by the user to
     adapt the Events and Services framework to a particular application.
 Notes

 History
 When           Who     What/Why
 -------------- ---     --------
 6/1/13         cmw      final 218C project code
 01/15/12 10:03 jec      started coding
*****************************************************************************/

#ifndef CONFIGURE_H
#define CONFIGURE_H

/****************************************************************************/
// The maximum number of services sets an upper bound on the number of
// services that the framework will handle. Reasonable values are 8 and 16
// HOWEVER: at this time only a value of 8 is supported.
#define MAX_NUM_SERVICES 8

/****************************************************************************/
// This macro determines that nuber of services that are *actually* used in
// a particular application. It will vary in value from 1 to MAX_NUM_SERVICES
#define NUM_SERVICES 1

/****************************************************************************/
// These are the definitions for Service 0, the lowest priority service
// every Events and Services application must have a Service 0. Further
// services are added in numeric sequence (1,2,3,...) with increasing
// priorities
// the header file with the public fuction prototypes
#define SERV_0_HEADER "PodRadioSM.h"
// the name of the Init function
#define SERV_0_INIT InitPodRadioSM
// the name of the run function
#define SERV_0_RUN RunPodRadioSM
// How big should this service's Queue be?
#define SERV_0_QUEUE_SIZE 3

/****************************************************************************/
// The following sections are used to define the parameters for each of the
// services. You only need to fill out as many as the number of services
// defined by NUM_SERVICES
/****************************************************************************/
// These are the definitions for Service 1
#if NUM_SERVICES > 1
// the header file with the public fuction prototypes
#define SERV_1_HEADER "TestService.h"
// the name of the Init function
#define SERV_1_INIT TestServiceInit
// the name of the run function
#define SERV_1_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_1_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 2
#if NUM_SERVICES > 2
// the header file with the public fuction prototypes
#define SERV_2_HEADER "TestService.h"
// the name of the Init function
#define SERV_2_INIT TestServiceInit
// the name of the run function
#define SERV_2_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_2_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 3
#if NUM_SERVICES > 3
// the header file with the public fuction prototypes
#define SERV_3_HEADER "TestService.h"
// the name of the Init function
#define SERV_3_INIT TestServiceInit
// the name of the run function
#define SERV_3_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_3_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 4
#if NUM_SERVICES > 4
// the header file with the public fuction prototypes
#define SERV_4_HEADER "TestService.h"
// the name of the Init function
#define SERV_4_INIT TestServiceInit
// the name of the run function
#define SERV_4_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_4_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 5
#if NUM_SERVICES > 5
// the header file with the public fuction prototypes
#define SERV_5_HEADER "TestService.h"
// the name of the Init function
#define SERV_5_INIT TestServiceInit
// the name of the run function
#define SERV_5_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_5_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 6
#if NUM_SERVICES > 6
// the header file with the public fuction prototypes
#define SERV_6_HEADER "TestService.h"
// the name of the Init function
#define SERV_6_INIT TestServiceInit
// the name of the run function
#define SERV_6_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_6_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 7
#if NUM_SERVICES > 7
// the header file with the public fuction prototypes
#define SERV_7_HEADER "TestService.h"
// the name of the Init function
#define SERV_7_INIT TestServiceInit
// the name of the run function
#define SERV_7_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_7_QUEUE_SIZE 3
#endif

/****************************************************************************/
// the name of the posting function that you want executed when a new
// keystroke is detected.
// The default initialization distributes keystrokes to all state machines
// DO NOT USE ED'S CHECK SERIAL PORT FUNCTION
//#define POST_KEY_FUNC ES_PostAll

/****************************************************************************/
// Name/define the events of interest
// Universal events occupy the lowest entries, followed by user-defined events
typedef enum {  ES_NO_EVENT = 0,
                ES_ERROR,  /* used to indicate an error from the service */
                ES_INIT,   /* used to transition from initial pseudo-state */
                ES_TIMEOUT, /* signals that the timer has expired */
                /* User-defined events start here */
                INPUT_SET,
                ASYNC_TX_EMPTY,
                ASYNC_BYTE_RECEIVED,
                SPI_BUFFER_FULL,
                SPI_PACKET_RECEIVED
             } ES_EventTyp_t ;

/****************************************************************************/
// These are the definitions for the Distribution lists. Each definition
// should be a comma seperated list of post functions to indicate which
// services are on that distribution list.
#define NUM_DIST_LISTS 0
#if NUM_DIST_LISTS > 0
#define DIST_LIST0 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 1
#define DIST_LIST1 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 2
#define DIST_LIST2 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 3
#define DIST_LIST3 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 4
#define DIST_LIST4 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 5
#define DIST_LIST5 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 6
#define DIST_LIST6 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 7
#define DIST_LIST7 PostTemplateFSM
#endif

/****************************************************************************/
// This are the name of the Event checking funcion header file.
#define EVENT_CHECK_HEADER "EventCheckers.h"

/****************************************************************************/
// This is the list of event checking functions
#define EVENT_CHECK_LIST checkTransmitEmpty, checkAsyncReceive

/****************************************************************************/
// These are the definitions for the post functions to be executed when the
// correspnding timer expires. All 8 must be defined. If you are not using
// a timers, then you can use TIMER_UNUSED
#define TIMER_UNUSED ((pPostFunc)0)
#define TIMER0_RESP_FUNC PostPodRadioSM
#define TIMER1_RESP_FUNC TIMER_UNUSED
#define TIMER2_RESP_FUNC TIMER_UNUSED
#define TIMER3_RESP_FUNC TIMER_UNUSED
#define TIMER4_RESP_FUNC TIMER_UNUSED
#define TIMER5_RESP_FUNC TIMER_UNUSED
#define TIMER6_RESP_FUNC TIMER_UNUSED
#define TIMER7_RESP_FUNC TIMER_UNUSED

/****************************************************************************/
// Give the timer numbers symbolc names to make it easier to move them
// to different timers if the need arises. Keep these definitons close to the
// definitions for the response functions to make it easire to check that
// the timer number matches where the timer event will be routed

#define KEY_DOWN_TIMER 0

#endif /* CONFIGURE_H */
EventCheckers.h
// prototypes for event checkers

#include "bitdefs.h"

boolean checkTransmitEmpty(void);
boolean checkAsyncReceive(void);
PodRadioSM.h
/****************************************************************************

  Header file for PodRadioSM.h
  based on the Gen2 Events and Services Framework

 ****************************************************************************/

#ifndef PodRadioSM_H
#define PodRadioSM_H

// Event Definitions
#include "ES_Configure.h"
#include "ES_Types.h"
#include "ES_Events.h"
#include "Const.h"

// typedefs for the states
// state definitions for use with the query function
typedef enum { waitingForResponse, waitingForMSB, waitingForLSB,
               suckingPacket, waitingForSPI,
               sendingCommandPacket
             } PodRadioSMState_t ;


// Public Function Prototypes

boolean InitPodRadioSM(uint8_t Priority);
boolean PostPodRadioSM(ES_Event ThisEvent);
ES_Event RunPodRadioSM(ES_Event ThisEvent);
PodRadioSMState_t QueryPodRadioSM(void);

void setCommandArray(unsigned char counter);
void writeStatusArray(unsigned char counter);
void gotoSPIState(void);


#endif /* PodRadioSM_H */

ROAMER 128

Constants.h
// Define constants that will be used throughout FSM

#include <hidef.h>
#include <mc9s12e128.h>
#include <S12e128bits.h>
#include <Bin_Const.h>


#define OneSec                   1000     //Waiting for over a second
//Based on 1ms tick rate

#define OneByte                  3        //Waiting for two byte times
//Which is just over 1ms

#define TransmitEmptyFlagMask    0x80
#define ReceiveFlagMask          0x20
#define SPIBufferFlagMask        0x80
#define SPITransmitEmpty         0x20
#define SPECIALLightMask         0x07

#define WHEEL_PWM_PORT           PTU
#define LEFT_WHEEL_CHANNEL       0
#define RIGHT_WHEEL_CHANNEL      1
#define LEFT_WHEEL_DIRECTION     BIT2HI
#define RIGHT_WHEEL_DIRECTION    BIT3HI
#define PWM_FREQUENCY            5000

#define MAX_LEFT_DC              100
#define MAX_RIGHT_DC             100

#define SPECIAL_SERVO_PORT       PTT
#define SPECIAL_SERVO_PIN        BIT0HI
#define SPECIAL_CHANNEL          0
#define SPECIAL_FORWARD          2700
#define SPECIAL_BACKWARD         680

#define GRIPPER_PORT             PTU
#define GRIPPER_DIRECTION        BIT5HI
#define GRIPPER_CHANNEL          4
#define GRIPPER_PWM              100

#define ROAMER_SELECT_PIN        AD0

#define INPUT_PORT         PTP
#define INPUT_PIN_MASK     0x01
ES_Configure.h
/****************************************************************************
 Module
     ES_Configure.h
 Description
     This file contains macro definitions that are edited by the user to
     adapt the Events and Services framework to a particular application.
 Notes

 History
 When           Who     What/Why
 -------------- ---     --------
 01/15/12 10:03 jec      started coding
*****************************************************************************/

#ifndef CONFIGURE_H
#define CONFIGURE_H

/****************************************************************************/
// The maximum number of services sets an upper bound on the number of
// services that the framework will handle. Reasonable values are 8 and 16
// HOWEVER: at this time only a value of 8 is supported.
#define MAX_NUM_SERVICES 8

/****************************************************************************/
// This macro determines that nuber of services that are *actually* used in
// a particular application. It will vary in value from 1 to MAX_NUM_SERVICES
#define NUM_SERVICES 2

/****************************************************************************/
// These are the definitions for Service 0, the lowest priority service
// every Events and Services application must have a Service 0. Further
// services are added in numeric sequence (1,2,3,...) with increasing
// priorities
// the header file with the public fuction prototypes
#define SERV_0_HEADER "RoamerRadioSM.h"
// the name of the Init function
#define SERV_0_INIT InitRoamerRadioSM
// the name of the run function
#define SERV_0_RUN RunRoamerRadioSM
// How big should this services Queue be?
#define SERV_0_QUEUE_SIZE 3

/****************************************************************************/
// The following sections are used to define the parameters for each of the
// services. You only need to fill out as many as the number of services
// defined by NUM_SERVICES
/****************************************************************************/
// These are the definitions for Service 1
#if NUM_SERVICES > 1
// the header file with the public fuction prototypes
#define SERV_1_HEADER "RoamerService.h"
// the name of the Init function
#define SERV_1_INIT InitRoamerService
// the name of the run function
#define SERV_1_RUN RunRoamerService
// How big should this services Queue be?
#define SERV_1_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 2
#if NUM_SERVICES > 2
// the header file with the public fuction prototypes
#define SERV_2_HEADER "TestService.h"
// the name of the Init function
#define SERV_2_INIT TestServiceInit
// the name of the run function
#define SERV_2_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_2_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 3
#if NUM_SERVICES > 3
// the header file with the public fuction prototypes
#define SERV_3_HEADER "TestService.h"
// the name of the Init function
#define SERV_3_INIT TestServiceInit
// the name of the run function
#define SERV_3_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_3_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 4
#if NUM_SERVICES > 4
// the header file with the public fuction prototypes
#define SERV_4_HEADER "TestService.h"
// the name of the Init function
#define SERV_4_INIT TestServiceInit
// the name of the run function
#define SERV_4_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_4_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 5
#if NUM_SERVICES > 5
// the header file with the public fuction prototypes
#define SERV_5_HEADER "TestService.h"
// the name of the Init function
#define SERV_5_INIT TestServiceInit
// the name of the run function
#define SERV_5_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_5_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 6
#if NUM_SERVICES > 6
// the header file with the public fuction prototypes
#define SERV_6_HEADER "TestService.h"
// the name of the Init function
#define SERV_6_INIT TestServiceInit
// the name of the run function
#define SERV_6_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_6_QUEUE_SIZE 3
#endif

/****************************************************************************/
// These are the definitions for Service 7
#if NUM_SERVICES > 7
// the header file with the public fuction prototypes
#define SERV_7_HEADER "TestService.h"
// the name of the Init function
#define SERV_7_INIT TestServiceInit
// the name of the run function
#define SERV_7_RUN TestServiceRun
// How big should this services Queue be?
#define SERV_7_QUEUE_SIZE 3
#endif

/****************************************************************************/
// the name of the posting function that you want executed when a new
// keystroke is detected.
// The default initialization distributes keystrokes to all state machines
#define POST_KEY_FUNC ES_PostAll

/****************************************************************************/
// Name/define the events of interest
// Universal events occupy the lowest entries, followed by user-defined events
typedef enum {  ES_NO_EVENT = 0,
                ES_ERROR,  /* used to indicate an error from the service */
                ES_INIT,   /* used to transition from initial pseudo-state */
                ES_NEW_KEY, /* signals a new key received from terminal */
                ES_TIMEOUT, /* signals that the timer has expired */
                /* User-defined events start here */
                ASYNC_BYTE_RECEIVED,
                ASYNC_TX_EMPTY,
                SPI_BUFFER_FULL,
                INPUT_SET
             } ES_EventTyp_t ;

/****************************************************************************/
// These are the definitions for the Distribution lists. Each definition
// should be a comma seperated list of post functions to indicate which
// services are on that distribution list.
#define NUM_DIST_LISTS 0
#if NUM_DIST_LISTS > 0
#define DIST_LIST0 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 1
#define DIST_LIST1 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 2
#define DIST_LIST2 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 3
#define DIST_LIST3 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 4
#define DIST_LIST4 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 5
#define DIST_LIST5 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 6
#define DIST_LIST6 PostTemplateFSM
#endif
#if NUM_DIST_LISTS > 7
#define DIST_LIST7 PostTemplateFSM
#endif

/****************************************************************************/
// This are the name of the Event checking funcion header file.
#define EVENT_CHECK_HEADER "EventCheckers.h"

/****************************************************************************/
// This is the list of event checking functions
#define EVENT_CHECK_LIST checkAsyncReceive, checkTransmitEmpty

/****************************************************************************/
// These are the definitions for the post functions to be executed when the
// correspnding timer expires. All 8 must be defined. If you are not using
// a timers, then you can use TIMER_UNUSED
#define TIMER_UNUSED ((pPostFunc)0)
#define TIMER0_RESP_FUNC PostRoamerRadioSM
#define TIMER1_RESP_FUNC PostRoamerRadioSM
#define TIMER2_RESP_FUNC PostRoamerService
#define TIMER3_RESP_FUNC TIMER_UNUSED
#define TIMER4_RESP_FUNC TIMER_UNUSED
#define TIMER5_RESP_FUNC TIMER_UNUSED
#define TIMER6_RESP_FUNC TIMER_UNUSED
#define TIMER7_RESP_FUNC TIMER_UNUSED

/****************************************************************************/
// Give the timer numbers symbolc names to make it easier to move them
// to different timers if the need arises. Keep these definitons close to the
// definitions for the response functions to make it easier to check that
// the timer number matches where the timer event will be routed

#define DISCONNECT_TIMER   0
#define BYTE_TIMER         1
#define GRIPPER_TIMER      2

#endif /* CONFIGURE_H */
EventCheckers.h
// prototypes for RoamerRadio event checkers

boolean checkAsyncReceive(void);
boolean checkTransmitEmpty(void);

#include <bitdefs.h>

// #defines that go with the Event Checkers

#define LOCK_PIN    BIT0HI
#define UNLOCK_PIN  BIT1HI
RoamerRadioSM.h
/****************************************************************************

  Header file for template Flat Sate Machine
  based on the Gen2 Events and Services Framework

 ****************************************************************************/

#ifndef FSMRoamer_H
#define FSMRoamer_H

// Event Definitions
#include "ES_Configure.h"
#include "ES_Types.h"

// typedefs for the states
// State definitions for use with the query function
typedef enum { waitingForResponse, waitingForMSB, waitingForLSB,
               suckingPacket, transmittingSPI, sendingStatusPacket
             } RoamerRadioState_t ;


// Public Function Prototypes

boolean InitRoamerRadioSM(uint8_t Priority);
boolean PostRoamerRadioSM(ES_Event ThisEvent);
ES_Event RunRoamerRadioSM(ES_Event ThisEvent);
RoamerRadioState_t QueryRoamerRadioSM(void);

unsigned char RetrieveCommand(unsigned char CommandNumber);
unsigned char GetLowerAddressByte(void);

#endif /* FSMTemplate_H */
RoamerService.h
/****************************************************************************
 
  Header file for template service 
  based on the Gen 2 Events and Services Framework

 ****************************************************************************/

#ifndef ServRoamer_H
#define ServRoamer_H

#include "ES_Types.h"

// Public Function Prototypes

boolean InitRoamerService ( uint8_t Priority );
boolean PostRoamerService( ES_Event ThisEvent );
ES_Event RunRoamerService( ES_Event ThisEvent );

unsigned char RetrieveRoamerNumber( void );

#endif /* ServTemplate_H */

Sources

POD Brain

ADPic.c
#include <htc.h>
#include "ADPic.h"
#include "BITDEFS.H"

// definitions for channels when ADCON0 written
static const unsigned char ADReadArray[] = {0b00000001,
                                            0b00000101,
                                            0b00001001,
                                            0b00011101,
                                            0b00101101
                                           };
// initialization
void AD_Init(void)
{
    TRISA |= 0b00000111;        //A0,A1,A2 as inputs
    TRISB |= 0b00100000;        //B5 as input
    TRISC |= 0b00001000;        //C3 as input
    ANSEL |= 0b10000111;        //AN0,AN1,AN2,AN7 as analog
    ANSELH |= 0b00001000;       //AN11 as analog
}


//***************************************************************
//      Channel Select =    0 (AN0)
//                          1 (AN1)
//                          2 (AN2)
//                          3 (AN7)
//                          4 (AN11)
//***************************************************************
unsigned char AD_Read(unsigned char channel)
{
    int i;
    // write ADCON0 with desired channel
    ADCON0 = ADReadArray[channel];
    // wait required time interval
    for (i = 0; i < 36; i++);
    // set GO bit, wait until conversion complete
    GO = 1;
    while (GO == 1);
    return ADRESH;
}
ES_Port.c
#include <htc.h>
#include "ES_Port.h"
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "ES_Timers.h"
#include "PodBrainSM.h"

static unsigned char TickCount;

/****************************************************************************
 Function
     ES_Timer_Init
 Parameters
     unsigned char Rate set to one of the TMR_RATE_XX values to set the
     RTI rate
 Returns
     None.
 Description
     Initializes the timer module by setting up the RTI with the requested
    rate. Edited to initialize timer 0.
 Notes
     For PIC testing: a quick hack that fixes the rate at 10ms
 Author
     J. Edward Carryer, 02/24/97 14:23
****************************************************************************/
void ES_Timer_Init(TimerRate_t Rate)
{
    // keep the compiler from compaining about not referenceing param
    // since in this simple test, we are not allowing a different rate
    Rate;

    // Ed's timer 1 initialization moved to timer 0

    // 2 MHz clock, 4 clocks per instruction/tick, 1ms tick
#define COUNTS (2000000/4/1000);

    // BIT3HI = no prescaler
    // BIT5LO = use internal instruction clock
    OPTION_REG |= 0b00001000;
    OPTION_REG &= ~0b00100000;

    // BIT2HI = Timer0 Overflow Interrupt Flag
    // BIT5HI = Timer0 Overflow Interrupt Enable
    // BIT7HI = Global Interrupt Enable
    INTCON |= 0b10100000;

    // Write 2's complement of delay
    TMR0 = 0xFF - COUNTS;
    TMR0 = TMR0 + 1;

    // end timer 0 initialization/ implementation in timing

    EnableInterrupts; /* make sure that interrupts are enabled*/
}

void interrupt ISR(void)
{
    // interrupt processing of timer 0
    // check whether overflow flag set
    if (T0IF == 1)
    {
#define ES_TIMER_RATE_MS 80
        static unsigned char TimerCount = 0;

        // Increment timer tick count
        TimerCount++;
        // Clear flag
        T0IF = 0;
        // If we get to our desired time for ES_Timer tick rate
        if (TimerCount == ES_TIMER_RATE_MS)
        {
            // Reset internal counter
            TimerCount = 0;
            // Increment framework Timer tick
            TickCount++;
        }
    }

    // interrupt processing of SPI
    if (SSPIF == 1)
    {
        ES_Event ThisEvent;

        // clear interrupt flag
        SSPIF = 0;

        // event detected, so post detected event
        ThisEvent.EventType = SPI_RECEIVE_FLAG_HI;
        ThisEvent.EventParam = 1;
        PostPodBrainSM(ThisEvent);
    }
}

unsigned char IsTimerPending(void)
{
    if (TickCount > 0)
    {
        TickCount--;
        return (1);
    }
    else
    {
        return (0);
    }
}
EventCheckers.c
// Event Checking functions for use in testing the CheckUserEvents
// functionality

#include "ES_Configure.h"
#include "ES_Framework.h"
#include "EventCheckers.h"
#include "Const.h"

#include <htc.h>

// this pulls in the headers for all services, which gets us the
// prototypes for all of the post functions
#include "ES_ServiceHeaders.h"

// event checker for connect button change to HI
boolean checkConnectButton(void)
{
    static unsigned char LastPinState = 0;
    unsigned char CurrentPinState;
    boolean ReturnVal = False;

    CurrentPinState = CONNECT_BUTTON_PORT & CONNECT_BUTTON_MASK;
    // check for pin high and different from last time
    if ((CurrentPinState != LastPinState) &&
            (CurrentPinState == CONNECT_BUTTON_MASK))
    {
        // event detected, so post detected event
        ES_Event ThisEvent;
        ThisEvent.EventType = CONNECT_BUTTON_PRESSED;
        ThisEvent.EventParam = 1;
        PostPodBrainSM(ThisEvent);
        ReturnVal = True;
    }
    LastPinState = CurrentPinState;

    return ReturnVal;
}
PodBrainSM.c
/****************************************************************************
 Module
   PodBrainSM.c

 Revision
   1.0.1

 Description
   This is the Pod Brain State Machine under the
   Gen2 Events and Services Framework.

 Notes

 History
 When           Who     What/Why
 -------------- ---     --------
 6/2/13         cmw      final implementation of 218C project
 01/15/12 11:12 jec      revisions for Gen2 framework
 11/07/11 11:26 jec      made the queue static
 10/30/11 17:59 jec      fixed references to CurrentEvent in RunTemplateSM()
 10/23/11 18:20 jec      began conversion from SMTemplate.c (02/20/07 rev)
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* include header files for this state machine as well as any machines at the
   next lower level in the hierarchy that are sub-machines to this machine
*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "PodBrainSM.h"
#include "Const.h"
#include "BITDEFS.H"
#include "ADPic.h"
#include "ServoPic.h"

/*----------------------------- Module Defines ----------------------------*/
#define SPI_BYTES_TO_SEND 12
/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/
void buildArray(void);
/*---------------------------- Module Variables ---------------------------*/
// everybody needs a state variable, you may need others as well.
// type of state variable should match htat of enum in header file
static PodBrainSMState_t CurrentState;

// with the introduction of Gen2, we need a module level Priority var as well
static uint8_t MyPriority;

unsigned char noReplyCount;
unsigned char disconFlag;
unsigned char SPIBytesSent;
unsigned char roamerSelectInput;
unsigned char commandArray[12];
unsigned char statusArray[12];
unsigned char roamerSelectReading;

/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitPodBrainSM

 Parameters
     uint8_t : the priorty of this service

 Returns
     boolean, False if error in initialization, True otherwise

 Description
     Saves away the priority, sets up the initial transition and does any
     other required initialization for this state machine
 Notes

 Author
     J. Edward Carryer, 10/23/11, 18:55
****************************************************************************/
boolean InitPodBrainSM(uint8_t Priority)
{
    // Initialize timer
    ES_Timer_Init(ES_Timer_RATE_4MS);

    // initialize variables
    // init command array
    commandArray[0] = 0x0B; // length LSB
    commandArray[1] = 0x01; // API ID
    commandArray[2] = 0x01; // frame ID
    commandArray[3] = 0x21; // add MSB for our ROAMER
    commandArray[4] = 0x84; // add LSB for our ROAMER
    commandArray[5] = 0x00; // command options
    commandArray[6] = 0x00; // data bit 0
    commandArray[7] = 0x00; // data bit 1
    commandArray[8] = 0x00; // data bit 2
    commandArray[9] = 0x00; // data bit 3
    commandArray[10] = 0x00; // data bit 4
    commandArray[11] = 0x00; // data bit 5

    // initialize counters, discon flag, roamer select
    noReplyCount = 0;
    disconFlag = 1; // currently disconnected
    SPIBytesSent = 0;
    roamerSelectReading = 0;

    // set active connection LED low for debugging
    ACTIVE_CONNECTION_INDICATOR_PORT =
        ACTIVE_CONNECTION_INDICATOR_PORT &
        ~ACTIVE_CONNECTION_INDICATOR_MASK;

    // initialize PWM for servo
    Servo_Init();

    MyPriority = Priority;

    // initialize state
    CurrentState = idleDisconnected;

    return True;

}

/****************************************************************************
 Function
     PostPodBrainSM

 Parameters
     EF_Event ThisEvent , the event to post to the queue

 Returns
     boolean False if the Enqueue operation failed, True otherwise

 Description
     Posts an event to this state machine's queue
 Notes

 Author
     J. Edward Carryer, 10/23/11, 19:25
****************************************************************************/
boolean PostPodBrainSM(ES_Event ThisEvent)
{
    return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunPodBrainSM

 Parameters
   ES_Event : the event to process

 Returns
   ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
   Runs the Pod Brain state machine
 Notes
   uses nested switch/case to implement the machine.
 Author
   J. Edward Carryer, 01/15/12, 15:23
****************************************************************************/
ES_Event RunPodBrainSM(ES_Event ThisEvent)
{
    ES_Event ReturnEvent;
    ReturnEvent.EventType = ES_NO_EVENT; // assume no errors

    switch (CurrentState)
    {
            // start in disconnected state, wait for button press
        case idleDisconnected:
            // if disconnected and connect button pressed
            if ((ThisEvent.EventType == CONNECT_BUTTON_PRESSED) &&
                    (disconFlag == 1))
            {
                // prepare to broadcast:
                // clear disconnected flag
                disconFlag = 0;
                // prepare array for transmitting broadcast
                commandArray[3] = 0xFF; // broadcast
                commandArray[4] = 0xFF; // broadcast
                // set data to all zeros
                commandArray[6] = 0x00;
                commandArray[8] = 0x00;
                commandArray[9] = 0x00;
                commandArray[10] = 0x00;
                commandArray[11] = 0x00;

                // read and save roamer select
                roamerSelectReading = AD_Read(ROAMER_SELECT_CHANNEL);
                if (roamerSelectReading > 0xC0)
                {
                    commandArray[7] = 0x03;
                }
                else if (roamerSelectReading < 0x40)
                {
                    commandArray[7] = 0x02;
                }
                else
                {
                    commandArray[7] = 0x01;
                }

                // set slave select low
                SLAVE_SELECT_PORT = (SLAVE_SELECT_PORT & ~SLAVE_SELECT_MASK);
                // read buffer before writing
                SSPBUF;
                // clear flag before writing
                SSPIF = 0;
                // write first byte to SPI
                SSPBUF = commandArray[0];
                // increment bytes sent
                SPIBytesSent = 1;
                // reset timeout timer for .2sec
                ES_Timer_InitTimer(TIMEOUT_TIMER, _200_MS);
                // update current state
                CurrentState = broadcasting;
            }
            break;
        case broadcasting:
            // if we get incoming SPI when broadcasting
            if (ThisEvent.EventType == SPI_RECEIVE_FLAG_HI)
            {
                // test whether sending in progress or finished
                if (SPIBytesSent < SPI_BYTES_TO_SEND)
                {
                    // save contents to status array
                    statusArray[SPIBytesSent - 1] = SSPBUF;
                    // write next command array contents to SPI
                    SSPBUF = commandArray[SPIBytesSent];
                    // increment counter
                    SPIBytesSent++;
                }
                else
                {
                    // save contents to status array
                    statusArray[SPIBytesSent - 1] = SSPBUF;
                    // reset counter
                    SPIBytesSent = 0;
                    // raise SS line
                    SLAVE_SELECT_PORT = (SLAVE_SELECT_PORT | SLAVE_SELECT_MASK);
                    // post packet received event
                    ES_Event ThisEvent;
                    ThisEvent.EventType = PACKET_RECEIVED;
                    ThisEvent.EventParam = 1;
                    PostPodBrainSM(ThisEvent);

                    // put in state to process packet
                    CurrentState = processingByte;
                }
            }
            // put back in this state if no reply received, initialize sending
            else if ((ThisEvent.EventType == ES_TIMEOUT) &&
                     (ThisEvent.EventParam == TIMEOUT_TIMER))
            {
                // set slave select low
                SLAVE_SELECT_PORT = (SLAVE_SELECT_PORT & ~SLAVE_SELECT_MASK);
                // read buffer before writing
                SSPBUF;
                // clear flag before writing
                SSPIF = 0;
                // write first byte to SPI
                SSPBUF = commandArray[0];
                // increment bytes sent
                SPIBytesSent = 1;
                // reset timeout timer for .2sec
                ES_Timer_InitTimer(TIMEOUT_TIMER, _200_MS);
            }
            break;
        case processingByte:
            if (ThisEvent.EventType == PACKET_RECEIVED)
            {
                // if byte 1 is zero, no reply received
                if (statusArray[1] == 0x00)
                {
                    // if count less than 4, increment count and put in
                    // broadcasting
                    if (noReplyCount < 4)
                    {
                        noReplyCount++;
                        CurrentState = broadcasting;
                    }
                    else
                    {
                        // reset counter
                        noReplyCount = 0;
                        // set disconnected flag; LED aleady off
                        disconFlag = 1;
                        CurrentState = idleDisconnected;
                    }
                }
                else if (statusArray[6] == 0x04)
                    // connect accepted
                {
                    // set connect LED high
                    ACTIVE_CONNECTION_INDICATOR_PORT =
                        ACTIVE_CONNECTION_INDICATOR_PORT |
                        ACTIVE_CONNECTION_INDICATOR_MASK;
                    // save incoming address
                    commandArray[3] = statusArray[2];
                    commandArray[4] = statusArray[3];
                    // reset no reply count
                    noReplyCount = 0;
                    CurrentState = connectedWaitingToTransmit;
                }
                else
                {
                    // if any other reply received, perform same
                    // actions as no reply
                    if (noReplyCount < 4)
                    {
                        noReplyCount++;
                        CurrentState = broadcasting;
                    }
                    else
                    {
                        // reset counter
                        noReplyCount = 0;
                        // set disconnected flag; LED aleady off
                        disconFlag = 1;
                        CurrentState = idleDisconnected;
                    }
                }
            }
            break;
        case connectedWaitingToTransmit:
            // if timeout received in waiting to transmit state,
            // transmit updated commands to SPI for sending to
            // radio PIC
            if ((ThisEvent.EventType == ES_TIMEOUT) &&
                    (ThisEvent.EventParam == TIMEOUT_TIMER))
            {
                buildArray();
                // set slave select low
                SLAVE_SELECT_PORT = (SLAVE_SELECT_PORT & ~SLAVE_SELECT_MASK);
                // clear flag before writing
                SSPIF = 0;
                // write first byte to SPI
                SSPBUF = commandArray[0];
                // increment bytes sent
                SPIBytesSent = 1;
                // reset timeout timer for .2sec
                ES_Timer_InitTimer(TIMEOUT_TIMER, _200_MS);
                CurrentState = transmitting;
            }
            else if (ThisEvent.EventType == CONNECT_BUTTON_PRESSED)
            {
                // set flag to disconnected
                disconFlag = 1;
                noReplyCount = 0;
                CurrentState = waitingToSendDisconnect;
            }
            break;
        case transmitting:
            // if incoming SPI received while transmitting
            if (ThisEvent.EventType == SPI_RECEIVE_FLAG_HI)
            {
                // check whether transfer in progress or complete
                if (SPIBytesSent < SPI_BYTES_TO_SEND)
                {
                    // save contents to status array
                    statusArray[SPIBytesSent - 1] = SSPBUF;
                    // write next command array contents to SPI
                    SSPBUF = commandArray[SPIBytesSent];
                    // increment counter
                    SPIBytesSent++;
                }
                else
                {
                    // transfer complete
                    // save contents to status array
                    statusArray[SPIBytesSent - 1] = SSPBUF;
                    // reset counter
                    SPIBytesSent = 0;
                    // raise SS line
                    SLAVE_SELECT_PORT = (SLAVE_SELECT_PORT | SLAVE_SELECT_MASK);
                    if (disconFlag == 1)
                    {
                        // disconnect request received previously
                        // set connect LED low
                        ACTIVE_CONNECTION_INDICATOR_PORT =
                            ACTIVE_CONNECTION_INDICATOR_PORT &
                            ~ACTIVE_CONNECTION_INDICATOR_MASK;
                        // set servo to zero
                        Servo_Set(0x00);
                        CurrentState = pauseBeforeDisconnect;
                    }
                    else
                    {
                        // post packet received event
                        ES_Event ThisEvent;
                        ThisEvent.EventType = PACKET_RECEIVED;
                        ThisEvent.EventParam = 1;
                        PostPodBrainSM(ThisEvent);
                        CurrentState = processingStatusByte;
                    }
                }
            }
            break;
        case processingStatusByte:
            // if full packet received, proceed to process
            if (ThisEvent.EventType == PACKET_RECEIVED)
            {
                // if first byte zero, it's a no reply
                if (statusArray[1] == 0x00)
                {
                    if (noReplyCount < 4)
                    {
                        noReplyCount++;
                        CurrentState = connectedWaitingToTransmit;
                    }
                    else
                    {
                        // if counter at 5, disconnect
                        // reset counter
                        noReplyCount = 0;
                        // turn off active connection indicator
                        ACTIVE_CONNECTION_INDICATOR_PORT =
                            ACTIVE_CONNECTION_INDICATOR_PORT &
                            ~ACTIVE_CONNECTION_INDICATOR_MASK;
                        disconFlag = 1;
                        // set timer
                        ES_Timer_InitTimer(TIMEOUT_TIMER, _200_MS);
                        CurrentState = pauseBeforeDisconnect;
                    }
                }
                else
                {
                    // good packet successfully received
                    // set battery charge indicator to incoming byte val
                    Servo_Set(statusArray[7]);
                    // reset no reply count
                    noReplyCount = 0;
                    CurrentState = connectedWaitingToTransmit;
                }
            }
            // if connect button pressed, prepare to disconnect
            else if (ThisEvent.EventType == CONNECT_BUTTON_PRESSED)
            {
                disconFlag = 1;
                noReplyCount = 0;
                CurrentState = waitingToSendDisconnect;
            }
            break;
            // waiting to send disconnect state - builds array for transmitting
            // disconnect
        case waitingToSendDisconnect:
            if ((ThisEvent.EventType == ES_TIMEOUT) &&
                    (ThisEvent.EventParam == TIMEOUT_TIMER))
            {
                commandArray[6] = 0x01;
                commandArray[7] = 0x00;
                commandArray[8] = 0x00;
                commandArray[9] = 0x00;
                commandArray[10] = 0x00;
                commandArray[11] = 0x00;
                // set slave select low
                SLAVE_SELECT_PORT = (SLAVE_SELECT_PORT & ~SLAVE_SELECT_MASK);
                // clear flag before writing
                SSPIF = 0;
                // write first byte to SPI
                SSPBUF = commandArray[0];
                // increment bytes sent
                SPIBytesSent = 1;
                // reset timeout timer for .2sec
                ES_Timer_InitTimer(TIMEOUT_TIMER, _200_MS);
                CurrentState = transmitting;
            }
            break;
            // state to pause before discon - ensures that discon/recon
            // does not happen too fast
        case pauseBeforeDisconnect:
            if ((ThisEvent.EventType == ES_TIMEOUT) &&
                    (ThisEvent.EventParam == TIMEOUT_TIMER))
            {
                CurrentState = idleDisconnected;
                // reset counters
                noReplyCount = 0;
                SPIBytesSent = 0;
            }
            break;
    }    // end switch on Current State
    return ReturnEvent;
}

/****************************************************************************
 Function
     QueryPodBrainSM

 Parameters
     None

 Returns
     PodBrainSMState_t The current state of the Pod Brain state machine

 Description
     returns the current state of the Template state machine
 Notes

 Author
     J. Edward Carryer, 10/23/11, 19:21
****************************************************************************/
PodBrainSMState_t QueryPodBrainSM(void)
{
    return (CurrentState);
}

/***************************************************************************
 private functions
 ***************************************************************************/
// Build array using current readings before sending
void buildArray(void)
{
    unsigned int intTemp;
    // build data portion of array
    // command designator
    commandArray[6] = 0x02;

    //left wheel command linear remapping: back emf -> 8 bit
    //0x80 - center of command interval (between 0x00 and 0xFF)
    //0x53 - analog value that left wheel sits at when not moving
    intTemp = (unsigned int)AD_Read(LEFT_WHEEL_CONTROL_CHANNEL) * 0x80 / 0x53;
    if (intTemp > 0xFF)
    {
        commandArray[7] = 0xFF;
    }
    else
    {
        commandArray[7] = intTemp;
    }

    //0x53 - analog value that right wheel sits at when not moving
    intTemp = (unsigned int)AD_Read(RIGHT_WHEEL_CONTROL_CHANNEL) * 0x80 / 0x53;
    if (intTemp > 0xFF)
    {
        commandArray[8] = 0xFF;
    }
    else
    {
        commandArray[8] = intTemp;
    }

    // build gripper byte
    commandArray[9] = AD_Read(GRIPPER_CONTROL_ANALOG_CHANNEL) / 2;
    commandArray[9] |= (GRIPPER_INPUT_DIGITAL_PORT & GRIPPER_INPUT_DIGITAL_MASK) << 4;

    // write camera control byte
    commandArray[10] = AD_Read(SPECIAL_POSITION_CONTROL_CHANNEL);

    // write digital input byte
    commandArray[11] = (LIGHT_SWITCH_2_PORT & LIGHT_SWITCH_2_MASK) |
                       (LIGHT_SWITCH_1_PORT & LIGHT_SWITCH_1_MASK) |
                       (LIGHT_SWITCH_0_PORT & LIGHT_SWITCH_0_MASK);
}
ServoPic.c
#include <htc.h>
#include "ServoPic.h"
#include "BITDEFS.H"

/*
 * Output is on port C5
 */

// initialization
void Servo_Init(void)
{
    // Number of cycles in Timer2 module
    PR2 = 255;
    // Initialize Timer2
    T2CON |= BIT2HI | BIT1HI;
    CCP1CON | = BIT3HI | BIT2HI | BIT1HI | BIT0HI;
}

void Servo_Set(unsigned char value)
{
    static unsigned int MIN_TIME = 500;
    static unsigned int MAX_TIME = 2700;
    static unsigned int time;
    static unsigned int counter;

    // Scale value to time (256 values to time)
    time = ((unsigned long)value) * (MAX_TIME - MIN_TIME) / 0xFF + MIN_TIME;

    // 16us resolution so divide to get count need
    // Subtract since PWM is active low
    counter = (unsigned int)1024 - (time / 16);
    CCPR1L = counter / 4;
    switch (counter % 4)
    {
        case 0:
            CCP1CON &= ~(BIT4HI | BIT5HI);
            break;
        case 1:
            CCP1CON |= BIT4HI;
            CCP1CON &= ~BIT5HI;
            break;
        case 2:
            CCP1CON |= BIT5HI;
            CCP1CON &= ~BIT4HI;
            break;
        case 3:
            CCP1CON |= (BIT4HI | BIT5HI);
            break;
    }
}
TestMain.c
#define _LEGACY_HEADERS
#include <htc.h>
#include <stdio.h>
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "ES_Timers.h"
#include "sci.h"
#include "BITDEFS.H"
#include "ADPic.h"
#include "ServoPic.h"

__CONFIG(INTIO & INTIO & WDTDIS & PWRTEN & MCLRDIS & UNPROTECT & BORDIS & IESODIS & FCMDIS);

void main(void)
{
    ES_Return_t ErrorType;

// hardware initialization
    /***** 16F690 Code ****
     *  Internal oscillator set to 2MHz
     */
    OSCCON  = 0b01010000;

// initially set all I/O to digital
    ANSEL = 0x00;
    ANSELH = 0x00;

// initialize I/O pins
// PORTA all inputs
    TRISB = ~BIT6HI;
    TRISC = ~BIT5HI & ~BIT4HI & ~BIT6HI & ~BIT7HI;

// initialize AD converter
    AD_Init();

// raise SS line
    PORTC = PORTC | BIT6HI;

// initialize SPI
    SSPCON = BIT5HI | BIT4HI | BIT1HI;

// clear interrupt flag
    SSPIF = 0;

// enable interrupt for incoming SPI bytes
    PIE1 = PIE1 | BIT3HI;

// global and peripheral interrupt enable
    INTCON = INTCON | (BIT7HI | BIT6HI);

// set connection inicator to output
    TRISC = TRISC & ~BIT4HI;

// now initialize the Events and Services Framework and start it running
    ErrorType = ES_Initialize(ES_Timer_RATE_1MS);
    if (ErrorType == Success)
    {

        ErrorType = ES_Run();

    }
//if we got to here, there was an error
    switch (ErrorType)
    {
        case FailedPointer:
            puts("Failed on NULL pointer");
            break;
        case FailedInit:
            puts("Failed Initialization");
            break;
        default:
            puts("Other Failure");
            break;
    }
    for (;;)
        ;

};

/*------------------------------- Footnotes -------------------------------*/
/*------------------------------ End of file ------------------------------*/

POD Radio

ES_Port.c
#include <htc.h>
#include "ES_Port.h"
#include "PodRadioSM.h"

static unsigned char TickCount;
static unsigned char SPIBytesReceived;


// timing parameter. This is based on an 8MHz internal clock and a divide by
// 4 prescaler on Timer 1 for the CCP and a desired tick rate of 100Hz.
#define COUNTS_PER_TICK ((8000000L/4/4)/100)
// total bytes sent/ received over SPI
#define SPI_BYTES_TO_RECEIVE 12

/****************************************************************************
 Function
     ES_Timer_Init
 Parameters
     unsigned char Rate set to one of the TMR_RATE_XX values to set the
     RTI rate
 Returns
     None.
 Description
     Initializes the timer module by setting up the RTI with the requested
    rate
 Notes
     For PIC testing: a quick hack that fixes the rate at 10ms
 Author
     J. Edward Carryer, 02/24/97 14:23
****************************************************************************/
void ES_Timer_Init(TimerRate_t Rate)
{
    // keep the compiler from compaining about not referenceing param
    // since in this simple test, we are not allowing a different rate
    Rate;
    /*  Timer 1 interrupt disabled.
     *  CCP Module 1 interrupt enabled.
     */
    PIE1    = 0b00001100;
    /*
     *  Peripheral interrupts enabled
     *  Global interrupt disabled during initialization
     */
    INTCON  = 0b01000000;
    /***** CCP Module 1 Code ****
    *  CCP Module 1 enabled
    *  CCP Module 1 in Compare mode
    *  Software interrupt on match
    */
    CCP1CON = 0b00001010;
    /*
     *  Comparator value set to 1
     */
    CCPR1L  = 0b00000001;
    /*
     *  Comparator value set to 1
     */
    CCPR1H  = 0b00000000;

    /***** Timer 1 Code ****
     *  Timer is active and running
     *  Timer derived from system clock
     *  Time is not Gated
     *  Timer prescaler is 1:4
     */
    T1CON = 0b00100001;

// initialize SPI bytes received to 1
    SPIBytesReceived = 1;

    EnableInterrupts; /* make sure that interrupts are enabled*/
}


void interrupt ISR(void)
{
    // make these static for speed in ISR
    static unsigned int LastTime;
    static unsigned int NextTime;

    if (CCP1IF == 1) // this was a timer interrupt
    {
        LastTime = ((unsigned int)CCPR1H << 8) + CCPR1L;
        NextTime = LastTime + COUNTS_PER_TICK;
        CCPR1H = (unsigned char)(NextTime >> 8);
        CCPR1L = (unsigned char)(NextTime & 0xFF);
        CCP1IF = 0; // clear source flag
        TickCount++; // count number of timer tick that need to be processed
    }

    // respond to incoming SPI byte
    if (SSPIF == 1)
    {
        // put state machine back in SPI state in order to respond
        gotoSPIState();
        // clear interrupt flag
        SSPIF = 0;

        // test whether SPI in progress or finished
        if (SPIBytesReceived < SPI_BYTES_TO_RECEIVE)
        {
            // save contents of buffer
            setCommandArray(SPIBytesReceived);
            // write next array value to buffer
            writeStatusArray(SPIBytesReceived);
            // increment SPIBytesReceived
            SPIBytesReceived++;
        }
        else
        {
            ES_Event ThisEvent;

            // save contents of buffer
            setCommandArray(SPIBytesReceived);
            // get status array byte 0 ready for next transfer
            writeStatusArray(0);
            // clear counter
            SPIBytesReceived = 1;

            // post packet received event to PodRadioSM
            ThisEvent.EventType = SPI_PACKET_RECEIVED;
            ThisEvent.EventParam = 1;
            PostPodRadioSM(ThisEvent);
        }
    }

}

unsigned char IsTimerPending(void)
{
    if (TickCount > 0)
    {
        TickCount--;
        return (1);
    }
    else
    {
        return (0);
    }
}
EventCheckers.c
// Event Checking functions for use in testing the CheckUserEvents
// functionality

#include "ES_Configure.h"
#include "ES_Framework.h"
#include "EventCheckers.h"
#include "Const.h"
#include "PodRadioSM.h"

#include <htc.h>

// this pulls in the headers for all services, which gets us the
// prototypes for all of the post functions
#include "ES_ServiceHeaders.h"

boolean checkTransmitEmpty(void)
{
    boolean ReturnVal = False;

    // check if flag high
    // only check if in sendingCommandPacket state
    if ((TXIF == 1) && (QueryPodRadioSM() == sendingCommandPacket))
    {
        // event detected, so post detected event
        ES_Event ThisEvent;
        ThisEvent.EventType = ASYNC_TX_EMPTY;
        ThisEvent.EventParam = 1;
        PostPodRadioSM(ThisEvent);
        ReturnVal = True;
    }

    return ReturnVal;
}

boolean checkAsyncReceive(void)
{
    boolean ReturnVal = False;

    // check if flag high
    if (RCIF == 1)
    {
        // event detected, so post detected event
        ES_Event ThisEvent;

        // make sure we're in a state where we're looking for receives
        if ((QueryPodRadioSM() != waitingForResponse) &&
                (QueryPodRadioSM() != waitingForMSB) &&
                (QueryPodRadioSM() != waitingForLSB) &&
                (QueryPodRadioSM() != suckingPacket))
        {
            // clear flag and return
            RCREG;
            return ReturnVal;
        }

        ThisEvent.EventType = ASYNC_BYTE_RECEIVED;
        ThisEvent.EventParam = 1;
        PostPodRadioSM(ThisEvent);
        ReturnVal = True;
    }

    return ReturnVal;
}
PodRadioSM.c
/****************************************************************************
 Module
   PodRadioSM.c

 Revision
   1.0.1

 Description
   This the PodRadioSM state machine implemented under the
   Gen2 Events and Services Framework.

 Notes

 History
 When           Who     What/Why
 -------------- ---     --------
 6/2/13         cmw      ME 218C project implementation
 01/15/12 11:12 jec      revisions for Gen2 framework
 11/07/11 11:26 jec      made the queue static
 10/30/11 17:59 jec      fixed references to CurrentEvent in RunTemplateSM()
 10/23/11 18:20 jec      began conversion from SMTemplate.c (02/20/07 rev)
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* include header files for this state machine as well as any machines at the
   next lower level in the hierarchy that are sub-machines to this machine
*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "PodRadioSM.h"
#include "Const.h"

/*----------------------------- Module Defines ----------------------------*/
#define ASYNC_BYTES_TO_SEND 14
#define SPI_BYTES_TO_RECEIVE 11

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/

/*---------------------------- Module Variables ---------------------------*/
// everybody needs a state variable, you may need others as well.
// type of state variable should match that of enum in header file
static PodRadioSMState_t CurrentState;

// with the introduction of Gen2, we need a module level Priority var as well
static uint8_t MyPriority;

// Module-level variables
static unsigned char commandArray[14];
static unsigned char statusArray[12];
static unsigned char statusArrayIncomingBuffer[12];
static unsigned char commandChecksum;
static unsigned char asyncBytesWritten;
static unsigned char asyncBytesToRead;
static unsigned char asyncBytesReadCount;
static unsigned char statusChecksum;
static unsigned char dummyVariable;


/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitPodRadioSM

 Parameters
     uint8_t : the priorty of this service

 Returns
     boolean, False if error in initialization, True otherwise

 Description
     Saves away the priority, sets up the initial transition and does any
     other required initialization for this state machine
 Notes

 Author
     J. Edward Carryer, 10/23/11, 18:55
****************************************************************************/
boolean InitPodRadioSM(uint8_t Priority)
{
    MyPriority = Priority;

    // Initialize bytes in command array
    commandArray[0] = 0x7E; // start delimiter
    commandArray[1] = 0x00;
    commandArray[2] = 0x00;
    commandArray[3] = 0x00;
    commandArray[4] = 0x00;
    commandArray[5] = 0x00;
    commandArray[6] = 0x00;
    commandArray[7] = 0x00;
    // start data packet
    commandArray[8] = 0x00;
    commandArray[9] = 0x00;
    commandArray[10] = 0x00;
    commandArray[11] = 0x00;
    commandArray[12] = 0x00;
    commandArray[13] = 0x00;
    // end data packet

    // initialize checksum
    commandChecksum = 0;

    // Initialize bytes in status array - to be filled on ROAMER reply
    statusArray[0] = 0x00; // length LSB
    statusArray[1] = 0x00; // API ID
    statusArray[2] = 0x00; // frame ID
    statusArray[3] = 0x00; // MSB for roamer address
    statusArray[4] = 0x00; // LSB for roamer address
    statusArray[5] = 0x00; // command options
    // start data packet
    statusArray[6] = 0x04; // data bit 0
    statusArray[7] = 0x00; // data bit 1
    statusArray[8] = 0x00; // data bit 2
    statusArray[9] = 0x00; // data bit 3
    statusArray[10] = 0x00; // data bit 4
    statusArray[11] = 0x00; // data bit 5 [unused]
    // end data packet

    // initialize state
    CurrentState = waitingForSPI;

    // put contents of first byte into buffer
    SSPBUF;
    SSPBUF = statusArray[0];

    asyncBytesWritten = 0;
    asyncBytesToRead = 0;
    asyncBytesReadCount = 0;
    statusChecksum = 0;

    return True;
}

/****************************************************************************
 Function
     PostPodRadioSM

 Parameters
     EF_Event ThisEvent , the event to post to the queue

 Returns
     boolean False if the Enqueue operation failed, True otherwise

 Description
     Posts an event to this state machine's queue
 Notes

 Author
     J. Edward Carryer, 10/23/11, 19:25
****************************************************************************/
boolean PostPodRadioSM(ES_Event ThisEvent)
{
    return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunPodRadioSM

 Parameters
   ES_Event : the event to process

 Returns
   ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
   Runs PodRadioSM state machine
 Notes
   uses nested switch/case to implement the machine.
 Author
   J. Edward Carryer, 01/15/12, 15:23
****************************************************************************/
ES_Event RunPodRadioSM(ES_Event ThisEvent)
{
    ES_Event ReturnEvent;
    ReturnEvent.EventType = ES_NO_EVENT; // assume no errors

    switch (CurrentState)
    {
        case waitingForSPI:
            // if full packet received via SPI interrupts
            if (ThisEvent.EventType == SPI_PACKET_RECEIVED)
            {
                // get ready for async transfer
                // clear TXIF flag
                TXIF = 0;
                // write first command byte to transfer register
                TXREG = commandArray[0];
                // increment counter
                asyncBytesWritten = 1;
                CurrentState = sendingCommandPacket;
            }
            break;
        case sendingCommandPacket:
            if (ThisEvent.EventType == ASYNC_TX_EMPTY)
            {
                // check whether transfer is in progress or finished
                if (asyncBytesWritten != ASYNC_BYTES_TO_SEND)
                {
                    // write next byte
                    TXREG = commandArray[asyncBytesWritten];
                    // increment bytes written
                    asyncBytesWritten++;
                }
                else
                {
                    // need to send checksum
                    int i;
                    // calculate and transmit checksum
                    TXREG = 0xFF - commandChecksum;
                    // clear overflow flag and receive register
                    CREN = 0;
                    CREN = 1;
                    RCREG;
                    RCREG;
                    // clear receive flag
                    RCIF = 0;
                    // clear status array in case no ROAMER update received
                    // next time
                    for (i = 0; i < 13; i++)
                    {
                        statusArrayIncomingBuffer[i] = 0;
                    }
                    // write first status byte to SPI buffer for next transfer
                    SSPBUF = statusArray[0];
                    // clear command checksum
                    commandChecksum = 0;
                    CurrentState = waitingForResponse;
                }
            }
            break;
        case waitingForResponse:
            // if byte has been received in async
            if (ThisEvent.EventType == ASYNC_BYTE_RECEIVED)
            {
                // check for start delimiter
                if (RCREG == 0x7E)
                {
                    CurrentState = waitingForMSB;
                    // else do nothing
                }
            }
            break;
        case waitingForMSB:
            // if byte received in async
            if (ThisEvent.EventType == ASYNC_BYTE_RECEIVED)
            {
                // clear receive register
                dummyVariable = RCREG;
                CurrentState = waitingForLSB;
            }
            break;
        case waitingForLSB:
            // if byte received in async register
            if (ThisEvent.EventType == ASYNC_BYTE_RECEIVED)
            {
                // set bytes to read equal to contents (length)
                asyncBytesToRead = RCREG;

                // Check the length of the next byte and reject it
                // if it is longer than the length of a message coming
                // from a ROAMER
                if (asyncBytesToRead > 0x0A)
                {
                    CurrentState = waitingForResponse;
                    break;
                }
                // save length LSB to status array buffer
                statusArrayIncomingBuffer[0] = asyncBytesToRead;
                // increment counter
                asyncBytesReadCount = 1;
                // initialize checksum
                statusChecksum = 0;
                CurrentState = suckingPacket;
            }
            break;
        case suckingPacket:
            // if byte received in async
            if (ThisEvent.EventType == ASYNC_BYTE_RECEIVED)
            {
                int i;
                // check whether transfer in progress or finished
                if (asyncBytesReadCount <= asyncBytesToRead)
                {
                    // save buffer contents to status array buffer
                    statusArrayIncomingBuffer[asyncBytesReadCount] = RCREG;
                    // calculate checksum
                    statusChecksum += statusArrayIncomingBuffer[asyncBytesReadCount];
                    // increment counter
                    asyncBytesReadCount++;
                }
                // if transfer finished, compare checksum
                else if (statusChecksum + RCREG != 0xFF)
                {
                    // if bad checksum, clear status array buffer
                    for (i = 0; i < 12; i++)
                    {
                        statusArrayIncomingBuffer[i] = 0;
                    }
                    // return to waiting for SPI state
                    CurrentState = waitingForSPI;
                }
                // check whether response is radio ack
                else if (statusArrayIncomingBuffer[1] == 0x89)
                {
                    // if radio ack, get ready to receive ROAMER
                    // status packet - clear status array
                    for (i = 0; i < 12; i++)
                    {
                        statusArrayIncomingBuffer[i] = 0;
                    }
                    // put back in waiting for async response state
                    CurrentState = waitingForResponse;
                }
                // if packet is possibly ROAMER status update, check for
                // cases that are not ROAMER status updates
                else if ((statusArrayIncomingBuffer[6] < 0x03) ||
                         (statusArrayIncomingBuffer[5] == 0x02))
                {
                    // bad incoming packet - clear incoming status buffer
                    for (i = 0; i < 12; i++)
                    {
                        statusArrayIncomingBuffer[i] = 0;
                    }

                    // throw out incoming broadcast messages
                    CurrentState = waitingForResponse;
                }
                else // good case
                {
                    // transfer buffer to status array
                    for (i = 0; i < 12; i++)
                    {
                        statusArray[i] = statusArrayIncomingBuffer[i];
                    }
                    // Write first byte to SPI
                    SSPBUF = statusArray[0];
                    // initialize checksum
                    commandChecksum = 0;
                    CurrentState = waitingForSPI;
                }
            }
            break;
    } // end switch on Current State
    return ReturnEvent;
}

/****************************************************************************
 Function
     QueryPodRadioSM

 Parameters
     None

 Returns
     PodRadioSMState_t The current state of the PodRadioSM state machine

 Description
     returns the current state of the PodRadioSM state machine
 Notes

 Author
     J. Edward Carryer, 10/23/11, 19:21
****************************************************************************/
PodRadioSMState_t QueryPodRadioSM(void)
{
    return (CurrentState);
}

/***************************************************************************
 private functions
 ***************************************************************************/

// make setting command array accessible outside module
void setCommandArray(unsigned char counter)
{
    // save buffer to command array
    commandArray[counter + 1] = SSPBUF;
    // calculate checksum
    if (counter > 1)
    {
        commandChecksum += commandArray[counter + 1];
    }
    return;
}

// make writing status array accessible outside module
void writeStatusArray(unsigned char counter)
{
    // write status array to buffer
    SSPBUF = statusArray[counter];
    return;
}

// function to change state machine state
void gotoSPIState(void)
{
    if (CurrentState != waitingForSPI)
    {
        // clear status array
        unsigned char i;
        for (i = 1; i < 12; i++)
        {
            statusArray[i] = 0;
        }
    }
    CurrentState = waitingForSPI;
}
TestMain.c
#define _LEGACY_HEADERS
#include <htc.h>
#include <stdio.h>
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "ES_Timers.h"
#include "sci.h"
#include "const.h"

__CONFIG(INTIO & WDTDIS & PWRTEN & MCLRDIS & UNPROTECT & BORDIS & IESODIS & FCMDIS);

void main(void)
{
    ES_Return_t ErrorType;

// hardware initialization

    /***** 16F690 Code ****
     *  Internal oscillator set to 8MHz
     */
    OSCCON  = 0b01110000;

// clear ANSEL registers - no analog I/O
    ANSEL = 0;
    ANSELH = 0;

// Set test LED pin low to start
    RC3 = 0;

// initialize SPI
    SSPCON = BIT5HI | BIT4HI | BIT2HI;

// clear flag before initialization
    SSPIF = 0;

// set SDO as output
    TRISC = TRISC & ~BIT7HI;

// enable interrupt for incoming SPI bytes
    PIE1 = PIE1 | BIT3HI;
// global and peripheral interrupt enable
    INTCON = INTCON | (BIT7HI | BIT6HI);

// set debugging LED to output and high
    TRISC = TRISC & ~BIT3HI;
    PORTC = PORTC | BIT3HI;

// Initialize serial communication
// Set baud rate to 9600
    SPBRG = BAUD9_6K;

// Set BAUDCTL register: transmit non-inverted data; 8-bit baud;
// wake-up disabled; auto-baud disabled
// Set all to 0
    BAUDCTL = 0;

// set prescalar values
    BRGH = 1;
    BRG16 = 1;

// Set TXSTA register: 8-bit transmission, transmit enabled, asynch mode,
// low-speed baud rate
    TXEN = 1;

// Set RCSTA register: serial port enabled, 8-bit receive, enable receiver
    SPEN = 1;
    CREN = 1;

// Set transmit pin to output
    TRISB7 = 0;

// now initialize the Events and Services Framework and start it running
    ErrorType = ES_Initialize(ES_Timer_RATE_1MS);
    if (ErrorType == Success)
    {

        ErrorType = ES_Run();

    }
//if we got to here, there was an error
    switch (ErrorType)
    {
        case FailedPointer:
            puts("Failed on NULL pointer");
            break;
        case FailedInit:
            puts("Failed Initialization");
            break;
        default:
            puts("Other Failure");
            break;
    }
    for (;;)
        ;

};

/*------------------------------- Footnotes -------------------------------*/
/*------------------------------ End of file ------------------------------*/

ROAMER E128

EventCheckers.c
// Event Checking functions for sample

#include "ES_Configure.h"
#include "ES_General.h"
#include "ES_Events.h"
#include "ES_PostList.h"
#include "EventCheckers.h"
#include "Constants.h"

// This include will pull in all of the headers from the service modules
// providing the prototypes for all of the post functions
#include "ES_ServiceHeaders.h"


boolean checkAsyncReceive(void)
{
    boolean ReturnVal = False;

    // check if flag high and in a state where we expect to receive messages (not transmittingSPI or sendingPacket)
    if ((SCI1SR1 & ReceiveFlagMask) == ReceiveFlagMask         // Receive Register is full
            && (QueryRoamerRadioSM() != transmittingSPI && QueryRoamerRadioSM() != sendingStatusPacket))
    {
        ES_Event ThisEvent;
        ThisEvent.EventType = ASYNC_BYTE_RECEIVED;
        ThisEvent.EventParam = 1;
        PostRoamerRadioSM(ThisEvent);
        ReturnVal = True;
    }

    return ReturnVal;
}


boolean checkTransmitEmpty(void)
{
    boolean ReturnVal = False;

    // check if flag high
    // only check if in sendingStatusPacket state
    if ((QueryRoamerRadioSM() == sendingStatusPacket)
            && ((SCI1SR1 & TransmitEmptyFlagMask) == TransmitEmptyFlagMask))
    {
        // event detected, so post detected event
        ES_Event ThisEvent;
        ThisEvent.EventType = ASYNC_TX_EMPTY;
        ThisEvent.EventParam = 1;
        PostRoamerRadioSM(ThisEvent);
        ReturnVal = True;
    }

    return ReturnVal;
}
RoamerRadioSM.c
/****************************************************************************
 Module
   RoamerRadioSM.c

 Revision
   1.0.1

 Description
   This is a template file for implementing flat state machines under the
   Gen2 Events and Services Framework.

 Notes

 History
 When           Who     What/Why
 -------------- ---     --------
 01/15/12 11:12 jec      revisions for Gen2 framework
 11/07/11 11:26 jec      made the queue static
 10/30/11 17:59 jec      fixed references to CurrentEvent in RunTemplateSM()
 10/23/11 18:20 jec      began conversion from SMTemplate.c (02/20/07 rev)
 05/11/13 15:13 jg       began converion to RoamerRadioSM
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* include header files for this state machine as well as any machines at the
   next lower level in the hierarchy that are sub-machines to this machine
*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "RoamerRadioSM.h"
#include "RoamerService.h"
#include "Constants.h"
#include <mc9s12e128.h>
#include <S12e128bits.h>
#include <stdio.h>
#include "S12Vec.h"

/*----------------------------- Module Defines ----------------------------*/
#define ASYNC_BYTES_TO_SEND   13
#define SPI_BYTES_TO_WRITE    4

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/
static void HandleIncomingMessage(void);
static void BeginSPI(void);
static void MotorStop(void);
/*---------------------------- Module Variables ---------------------------*/
// everybody needs a state variable, you may need others as well.
// type of state variable should match htat of enum in header file
static RoamerRadioState_t CurrentState;
static unsigned char dummyVariable;
static unsigned char asyncBytesToRead;
static unsigned char asyncBytesReadCount;
static unsigned char commandChecksum;
static unsigned char commandArray[11];
static unsigned char asyncBytesWritten;
static unsigned char statusChecksum;
static unsigned char statusArray[13];
static unsigned char SPIBytesWritten;

static unsigned char targetAddHi;
static unsigned char targetAddLo;

static unsigned char frameID;

static unsigned char lastCameraCommand;

static unsigned char connectionStatus;

// with the introduction of Gen2, we need a module level Priority var as well
static uint8_t MyPriority;


/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitTemplateFSM

 Parameters
     uint8_t : the priorty of this service

 Returns
     boolean, False if error in initialization, True otherwise

 Description
     Saves away the priority, sets up the initial transition and does any
     other required initialization for this state machine
 Notes

 Author
     J. Edward Carryer, 10/23/11, 18:55
****************************************************************************/
boolean InitRoamerRadioSM(uint8_t Priority)
{
    //Saving MyPriority to allow for Framework posting
    MyPriority = Priority;

    //Set initial state to be waitingForResponse to wait for transmission
    //over async
    CurrentState = waitingForResponse;

    //Set initial values for frameID (1) and connectionStatus (Disconnected)
    frameID = 1;
    lastCameraCommand = 0;
    connectionStatus = 0;

    // Initialize bytes in status array
    statusArray[0] = 0x7E;
    statusArray[1] = 0x00;
    statusArray[2] = 0x0A;
    statusArray[3] = 0x01;
    statusArray[4] = frameID;
    statusArray[5] = targetAddHi;
    statusArray[6] = targetAddLo;
    statusArray[7] = 0x00;
    // Start data packet
    statusArray[8] = RetrieveRoamerNumber();
    statusArray[9] = 0x00;
    statusArray[10] = 0x00;
    statusArray[11] = 0x00;
    statusArray[12] = 0x00;
    // End data packet
    statusChecksum = 0;

    //Initialize values that help in reading and transmitting Async Bytes
    asyncBytesToRead = 0;
    asyncBytesReadCount = 0;
    commandChecksum = 0;

    //2: Data Request to Special PIC, 1: Debug LED
    DDRP = BIT2HI | BIT1HI;
    // Set LED pin (P1) and Data Request (P2) to output

    PTP = (PTP & BIT2LO);
    PTP = (PTP & BIT1LO);

    //Initialize SCI Module
    //9600 Baud and idle high second edge active
    SCI1BDH = 0x00;
    SCI1BDL = 156;
    SCI1CR1 = 0x00;
    SCI1CR2 = 0b00001100;

    //Initialize SPI Module
    //Slave mode receive interrupt enabled
    SPICR1 = 0b11001100;
    SPICR2 = 0;

    //Initialize ES_timer rate to 1ms
    ES_Timer_Init(ES_Timer_RATE_1MS);

    //Call function to initialize motor settings in RoamerService
    MotorStop();

    return True;
}

/****************************************************************************
 Function
     PostRoamerRadioSM

 Parameters
     EF_Event ThisEvent , the event to post to the queue

 Returns
     boolean False if the Enqueue operation failed, True otherwise

 Description
     Posts an event to this state machine's queue
 Notes

 Author
     J. Edward Carryer, 10/23/11, 19:25
****************************************************************************/
boolean PostRoamerRadioSM(ES_Event ThisEvent)
{
    return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunRoamerRadioFSM

 Parameters
   ES_Event : the event to process

 Returns
   ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
   add your description here
 Notes
   uses nested switch/case to implement the machine.
 Author
   J. Edward Carryer, 01/15/12, 15:23
****************************************************************************/
ES_Event RunRoamerRadioSM(ES_Event ThisEvent)
{
    ES_Event ReturnEvent;
    ReturnEvent.EventType = ES_NO_EVENT; // assume no errors

    switch (CurrentState)
    {
        case waitingForResponse :       // If current state is waitingForResponse
            //If event is ASYNC_BYTE_RECEIVED
            if (ThisEvent.EventType == ASYNC_BYTE_RECEIVED)
            {
                //If RX Reg contents equal 7E
                if (SCI1DRL == 0x7E)
                {
                    //Start the Byte timer to check for missed bytes
                    ES_Timer_InitTimer(BYTE_TIMER, OneByte);
                    //Set currentState to waitingForMSB
                    CurrentState = waitingForMSB;
                }
                else
                {
                    // Do nothing (print bad first byte message)
                    printf("FB%x\n\r", SCI1DRL);
                }

            }
            else if (ThisEvent.EventType == ES_TIMEOUT &&
                     ThisEvent.EventParam == DISCONNECT_TIMER)   //If event is (~1s) Disconnect Timer
            {
                //Set connection Status to 0 (disconnected)
                connectionStatus = 0;
                //Stop the disconnect timer
                ES_Timer_StopTimer(DISCONNECT_TIMER);
                //Print debug messages and turn off LED
                printf("Disconnected\n\r");
                PTP_PTP1 = 0;
                printf("DT\n\r");
                //Set all motors to default positions
                MotorStop();
            }

            break;

        case waitingForMSB :
            //If event is ASYNC_BYTE_RECEIVED
            if (ThisEvent.EventType == ASYNC_BYTE_RECEIVED)
            {
                //Set data in Receive Register to some dummy variable
                dummyVariable = SCI1DRL;
                //Reset Byte Timer
                ES_Timer_InitTimer(BYTE_TIMER, OneByte);
                //Set currentState to waitingForLSB
                CurrentState = waitingForLSB;
            }

            //Else if event is RECEIVE_TIMEOUT
            else if ((ThisEvent.EventType == ES_TIMEOUT) &&
                     (ThisEvent.EventParam == BYTE_TIMER))
            {
                int i;
                //Clear BytesReadCount
                asyncBytesReadCount = 0;
                //Clear commandArray
                for (i = 0; i < 11; i++)
                {
                    commandArray[i] = 0;
                }
                // Print debug message
                printf("BT1\n\r");
                //CurrentState = waitingForResponse
                CurrentState = waitingForResponse;
            }
            break;

        case waitingForLSB :
            //If event is ASYNC_BYTE_RECEIVED
            if (ThisEvent.EventType == ASYNC_BYTE_RECEIVED)
            {
                //Set asyncBytesToRead = RX Reg contents
                asyncBytesToRead = SCI1DRL;
                //Check to make sure asyncBytesToRead is not more than the length
                // of a packet from a POD
                if (asyncBytesToRead > 0x0B)
                {
                    CurrentState = waitingForResponse;
                    break;
                }
                //Reset Byte Timer
                ES_Timer_InitTimer(BYTE_TIMER, OneByte);
                //Set bytesReadCount = 0
                asyncBytesReadCount = 0;
                //Set commandChecksum = 0
                commandChecksum = 0;
                //Set currentState to suckingPacket
                CurrentState = suckingPacket;
            }
            //Else if event is RECEIVE_TIMEOUT
            else if ((ThisEvent.EventType == ES_TIMEOUT) &&
                     (ThisEvent.EventParam == BYTE_TIMER))
            {
                int i;
                //Clear BytesReadCount
                asyncBytesReadCount = 0;
                //Clear commandArray
                for (i = 0; i < 11; i++)
                {
                    commandArray[i] = 0;
                }
                printf("BT2\n\r");

                //CurrentState = waitingForResponse
                CurrentState = waitingForResponse;
            }

            break;

        case suckingPacket :
            //If event is ASYNC_BYTE_RECEIVED
            if (ThisEvent.EventType == ASYNC_BYTE_RECEIVED)
            {
                //If bytesReadCount is not bytesToRead
                if (asyncBytesReadCount < asyncBytesToRead)
                {
                    //commandArray[bytesReadCount] = RX Reg contents
                    commandArray[asyncBytesReadCount] = SCI1DRL;
                    //Reset Byte Timer
                    ES_Timer_InitTimer(BYTE_TIMER, OneByte);
                    //commandChecksum += commandArray[bytesReadCount]
                    commandChecksum += commandArray[asyncBytesReadCount];
                    //Increment bytesReadCount
                    asyncBytesReadCount++;
                }
                //Else if FF - RX Reg contents does not equal commandChecksum
                else if ((0xFF - SCI1DRL) != commandChecksum)
                {
                    printf("BAD CHKSM\n\r");
                    ES_Timer_StopTimer(BYTE_TIMER);
                    //currentState = waitingForResponse
                    CurrentState = waitingForResponse;
                }
                //Else if commandArray[0] = 0x89
                else if (commandArray[0] == 0x89)
                {
                    // Response from POD radio - good or bad
                    int i;

                    ES_Timer_StopTimer(BYTE_TIMER);   // Stop Byte reception timer

                    //currentState = waitingForResponse
                    CurrentState = waitingForResponse;

                    // Debug printing of Radio Ack
                    for (i = 0; i < 3; i++)
                    {
                        printf("%x\n\r", commandArray[i]);
                    }
                    printf("\n\r");

                }
                //Else
                else
                {
                    //This else statement is only evaluated if message received is a complete
                    // packet with a valid checksum but not a transmission status
                    int i;
                    //Save Camera Command
                    lastCameraCommand = commandArray[9];
                    ES_Timer_StopTimer(BYTE_TIMER);   // Stop Byte reception timer

                    for (i = 0; i < 11; i++)
                    {
                        printf("%x\n\r", commandArray[i]);
                    }
                    printf("\n\r");
                    HandleIncomingMessage();
                }
            }
            else if ((ThisEvent.EventType == ES_TIMEOUT) &&
                     (ThisEvent.EventParam == BYTE_TIMER))
            {
                int i;

                printf("BT3\n\r");

                for (i = 0; i < asyncBytesReadCount; i++)
                {
                    printf("%x\n\r", commandArray[i]);
                }
                printf("\n\r");

                //Clear BytesReadCount
                asyncBytesReadCount = 0;
                //Clear commandArray

                for (i = 0; i < 11; i++)
                {
                    commandArray[i] = 0;
                }

                //CurrentState = waitingForResponse
                CurrentState = waitingForResponse;
            }

            break;

        case transmittingSPI:
            //If event is SPI_BUFFER_FULL
            if (ThisEvent.EventType == SPI_BUFFER_FULL)
            {

                //If SPI_BYTES_TO_WRITE is not SPIBytesWritten
                if (SPIBytesWritten != SPI_BYTES_TO_WRITE)
                {
                    //DONT WRITE FIRST BYTE - CRAIG SENDS A ZERO FIRST
                    if (SPIBytesWritten != 1)
                    {
                        //statusArray[SPIBytesWritten+2] = SSPBUF
                        statusArray[SPIBytesWritten + 7] = SPIDR;
                        //statusChecksum += statusArray[SPIBytesWritten +2]
                        //statusChecksum += statusArray[SPIBytesWritten + 8];
                    }
                    else
                    {
                        SPIDR;
                    }
                    //SSPBUF = commandArray[SPIBytesWritten]
                    if ((SPISR & SPITransmitEmpty) == SPITransmitEmpty)
                    {
                        SPIDR = 0;
                        PTP = (PTP & BIT2LO);   //Set Data request Low
                        //Increment SPIBytesWritten
                        SPIBytesWritten++;
                    }

                }
                //Else
                else
                {
                    int i;
                    //statusArray[SPIBytesWritten+2]
                    statusArray[SPIBytesWritten + 7] = SPIDR;
                    //statusChecksum += statusArray[SPIBytesWritten + 2]
                    //statusChecksum += statusArray[SPIBytesWritten + 8];
                    for (i = 3; i < 13; i++)
                    {
                        statusChecksum += statusArray[i];
                    }
                    //Read TXIF flag // to clear //TXREG = statusArray[0]
                    if ((SCI1SR1 & TransmitEmptyFlagMask) == TransmitEmptyFlagMask)
                    {
                        SCI1DRL = statusArray[0];
                    }
                    printf("SPI Done\n\r");
                    //asyncBytesWritten = 1
                    asyncBytesWritten = 1;
                    //currentState = sendingStatusPacket
                    CurrentState = sendingStatusPacket;
                }
            }

            else if (ThisEvent.EventType == ASYNC_BYTE_RECEIVED)
            {
                // Clear receive register if receive async byte when not expecting one
                dummyVariable = SCI1DRL;
            }
            break;

        case sendingStatusPacket:
            //If event is ASYNC_TX_EMPTY
            if (ThisEvent.EventType == ASYNC_TX_EMPTY)
            {
                //If asyncBytesWritten is not ASYNC_BYTES_TO_SEND
                if (asyncBytesWritten != ASYNC_BYTES_TO_SEND)
                {
                    //TXREG = statusArray[asyncBytesWritten]
                    SCI1DRL = statusArray[asyncBytesWritten];
                    //Increment asyncBytesWritten
                    asyncBytesWritten++;
                }
                //Else
                else
                {
                    int i;
                    /*for (i = 3; i < 13; i++)
                     {
                         statusChecksum += statusArray[i];
                     }*/
                    //TXREG = FF - statusChecksum
                    SCI1DRL = (0xFF - statusChecksum);
                    //Clear overflow flag OERR
                    if ((SCI1SR1 & 0x08) == 0x08)  //Check for Overflow flag
                    {
                        dummyVariable = SCI1DRL;
                    }
                    printf("Sent:\n\r");
                    for (i = 0; i < 13; i++)
                    {
                        printf("%x\n\r", statusArray[i]);
                    }
                    printf("CHKSUM = %x\n\r", (0xFF - statusChecksum));
                    printf("\n\r");
                    //currentState = waitingForResponse
                    CurrentState = waitingForResponse;
                }
            }
            else if (ThisEvent.EventType == ASYNC_BYTE_RECEIVED)
            {
                // Clear receive register if receive async byte when not expecting one
                dummyVariable = SCI1DRL;
                printf("RwS\n\r");
            }

            break;


    }                                   // end switch on Current State
    return ReturnEvent;
}

/****************************************************************************
 Function
     QueryRoamerRadioSM

 Parameters
     None

 Returns
     RoamerRadioState_t The current state of the Roamer Radio state machine

 Description
     returns the current state of the Roamer Radio state machine
 Notes

 Author
     J. Edward Carryer, 10/23/11, 19:21
****************************************************************************/
RoamerRadioState_t QueryRoamerRadioSM(void)
{
    return (CurrentState);
}

/****************************************************************************
 Function
     RetrieveCommand

 Parameters
     unsigned char CommandNumber is the number of the command in the command
     data packet that is to be returned

 Returns
     unsigned char The command that was requested from the commandArray

 Description
     returns a specified command from the commandArray specified by the index
     determined in the ME218c communications protocol

     0 = Left Wheel
     1 = Right Wheel
     2 = Gripper
     3 = Camera
 Notes

 Author
     Jason Gonzalez, 05/12/13, 18:28
****************************************************************************/
unsigned char RetrieveCommand(unsigned char CommandNumber)
{
    return (commandArray[CommandNumber + 6]);
}

/****************************************************************************
 Function
     GetLowerAddressByte

 Parameters
     None

 Returns
     unsigned char lower byte of the connected POD

 Description
     returns the lower byte of the connected POD
 Notes

 Author
     David Choy, 05/27/13, 21:51
****************************************************************************/
unsigned char GetLowerAddressByte()
{
    return statusArray[6];
}

/***************************************************************************
 private functions
 ***************************************************************************/

static void HandleIncomingMessage(void)
{
    //Function that gets called when an entire valid packet is received over
    //Async in order to determine what the message is and the appropriate
    //Response

    if (commandArray[0] == 0x81)        //Check to make sure packet is message from another radio
    {

        // If received a broadcast message requesting a connection
        // and we are currently disconnected and the message is for
        // our current ROAMER number
        if ((commandArray[4] == 0x02 /* Broadcast Message */) &&
                (connectionStatus == 0x00 /* Currently Disconnected */) &&
                (commandArray[5] == 0x00 /* Connection Request */) &&
                (commandArray[6] == RetrieveRoamerNumber() /*Requested Roamer Matches my Roamer Number*/))
        {
            connectionStatus = 0x01;            // Set status to connected
            targetAddHi = commandArray[1];      // Save Target Address of POD
            targetAddLo = commandArray[2];      // Save Target Address of POD
            statusArray[5] = targetAddHi;       // Write Address to Status Array
            statusArray[6] = targetAddLo;       // Write Address to Status Array
            statusArray[8] = 0x04;              // Send Back Connetion Accepted
            ES_Timer_InitTimer(DISCONNECT_TIMER, OneSec); // Start Disconnect Timer (~1s)
            BeginSPI();                         // Call Function that starts SPI Transmission
            // with SPECIAL PIC
            printf("Connected\n\r");            // Print Debug Message
            PTP_PTP1 = 1;                       // Turn on LED
        }

        // If received direct message to disconnect and we are
        // Currently connected and the Address matches our target address
        // From controller POD
        else if ((commandArray[4] == 0x00 /* directed to specific address */) &&
                 (connectionStatus == 0x01 /* we are currently connected */) &&
                 (commandArray[5] == 0x01 /* disconnect request attempt */) &&
                 (commandArray[1] == targetAddHi /* directed to our address */) &&
                 (commandArray[2] == targetAddLo /* directed to our address */))
        {

            connectionStatus = 0x00;        // Set status to disconnected
            statusArray[8] = 0x05;          // Send back disconnect accepted
            ES_Timer_StopTimer(DISCONNECT_TIMER);   // Stop Disconnect Timer since already disconnected
            BeginSPI();                     // Call Function that starts SPI Transmission
            // with SPECIAL PIC
            printf("Disconnected\n\r");     // Print debug message
            PTP_PTP1 = 0;                   // Turn off LED
            MotorStop();                    // Set motors to default
        }

        // If received direct command message and we are currently
        // connected and the Address matches our target address
        else if ((commandArray[4] == 0x00 /* Direct message */) &&
                 (connectionStatus == 0x01 /* Currently Connected */) &&
                 (commandArray[5] == 0x02 /* Command Message from POD */) &&
                 (commandArray[1] == targetAddHi /* message address matches target */) &&
                 (commandArray[2] == targetAddLo /* message address matches target */))
        {
            ES_Event NewEvent;

            statusArray[8] = 0x03;          // Send back ROAMER status
            ES_Timer_InitTimer(DISCONNECT_TIMER, OneSec);   // Reset Disconnect Status
            BeginSPI();                     // Call Function that starts SPI Transmission
            // with SPECIAL PIC
            printf("Commanded\n\r");

            NewEvent.EventType = INPUT_SET; // Post INPUT_SET event to RoamerService
            PostRoamerService(NewEvent);    // To Set outputs as necessary
        }
        else
        {
            //Clear overflow flag OERR
            if ((SCI1SR1 & 0x08) == 0x08)  //Check for Overflow flag
            {
                dummyVariable = SCI1DRL;
            }
            CurrentState = waitingForResponse;
        }
    }

}

static void BeginSPI(void)
{
    CurrentState = transmittingSPI;

    while ((SPISR & _S12_SPTEF) != _S12_SPTEF)
    {
        printf("N");
    }

    SPIDR = (commandArray[10] & SPECIALLightMask);
    //SPIDR = commandArray[10];
    //SPIBytesWritten = 1 // Already wrote byte 1
    SPIBytesWritten = 1;
    //statusChecksum = 0
    statusChecksum = 0;
    //currentState = transmittingSPI
    PTP = (PTP | BIT2HI);   // Raise Data Request to ask
    // master SPECIAL PIC to begin
    // SPI transfer


}

static void MotorStop(void)
{
    // Function to stop motors/place them in default positions
    ES_Event NewEvent;
    commandArray[6] = 0x80; // Stop left motor
    commandArray[7] = 0x80; // Stop right motor
    commandArray[8] = 0x00; // Open gripper
    commandArray[9] = 0x80; // Face SPECIAL toward gripper
    NewEvent.EventType = INPUT_SET;
    RunRoamerService(NewEvent);
}

void interrupt _Vec_SPI SPIInterrupt(void)
{
    // check if flag high
    // only check if in sendingStatusPacket state
    if ((SPISR & SPIBufferFlagMask) == SPIBufferFlagMask)
    {
        // event detected, so post detected event
        ES_Event ThisEvent;
        ThisEvent.EventType = SPI_BUFFER_FULL;
        ThisEvent.EventParam = 1;
        RunRoamerRadioSM(ThisEvent);  //Directly post event to avoid the entire framework
        //Running while expeting more SPI interrupts
    }

}
RoamerService.c
/****************************************************************************
 Module
   RoamerService.c

 Revision
   1.0.1

 Description
   This is a template file for implementing a simple service under the
   Gen2 Events and Services Framework.

 Notes

 History
 When           Who     What/Why
 -------------- ---     --------
 01/16/12 09:58 jec      began conversion from TemplateFSM.c
 05/12/13 18:31 jg       began conversion to RoamerService.c
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* include header files for this state machine as well as any machines at the
   next lower level in the hierarchy that are sub-machines to this machine
*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "RoamerService.h"
#include "RoamerRadioSM.h"
#include <stdio.h>
#include <mc9s12e128.h>
#include <S12e128bits.h>
#include "Constants.h"
#include "PWM.h"
#include "ADS12.h"
#include "Servo.h"
#include <stdlib.h>
#include <math.h>

/*----------------------------- Module Defines ----------------------------*/

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this service.They should be functions
   relevant to the behavior of this service
*/
static void ControlMotors(void);
static void ControlGripper(void);
static void ControlSPECIAL(void);
static void DetermineRoamerNumber(void);

/*---------------------------- Module Variables ---------------------------*/
// with the introduction of Gen2, we need a module level Priority variable
static uint8_t MyPriority;

//PlaceHolder variable, replace in function with numerical math
static unsigned char MyRoamerNumber;

/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitRoamerService

 Parameters
     uint8_t : the priorty of this service

 Returns
     boolean, False if error in initialization, True otherwise

 Description
     Saves away the priority, and does any
     other required initialization for this service
 Notes

 Author
     J. Edward Carryer, 01/16/12, 10:00
****************************************************************************/
boolean InitRoamerService(uint8_t Priority)
{
ES_Event ThisEvent;

MyPriority = Priority;
/********************************************
     in here you write your initialization code
     *******************************************/

// Initialize Roamer Select Pin for Analog Read
ADS12_Init("IIIIIIIA");


//PWM Initialization
DDRU |= (LEFT_WHEEL_DIRECTION | RIGHT_WHEEL_DIRECTION);
PTU &= ~(LEFT_WHEEL_DIRECTION | RIGHT_WHEEL_DIRECTION);

PWM_Init(LEFT_WHEEL_CHANNEL);
PWM_Init(RIGHT_WHEEL_CHANNEL);

PWM_Set_Frequency(LEFT_WHEEL_CHANNEL, PWM_FREQUENCY);
PWM_Set_Frequency(RIGHT_WHEEL_CHANNEL, PWM_FREQUENCY);

PWM_Set_Duty_Cycle(LEFT_WHEEL_CHANNEL, 0);
PWM_Set_Duty_Cycle(RIGHT_WHEEL_CHANNEL, 0);


//Gripper Initialization
DDRU |= GRIPPER_DIRECTION;
GRIPPER_PORT &= ~GRIPPER_DIRECTION;

PWM_Init(GRIPPER_CHANNEL);
PWM_Set_Frequency(GRIPPER_CHANNEL, PWM_FREQUENCY);

// Initially set gripper open but post event to turn motor off
ThisEvent.EventType = ES_INIT;
PostRoamerService(ThisEvent);
PWM_Set_Duty_Cycle(GRIPPER_CHANNEL, 0);

//SPECIAL Servo Initialization
Servo_Init();
Servo_Enable(SPECIAL_CHANNEL);

DetermineRoamerNumber();

// post the initial transition event
ThisEvent.EventType = ES_INIT;
if (ES_PostToService(MyPriority, ThisEvent) == True)
{
return True;
}
else
{
return False;
}
}

/****************************************************************************
 Function
     PostRoamerService

 Parameters
     EF_Event ThisEvent ,the event to post to the queue

 Returns
     boolean False if the Enqueue operation failed, True otherwise

 Description
     Posts an event to this state machine's queue
 Notes

 Author
     J. Edward Carryer, 10/23/11, 19:25
****************************************************************************/
boolean PostRoamerService(ES_Event ThisEvent)
{
return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunRoamerService

 Parameters
   ES_Event : the event to process

 Returns
   ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
   add your description here
 Notes

 Author
   J. Edward Carryer, 01/15/12, 15:23
****************************************************************************/
ES_Event RunRoamerService(ES_Event ThisEvent)
{
ES_Event ReturnEvent;

ReturnEvent.EventType = ES_NO_EVENT; // assume no errors
/********************************************
     in here you write your service code
     *******************************************/

if (ThisEvent.EventType == INPUT_SET)
{
// Call all functions that will set motors based on POD commands
ControlMotors();

ControlGripper();

ControlSPECIAL();
}

else if (ThisEvent.EventType == ES_INIT)
{
// Initial turning on of gripper motor to open but set timer to turn motor off
PWM_Set_Duty_Cycle(GRIPPER_CHANNEL, GRIPPER_PWM);
GRIPPER_PORT |= GRIPPER_DIRECTION;
ES_Timer_InitTimer(GRIPPER_TIMER, 1000);
}

else if ((ThisEvent.EventType == ES_TIMEOUT) &&
     (ThisEvent.EventParam == GRIPPER_TIMER))
{
// When gripper timer expires, turn motor off to save power
PWM_Set_Duty_Cycle(GRIPPER_CHANNEL, 0);
ES_Timer_StopTimer(GRIPPER_TIMER);
printf("OPEN TIMER\n\r");
}

return ReturnEvent;
}

/****************************************************************************
 Function
     RetreiveRoamerNumber

 Parameters
     none

 Returns
     unsigned char The roamer number as determined by tristate switch

 Description
     returns a roamer number that is determined by a second function call.
     this call to DetermineRoamerNumber handles mapping of values
 Notes

 Author
     Jason Gonzalez, 05/12/13, 18:28
****************************************************************************/
unsigned char RetrieveRoamerNumber(void)
{
DetermineRoamerNumber();
return (MyRoamerNumber);
}

/***************************************************************************
 private functions
 ***************************************************************************/

static void ControlMotors(void)
{
// Function handles the control of the drive motors based on the Left and Right Motor command Bytes
// Two different value mappings are used to maximize performance with our POD as well as with others
unsigned int LeftMotorControl = (unsigned int) RetrieveCommand(0);
unsigned int RightMotorControl = (unsigned int) RetrieveCommand(1);
unsigned char SPECIALPosition = RetrieveCommand(3);

// If SPECIAL is facing backward
if (SPECIALPosition < 0x40 || SPECIALPosition > 0xC0)
{
// Reverse motors so you can still drive properly;
unsigned int Temp = LeftMotorControl;
LeftMotorControl = 0xFF - RightMotorControl;
RightMotorControl = 0xFF - Temp;
}

// This linear mapping is only used when communicating with our own POD
// It incoporates a dead-band about the full-stop command (0x80) to avoid unwanted
// Driving due to noise. Values are linearly mapped from Threshold-0xFF to 50-100% Duty Cycle
// To make use of Drive-Brake Mode in our motor circuit
if (GetLowerAddressByte() == 0x84)
{
#define UPPER_THRESHOLD_2084 0x83
#define LOWER_THRESHOLD_2084 0x7D

// Left Motor Control
if (LeftMotorControl > UPPER_THRESHOLD_2084)
{
    unsigned char LeftDutyCycle = (unsigned char)((MAX_LEFT_DC - 50) * (LeftMotorControl - UPPER_THRESHOLD_2084) / (0xFF - UPPER_THRESHOLD_2084 + 1) + 50);
    printf("LD PWM %d\n\r", LeftDutyCycle);
    PWM_Set_Duty_Cycle(LEFT_WHEEL_CHANNEL, LeftDutyCycle);
    PWM_Set_Polarity(LEFT_WHEEL_CHANNEL, PWM_POLARITY_LOW);
    WHEEL_PWM_PORT |= LEFT_WHEEL_DIRECTION;


}
else if (LeftMotorControl < LOWER_THRESHOLD_2084)
{
    unsigned char LeftDutyCycle = (unsigned char)((MAX_LEFT_DC - 50) * (LOWER_THRESHOLD_2084 - LeftMotorControl) / (LOWER_THRESHOLD_2084) + 50);
    printf("LD PWM %d\n\r", LeftDutyCycle);
    PWM_Set_Duty_Cycle(LEFT_WHEEL_CHANNEL, LeftDutyCycle);
    PWM_Set_Polarity(LEFT_WHEEL_CHANNEL, PWM_POLARITY_HIGH);
    WHEEL_PWM_PORT &= ~LEFT_WHEEL_DIRECTION;
}
else
{
    PWM_Set_Duty_Cycle(LEFT_WHEEL_CHANNEL, 0);
    PWM_Set_Polarity(LEFT_WHEEL_CHANNEL, PWM_POLARITY_LOW);
    WHEEL_PWM_PORT |= LEFT_WHEEL_DIRECTION;
}

// Right Motor Control
if (RightMotorControl > UPPER_THRESHOLD_2084)
{
    unsigned char RightDutyCycle = (unsigned char)((MAX_RIGHT_DC - 50) * (RightMotorControl - UPPER_THRESHOLD_2084) / (0xFF - UPPER_THRESHOLD_2084 + 1) + 50);
    PWM_Set_Duty_Cycle(RIGHT_WHEEL_CHANNEL, RightDutyCycle);
    PWM_Set_Polarity(RIGHT_WHEEL_CHANNEL, PWM_POLARITY_LOW);
    WHEEL_PWM_PORT |= RIGHT_WHEEL_DIRECTION;
}
else if (RightMotorControl < LOWER_THRESHOLD_2084)
{
    unsigned char RightDutyCycle = (unsigned char)((MAX_RIGHT_DC - 50) * (LOWER_THRESHOLD_2084 - RightMotorControl) / (LOWER_THRESHOLD_2084) + 50);
    PWM_Set_Duty_Cycle(RIGHT_WHEEL_CHANNEL, RightDutyCycle);
    PWM_Set_Polarity(RIGHT_WHEEL_CHANNEL, PWM_POLARITY_HIGH);
    WHEEL_PWM_PORT &= ~RIGHT_WHEEL_DIRECTION;
}
else
{
    PWM_Set_Duty_Cycle(RIGHT_WHEEL_CHANNEL, 0);
    PWM_Set_Polarity(RIGHT_WHEEL_CHANNEL, PWM_POLARITY_LOW);
    WHEEL_PWM_PORT |= RIGHT_WHEEL_DIRECTION;
}
}

// This non-linear mapping is only used when communicating with our other PODs
// It incoporates a larger dead-band about the full-stop command (0x80) to avoid unwanted
// Driving due to larger, unknown noise from other PODs. Values are mapped from Threshold-0xFF
// to 50-100% Duty Cycle but following a Square power curve. This allows for finer control at
// lower motor commands while still providing full motor control.
else
{
#define UPPER_THRESHOLD_OTHER 0x88
#define LOWER_THRESHOLD_OTHER 0x78

// Set to >= 50
#define MAX_LEFT_DC_OTHER 80
#define MAX_RIGHT_DC_OTHER 80

unsigned char LeftDutyCycle;
unsigned char RightDutyCycle;
unsigned long LeftTemp = (unsigned long)pow(abs(LeftMotorControl - 0x80) - (UPPER_THRESHOLD_OTHER - 0x80), 2);
unsigned long RightTemp = (unsigned long)pow(abs(RightMotorControl - 0x80) - (UPPER_THRESHOLD_OTHER - 0x80), 2);

LeftTemp = (unsigned long)(LeftTemp * (MAX_LEFT_DC_OTHER - 50) / pow(0xFF - UPPER_THRESHOLD_OTHER, 2));
RightTemp = (unsigned long)(RightTemp * (MAX_RIGHT_DC_OTHER - 50) / pow(LOWER_THRESHOLD_OTHER, 2));

LeftDutyCycle = (unsigned char)(50 + LeftTemp);
RightDutyCycle = (unsigned char)(50 + RightTemp);

// Left Motor Control
if (LeftMotorControl > UPPER_THRESHOLD_OTHER)
{
    PWM_Set_Duty_Cycle(LEFT_WHEEL_CHANNEL, LeftDutyCycle);
    PWM_Set_Polarity(LEFT_WHEEL_CHANNEL, PWM_POLARITY_LOW);
    WHEEL_PWM_PORT |= LEFT_WHEEL_DIRECTION;
}
else if (LeftMotorControl < LOWER_THRESHOLD_OTHER)
{
    PWM_Set_Duty_Cycle(LEFT_WHEEL_CHANNEL, LeftDutyCycle);
    PWM_Set_Polarity(LEFT_WHEEL_CHANNEL, PWM_POLARITY_HIGH);
    WHEEL_PWM_PORT &= ~LEFT_WHEEL_DIRECTION;
}
else
{
    PWM_Set_Duty_Cycle(LEFT_WHEEL_CHANNEL, 0);
    PWM_Set_Polarity(LEFT_WHEEL_CHANNEL, PWM_POLARITY_LOW);
    WHEEL_PWM_PORT |= LEFT_WHEEL_DIRECTION;
}

// Right Motor Control
if (RightMotorControl > UPPER_THRESHOLD_OTHER)
{
    PWM_Set_Duty_Cycle(RIGHT_WHEEL_CHANNEL, RightDutyCycle);
    PWM_Set_Polarity(RIGHT_WHEEL_CHANNEL, PWM_POLARITY_LOW);
    WHEEL_PWM_PORT |= RIGHT_WHEEL_DIRECTION;
}
else if (RightMotorControl < LOWER_THRESHOLD_OTHER)
{
    PWM_Set_Duty_Cycle(RIGHT_WHEEL_CHANNEL, RightDutyCycle);
    PWM_Set_Polarity(RIGHT_WHEEL_CHANNEL, PWM_POLARITY_HIGH);
    WHEEL_PWM_PORT &= ~RIGHT_WHEEL_DIRECTION;
}
else
{
    PWM_Set_Duty_Cycle(RIGHT_WHEEL_CHANNEL, 0);
    PWM_Set_Polarity(RIGHT_WHEEL_CHANNEL, PWM_POLARITY_LOW);
    WHEEL_PWM_PORT |= RIGHT_WHEEL_DIRECTION;
}
}

}

static void ControlGripper(void)
{
// Reads the MSB (digital control bit) of Gripper command byte
// and opens or closes accordingly
unsigned char gripperPosition = RetrieveCommand(2);
if (gripperPosition & BIT7HI)
{
// If opening gripper, start timer that will kill current to gripper motor
PWM_Set_Duty_Cycle(GRIPPER_CHANNEL, GRIPPER_PWM);
GRIPPER_PORT &= ~GRIPPER_DIRECTION;
ES_Timer_InitTimer(GRIPPER_TIMER, 1000);
}
else
{
PWM_Set_Duty_Cycle(GRIPPER_CHANNEL, GRIPPER_PWM);
GRIPPER_PORT |= GRIPPER_DIRECTION;
}

}

static void ControlSPECIAL(void)
{
// Takes SPECIAL Position Command and sets it to default forward with hysteresis around
// 0x80 and backwards for all others.
unsigned char TempSpecialCommand = RetrieveCommand(3);

if (TempSpecialCommand >= 0x40 && TempSpecialCommand <= 0xC0)
{
Servo_Set_Pulse_Width(SPECIAL_CHANNEL, SPECIAL_FORWARD);
}
else
{
Servo_Set_Pulse_Width(SPECIAL_CHANNEL, SPECIAL_BACKWARD);
}
}

static void DetermineRoamerNumber(void)
{
// Function maps the value from a tristate switch to three roamer numbers
unsigned char TempRoamerNum = ADS12_ReadADPin(0);

if (TempRoamerNum > 0xC0)
{
MyRoamerNumber = 0x01;
}
else if (TempRoamerNum < 0x40)
{
MyRoamerNumber = 0x02;
}
else
{
MyRoamerNumber = 0x03;
}
}


/*------------------------------- Footnotes -------------------------------*/
/*------------------------------ End of file ------------------------------*/

SPECIAL PIC

SpecialPIC.asm
    list P=PIC16F690
#include "p16F690.inc"
    __config (_CP_OFF & _CPD_OFF & _BOR_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTOSC)

;
;   variable definitions
;

; state definitions
queryingBattery         equ     0
queryingLEDs            equ     1
updatingBrain           equ     2

; constant definitions
BATT_BYTES_TO_TRANSFER  equ     4
LED_BYTES_TO_TRANSFER   equ     4
BRAIN_TRANSF_REQ_LINE   equ     2
SPECIAL_SS_LINE         equ     0
BRAIN_SS_LINE           equ     1
TIMER_HI_SET_VALUE      equ     b'11111110'
BATTERY_STATUS_CMD      equ     0x3F
BATT_CHARGE_BIT         equ     3
FIRST_BYTE              equ     1
SECOND_BYTE             equ     2
THIRD_BYTE              equ     3
LED_CMD_MASK            equ     b'11110000'

; define variable addresses
; put in upper registers to make universally accessible
                        cblock 0x71
currentState                        ; current state in state machine
battCharge                          ; last-read charge of battery
battChargingState                   ; last-read state of battery charging/ not charging
battBytesTransferred                ; counter for transferring bytes
LEDBytesTransferred                 ; counter for transferring bytes
brainBytesTransferred               ; counter for transferring bytes
lastLEDCMD                          ; last-sent command to LEDs
LEDStatus                           ; last-read status of LEDs
brainTransferIPFlag                 ; saves whether transfer requested
                        endc
                        
                        org     0
                        GOTO    Main
                        org     5

Main:
; set internal oscillator for 1 MHz; slower speed ensures that
; slave has time to respond to master
                        BSF     OSCCON, IRCF2
                        BCF     OSCCON, IRCF1
                        BCF     OSCCON, IRCF0
                        BSF     OSCCON, OSTS ; clock defined in CONFIG

; set all pins to digital I/O
                        banksel ANSEL
                        CLRF    ANSEL
                        CLRF    ANSELH
                        
; initialize SPI
; configure for mode 3

; SSPSTAT already configured - transmit on rising edge - all defaults at zero

; SSPCON: SSPEN high, CKP idle high, master mode at FOSC/4
                        banksel SSPCON
                        MOVLW   1<<SSPEN|1<<CKP
                        MOVWF   SSPCON

; set SCK, SDO to outputs
                        banksel TRISB
                        BCF     TRISB, RB6
                        banksel TRISC
                        BCF     TRISC, RC7

; initialize 2ms timer on timer 1
; for ~2ms overflow rate: 65,536 ticks/ (250000 ticks/sec) = .2621 sec overflow rate
; .002 sec/ .2621 sec = approx 0.76% of max timer ticks needed
; set to 65536 ticks * (1 - .0076) = 65036 ticks when initializing
; result: set high register to 11111110, clear low register OK
                        banksel T1CON
                        BSF     T1CON, TMR1ON   ; timer 1 enable

; initialize slave select line outputs
                        banksel TRISA
                        MOVLW   b'100' ; set lines 0 and 1 as outputs
                                       ; 2 as input
                        MOVWF   TRISA
                        banksel PORTA
                        BSF     PORTA, SPECIAL_SS_LINE  ; raise SS lines
                        BSF     PORTA, BRAIN_SS_LINE    ; raise SS lines
                        
; clear variables to initialize
                        CLRF    battBytesTransferred
                        CLRF    LEDBytesTransferred
                        CLRF    brainBytesTransferred
                        MOVLW   b'11110000'             ; initial state of LEDs
                        MOVWF   lastLEDCMD
                        CLRF    brainTransferIPFlag
                        MOVLW   queryingBattery         ; set current state
                        MOVWF   currentState
; set timer
                        banksel TMR1L
                        CLRF    TMR1L
                        banksel TMR1H
                        MOVLW   TIMER_HI_SET_VALUE
                        MOVWF   TMR1H
                        banksel PIR1
                        BCF     PIR1, TMR1IF            ; clear timer expired flag
                        
; Main loop: continually request updates from SPECIAL and check for update request 
; from brain
MainLoop:

; test to enter into updatingBattery state when timer times out
; test state
                        MOVLW   queryingBattery
                        SUBWF   currentState, W     ; subtract values, test result
                        BTFSC   STATUS, Z           ; if set, state is queryingBattery
                        GOTO    battTimerTest       ; go to timer expiration test
                        GOTO    LEDTest             ; not set - test next state

; test for expired timer
battTimerTest:
                        banksel PIR1
                        BTFSC   PIR1, TMR1IF        ; test for expired timer
                        GOTO    battBytesTransfZero ; request batt status update
                        GOTO    LEDTest             ; not set - test next state
                        
; test for batt counter = zero?
battBytesTransfZero:
                        MOVF    battBytesTransferred, F ; move to self
                        BTFSC   STATUS, Z               ; test if result zero
                        GOTO    battSendFirstByte       ; counter zero - send 1st byte
                        GOTO    battSendAllBytes        ; counter not zero - continue
                                                        ; with SPI transmission

; send first byte of battery status update transmission
battSendFirstByte:
                        banksel PORTA
                        BCF     PORTA, SPECIAL_SS_LINE  ; lower slave select
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full bit
                        MOVLW   BATTERY_STATUS_CMD
                        MOVWF   SSPBUF                  ; write battery status request
                                                        ; to SSPBUF
                        INCF    battBytesTransferred    ; increment counter
                        GOTO    MainLoop                ; return to beginning of loop

; send remaining bytes for battery status update transmission
battSendAllBytes:
                        banksel SSPSTAT
                        BTFSC   SSPSTAT, BF             ; test buffer full bit
                        GOTO    battEvalCount           ; bit set - transfer bytes
                        GOTO    MainLoop                ; bit not yet set - return to loop

; determine whether counter val is less than bytes to transfer
battEvalCount:
                        MOVF    battBytesTransferred, W ; move count to W
                        ADDLW   -BATT_BYTES_TO_TRANSFER ; add (-) constant
                        BTFSC   STATUS, C               ; test carry bit
                        GOTO    advanceToQueryLEDs      ; two values equal, trans. done
                        GOTO    battSendMiddleBytes     ; bytes sent < bytes to send,
                                                        ; continue transfer

; send bytes 2, 3, 4 for battery status
battSendMiddleBytes:
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full flag
                        MOVLW   BATT_CHARGE_BIT         ; test if current count is 3rd bit
                        SUBWF   battBytesTransferred, W     
                        BTFSC   STATUS, Z               ; test if result zero
                        GOTO    saveThirdByte           ; count is 3; save byte
                        GOTO    finishSendingBatt       ; count not 3; don't save contents

; save third byte - this is battery charge byte
saveThirdByte:
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; save SSPFBUF contents
                        MOVWF   battCharge              ; update batt charge variable

; now send remaining bytes
finishSendingBatt:
                        banksel SSPBUF
                        CLRF    SSPBUF                  ; write 0 to SSPBUF
                        INCF    battBytesTransferred    ; increment counter
                        GOTO    MainLoop                ; return to beginning of loop

; all bytes sent - execute "state exit commands" and change states
advanceToQueryLEDs:
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full flag
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; save SSPBUF contents
                        MOVWF   battChargingState       ; update charge state variable
                        CLRF    battBytesTransferred    ; set counter back to zero
                        banksel PORTA
                        BSF     PORTA, SPECIAL_SS_LINE  ; raise special SS line
                        banksel TMR1L
                        CLRF    TMR1L                   ; reset timer
                        MOVLW   TIMER_HI_SET_VALUE      ;
                        MOVWF   TMR1H                   ;
                        banksel PIR1
                        BCF     PIR1, TMR1IF            ; clear timer expired flag
                        MOVLW   queryingLEDs            ; set current state to query LEDs
                        MOVWF   currentState
                        GOTO    MainLoop                ; return to beginning of loop

; test to enter into LED status update state
LEDTest:
; test state
                        MOVLW   queryingLEDs
                        SUBWF   currentState, W     ; subtract values, test result
                        BTFSC   STATUS, Z           ; if set, state is queryingLEDs
                        GOTO    LEDTimerTest        ; go to timer expiration test
                        GOTO    brainTest           ; not set - test next state

; test for expired timer
LEDTimerTest:
                        banksel PIR1
                        BTFSC   PIR1, TMR1IF        ; test for expired timer
                        GOTO    LEDBytesTransfZero  ; request LED status update
                        GOTO    brainTest           ; not set - test next state

; test for LED counter = zero?
LEDBytesTransfZero:
                        MOVF    LEDBytesTransferred, F  ; move to self
                        BTFSC   STATUS, Z               ; test if result zero
                        GOTO    LEDSendFirstByte        ; counter zero - send 1st byte
                        GOTO    LEDSendAllBytes         ; counter not zero - continue
                                                        ; with SPI transmission

; send first byte of LED status update transmission
LEDSendFirstByte:
                        banksel PORTA
                        BCF     PORTA, SPECIAL_SS_LINE  ; lower slave select
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full bit
                        banksel SSPBUF
                        MOVF    lastLEDCMD, W           ; send last command to SPECIAL
                        IORLW   LED_CMD_MASK            ; mask LED command
                        MOVWF   SSPBUF                  ; write to buffer
                        INCF    LEDBytesTransferred     ; increment counter
                        GOTO    MainLoop                ; return to beginning of loop
                        
; send remaining bytes for LED status update transmission
LEDSendAllBytes:
                        banksel SSPSTAT
                        BTFSC   SSPSTAT, BF             ; test buffer full bit
                        GOTO    LEDEvalCount            ; bit set - transfer bytes
                        GOTO    MainLoop                ; bit not yet set - return to loop

; determine whether counter val is less than bytes to transfer
LEDEvalCount:
                        MOVF    LEDBytesTransferred, W  ; move count to W
                        ADDLW   -LED_BYTES_TO_TRANSFER  ; add (-) constant
                        BTFSC   STATUS, C               ; test carry bit
                        GOTO    advanceToUpdateBrain    ; two values equal, trans. done
                        GOTO    LEDSendMiddleBytes      ; bytes sent < bytes to send,
                                                        ; continue transfer
                                                        
; send bytes 2, 3, 4 for LED status
LEDSendMiddleBytes: 
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full flag
                        banksel SSPBUF
                        CLRF    SSPBUF                  ; write 0 to SSPBUF
                        INCF    LEDBytesTransferred     ; increment counter
                        GOTO    MainLoop                ; return to beginning of loop

; all bytes sent - execute "state exit commands" and change states
advanceToUpdateBrain:
                        banksel SSPBUF                  ; clear buffer full flag
                        MOVF    SSPBUF, W               ; save SSPBUF contents
                        MOVWF   LEDStatus               ; update LED status variable
                        CLRF    LEDBytesTransferred     ; set counter back to zero
                        banksel PORTA
                        BSF     PORTA, SPECIAL_SS_LINE  ; raise special SS line
                        banksel TMR1L
                        CLRF    TMR1L                   ; reset timer
                        MOVLW   TIMER_HI_SET_VALUE      ;
                        MOVWF   TMR1H                   ;
                        banksel PIR1
                        BCF     PIR1, TMR1IF            ; clear timer expired flag
                        MOVLW   updatingBrain           ; set current state to updateBrain
                        MOVWF   currentState
                        GOTO    MainLoop                ; return to beginning of loop

; test to enter into updatingBrain state when timer times out
; test state
brainTest:
                        MOVLW   updatingBrain
                        SUBWF   currentState, W     ; subtract values, test result
                        BTFSC   STATUS, Z           ; if set, state is updatingBrain
                        GOTO    brainTimerTest      ; go to timer expiration test
                        GOTO    MainLoop            ; not set - return to start of loop

; test for expired timer
brainTimerTest:
                        banksel PIR1
                        BTFSC   PIR1, TMR1IF            ; test for expired timer
                        GOTO    brainBytesTransfZero    ; update brain if flag expired
                        GOTO    MainLoop                ; not set - go to loop start

; test whether brain is requesting update
brainBytesTransfZero:
                        banksel PORTA
                        BTFSC   PORTA, BRAIN_TRANSF_REQ_LINE    ; test whether input hi
                        BSF     brainTransferIPFlag, 0          ; line high, set flag by
                                                                ; setting bit 0
                        BTFSC   brainTransferIPFlag, 0          ; test whether transfer
                                                                ; in progress
                        GOTO    brainCounterTestByteZero        ; transfer in progress
                        GOTO    advanceToMainLoop               ; no transfer IP

; transfer started or in progress - find which byte being sent
brainCounterTestByteZero:
                        MOVF    brainBytesTransferred, F    ; move to self
                        BTFSC   STATUS, Z                   ; test if result zero
                        GOTO    brainSendFirstByte          ; counter zero - send 1st byte
                        GOTO    brainCounterTestBuffer      ; counter not zero - continue
                                                            ; with SPI transmission

; haven't sent bytes yet - send byte zero
brainSendFirstByte:
                        banksel PORTA
                        BCF     PORTA, BRAIN_SS_LINE    ; lower slave select
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full bit
                        CLRF    SSPBUF                  ; send first byte - write zero to BUF
                        INCF    brainBytesTransferred   ; increment counter
                        GOTO    MainLoop                ; return to beginning of loop

; upon expired flag, send remaining bytes
brainCounterTestBuffer:
                        banksel SSPSTAT
                        BTFSC   SSPSTAT, BF             ; test buffer full bit
                        GOTO    brainCounterTestByteOne ; bit set - transfer bytes
                        GOTO    MainLoop                ; bit not yet set - return to loop

; test whether one byte sent
brainCounterTestByteOne:
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full flag
                        MOVLW   FIRST_BYTE              ; test if one byte sent
                        SUBWF   brainBytesTransferred, W
                        BTFSC   STATUS, Z               ; test if result zero
                        GOTO    brainSendSecondByte     ; count is 1; send 2nd byte
                        GOTO    brainCounterTestByteTwo ; count not 1; test if it's 2

; save first byte sent in, send 2nd byte
brainSendSecondByte:
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full bit
                        MOVWF   lastLEDCMD              ; save incoming byte one
                                                        ; as LED command
                        MOVF    battCharge, W           ; send second byte - batt charge
                        MOVWF   SSPBUF                  ; write charge to buffer
                        INCF    brainBytesTransferred   ; increment counter
                        GOTO    MainLoop                ; return to beginning of loop

; test whether two bytes sent
brainCounterTestByteTwo:
                        banksel SSPBUF
                        MOVF    SSPBUF, W                   ; clear buffer full flag
                        MOVLW   SECOND_BYTE                 ; test if two bytes sent
                        SUBWF   brainBytesTransferred, W
                        BTFSC   STATUS, Z                   ; test if result zero
                        GOTO    brainSendThirdByte          ; count is 2; send 3rd byte
                        GOTO    brainCounterTestByteThree   ; count not 2; test if it's 3

; throw out second byte sent in, send 3rd byte
brainSendThirdByte:
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full bit
                                                        ; don't need 2nd incoming byte
                        MOVF    battChargingState, W    ; send third byte - batt charge
                        MOVWF   SSPBUF                  ; write charge state to buffer
                        INCF    brainBytesTransferred   ; increment counter
                        GOTO    MainLoop                ; return to beginning of loop

; test whether three bytes sent
brainCounterTestByteThree:
                        banksel SSPBUF
                        MOVF    SSPBUF, W                   ; clear buffer full flag
                        MOVLW   THIRD_BYTE                  ; test if three bytes sent
                        SUBWF   brainBytesTransferred, W
                        BTFSC   STATUS, Z                   ; test if result zero
                        GOTO    brainSendFourthByte         ; count is 3; send 4th byte
                        GOTO    advanceToMainLoop           ; count not 3; exit state

; throw out third byte sent in, send 4th byte
brainSendFourthByte:
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full bit
                                                        ; don't need 3rd incoming byte
                        MOVF    LEDStatus, W            ; send fourth byte - batt charge
                        MOVWF   SSPBUF                  ; write LED status to buffer
                        INCF    brainBytesTransferred   ; increment counter
                        GOTO    MainLoop                     

; clear timer flag, reset timer, set state identical to before
advanceToMainLoop:
                        banksel SSPBUF
                        MOVF    SSPBUF, W               ; clear buffer full bit
                                                        ; throw away incoming byte 4
                        CLRF    brainBytesTransferred   ; clear counter
                        BCF     brainTransferIPFlag, 0  ; clear transf in prog flag
                        banksel PORTA
                        BSF     PORTA, BRAIN_SS_LINE    ; raise slave select
                        banksel TMR1L
                        CLRF    TMR1L                   ; reset timer
                        MOVLW   TIMER_HI_SET_VALUE      ;
                        MOVWF   TMR1H                   ;
                        banksel PIR1
                        BCF     PIR1, TMR1IF            ; clear timer expired flag
                        MOVLW   queryingBattery         ; set current state to updateBrain
                        MOVWF   currentState
                        GOTO    MainLoop                ; return to beginning of loop

                        
                        END


Bill of Materials

Item Distributor Quantity Unit Unit Cost Total Cost
ROAMER Mechanical Components
Drive Motor SPDL 2 ea. $0.00 $0.00
Vigor VTS-08B Servo SPDL 1 ea. $6.15 $6.15
Linear Actuator SPDL 1 ea. $5.25 $5.25
Wheels Servo City 2 ea. $6.99 $13.98
Spider Coupler SPDL 2 ea. $3.75 $7.50
1/4" Clamping Hub Servo City 2 ea. $7.99 $15.98
Axles McMaster-Carr 0.5 ft. $1.013 $0.51
Steel Shaft Collar McMaster-Carr 2 ea. $0.63 $1.26
1-1/2" Diameter PVC Pipe SPDL 6 in. $0.00 $0.00
1-1/2" Diameter PVC Pipe Cover (2) Ace 1 pk. $1.29 $1.29
Ping Pong Ball SPDL 1 ea. $0.00 $0.00
L-Brackets McMaster-Carr 12 ea. $0.37 $4.44
8-32 Nuts McMaster-Carr 40 ea. $0.015 $0.60
8-32 Washers McMaster-Carr 40 ea. $0.01 $0.40
8-32 5/8" Screws McMaster-Carr 40 ea. $0.05 $2.00
4-40 Lock Nuts McMaster-Carr 40 ea. $0.0255 $1.02
4-40 Washers McMaster-Carr 100 ea. $0.0296 $2.96
4-40 5/8" Screws McMaster-Carr 40 ea. $0.034 $1.36
4-40 3" Screws McMaster-Carr 3 ea. $1.48 $4.44
1-3/4" Female Standoff McMaster-Carr 4 ea. $0.62 $2.48
2-1/2" Female Standoff McMaster-Carr 4 ea. $0.67 $2.68
1/4" Male-Female Standoff McMaster-Carr 4 ea. $0.46 $1.84
Duron PRL 1 sheet $8.00 $8.00
Subtotal $84.14
ROAMER Connectors and Fasteners
2 pin male Molex connector SPDL 16 ea. $0.10 $1.60
2 pin female Molex connector SPDL 14 ea. $0.10 $1.40
3 pin male Molex connector SPDL 6 ea. $0.10 $0.60
3 pin female Molex connector SPDL 6 ea. $0.10 $0.60
4 pin male Molex connector SPDL 1 ea. $0.10 $0.10
4 pin female Molex connector SPDL 1 ea. $0.10 $0.10
5 pin female Molex connector SPDL 1 ea. $0.10 $0.10
6 pin male Molex connector SPDL 2 ea. $0.10 $0.20
6 pin female Molex connector SPDL 2 ea. $0.10 $0.20
20-pin DIP socket SPDL 2 ea. $0.50 $1.00
24-pin DIP socket SPDL 1 ea. $0.60 $0.60
Crimps Jameco 59 ea. $0.022 $1.30
Twisted pair wire SPDL 15 ft. $0.10 $1.50
Jumper wire Lab kit 60 ea. $0.00 $0.00
Heat shrink SPDL 2 ft. $0.25 $0.50
4 wire ribbon cable SPDL 1 ft. $0.25 $0.25
Terminal block Digi-Key 3 ea. $0.50 $1.50
Battery connector SPDL 3 ea. $2.05 $6.15
Subtotal $17.70
ROAMER Electrical Components
H-bridge kit (LTE5206) SPDL 3 ea. $5.50 $16.50
+5V 1A regulator SPDL 1 ea. $0.75 $0.75
10K ohm resistor SPDL 2 ea. $0.01 $0.02
0.1 nF capacitor SPDL 1 ea. $0.10 $0.10
10 uF capacitor SPDL 1 ea. $0.10 $0.10
22 uF capacitor SPDL 1 ea. $0.10 $0.10
7.2V NiCd Battery SPDL 3 ea. $7.75 $23.25
Toggle Switch Room 36 1 ea. $1.25 $1.25
Sliding Switch SPDL 1 ea. $0.00 $0.00
Fuse Holder Jameco 2 ea. $0.69 $1.38
1A Fuse Room 36 1 ea. $0.25 $0.25
5A Fuse Room 36 1 ea. $0.25 $0.25
Perfboard Room 36 1 ea. $1.50 $1.50
Heat Sink Room 36 4 ea. $0.25 $1.00
Subtotal $46.45
ROAMER Miscellaneous
Freescale MC9S12E128 SPDL 1 ea. $0.00 $0.00
PIC16F690 SPDL 1 ea. $0.00 $0.00
XBee Radio SPDL 1 ea. $0.00 $0.00
Subtotal $0.00
ROAMER Subtotal
Subtotal $148.29
POD Mechanical Components and Sensors
Foam Wheel and Motor SPDL 1 pk. $3.00 $3.00
Hextronik HXT900 Servo SPDL 1 ea. $3.60 $3.60
1-1/2" Diameter PVC Pipe SPDL 8 ea. $0.00 $0.00
Foam SPDL 8 sq. in. $0.00 $0.00
Velcro SPDL 6 sq. in. $0.00 $0.00
Hinges McMaster-Carr 2 ea. $0.61 $1.22
8-32 Nuts McMaster-Carr 4 ea. $0.015 $0.06
8-32 Washers McMaster-Carr 12 ea. $0.014 $0.17
8-32 1" Screws McMaster-Carr 4 ea. $0.15 $0.60
4-40 Lock Nuts McMaster-Carr 8 ea. $0.03 $0.24
4-40 Nuts McMaster-Carr 12 ea. $0.016 $0.19
4-40 Washers McMaster-Carr 12 ea. $0.03 $0.36
4-40 5/8" Screws McMaster-Carr 12 ea. $0.034 $0.41
4-40 7/16" Screws McMaster-Carr 12 ea. $0.08 $0.98
4" Female Standoff McMaster-Carr 4 ea. $0.77 $3.08
Duron PRL 0.5 sheet $8.00 $4.00
High-gauge metal wire SPDL 0.5 ft. $0.10 $0.05
Zip tie PRL 12 ea. $0.00 $0.00
Vinyl PRL 50 lin. in. $0.10 $5.00
Subtotal $22.96
POD Connectors and Fasteners
2 pin male Molex connector SPDL 8 ea. $0.10 $0.80
2 pin female Molex connector SPDL 8 ea. $0.10 $0.80
3 pin male Molex connector SPDL 5 ea. $0.10 $0.50
3 pin female Molex connector SPDL 4 ea. $0.10 $0.40
6 pin male Molex connector SPDL 1 ea. $0.10 $0.10
6 pin female Molex connector SPDL 2 ea. $0.10 $0.20
14-pin DIP socket SPDL 1 ea. $0.40 $0.40
20-pin DIP socket SPDL 1 ea. $0.50 $0.50
Crimps SPDL 40 ea. $0.022 $0.88
Twisted pair wire SPDL 7 ft. $0.10 $0.70
Jumper wire Lab kit 80 ea. $0.00 $0.00
Heat shrink SPDL 2 ft. $0.25 $0.50
Terminal block Digi-Key 1 ea. $0.50 $2.69
Battery connector SPDL 1 ea. $2.05 $2.05
Subtotal $8.33
POD Electrical Components
Op-amp (LM324) Lab Kit 1 ea. $0.00 $0.00
N-Channel MOSFET (2N7000) Lab kit 1 ea. $0.00 $0.00
+5V 1A regulator SPDL 1 ea. $0.75 $0.75
10K ohm resistor SPDL 8 ea. $0.01 $0.08
100K ohm resistor SPDL 6 ea. $0.01 $0.06
200 ohm resistor SPDL 2 ea. $0.01 $0.02
1M ohm resistor SPDL 2 ea. $0.01 $0.00
0.1 nF capacitor SPDL 2 ea. $0.10 $0.20
22 uF capacitor SPDL 2 ea. $0.10 $0.20
7.2V NiCd Battery SPDL 1 ea. $7.75 $7.75
Toggle Switch Room 36 1 ea. $1.25 $1.25
Rocker Switch Sparkfun 4 ea. $0.50 $2.00
Sliding Switch SPDL 1 ea. $0.00 $0.00
Push Button Sparkfun 1 ea. $0.95 $0.95
Force-sensitive Resistor Digi-Key 1 ea. $3.19 $3.19
Rotary Potentiometer Sparkfun 1 ea. $0.95 $0.95
Fuse Holder Jameco 1 ea. $0.69 $0.69
1A Fuse Room 36 1 ea. $0.25 $0.25
Green LED SPDL 1 ea. $0.25 $0.25
Perfboard Room 36 1 ea. $1.50 $1.50
Heat Sink Room 36 1 ea. $0.25 $0.25
Subtotal $20.34
POD Miscellaneous
PIC16F690 SPDL 2 ea. $0.00 $0.00
Subtotal $0.00
POD Subtotal
Subtotal $51.63
Grand Total
Total $199.92

Gems of Wisdom

  1. Start From Scratch
    Don't reuse your old project parts. Just start from scratch so errors don't propogate between terms.
  2. Keep It Simple
    The project is too complex and the time frame too short to make things overly complicated. Shooting for basic functionality will save you many hours of debugging.
  3. Plan Ahead
    Set milestones, and try to have check-off requirements locked down on or before the day of the check-off. Returning to modify already-finished designs can cause many problems.
  4. Stick to a Schedule
    The project is tough and requires long hours, but you don't have to be in lab every day and pull all-nighters. A consistent work schedule will keep things on track and prevent panic.
  5. Design Before Executing
    Make smart decisions regarding communication protocol and nodes early on. Eliminate any unnecessary components, because each one requires its own code and debugging and brings an added layer of unknowns.

Project Logbook