This page details the design choices and implementation of all the software used in the completion of this project.
Given that we are using XBee modules to communicate via the Zigbee protocol between two PIC32s we decided to create two copies of the Framework4PIC32. One for the PIC32 on the boat called "boatFramework" and another for the PIC32 on the controller called "controllerFramework". This helped with organization of the events and services that each PIC32 is concerned about. For each framework their will be an overall description of the services used and the pseudo-code for them. All the detailed code can be found in the GitHub repository found in this link.
The Mallard Module shall have two modes: Drive Mode and Refuel Mode. It must be impossible to be in both modes simultaneously
Drive Mode
1. When in Drive Mode, the Mallard Module shall be capable of controlling the propulsion.
2. Any recharging input shall be ignored while in this state.
Refuel Mode
1. When in Refuel Mode, the Mallard Module shall be allowed to increase the steam pressure of the Quackraft.
2. Any driving or mechanism control inputs shall be ignored while in this state.
3. While in this state, refueling should only occur while the user of the Mallard Module partakes in some sort of refueling action.
4. The refueling action of the Mallard Module must implement an activity consisting of large-scale human body movements. It should be inventive and interesting for the audience to watch. Use of actions that make the Mallard Module operator look and feel foolish are encouraged.
5. The required Mallard Module refueling action must last continuously for at least 5 seconds. Partial completion of a refueling action should result in partial refueling of the Quackraft, proportional to time spent performing the refueling action.
For our state machines we need limited communication between the two PIC32s. In total we have about 15 defined commands that trigger transitions using information from the other PIC32. For that reason on the leader side we are using a simple timer to trigger a send message (SPIOperate_SPI1_Send8(message2send);) and an Event Checker on the follower for (SPIOperate_HasSS1_Risen()) to read a new message like (uint8_t newMessage = (uint8_t)SPIOperate_ReadData(SPI_SPI1);) when the SPI chip select line has risen indicating a tranmission was completed. Then, in order to organize and send the commands we have ES events for commands to be sent (ES_NEW_SPI_CMD_SEND) or indicate that we received a command (ES_NEW_SPI_CMD_RECEIVED) for which we encode the 8 bit message in the event parameter. The command values for the event parameters are defined in the SPI header so that any other service can see them and use them with ease.
Consists of two main serives. One that handles the main controller logic and actions based on inputs, sensors and information received over the XBee messages (MallardCommunicationService); and another for the fuel logic and display (FuelServoService). The main inputs are a potentiometer for selecting a boat to pair with, a limit switch for indicating refueling, two joystick presses digital inputs for pairing and shooting, and two analog joystick inputs to command throttle and direction.
Event checkers for the start button, and for the SPI chip select line in our SPI communication protocol.
/*** EVENT CHECKER: Start Button ***/
Function Check4StartButton
set ReturnValue to false
set static variable Initialized to false
if (is Not Initialized)
// Disable analog, set input, set low
BUTTON_TRIS = 1; // input
BUTTON_LAT = 0; // set low initially
Initialized = true;
set static variable LastButtonState to 1(assumed not pressed)
read CurrentButtonState from button input pin
compare to static LastButtonState
if (CurrentButtonState is not equal to LastButtonState and CurrentButtonState is 0)
set ReturnValue to true
create new event
set event type to ES_START_DOWN
set event parameter to CurrentButtonState
post event to PostOperatorFSM
set ReturnValue to true
end if
update LastButtonState to CurrentButtonState
return ReturnValue
/*** EVENT CHECKER: SPI Follow ***/
Function Check4SPI
if (SPIOperate_HasSS1_Risen()) // chip select went high, which means a transmission was completed
Set newMessage to value from SPI1 buffer, SPIOperate_ReadData(SPI_SPI1);
create new event
set event type to ES_SPI_COMPLETE
set event parameter to newMessage
post event to all
return true
end if
return false
Module-level variables: MyPriority
/*------------------------------ Module Code ------------------------------*/
Function InitFieldSideServoService(Priority)
set MyPriority to Priority
Initialize PWM for servo
Setup timer 3 with prescaler 64 and 6250 ticks for the required 50Hz
Setup channel 3 for this servo
Map RB10 to this output
Set pulse width to neutral position
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostFieldSideServoService(ThisEvent)
Post event to this queue
Function RunFieldSideServoService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType)
case ES_NEW_SPI_CMD_RECEIVED:
if (ThisEvent.EventParam is CMD_SPI_BLUE_TEAM)
Set Duty Cycle to corresponding length for going full to blue side
if (ThisEvent.EventParam is CMD_SPI_GREEN_TEAM)
Set Duty Cycle to corresponding length for going full to green side
break;
case ES_RESET:
Set pulse width for neutral position
break;
return ReturnEvent
Starts in neutral position and moves the indicator to either blue or green when the Drive and Navigation PIC identifies the side. Uses channel 3 with timer 3.
Module-level variables: MyPriority
/*------------------------------ Module Code ------------------------------*/
Function InitFieldSideServoService(Priority)
set MyPriority to Priority
Initialize PWM for servo
Setup timer 3 with prescaler 64 and 6250 ticks for the required 50Hz
Setup channel 3 for this servo
Map RB10 to this output
Set pulse width to neutral position
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostFieldSideServoService(ThisEvent)
Post event to this queue
Function RunFieldSideServoService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType)
case ES_NEW_SPI_CMD_RECEIVED:
if (ThisEvent.EventParam is CMD_SPI_BLUE_TEAM)
Set Duty Cycle to corresponding length for going full to blue side
if (ThisEvent.EventParam is CMD_SPI_GREEN_TEAM)
Set Duty Cycle to corresponding length for going full to green side
break;
case ES_RESET:
Set pulse width for neutral position
break;
return ReturnEvent
PIC32_PWM_Lib: Output compare library to set up timers, channels, modes, and mapping pins. Primary use is servo PWM.
See github for full source code on this link.