This page details the design choices and implementation of all the software used in the completion of this project. In particular, the boatFramework consists of four main services.
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.
Following the communcations protocol, the main messages used on the boat have the following structure and logic.
Receiving pairing message from mallard modules and pair:
0x7E - 0x00 - 0x09 (Length LSB) - 0x81 - 0x21 (MyAddress) - (SourceMSB & LSB) - (RSSI Byte) - (OptByte) - 0x02 (Status) - (SourceMSB) - (SourceLSB) - 0x00 - CheckSum
Responding with fuel status (ChargeVal is 0xFF before pairing and the calculated charge value after):
0x7E - 0x00 - 0x06 (Length LSB) - 0x01 - 0x00 - (DestinationMSB & LSB) - 0x01 (OptByte) - ChargeVal - CheckSum
Handles wireless communication between the Quackraft and the Mallard controller module over UART. It receives and validates command packets, manages controller pairing and connection timeouts, forwards driving and cannon-control commands to the appropriate services, and tracks the boat’s charge/fuel state. It also sends status updates, including the current charge level, back to the controller.
Module-level variables: MyPriority, pairedStatus, pairedAddressLSB, ChargeVal, InitializeFuel, rxBuf[], txBuf[], receivedByte, CheckSumVal, newMessageStarted
/*------------------------------ Module Code ------------------------------*/
Function InitCommunicationServiceService(Priority)
Set MyPriority to Priority
Print initialization messages
Post ES_INIT event to this queue
Return true if successful, false otherwise
Function PostCommunicationServiceService(ThisEvent)
Post event to this queue
Function RunCommunicationServiceService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType)
case ES_INIT:
Initialize UART hardware
Enable UART receive interrupts
break
case ES_TIMEOUT:
if(EventParam is UNPAIRING_TIMER)
Set pairedStatus to false
Post ES_UNPAIRED to PairedServoService
Post ES_UNPAIRED to DrivingService
break
case ES_RX_BYTE:
if(received byte is START_BYTE)
Clear receive buffer
Begin assembling a new message
Store START_BYTE
else if(new message is being assembled)
Shift receive buffer
Add newest byte
if(receive buffer contains complete message)
Validate checksum and packet fields
if(message is valid)
Interpret message contents
Send response packet containing current ChargeVal
Stop assembling current message
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
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 ValidReceivedMessage()
Compute expected checksum
If checksum does not match
Return false
Verify:
Start byte
Length bytes
API identifier
Destination address
Return true if all fields are valid
Function InterpretMessage()
// Pairing request
If not paired and received pairing message
Save controller address
Set pairedStatus true
Start UNPAIRING_TIMER
Reset ChargeVal
Enable fuel initialization
Post ES_PAIRED to PairedServoService
// Messages from paired controller
Else if paired and source address matches
If charging message
Restart UNPAIRING_TIMER
Increase ChargeVal by 8
Limit ChargeVal to maximum value
Post ES_CHARGING
Else if driving message
If first drive message after pairing
Initialize ChargeVal to full
Clear initialization flag
Restart UNPAIRING_TIMER
Extract:
Throttle joystick value
Steering joystick value
Digital shoot command
If ChargeVal is zero
Force throttle to neutral
Force steering to neutral
Post ES_CANNON_STOP
Else
If boat is moving
Decrease ChargeVal
If shoot button pressed
Decrease ChargeVal
Post ES_CANNON_START
Else
Post ES_CANNON_STOP
Combine steering and throttle values
Post ES_DRIVE to DrivingService
Function SendMsgToMallardModule(Charge)
Construct transmit packet:
Start byte
Length bytes
API fields
Paired controller address
Current Charge value
Compute checksum
Append checksum
Transmit packet byte-by-byte through UART
This service uses Timer 2 with Output Compares 2 and 3 for the left and right DC motors on the drive train, respectively.
Module-level variables: MyPriority
/*------------------------------ Module Code ------------------------------*/
Function InitDCMotorService(Priority)
set MyPriority to Priority
Initialize OCs 2 and 3 on timer 2 using our PIC32_PWM_Lib calls:
configure timer, set channel, assign channel to timer, map channeltoOutputPin,
Initialize analog, digital, output for all the motor pins
Set the dutyCycle in both motors to 0, to ensure they start off
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostDCMotorService(ThisEvent)
Post event to this queue
Function RunDCMotorService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType)
case ES_INIT:
// Do nothing, announce
break;
case ES_MOTORS_OFF:
// Stop the robot, set duty cycle to 0.
break;
case ES_MOTOR_PRIMITIVE:
Execute primitive according to ThisEvent.EventParam
(Function that maps forwards, backwards, rotateCW, etc. with corresponding duty cycles)
For primitives that use line following or encoder counts, post corresponding events to DriveCorrectionService to start that functionality
break;
return ReturnEvent
This service uses Timer 2 with Output Compares 2 and 3 for the left and right DC motors on the drive train, respectively.
Module-level variables: MyPriority
/*------------------------------ Module Code ------------------------------*/
Function InitDCMotorService(Priority)
set MyPriority to Priority
Initialize OCs 2 and 3 on timer 2 using our PIC32_PWM_Lib calls:
configure timer, set channel, assign channel to timer, map channeltoOutputPin,
Initialize analog, digital, output for all the motor pins
Set the dutyCycle in both motors to 0, to ensure they start off
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostDCMotorService(ThisEvent)
Post event to this queue
Function RunDCMotorService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType)
case ES_INIT:
// Do nothing, announce
break;
case ES_MOTORS_OFF:
// Stop the robot, set duty cycle to 0.
break;
case ES_MOTOR_PRIMITIVE:
Execute primitive according to ThisEvent.EventParam
(Function that maps forwards, backwards, rotateCW, etc. with corresponding duty cycles)
For primitives that use line following or encoder counts, post corresponding events to DriveCorrectionService to start that functionality
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.