In this section, we will complete the component implementation by transmitting a telemetry channel, and implementing the behavior of the run
port, which is called by the rate-group.
Refer back to the component design for explanations of what each of these items is intended to do.
Telemetry channels represent the state of the system. Typically, telemetry channels are defined for any states that give crucial insight into the component's behavior. This tutorial defines two channels: one will be shown, and the other is left up to the student.
Inside your led-blinker/Components/Led
directory, open the Led.fpp
file. After the events you added in the previous implementation section, add a telemetry channel of type Fw.On
to report the blinking state.
@ Telemetry channel to report blinking state.
telemetry BlinkingState: Fw.On
Save the file. In the terminal, run the following to verify your component is building correctly.
# In led-blinker/Components/Led
fprime-util build
Fix any errors that occur before proceeding with the rest of the tutorial.
Inside your led-blinker/Components/Led
directory, open Led.cpp
, and navigate to the BLINKING_ON_OFF
command. Report the blinking state via the telemetry channel we just added. To do so, replace the following:
// TODO: Report the blinking state via a telemetry channel.
// NOTE: This telemetry channel will be added during the "Telemetry" exercise.
with the command to send the telemetry channel:
this->tlmWrite_BlinkingState(on_off);
Save the file. In the terminal, run the following to verify your component is building correctly.
# In led-blinker/Components/Led
fprime-util build
Fix any errors that occur before proceeding with the rest of the tutorial.
Now complete the component's telemetry model by adding the LedTransitions
channel of type U32
to Led.fpp
. You will emit this telemetry channel in a further section.
Build with that new channel and fix any errors before continuing.
Some users choose to send telemetry packets instead of raw channels to the ground system. Although this tutorial will not use telemetry packets, it is best practice to keep the packet definitions up-to-date to make switching to telemetry packets seamless should the user choose to do so.
Add the following to led-blinker/LedBlinker/Top/LedBlinkerPackets.xml
:
<packet name="LedChannels" id="8" level="1">
<channel name="led.LedTransitions"/>
<channel name="led.BlinkingState"/>
</packet>
Add this after the opening
<packet>
tag and before the closing</packet>
tag as a separate block.
Now that this has been added, build the topology:
# In led-blinker/LedBlinker
fprime-util build
Fix any errors before continuing
Parameters are ground-controllable settings for the system. Parameters are used to set settings of the system that the ground may need to change at some point during the lifetime of the system. This tutorial sets one parameter, the blink interval.
For each parameter you define in your fpp, the F´ autocoder will autogenerate a SET and SAVE command. The SET command allows ground to update the parameter. The SAVE command allows ground to save the current value of the parameter for use even after FSW reboots.
In your led-blinker/Components/Led
directory, open the Led.fpp
file. After the telemetry channels you added previously, add a parameter for the blinking interval. Give the parameter the name BLINK_INTERVAL
and type U32
.
@ Blinking interval in rate group ticks
param BLINK_INTERVAL: U32
Save the file. In the terminal, run the following to verify your component is building correctly.
# In led-blinker/Components/Led
fprime-util build
In your led-blinker/Components/Led
directory, open the file Led.hpp
and add the following public function signature after the destructor:
//! Emit parameter updated EVR
//!
void parameterUpdated(FwPrmIdType id /*!< The parameter ID*/
);
This function is called when a parameter is updated via the generated SET command. Although the value is updated automatically, this function gives developers a chance to respond to changing parameters. This tutorial uses it to emit an updated Event.
Save file and in your led-blinker/Components/Led
directory, open Led.cpp
and add the implementation for parameterUpdated
:
void Led ::parameterUpdated(FwPrmIdType id) {
// Read back the parameter value
Fw::ParamValid isValid;
U32 interval = this->paramGet_BLINK_INTERVAL(isValid);
// NOTE: isValid is always VALID in parameterUpdated as it was just properly set
FW_ASSERT(isValid == Fw::ParamValid::VALID, isValid);
// Check the parameter ID is expected
if (PARAMID_BLINK_INTERVAL == id) {
// Emit the blink interval set event
// TODO: Add an event with, severity activity high, named BlinkIntervalSet that takes in an argument of type U32 to report the blink interval.
}
}
When you are done, save the file. In the terminal, run the following to verify your component is building correctly.
# In led-blinker/Components/Led
fprime-util build
Resolve any errors before continuing
Any communication between components should be accomplished through F´ ports. Thus far we have been using a set of standard ports for handling Commands, Telemetry, Events, and Parameters. This section will add two specific ports to our component: input run
to be called from the rate group, and output gpioSet
to drive the GPIO driver.
In your led-blinker/Components/Led
directory, open the Led.fpp
file. After the parameters you added previously, add the following two ports:
@ Port receiving calls from the rate group
sync input port run: Svc.Sched
@ Port sending calls to the GPIO driver
output port gpioSet: Drv.GpioWrite
Input ports can be given any name that you choose. In this example, we choose
run
andgpioSet
since these names capture the behavioral intent. The types ofSvc.Sched
andDrv.GpioWrite
are significant as these types must match the remote component.
In your led-blinker/Components/Led
directory, run the following to autogenerate stub functions for the run
input port we just added.
# In led-blinker/Components/Led
fprime-util impl
In your led-blinker/Components/Led
directory, open Led.hpp-template
file and copy this block over to Led.hpp
as we did before
PRIVATE:
// ----------------------------------------------------------------------
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
//! Handler implementation for run
//!
void run_handler(
const NATIVE_INT_TYPE portNum, /*!< The port number*/
NATIVE_UINT_TYPE context /*!<
The call order
*/
);
In your led-blinker/Components/Led
directory, open Led.cpp-template
file and copy this block over to Led.cpp
.
// ----------------------------------------------------------------------
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------
void Led ::
run_handler(
const NATIVE_INT_TYPE portNum,
NATIVE_UINT_TYPE context
)
{
// TODO
}
The run
port will be invoked repeatedly on each cycle of the rate group. Each invocation will call into the run_handler
function such that the component may perform behavior on each cycle.
Here we want to turn the LED on or OFF based on a cycle count to implement the "blinking" behavior we desire.
In your led-blinker/Components/Led
directory, open Led.cpp
, copy in the following block of code, and try filling-in the TODOs based on what you learned and defined in previous sections.
Don't forget to read the code and comments to understand more about how to use F´.
void Led ::
run_handler(
const NATIVE_INT_TYPE portNum,
NATIVE_UINT_TYPE context)
{
// Read back the parameter value
Fw::ParamValid isValid;
U32 interval = 0; // TODO: Get BLINK_INTERVAL parameter value
// Force interval to be 0 when invalid or not set
interval = ((Fw::ParamValid::INVALID == isValid) || (Fw::ParamValid::UNINIT == isValid)) ? 0 : interval;
// Only perform actions when set to blinking
bool is_blinking = this->blinking;
if (is_blinking)
{
Fw::On new_state = this->state;
// Check for transitions
if ((0 == this->count) && (this->state == Fw::On::OFF))
{
new_state = Fw::On::ON;
}
else if (((interval / 2) == this->count) && (this->state == Fw::On::ON))
{
new_state = Fw::On::OFF;
}
// A transition has occurred
if (this->state != new_state)
{
this->transitions = this->transitions + 1;
// TODO: Add a telemetry to report the number of LED transitions (this->transitions)
// Port may not be connected, so check before sending output
if (this->isConnected_gpioSet_OutputPort(0))
{
this->gpioSet_out(0, (Fw::On::ON == new_state) ? Fw::Logic::HIGH : Fw::Logic::LOW);
}
// TODO: Add an event to report the LED state (new_state).
this->state = new_state;
}
this->count = ((this->count + 1) >= interval) ? 0 : (this->count + 1);
}
}
Save the file and in the terminal, run the following to verify your component is building correctly.
# In led-blinker/Components/Led
fprime-util build
Resolve any errors and finish any TODOs before continuing.
Congratulations! You just completed the implementation of your component. It is time to finish implementation and run on hardware!