This page details the design choices and implementation of all the software used in the completion of this project. In particular, the controller Framework consists of two main services. MallarCommunicationServices deals with the message handling to the boat, as well as the Drive and Refuel modes logic. Given that our refuel mechanism sends 5 refuel messages per action we implemented the switching between modes via software rather than hardware.
In driving mode the controller sends propulsion and shooting commands from the joysticks, and in refuel mode it sends the corresponding refueling command.
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.
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.
Following the communcations protocol, the main messages used on the boat have the following structure and logic.
Sending pairing message to boats:
0x7E - 0x00 - 0x09 (Length LSB) - 0x01 - 0x00 - (Destination MSB & LSB) - 0x01 (OptByte) - 0x02 (Status) - (MyController MSB & LSB) - 0x00 - CheckSum
Receiving pairing confirmation message from boat:
0x7E - 0x00 - 0x06 (Length LSB) - 0x81 - (Source MSB & LSB) - (RSSI Byte) - (OptByte) - 0xFF (ChargeVal) - CheckSum
Sending driving messages to boat:
0x7E - 0x00 - 0x09 (Length LSB) - 0x01 - 0x00 - (Destination MSB & LSB) - 0x01 (OptByte) - 0x00 (Driving) - (Joy1) - (Joy2) - (Digi)- CheckSum
Note: For our boat the we only use bit 1 of the "Digi" byte to represent shooting (1) or not shooting (0)
Sending charging message to boat:
0x7E - 0x00 - 0x09 (Length LSB) - 0x01 - 0x00 - (Destination MSB & LSB) - 0x01 (OptByte) - 0x01 (Charging) - 0x00 - 0x00 - 0x00 - CheckSum
Receiving charge value update messages from boat:
0x7E - 0x00 - 0x06 (Length LSB) - 0x81 - (Source MSB & LSB) - (RSSI Byte) - (OptByte) - ChargeVal - CheckSum
Event checkers for the pairing button, refuel input, and shooting button. Note that all these inputs are active low and passive floating, so we enable the internal pull-up functionality for all three inputs. I.e. CNPUBbits.CNPUB13 = 1; to enable the pull-up for pin in register RPB13.
/*** EVENT CHECKER: Pair Button RB15***/
Function Check4PairButton
set ReturnValue to false
set static variable Initialized to false
if (is Not Initialized)
// Disable analog, set input, set low
PAIR_BUTTON_ANSEL = 0; // digitial
PAIR_BUTTON_TRIS = 1; // input
PAIR_BUTTON_CNPU = 1; // enable internal pull-up
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) // changed from high (passive) to low (active)
set ReturnValue to true
create new event
set event type to ES_START_PAIRING
post event to MallardCommunicationService
set ReturnValue to true
end if
update LastButtonState to CurrentButtonState
return ReturnValue
/*** EVENT CHECKER: Refuel Input RB12***/
Function Check4RefuelInput
set ReturnValue to false
set static variable Initialized to false
if (is Not Initialized)
// Disable analog, set input, set low
REFUEL_INPUT_ANSEL = 0; // digitial
REFUEL_INPUT_TRIS = 1; // input
REFUEL_INPUT_CNPU = 1; // enable internal pull-up
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) // changed from high (passive) to low (active)
set ReturnValue to true
create new event
set event type to ES_REFUEL_INPUT
post event to MallardCommunicationService
set ReturnValue to true
end if
update LastButtonState to CurrentButtonState
return ReturnValue
/*** EVENT CHECKER: Shoot Button RB13***/
Function Check4ShootButton
set ReturnValue to false
set static variable Initialized to false
if (is Not Initialized)
// Disable analog, set input, set low
SHOOT_BUTTON_ANSEL = 0; // digitial
SHOOT_BUTTON_TRIS = 1; // input
SHOOT_BUTTON_CNPU = 1; // enable internal pull-up
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) // changed from high (passive) to low (active)
set ReturnValue to true
create new event
set event type to ES_SHOOT
post event to MallardCommunicationService
set ReturnValue to true
end if
update LastButtonState to CurrentButtonState
return ReturnValue
Handles wireless communication between the Mallard controller and Quackraft module over UART. It reads joystick, boat-selection potentiometer, and shoot/refuel inputs, handles pairing and connection monitoring, transmits status (pairing/driving/charging) messages to the selected boat, receives Charge Value updates, and forwards fuel information to FuelServoService.
Module-level variables: MyPriority, pairedStatus, desiredAddressLSB, ChargeVal, StatusVal, Joy1Val, Joy2Val, DigiVal, BoatPotVal, rxBuf[], txBuf[], ChargingBytesPending
/*------------------------------ Module Code ------------------------------*/
Function InitMallardCommunicationService(Priority)
set MyPriority to Priority
Print initialization message
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostMallardCommunicationService(ThisEvent)
Post event to this queue
Function RunMallardCommunicationService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch(ThisEvent.EventType)
case ES_INIT:
Initialize UART hardware
Configure ADC channels for:
Joystick 1
Joystick 2
Boat selection potentiometer
Configure pair button input
Configure status LEDs
Initialize variables:
desired boat address
pairing status
joystick midpoint
charge value
pending charge/shoot counters
break
case ES_START_PAIRING:
Read boat selection potentiometer
Determine desired boat address from potentiometer position
Set communication status to PAIRING
Start periodic transmit timer
break
case ES_TIMEOUT:
if SEND_MSG_TIMER expired
Read joystick and potentiometer values
Read shoot button state
Flash pairing LED if not paired
Send communication packet to boat
Restart SEND_MSG_TIMER
if UNPAIRING_TIMER expired
Set pairedStatus false
Turn off paired LED
Return to pairing mode
break
case ES_REFUEL_INPUT:
if paired // Only prepare refuel input commands when paired with a boat
Set communication status to CHARGING
Add charging messages to pending queue
break
case ES_RX_BYTE:
Assemble incoming UART message
When complete message received
Validate message
If valid
Interpret received message
break
default
break
return ReturnEvent
// Helper Functions
Function InitUART()
Configure UART2 for 9600 baud
Configure TX and RX pins
Enable transmitter and receiver
Enable UART RX interrupts
Enable global interrupts
Enable UART
// Using interrupts for receiving messages
Function UART_RX_ISR()
While UART receive buffer contains data
Read received byte
Post ES_RX_BYTE event containing received byte
Clear UART interrupt flag
Function ComputeCheckSum(DataLength)
Sum all bytes in data frame
Compute checksum as 0xFF minus sum
Store checksum in CheckSumVal
Function ReadADCValues()
Read joystick ADC channels
Convert readings to 8-bit values (the ADC values are 10-bits on the PIC32)
Read boat-selection potentiometer
Store values for transmission
Function ValidReceivedMessage()
Verify checksum
Verify:
Start byte
Length bytes
API identifier
Address fields
Return true if message is valid
Function InterpretMessage()
If not paired and valid pairing response received
Set pairedStatus true
Turn on paired LED
Change status to DRIVING
Start unpairing timeout timer
Post ES_PAIRED event
Else if already paired and valid message received
Restart unpairing timeout timer
Update ChargeVal from received packet
Post ES_FUEL_VAL_RECEIVED to FuelServoService
Function SendMsgToQuackraft(Status, Joy1, Joy2, Digi)
Construct communication packet
Insert:
Destination address
Current status
Joystick values
Shoot command
If charging mode
Send charging packet
Decrement pending charging count
Compute checksum
Append checksum
Transmit packet byte-by-byte through UART
Starts in neutral position and moves the corresponding fuel level received from a MallardCommunicationService event. Uses channel 4 with timer 2 on pin RPA2.
Module-level variables: MyPriority, ServoPosition, CurrentFuelVal
/*------------------------------ Module Code ------------------------------*/
Function InitFuelServoService(Priority)
set MyPriority to Priority
Initialize PWM for servo
Setup timer 2 with prescaler 64 and 6250 ticks for the required 50Hz
Setup channel 4 for this servo
Map RPA2 to this output
Set pulse width to neutral position
Initialize CurrentFuelVal to the maximum fuel value
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostFuelServoService(ThisEvent)
Post event to this queue
Function RunFuelServoService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType)
case ES_FUEL_VAL_RECEIVED:
Set CurrentFuelVal to the param of this event
Clamp CurrentFuelVal between min and max fuel values in case of an invalid value
Map the CurrentFuelVal to the range of servo positions
Set the mapped value to ServoPosition
Set pulse width on channel 4 to corresponding calculated value
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.