13. Monome Hardware (Arc, Grid) and Libmonome

The monome is one of the core peripherals in monolith. This will outline how this peripheral is managed.

The libmonome library will be used to directly access the monome hardware instead of using the OSC interface. This is done because of previous problems had using the OSC interface and the liblo library. At this level, it is overkill.

13.1. Header Includes

The libmonome library uses the header file monome.h.

<<system_includes>>=
#include <monome.h>

13.2. Device Location (Platform-dependent)

The libmonome library communicates with the monome directly, rather than through an OSC layer. This path is OS dependent, so C macros are used to define the monome path.

<<macros>>=
#ifdef MONOLITH_LINUX
#define MONOME_DEVICE monolith_path_grid()
#endif

#ifdef MONOLITH_OSX
#define MONOME_DEVICE "/dev/cu.usbserial-m1000536"
#endif

Arc macros happen below as ARC_DEVICE.

<<macros>>=
#ifdef MONOLITH_LINUX
#define ARC_DEVICE monolith_path_arc()
#endif

#ifdef MONOLITH_OSX
#define ARC_DEVICE "/dev/cu.usbserial-m1100203"
#endif

13.3. Determining Device Path (Linux Only)

On Linux, both the Arc and Grid are listed as being /dev/ttyN, where N could be 0 or 1. In order to run both, there needs to be extra work in determing which one is which. This can be done by examing symlinks created when the devices are plugged in. This is unfortunately the best way at the moment. Using the symlinks directly doesn't seem to work because the symlink permissions are wrong (and I do NOT want to muck with that).

<<function_declarations>>=
#ifdef MONOLITH_LINUX
<<pathfinder_declarations>>
#endif
<<functions>>=
#ifdef MONOLITH_LINUX
<<pathfinder_static_functions>>
<<pathfinder_functions>>
#endif

13.3.1. Path finder system includes

<<system_includes>>=
#ifdef MONOLITH_LINUX
#include <sys/types.h>
#include <sys/stat.h>
#endif

13.3.2. Get TTY Function

This is code directly copied and pasted from my initil playground. Is is a function that checks symlinks of an input path, and determines if the path links to either tty0 or tty1. Not exactly the most robust function, but it works.

<<pathfinder_static_functions>>=
#ifndef TTY_PATH_MAX
#define TTY_PATH_MAX 1024
#endif
enum {
    SLOT_NONE,
    SLOT_TTYUSB0,
    SLOT_TTYUSB1
};
static const char * get_tty(const char *address)
{
    struct stat sb;
    char *buf;
    ssize_t nbytes, bufsiz;
    char *tmp;
    int i;
    int size;
    int rc;

    rc = SLOT_NONE;

    if (lstat(address, &sb) == -1) {
        perror("lstat");
        return NULL;
    }

    /* Add one to the link size, so that we can determine whether
        the buffer returned by readlink() was truncated. */

    bufsiz = sb.st_size + 1;

    /* Some magic symlinks under (for example) /proc and /sys
        report 'st_size' as zero. In that case, take PATH_MAX as
        a "good enough" estimate. */

    if (sb.st_size == 0)
        bufsiz = TTY_PATH_MAX;

    buf = malloc(bufsiz);
    if (buf == NULL) {
        perror("malloc");
        return NULL;
    }

    nbytes = readlink(address, buf, bufsiz);
    if (nbytes == -1) {
        perror("readlink");
        return NULL;
    }

    tmp = buf;
    size = nbytes;
    for(i = 0; i < nbytes; i++) {
        if(buf[i] == 't') break; /* find beginning of ttyUSBX */
        tmp++;
        size--;
    }

    if(!strncmp(tmp, "ttyUSB0", size)) {
        rc = SLOT_TTYUSB0;
    } else if(!strncmp(tmp, "ttyUSB1", size)) {
        rc = SLOT_TTYUSB1;
    }


    /* If the return value was equal to the buffer size, then the
        the link target was larger than expected (perhaps because the
        target was changed between the call to lstat() and the call to
        readlink()). Warn the user that the returned target may have
        been truncated. */

    /* if (nbytes == bufsiz) */
    /*     printf("(Returned buffer may have been truncated)\n"); */

    free(buf);

    if(rc == SLOT_TTYUSB0) {
        return "/dev/ttyUSB0";
    } else if(rc == SLOT_TTYUSB1) {
        return "/dev/ttyUSB1";
    } else {
        return NULL;
    }
}

13.3.3. Get Grid Path

The function monolith_path_grid returns the proper path to the monome grid.

<<pathfinder_declarations>>=
const char * monolith_path_grid(void);
<<pathfinder_functions>>=
const char * monolith_path_grid(void)
{
    const char *grid_address =
        "/dev/serial/by-id/usb-monome_monome_m1000536-if00-port0";
    return get_tty(grid_address);
}

13.3.4. Get Arc Path

The function monolith_path_grid returns the proper path to the monome arc.

<<pathfinder_declarations>>=
const char * monolith_path_arc(void);
<<pathfinder_functions>>=
const char * monolith_path_arc(void)
{
    const char *arc_address =
        "/dev/serial/by-id/usb-monome_monome_m1100203-if00-port0";
    return get_tty(arc_address);
}

13.4. Libmonome Data (grid + arc)

libmonome handles two monome devices: the arc and the grid. The grid data is stored in the monome struct, while the arc data is stored in the arc struct.

<<struct_contents>>=
monome_t *monome;
<<init>>=
m->monome = NULL;
<<struct_contents>>=
monome_t *arc;
<<init>>=
m->arc = NULL;

13.5. Monome (Grid) Setup and Cleanup

Grid setup and cleanup is done with the functions monolith_monome_setup and monolith_monome_cleanup.

13.5.1. Monome Setup/Cleanup Top-level Callbacks

<<function_declarations>>=
void monolith_monome_setup(monolith_d *monome);
void monolith_monome_cleanup(monolith_d *monome);
<<functions>>=
void monolith_monome_setup(monolith_d *m)
{
    if(m->monome != NULL) return;
    if(!(m->monome = monome_open(MONOME_DEVICE, "8000"))) return;
    monome_led_all(m->monome, 0);
<<set_monome_press_handlers>>
<<set_monome_callbacks>>
}
void monolith_monome_cleanup(monolith_d *m)
{
    if(m->monome == NULL) return;
    monome_led_all(m->monome, 0);
    monome_close(m->monome);
    m->monome = NULL;
}

Monome setup is implemented as a function called monolith:monome-setup.

<<primitive_entries>>=
{"monolith:monome-setup", pp_monome_setup, 0, 0, {CHR,___,___}},
<<scheme_functions>>=
static cell pp_monome_setup(cell x) {
    monolith_monome_setup(monolith_data_get());
    return UNSPECIFIC;
}

Cleanup can happen at cleanup. Since it is does a null check, it is reasonably safe to call this even if the monome server hasn't been started.

<<hardware_cleanup>>=
monolith_monome_cleanup(m);

13.5.2. Monome Press Handlers

<<set_monome_press_handlers>>=
monome_register_handler(m->monome, MONOME_BUTTON_DOWN, handle_press_down, m);
monome_register_handler(m->monome, MONOME_BUTTON_UP, handle_press_up, m);
<<static_function_declarations>>=
static void handle_press_down(const monome_event_t *e, void *data);
static void handle_press_up(const monome_event_t *e, void *data);
<<functions>>=
static void handle_press_down(const monome_event_t *e, void *data)
{
    monolith_d *m;

    m = (monolith_d *)data;

    monolith_monome_press(&m->vmonome, e->grid.x, e->grid.y, 1);
}

static void handle_press_up(const monome_event_t *e, void *data)
{
    monolith_d *m;

    m = (monolith_d *)data;

    monolith_monome_press(&m->vmonome, e->grid.x, e->grid.y, 0);
}

13.5.3. Monome Virtual Interface Binding

In this section, the monome will bind callbacks to the monome virtual interface.

<<static_function_declarations>>=
<<monome_callback_declarations>>
<<functions>>=
<<monome_callbacks>>
13.5.3.1. Set Monome Hardware Data

The only data the monome virtual interface should need to communicate with the monome is the monome_d struct. This gets set with the function monolith_monome_data_set.

<<set_monome_callbacks>>=
monolith_monome_data_set(&m->vmonome, m->monome);
13.5.3.2. Set Monome Hardware Press Callback

<<monome_callback_declarations>>=
static void monome_press(monolith_monome_d *m, int x, int y, int s);
<<monome_callbacks>>=
static void monome_press(monolith_monome_d *m, int x, int y, int s)
{
    if(m->md->curpage != NULL) {
        monolith_page_press(m->md->curpage, x, y, s);
    }
}
<<set_monome_callbacks>>=
monolith_monome_press_set(&m->vmonome, monome_press);
13.5.3.3. Set Monome LED set Callback

<<monome_callback_declarations>>=
static void led_set(monolith_monome_d *m, int x, int y, int s);
<<monome_callbacks>>=
static void led_set(monolith_monome_d *m, int x, int y, int s)
{
    monome_t *monome;
    monome = m->ud;
    monome_led_set(monome, x, y, s);
}
<<set_monome_callbacks>>=
monolith_monome_led_set_set(&m->vmonome, led_set);
13.5.3.4. Set Monome LED row callback

<<monome_callback_declarations>>=
static void led_row(monolith_monome_d *m, int x, int y, int s);
<<monome_callbacks>>=
static void led_row(monolith_monome_d *m, int x, int y, int s)
{
    monome_t *monome;
    monome = m->ud;
    monome_led_row(monome, x, y, 1, (uint8_t *)&s);
}
<<set_monome_callbacks>>=
monolith_monome_led_row_set(&m->vmonome, led_row);
13.5.3.5. Set Monome LED all callback

<<monome_callback_declarations>>=
static void led_all(monolith_monome_d *m, int s);
<<monome_callbacks>>=
static void led_all(monolith_monome_d *m, int s)
{
    monome_t *monome;
    monome = m->ud;
    monome_led_all(monome, s);
}
<<set_monome_callbacks>>=
monolith_monome_led_all_set(&m->vmonome, led_all);
13.5.3.6. DONE Set Monome LED column Callback

CLOSED: [2019-12-05 Thu 19:31]

<<monome_callback_declarations>>=
static void led_col(monolith_monome_d *m, int x, int y, int s);
<<monome_callbacks>>=
static void led_col(monolith_monome_d *m, int x, int y, int s)
{
    monome_t *monome;
    monome = m->ud;
    monome_led_col(monome, x, y, 1, (uint8_t *)&s);
}
<<set_monome_callbacks>>=
monolith_monome_led_col_set(&m->vmonome, led_col);

13.6. Arc Setup and Cleanup

Arc and setup and cleanup is done with the functions monolith_arc_setup and monolith_arc_cleanup.

13.6.1. Arc Setup/Cleanup Top-level Callbacks

<<function_declarations>>=
void monolith_arc_setup(monolith_d *monome);
void monolith_arc_cleanup(monolith_d *monome);
<<functions>>=
void monolith_arc_setup(monolith_d *m)
{
    int enc;
    if(m->arc != NULL) return;
    if(!(m->arc = monome_open(ARC_DEVICE, "8000"))) return;
    for(enc = 0; enc < 4; enc++) monome_led_ring_all(m->arc, enc, 0);
<<set_arc_handlers>>
<<set_arc_callbacks>>
}
void monolith_arc_cleanup(monolith_d *m)
{
    int enc;
    if(m->arc == NULL) return;
    for(enc = 0; enc < 4; enc++) monome_led_ring_all(m->arc, enc, 0);
    monome_close(m->arc);
    m->arc = NULL;
}

Monome setup is implemented as a function called monolith:monome-setup.

<<primitive_entries>>=
{"monolith:arc-setup", pp_arc_setup, 0, 0, {___,___,___}},
<<scheme_functions>>=
static cell pp_arc_setup(cell x) {
    monolith_arc_setup(monolith_data_get());
    return UNSPECIFIC;
}

Cleanup can happen at cleanup. Since it does a null check, it is reasonably safe to call this even if the monome server hasn't been started.

<<hardware_cleanup>>=
monolith_arc_cleanup(m);

13.6.2. Arc Handlers

<<set_arc_handlers>>=
monome_register_handler(m->arc, MONOME_ENCODER_DELTA, handle_delta, m);
<<static_function_declarations>>=
static void handle_delta(const monome_event_t *e, void *data);
<<functions>>=
static void handle_delta(const monome_event_t *e, void *data)
{
    monolith_d *m;

    m = (monolith_d *)data;

    monolith_arc_delta(&m->varc, e->encoder.number, e->encoder.delta);
}

13.6.3. Arc Virtual Interface Binding

In this section, the monome will bind callbacks to the monome virtual interface.

<<static_function_declarations>>=
<<arc_callback_declarations>>
<<functions>>=
<<arc_callbacks>>
13.6.3.1. Set Arc Hardware Data

<<set_arc_callbacks>>=
monolith_arc_data_set(&m->varc, m->arc);
13.6.3.2. Set Arc Hardware Delta Callback

<<arc_callback_declarations>>=
static void arc_delta(monolith_arc_d *a, int n, int delta);
<<arc_callbacks>>=
static void arc_delta(monolith_arc_d *a, int n, int delta)
{
    if(a->md->curpage != NULL) {
        monolith_page_delta(a->md->curpage, n, delta);
    }
}
<<set_arc_callbacks>>=
monolith_arc_delta_set(&m->varc, arc_delta);
13.6.3.3. Set Arc Map Callback

<<arc_callback_declarations>>=
static void arc_map(monolith_arc_d *a, int n, unsigned char *map);
<<arc_callbacks>>=
static void arc_map(monolith_arc_d *a, int n, unsigned char *map)
{
    monome_t *arc;
    arc = a->ud;
    monome_led_ring_map(arc, n, map);
}
<<set_arc_callbacks>>=
monolith_arc_map_set(&m->varc, arc_map);
13.6.3.4. Set Arc All Callback

<<arc_callback_declarations>>=
static void arc_all(monolith_arc_d *a, int n, int l);
<<arc_callbacks>>=
static void arc_all(monolith_arc_d *a, int n, int l)
{
    monome_t *arc;
    arc = a->ud;
    monome_led_ring_all(arc, n, l);
}
<<set_arc_callbacks>>=
monolith_arc_all_set(&m->varc, arc_all);

13.7. Polling and Processing Monome/Arc Events

These function is called inside the listener thread.

<<poll_the_monome>>=
monolith_monome_poll(m);
monolith_arc_poll(m);

13.7.1. Monome (Grid)

Monome events are polled with the function monolith_monome_poll.

<<function_declarations>>=
void monolith_monome_poll(monolith_d *m);
<<functions>>=
void monolith_monome_poll(monolith_d *m)
{
    if(m->monome == NULL) return;
    while(monome_event_handle_next(m->monome));
}

13.7.2. DONE Arc

CLOSED: [2019-05-27 Mon 12:19] Monome Arc events are polled with the function monolith_arc_poll.

<<function_declarations>>=
void monolith_arc_poll(monolith_d *m);
<<functions>>=
void monolith_arc_poll(monolith_d *m)
{
    if(m->arc == NULL) return;
    while(monome_event_handle_next(m->arc));
}



prev | home | next