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
.
#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.
#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
.
#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).
#ifdef MONOLITH_LINUX
<<pathfinder_declarations>>
#endif
#ifdef MONOLITH_LINUX
<<pathfinder_static_functions>>
<<pathfinder_functions>>
#endif
13.3.1. Path finder 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.
#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.
const char * monolith_path_grid(void);
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.
const char * monolith_path_arc(void);
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.
monome_t *monome;
m->monome = NULL;
monome_t *arc;
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
void monolith_monome_setup(monolith_d *monome);
void monolith_monome_cleanup(monolith_d *monome);
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
.
{"monolith:monome-setup", pp_monome_setup, 0, 0, {CHR,___,___}},
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.
monolith_monome_cleanup(m);
13.5.2. 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 void handle_press_down(const monome_event_t *e, void *data);
static void handle_press_up(const monome_event_t *e, void *data);
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.
<<monome_callback_declarations>>
<<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
.
monolith_monome_data_set(&m->vmonome, m->monome);
13.5.3.2. Set Monome Hardware Press Callback
static void monome_press(monolith_monome_d *m, int x, int y, int s);
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);
}
}
monolith_monome_press_set(&m->vmonome, monome_press);
13.5.3.3. Set Monome LED set Callback
static void led_set(monolith_monome_d *m, int x, int y, int s);
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);
}
monolith_monome_led_set_set(&m->vmonome, led_set);
13.5.3.4. Set Monome LED row callback
static void led_row(monolith_monome_d *m, int x, int y, int s);
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);
}
monolith_monome_led_row_set(&m->vmonome, led_row);
13.5.3.5. Set Monome LED all callback
static void led_all(monolith_monome_d *m, int s);
static void led_all(monolith_monome_d *m, int s)
{
monome_t *monome;
monome = m->ud;
monome_led_all(monome, s);
}
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]
static void led_col(monolith_monome_d *m, int x, int y, int s);
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);
}
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
void monolith_arc_setup(monolith_d *monome);
void monolith_arc_cleanup(monolith_d *monome);
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
.
{"monolith:arc-setup", pp_arc_setup, 0, 0, {___,___,___}},
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.
monolith_arc_cleanup(m);
13.6.2. Arc Handlers
monome_register_handler(m->arc, MONOME_ENCODER_DELTA, handle_delta, m);
static void handle_delta(const monome_event_t *e, void *data);
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.
<<arc_callback_declarations>>
<<arc_callbacks>>
13.6.3.1. Set Arc Hardware Data
monolith_arc_data_set(&m->varc, m->arc);
13.6.3.2. Set Arc Hardware Delta Callback
static void arc_delta(monolith_arc_d *a, int n, int delta);
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);
}
}
monolith_arc_delta_set(&m->varc, arc_delta);
13.6.3.3. Set Arc Map Callback
static void arc_map(monolith_arc_d *a, int n, unsigned char *map);
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);
}
monolith_arc_map_set(&m->varc, arc_map);
13.6.3.4. Set Arc All Callback
static void arc_all(monolith_arc_d *a, int n, int l);
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);
}
monolith_arc_all_set(&m->varc, arc_all);
13.7. Polling and Processing Monome/Arc Events
These function is called inside the listener thread.
monolith_monome_poll(m);
monolith_arc_poll(m);
13.7.1. Monome (Grid)
Monome events are polled with the function
monolith_monome_poll
.
void monolith_monome_poll(monolith_d *m);
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
.
void monolith_arc_poll(monolith_d *m);
void monolith_arc_poll(monolith_d *m)
{
if(m->arc == NULL) return;
while(monome_event_handle_next(m->arc));
}
prev | home | next