Author Topic: Writing modules tutorial #1: Hello, World!  (Read 2219 times)

a1ex

  • Administrator
  • Hero Member
  • *****
  • Posts: 10061
  • 5D Mark Free
Writing modules tutorial #1: Hello, World!
« on: March 16, 2017, 10:24:14 PM »
So far, if you wanted to write your own module, the best sources of documentation were (and probably still are) reading the source code, the forum, the old wiki, and experimenting. As a template for new modules, you probably took one of the existing modules and removed the extra code.

This is one tiny step to improve upon that: I'd like to write a series of guides on how to write your own modules and how to use various APIs provided by Magic Lantern (some of them tightly related to APIs reverse engineered from Canon firmware, such as properties or file I/O, others less so, such as ML menu).

Will start with the simplest possible module:

Hello, World!




Let's start from scratch:
Code: [Select]
hg clone -u unified https://bitbucket.org/hudson/magic-lantern
cd magic-lantern/modules/
mkdir hello
cd hello
touch hello.c

Now edit hello.c in your favorite text editor:
Code: [Select]
/* A very simple module
 * (example for module authors)
 */
#include <dryos.h>
#include <module.h>
#include <menu.h>
#include <config.h>
#include <console.h>

/* Config variables. They are used for persistent variables (usually settings).
 *
 * In modules, these variables also have to be declared as MODULE_CONFIG.
 */
static CONFIG_INT("hello.counter", hello_counter, 0);


/* This function runs as a new DryOS task, in parallel with everything else.
 *
 * Tasks started in this way have priority 0x1A (see run_in_separate_task in menu.c).
 * They can be interrupted by other tasks with higher priorities (lower values)
 * at any time, or by tasks with equal or lower priorities while this task is waiting
 * (msleep, take_semaphore, msg_queue_receive etc).
 *
 * Tasks with equal priorities will never interrupt each other outside the
 * "waiting" calls (cooperative multitasking).
 *
 * Additionally, for tasks started in this way, ML menu will be closed
 * and Canon's powersave will be disabled while this task is running.
 * Both are done for convenience.
 */
static void hello_task()
{
    /* Open the console. */
    /* Also wait for background tasks to settle after closing ML menu */
    msleep(2000);
    console_clear();
    console_show();

    /* Plain printf goes to console. */
    /* There's very limited stdio support available. */
    printf("Hello, World!\n");
    printf("You have run this demo %d times.\n", ++hello_counter);
    printf("Press the shutter halfway to exit.\n");

    /* note: half-shutter is one of the few keys that can be checked from a regular task */
    /* to hook other keys, you need to use a keypress hook - see hello2 */
    while (!get_halfshutter_pressed())
    {
        /* while waiting for something, we must be nice to other tasks as well and allow them to run */
        /* (this type of waiting is not very power-efficient nor time-accurate, but is simple and works well enough in many cases */
        msleep(100);
    }

    /* Finished. */
    console_hide();
}

static struct menu_entry hello_menu[] =
{
    {
        .name       = "Hello, World!",
        .select     = run_in_separate_task,
        .priv       = hello_task,
        .help       = "Prints 'Hello, World!' on the console.",
    },
};

/* This function is called when the module loads. */
/* All the module init functions are called sequentially,
 * in alphabetical order. */
static unsigned int hello_init()
{
    menu_add("Debug", hello_menu, COUNT(hello_menu));
    return 0;
}

/* Note: module unloading is not yet supported;
 * this function is provided for future use.
 */
static unsigned int hello_deinit()
{
    return 0;
}

/* All modules have some metadata, specifying init/deinit functions,
 * config variables, event hooks, property handlers etc.
 */
MODULE_INFO_START()
    MODULE_INIT(hello_init)
    MODULE_DEINIT(hello_deinit)
MODULE_INFO_END()

MODULE_CONFIGS_START()
    MODULE_CONFIG(hello_counter)
MODULE_CONFIGS_END()

We still need a Makefile; let's copy it from another module:
Code: [Select]
cp ../ettr/Makefile .
sed -i "s/ettr/hello/" Makefile

Let's compile it:
Code: [Select]
make

The build process created a file named README.rst. Update it and recompile.

Code: [Select]
make clean; make

Now you are ready to try your module in your camera. Just copy the .mo file to ML/MODULES on your card.

If your card is already configured for the build system, all you have to do is:
Code: [Select]
make install

Otherwise, try:
Code: [Select]
make install CF_CARD=/path/to/your/card

or, if you have such device:
Code: [Select]
make install WIFI_SD=y

That's it for today.



To decide what to cover in future episodes, I'm looking for feedback from anyone who tried (or wanted to) write a ML module, even if you were successful or not.

Some ideas:
- printing on the screen (bmp_printf, NotifyBox)
- keypress handlers
- more complex menus
- properties (Canon settings)
- file I/O
- status indicators (lvinfo)
- animations (e.g. games)
- capturing images
- GUI modes (menu, play, LiveView, various dialogs)
- semaphores, message queues
- DryOS internals (memory allocation, task creation etc)
- custom hooks in Canon code
- your ideas?

Of course, the advanced topics are not for second or third tutorial.

Meloware

  • New to the forum
  • *
  • Posts: 27
Re: Writing modules tutorial #1: Hello, World!
« Reply #1 on: March 17, 2017, 05:05:41 PM »
This is great! Thank you for starting this. It is really the level of introduction I need.

Don't local variables normally only need to be defined within the function which uses them? In hello.c you seem to be defining a local(?) variable after the #include statements.
Code: [Select]
/* Config variables. They are used for persistent variables (usually settings).
 *
 * In modules, these variables also have to be declared as MODULE_CONFIG.
 */
static CONFIG_INT("hello.count", hello_count, 0);

I guess the first thing the module does when loaded is to execute the commands listed at the end. These are the MODULE_INFO and MODULE_CONFIGS commands. The MODULE_CONFIGS seems to be registering the hello_count declared at the beginning of hello.c .
Code: [Select]
MODULE_CONFIGS_START()
    MODULE_CONFIG(hello_count)
MODULE_CONFIGS_END()

Could you please explain this some more? Do all variables need to be defined in this way?

Code: [Select]
static CONFIG_INT("hello.count", hello_count, 0); has 3 elements within the parenthesis and MODULE_CONFIG has only a reference to hello_count. What are these three elements?
Let's say that I needed an integer variable, named “temp” in this module. How would this new variable be setup in the same module?

***
As far as your feedback questions, my answer is "yes, I want all of that". Once I learn more, I may become more specific. For my movie transfer, I would be very interested in how an image is saved or a frame is added to an MLV video. How might the code know when the camera is ready for another exposure? What is known about the DMA channel? Of course, one must first learn to crawl, before they may be expected to walk.

a1ex

  • Administrator
  • Hero Member
  • *****
  • Posts: 10061
  • 5D Mark Free
Re: Writing modules tutorial #1: Hello, World!
« Reply #2 on: March 17, 2017, 07:05:29 PM »
The above config declaration expands to (see config.h)

Code: [Select]
static int hello_count = 0;
[...]

That means, a variable local to the module (not exported to other modules), but usable in all functions from your module. It's not possible to declare config variables local to functions.

The parts are: name (as it appears in the config file), C variable name, default value.

Only config variables have to be registered with MODULE_CONFIG. Other special things also have to be registered with MODULE macros; will cover them in another tutorial.

As for why the were declared in this way - historical reasons. Pretty sure one can find a way to simplify the definitions (e.g. figure out the entry name from variable name, or register them automatically without requiring a MODULE_CONFIG), but so far there wasn't a pressing need to do so.

The "static" keyword from all other functions has the same meaning. If you don't write it, functions or top-level variables are exported to other modules (or to ML core, in some cases). That's nothing specific to ML; it's how the C language behaves when your program has more than one source file.

More about the static keyword in C here and here.

Note: ML build system shows the exported symbols, as (per C standard) you have to tag those functions/variables that you don't have to export (and do nothing for those that you do).

reddeercity

  • Hero Member
  • *****
  • Posts: 1366
Re: Writing modules tutorial #1: Hello, World!
« Reply #3 on: March 17, 2017, 09:54:35 PM »
Thanks a1ex , this is great ! it filling gapes in my knowledge . I generally kind of understood how modules work , now this give me some ideas that now I can
develop , (Mjpeg 4.2.2 , it's a dream :D)  , this will certainty help me in debugging .
Can't wait for more .

Jamesvep

  • Just arrived
  • *
  • Posts: 2
Writing modules tutorial #1 Hello World
« Reply #4 on: May 22, 2017, 01:21:43 PM »
nice tutorial, I hope I can follow it to improve my synfig skill this weekend:

Thanks pupazzo, I saw you did a huge amount improvements as well for wiki, thanks

okodoko

  • Just arrived
  • *
  • Posts: 1
Re: Writing modules tutorial #1: Hello, World!
« Reply #5 on: May 27, 2017, 10:51:04 PM »
Here's an idea.  I would find this very useful. I do multi cam and I'd like to arrange the clips on the timeline easily.  If I could have the system time in the file name, it would be easy (assuming the cameras' system times are in sync).  In addition to system time [hour/min/sec] it would be nice to have a field in the file name as [Acam, Bcam, Ccam, ...etc] and a date field [year, month, day]. I would still have to sync them by either audio or slate, but that would be simple enough.  If you surround the date field such as [ML20170527V], then searching for all the media associated with one project would be easy using that string.