Skip to content

Commit

Permalink
feat: (slightly) better course example code
Browse files Browse the repository at this point in the history
  • Loading branch information
oystub committed Sep 19, 2024
1 parent e976103 commit 2afbafc
Show file tree
Hide file tree
Showing 17 changed files with 312 additions and 171 deletions.
Empty file.
4 changes: 4 additions & 0 deletions src/modules/course/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
menuconfig MODULES_COURSE
bool "course"
default n
rsource "*/Kconfig"
20 changes: 20 additions & 0 deletions src/modules/course/hello_world/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# This tells PX4 that this directory contains a module.
# It is also possible to add a library or a test. (But we will not cover that in this course.)
px4_add_module(
# This is the name of the module. It is common to use the following format:
# (It is possible to have one extra level of submodules, like we have here.)
# MODULE <namespace>__<module_name>
MODULE modules__course__hello_world

# This is the name of the main function of the module.
# This means that there must be a function with this name, postfixed with "_main" somewhere in the source code.
# E.g. if this is "hello_world", there must be a function called "hello_world_main".
MAIN hello_world

# This is a list of source code files that are part of the module and should be built.
# The source files are relative to the directory of this CMakeLists.txt file.
SRCS
hello_world.cpp
)

# To see the full list of options, see the file `cmake/px4_add_module.cmake`
6 changes: 6 additions & 0 deletions src/modules/course/hello_world/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
menuconfig MODULES_COURSE_HELLO_WORLD
depends on MODULES_COURSE
bool "hello"
default y
---help-- -
Enable the simple hello world module
25 changes: 25 additions & 0 deletions src/modules/course/hello_world/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Hello, world!

This is one of the smallest possible programs for PX4.

Notice how we have four different files in this module :
- `Kconfig`
- `CMakeLists.txt`
- `hello_world.cpp`
- `hello_world.h`


## Kconfig
The `Kconfig` file is the configuration file for the module. It tells the build system that this module exists. We can also add configuration options here.
For this simple module, the only configuration option is whether the module is enabled or not.
The options we define in the `Kconfig` file are available in the `make <build target> boardconfig` menu.

## CMakeLists
The `CMakeLists.txt` file is the build script for the module. It tells the build system how to build the module.
See the comments in the file for more information.

## hello_world.cpp
This is the main source file for the module. Right now, it contains only a main function.

## hello_world.h
This is the header file for the module. It contains the function prototype for the `hello_world` function.
30 changes: 30 additions & 0 deletions src/modules/course/hello_world/hello_world.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include <px4_platform_common/log.h>
#include <stdio.h>

#include <uORB/topics/tune_control.h>

#include "hello_world.h"

using namespace time_literals;

void play_note(int frequency = 440, int duration = 250_ms, int volume = 20)
{
// The tune_control topic is used to play tunes or individual notes on the buzzer
tune_control_s tune{};

tune.timestamp = hrt_absolute_time(); // Must be set to the current time
tune.tune_id = 0; // Indicates a custom note
tune.frequency = frequency;
tune.volume = volume;
tune.duration = duration;

// Publish the tune_control message
orb_advertise(ORB_ID(tune_control), &tune);
}

int hello_world_main(int argc, char *argv[])
{
PX4_INFO("Hello, World!");
// TODO: Play a note
return 0;
}
5 changes: 5 additions & 0 deletions src/modules/course/hello_world/hello_world.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# pragma once

// The main function must be declared as extern "C" to prevent name mangling.
// Basically, this is so that a C program can call this function, even though it is written in C++.
extern "C" __EXPORT int hello_world_main(int argc, char *argv[]);
8 changes: 8 additions & 0 deletions src/modules/course/task_module/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
px4_add_module (
MODULE modules__course__task_module
MAIN task_module
SRCS
px4_course_task_module.cpp
MODULE_CONFIG
module.yaml
)
6 changes: 6 additions & 0 deletions src/modules/course/task_module/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
menuconfig MODULES_COURSE_TASK_MODULE
depends on MODULES_COURSE
bool "task_module"
default y
---help---
Enable the Ascend PX4 course module
22 changes: 22 additions & 0 deletions src/modules/course/task_module/module.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module_name: PX4 Course, Thread Example

parameters:
- group: PX4 Course
definitions:
COURSE_HI_NUM:
description:
short: How many times to greet
long: |
When running the thread example, the program will say Hello" this many times :)
type: int32
min: 1
max: 20
default: 2
COURSE_HI_LONG:
description:
short: Long greeting
long: |
If set to false, the program will only say "Hi!".
If set to true, the program will say "Hello, PX4 Course!" instead.
type: boolean
default: false
142 changes: 142 additions & 0 deletions src/modules/course/task_module/px4_course_task_module.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#include <cstdio>

#include "px4_course_task_module.h"
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))

using namespace time_literals;

int TaskModule::print_usage(const char *reason)
{
printf("usage: task_module {start|stop|status}\n");
return 0;
}

int TaskModule::task_spawn(int argc, char *argv[])
{
_task_id = px4_task_spawn_cmd("module",
SCHED_DEFAULT,
SCHED_PRIORITY_DEFAULT,
1024,
(px4_main_t)&run_trampoline,
(char *const *)argv);

if (_task_id < 0) {
_task_id = -1;
return -errno;
}

return 0;
}

/*
* We can use this function to implement custom commands for our module.
* I.e. things other than the standard start/stop/status/print_usage.
*/
int TaskModule::custom_command(int argc, char *argv[])
{
printf("User ran command \"%s\" with arguments \"", argv[0]);

for (int i = 1; i < argc; ++i) {
printf("%s ", argv[i]);
}

printf("\"\n");
return 1;
}

/*
* This function is called from the new thread that is created for the module.
* It should create an instance of the module and return it.
* It can also parse the arguments passed to the module, and use them to configure the module.
* You don't need to modify this function.
*/
TaskModule *TaskModule::instantiate(int argc, char *argv[])
{
TaskModule *instance = new TaskModule();

if (instance == nullptr) {
PX4_ERR("alloc failed");
}

return instance;
}

/*
* Constructor for the TaskModule class.
*/
TaskModule::TaskModule()
: ModuleParams(nullptr)
{
}

void TaskModule::greet(uint32_t num_greetings, bool long_greeting)
{
constexpr const char *short_greeting_str = "Hi!";
constexpr const char *long_greeting_str = "Hello, PX4 course!";

for (uint32_t i = 0; i < num_greetings; i++) {
printf("%s\n", long_greeting ? long_greeting_str : short_greeting_str);
}
}

void TaskModule::sound_alert()
{
tune_control_s tune{};
tune.tune_override = true;
tune.timestamp = hrt_absolute_time();
tune.tune_id = 0;
tune.frequency = 2000;
tune.volume = 20;
tune.duration = 150_ms;

_tune_control_pub.publish(tune);

}

/*
* The run function is called when the module is started.
* This is where the main logic of the module should be.
* Notice that the function should return when should_exit() returns true.
*/
void TaskModule::run()
{
// NB! Most time related functions in PX4 are in microseconds, not milliseconds!
// By using time_literals we can convert to microseconds easily, like below.
uint64_t greet_interval = 10_s;
hrt_abstime last_greet{0};

while (!should_exit()) {
// Check for parameter updates. Maybe the greet interval has changed.
if (_parameter_update_sub.updated()) {
// clear update so that updated() returns false next time
parameter_update_s update;
_parameter_update_sub.copy(&update);

// Update parameters from storage
// This is provided by the ModuleParams class we inherit from
updateParams();
}

auto time_since_last_greet = hrt_elapsed_time(&last_greet);

if (time_since_last_greet >= greet_interval) {
greet(_param_course_hi_num.get(), _param_course_hi_long.get());
last_greet = hrt_absolute_time();
}

// TODO: Check for acceleration updates and sound alarm if
// the acceleration is above a certain value

// Sleep for 0.1 second or until the next greet time
px4_usleep(MIN(100_ms, greet_interval - time_since_last_greet));
}

}

/*
* You don't need to change this function, it just calls main in the class.
*/
extern "C" __EXPORT int task_module_main(int argc, char *argv[])
{
return TaskModule::main(argc, argv);
}
44 changes: 44 additions & 0 deletions src/modules/course/task_module/px4_course_task_module.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/Publication.hpp>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/vehicle_acceleration.h>
#include <uORB/topics/tune_control.h>

using namespace time_literals;
extern "C" __EXPORT int task_module_main(int argc, char *argv[]);

// Note that the TaskModule inherits from the ModuleBase, which gives us some useful features

class TaskModule : public ModuleBase<TaskModule>, public ModuleParams
{
public:
TaskModule();

virtual ~TaskModule() = default;

// All classes that inherit from ModuleBase and run in their own thread
// must implement the funtions below.
static int task_spawn(int argc, char *argv[]);
static TaskModule *instantiate(int argc, char *argv[]);
static int custom_command(int argc, char *argv[]);
static int print_usage(const char *reason = nullptr);
void run() override;

private:
void greet(uint32_t num_greetings, bool long_greeting);
void sound_alert();

DEFINE_PARAMETERS(
(ParamInt<px4::params::COURSE_HI_NUM>) _param_course_hi_num,
(ParamBool<px4::params::COURSE_HI_LONG>) _param_course_hi_long
)

// Subscriptions
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::SubscriptionInterval _vehicle_acceleration_sub{ORB_ID(vehicle_acceleration), 100_ms};
uORB::Publication<tune_control_s> _tune_control_pub{ORB_ID(tune_control)};
};
8 changes: 0 additions & 8 deletions src/modules/px4_course/CMakeLists.txt

This file was deleted.

5 changes: 0 additions & 5 deletions src/modules/px4_course/Kconfig

This file was deleted.

14 changes: 0 additions & 14 deletions src/modules/px4_course/module.yaml

This file was deleted.

Loading

0 comments on commit 2afbafc

Please sign in to comment.