lslder_uni.cc 6.04 KB
Newer Older
1
// lslder.cc -- GW/20200110
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
2
//
3
4
// LSL Digital Event Recorder
//
Your Name's avatar
Your Name committed
5
6
7
8
9
10
11
// This program offers a maximum of eight LSL event streams, each triggered by
// positive edges on raspberry pi GPIO digital inputs. Visual feedback is
// provided bij eight LEDs. The stream type is "Digital Events - $HOSTNAME",
// where HOSTNAME is the ip hostname of the raspberry. This is to uniquely
// identify devices on the network. The data sent with the events is an int8
// with value 1, with 1 indicating a rising edge. Falling edges can be sent with
// a 0 value. However this is not implemented in this program.
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
12
//
Your Name's avatar
Your Name committed
13
// Dependencies: labstreaminglayer C++
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
14

15
16
// CHANGES
// GW/20180716 initial version for 8-channel LSL Digital Event Recorder
Your Name's avatar
Your Name committed
17
18
19
// GW/20210210 remove deprecated wiringPi and use pigpio instead
// TODO: use ISR tick for debounce timing
// TODO: add support to register falling edges
20

Your Name's avatar
Your Name committed
21
22
23
24
#include "led.h"
#include <chrono> // std::chrono::seconds
#include <cstring>
#include <iostream>
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
25
#include <lsl_cpp.h>
Your Name's avatar
Your Name committed
26
27
28
29
#include <mutex>
#include <pigpio.h>
#include <sched.h>
#include <signal.h>
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
30
#include <stdlib.h>
Your Name's avatar
Your Name committed
31
#include <thread> // std::this_thread::sleep_for
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
32
33
#include <unistd.h>

Gunter Windau's avatar
Gunter Windau committed
34
#define DEBUG
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
35
36
37

using namespace lsl;

38
39
40
#ifdef LSLDER_1CHAN

// 1 channel portable version without LEDs
Gunter Windau's avatar
Gunter Windau committed
41
const int ngpio = 1;
Your Name's avatar
Your Name committed
42
const int gpioinp[ngpio] = {8};
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
43

Gunter Windau's avatar
Gunter Windau committed
44
45
const int default_debouncetime = 100;

46
47
48
49
50
const int *ledgpio = 0;
const int nled = 0;

#endif

51
52
53
54
#ifdef LSLDER_4CHAN

// 4-chan version with 4 LEDs
const int ngpio = 4;
55
const int gpioinp[ngpio] = {15, 18, 23, 24};
56
57
58

const int default_debouncetime = 0;

59
const int ledgpio[] = {2, 3, 14, 5};
60
61
62
63
const int nled = 4;

#endif

64
65
66
67
#ifdef LSLDER_8CHAN

// 8-chan version with 8 LEDs
const int ngpio = 8;
Your Name's avatar
Your Name committed
68
const int gpioinp[ngpio] = {12, 7, 8, 25, 24, 23, 18, 15};
69

Gunter Windau's avatar
Gunter Windau committed
70
71
const int default_debouncetime = 0;

Your Name's avatar
Your Name committed
72
const int ledgpio[] = {21, 20, 16, 13, 5, 14, 3, 2};
73
74
75
76
const int nled = 8;

#endif

Your Name's avatar
Your Name committed
77
78
79
80
const int maxgpio = 54; // BCM chip has 54 GPIO
int gpiomap[maxgpio];

void init_gpiomap() {
Your Name's avatar
Your Name committed
81
82
83
84
    // map BNC input channel wiring 0..ngpio-1 to BCM GPIO
    for (int bnc_input = 0; bnc_input < ngpio; bnc_input++) {
        int bcmgpio = gpioinp[bnc_input];
        gpiomap[bcmgpio] = bnc_input;
Your Name's avatar
Your Name committed
85
86
    }
}
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
87

Your Name's avatar
Your Name committed
88
89
90
91
92
93
94
95
96
97
98
99
100
101
int ledtime = 2; // in 1/100 sec., see infinite loop in main()
volatile int ledtimer[ngpio] = {
    0,
};
int ledstatus[ngpio] = {
    0,
};

int debouncetimes[ngpio] = {
    0,
};
volatile int debouncetimer[ngpio] = {
    0,
};
Gunter Windau's avatar
Gunter Windau committed
102

Gunter Windau's avatar
azmq rm    
Gunter Windau committed
103
#ifdef DEBUG
Your Name's avatar
Your Name committed
104
105
106
volatile int interrupt_count[ngpio] = {
    0,
};
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
107
108
109
110
111
112
#endif

stream_outlet *outlet[ngpio];
std::mutex lsl_mutex;
typedef char int8;

Your Name's avatar
Your Name committed
113
114
115
116
117
volatile int run = 1;

void stop(int signum) { run = 0; }

void gpioISR(int gpio, int level, uint32_t tick) {
Your Name's avatar
Your Name committed
118
119
    int bnc_input = gpiomap[gpio];

Your Name's avatar
Your Name committed
120
121
122
    if (level == 2) // timeout call
        return;

Your Name's avatar
Your Name committed
123
    if (debouncetimer[bnc_input])
Your Name's avatar
Your Name committed
124
        return;
Gunter Windau's avatar
Gunter Windau committed
125
    else
Your Name's avatar
Your Name committed
126
        debouncetimer[bnc_input] = debouncetimes[bnc_input];
Gunter Windau's avatar
Gunter Windau committed
127

Your Name's avatar
Your Name committed
128
    static int edge = 1;
Gunter Windau's avatar
Gunter Windau committed
129
    std::lock_guard<std::mutex> guard(lsl_mutex);
Your Name's avatar
Your Name committed
130
131
132
133
134
135
#ifdef DEBUG
    std::cout << "Interrupt on bnc_input " << bnc_input << " (GPIO "
              << gpioinp[bnc_input] << ")" << std::endl;
#endif
    outlet[bnc_input]->push_sample(&edge);
    ledtimer[bnc_input] = ledtime;
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
136
137

#ifdef DEBUG
Your Name's avatar
Your Name committed
138
139
    interrupt_count[bnc_input]++;
    std::cout << bnc_input << " (count=" << interrupt_count[bnc_input] << ")"
Your Name's avatar
Your Name committed
140
              << std::endl;
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
141
142
143
#endif
}

Your Name's avatar
Your Name committed
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
void handleLeds() {
    for (int i = 0; (i < ngpio) && (i < nled); i++) {
        if (ledtimer[i] > 0) {
            if (!ledstatus[i]) {
                setled(i, 1);
                ledstatus[i] = 1;
            }
            ledtimer[i]--;
        }
        else {
            if (ledstatus[i]) {
                setled(i, 0);
                ledstatus[i] = 0;
            }
        }
    }
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
160
161
}

Your Name's avatar
Your Name committed
162
163
int rtpriority(int n) {
    struct sched_param sched;
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
164

Your Name's avatar
Your Name committed
165
    memset(&sched, 0, sizeof(sched));
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
166

Your Name's avatar
Your Name committed
167
168
169
170
    if (n > sched_get_priority_max(SCHED_RR))
        sched.sched_priority = sched_get_priority_max(SCHED_RR);
    else
        sched.sched_priority = n;
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
171

Your Name's avatar
Your Name committed
172
    return sched_setscheduler(0, SCHED_RR, &sched);
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
173
174
}

Your Name's avatar
Your Name committed
175
176
int main(int argc, char *argv[]) {
    init_gpiomap();
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
177

Your Name's avatar
Your Name committed
178
179
180
181
    if (rtpriority(99) < 0) {
        perror("rtpriority");
        return 1;
    }
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
182

Your Name's avatar
Your Name committed
183
184
185
186
187
188
    // configure input channels
    int r = gpioInitialise(); // use Broadcom pin numbering
    if (r < 0) {
        perror("gpioInitialise");
        return 1;
    }
189

Your Name's avatar
Your Name committed
190
191
    gpioSetSignalFunc(SIGINT, stop);
    gpioSetSignalFunc(SIGABRT, stop);
192

Your Name's avatar
Your Name committed
193
    printf("Press control C to stop.\n");
194

Your Name's avatar
Your Name committed
195
    initleds(nled, ledgpio);
196

Your Name's avatar
Your Name committed
197
    for (int i = 0; i < ngpio; i++) {
Your Name's avatar
Your Name committed
198
        int bcmgpio = gpioinp[i]; // BCM GPIO number
Your Name's avatar
Your Name committed
199
200
201
202
        //if (gpioSetMode(bcmgpio, PI_INPUT) < 0) {
        //    perror("gpioSetMode");
        //    return 1;
        // }
Gunter Windau's avatar
Gunter Windau committed
203

Your Name's avatar
Your Name committed
204
        debouncetimes[i] = default_debouncetime;
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
205

Your Name's avatar
Your Name committed
206
207
208
209
        const int timeout_ms = 10000;
        // The 4N35 optocoupler in the input circuit inverts the signal, we want
        // to detect rising edges in the input signal, so here we program
        // FALLING_EDGE.
Your Name's avatar
Your Name committed
210
        if (gpioSetISRFunc(bcmgpio, FALLING_EDGE, timeout_ms, gpioISR) < 0) {
Your Name's avatar
Your Name committed
211
212
            perror("gpioSetISRFunc");
            return 1;
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
213
        }
Your Name's avatar
Your Name committed
214
215
216
217
218
        if (gpioSetPullUpDown(bcmgpio, PI_PUD_UP) < 0) {
            perror("gpioSetMode");
            return 1;
        }

Your Name's avatar
Your Name committed
219
220
221
222
#ifdef DEBUG
        std::cout << "Set IRQ handler for gpio " << bcmgpio << std::endl;
#endif

Your Name's avatar
Your Name committed
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
    }

    char hostname[255];
    gethostname(hostname, 255);
    for (int i = 0; i < ngpio; i++) {
        // make a new stream_info and open an outlet with it
        char info_name[255];
        sprintf(info_name, "Digital Events %d", i);

        char info_type[255 + 18];
        sprintf(info_type, "Digital Events @ %s", hostname);

        stream_info info(info_name, info_type, 1, lsl::IRREGULAR_RATE,
                         lsl::cf_int8, "Raspberry Pi Digital Event Recorder");
        outlet[i] = new stream_outlet(info);
    }

    // do this forever
    while (run) {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        handleLeds();

        for (int i = 0; i < ngpio; i++) {
            if (debouncetimer[i] > 0)
                debouncetimer[i]--;
Gunter Windau's avatar
Gunter Windau committed
248
        }
Your Name's avatar
Your Name committed
249
250
251
    }
    gpioTerminate();
    return 0;
Gunter Windau's avatar
azmq rm    
Gunter Windau committed
252
}