gilestrolab / ethoscope

a platform from monitoring animal behaviour in real time from a raspberry pi
http://lab.gilest.ro/ethoscope/
GNU General Public License v3.0
17 stars 25 forks source link

Optomotor module, connecting 20 LEDs TLC 5947 #134

Closed Georges-Farkouh closed 3 years ago

Georges-Farkouh commented 3 years ago

In the optomotor module there is a place to mount 20 LEDs, However on the Adafruit TLC 5947 only 10 outputs pins are used to drive the LEDs. I would like to know how we can connect 20 LEDs to 10 outputs pins of the TLC.

ggilestro commented 3 years ago

The TLC5947 has 24 output so you can connect 20 LEDs and up to four motors. If you want to connect 20 LEDs and 10 motors you will have to chain to a second board, as explained here.

If you decide to chain, you will have to change the code and make sure N_OUTPUT is set to the output you desire and that mapping is appropriate.

Georges-Farkouh commented 3 years ago

I have connected the 20 fibers to the output pins of TLC5947 (00 to 09 and 14 to 23), I am using the interactor option OptomotorSleepDepriverSystematic to deliver light pluses for optogenetics stimulation. We have to enter 1 or 2 for choosing opto or moto, and both options will send signal to only 10 of the 20 fibers. I would like to know how can I use all output for opto stimulation only. Capture d’écran 2020-10-23 à 14 47 45

ggilestro commented 3 years ago

The function controlling that is this one: https://github.com/gilestrolab/ethoscope/blob/fe74f69150779be4e3cff9b7eaf7695c62a21eb9/src/ethoscope/stimulators/sleep_depriver_stimulators.py#L348-L398

You will have to create a new class similar to that one and remapping the _roi_to_channel_opto to include the mapping that you need. You have ROIs on the left and pin output on the right of that dictionary.

What is your LED trigger? If you are planning to use the same trigger then you can use the same _decide function. If you need a different LED trigger then you will have to re-write the _decide function accordingly.

ThHoler commented 3 years ago

I work with Georges as an intern. We modified the function as you described, adding another class for our purposes and remapping the _roi_to_channel_opto to associate each of the 20 pins to a LED. Our LED trigger is the same as the one used in OptomotorSleepDepriverSystematic so we didn't modify that.

However for some unknown reason, the module works fine for any number of LEDs up to 19, but not for 20 LEDs. If we remove 1 LED from the _roi_to_channel_opto mapping, the 19 remaining LEDs behave as expected; but if we include all 20, they light up once (sometimes twice) after the target acquisition and testing round, then stop working entirely.

ggilestro commented 3 years ago

Hi @ThHoler can you please post a copy of the code that works and one of the code that behaves weirdly? I suppose the hardware connection to the LED is the same in both cases.

ThHoler commented 3 years ago

Hello @ggilestro ,

Here's the class we created. For both ethoscopes we tried it with, the LEDs would flicker once or twice before shutting down if we kept all 20 LEDs mapped in the _roi_to_channel_opto.

However, when changing the _roi_to_channel_opto by unmapping one or more LEDs, the machine behave as expected. For our first machine we had to remove at least 1 LED from the mapping (any LED works), for our second ethoscope we had to remove at least 2 LEDs. We used the same module with both ethoscope and didn't touch any of the hardware connections.

class OptogeneticsStimulationSystematic(OptomotorSleepDepriver):
    _description = {"overview": "A stimulator to sleep deprive an animal using gear motors. See https://github.com/gilestrolab/ethoscope_hardware/tree/master/modules/gear_motor_sleep_depriver",
                    "arguments": [

                                    {"type": "number", "min": 1, "max": 3600*12, "step":1, "name": "interval", "description": "The recurence of the stimulus","default":120},
                                    {"type": "number", "min": 500, "max": 10000 , "step": 50, "name": "pulse_duration", "description": "For how long to deliver the stimulus(ms)", "default": 1000},
                                    {"type": "number", "min": 0, "max": 3, "step": 1, "name": "stimulus_type",  "description": "1 = opto, 2= moto", "default": 1},
                                    {"type": "date_range", "name": "date_range",
                                     "description": "A date and time range in which the device will perform (see http://tinyurl.com/jv7k826)",
                                     "default": ""}
                                   ]}

    _HardwareInterfaceClass = OptoMotor
    _roi_to_channel_opto = {1:0, 2:1, 3:2, 4:3, 5:4, 6:5, 7:6, 8:7, 9:8, 10:9, 
                            11:23, 12:22, 13:21, 14:20, 15:19, 16:18, 17:17, 18:16, 19:15, 20:14}
    _roi_to_channel_moto = {}

    def __init__(self,
                 hardware_connection,
                 interval=120,  # s
                 pulse_duration = 1000,  #ms
                 stimulus_type = 1,  # 1 = opto, 2= moto, 3 = both
                 date_range=""
                 ):

        self._interval = interval  *1000 # ms used internally

        super(OptogeneticsStimulationSystematic, self).__init__(hardware_connection, 0,0,
                                                               pulse_duration, stimulus_type,
                                                               date_range)

        self._t0 = 0

    def _decide(self):
        roi_id = self._tracker._roi.idx
        try:
            channel = self._roi_to_channel[roi_id]
        except KeyError:
            return HasInteractedVariable(False), {}
        now = self._tracker.last_time_point + roi_id *100
        if now - self._t0 > self._interval:
            dic = {"channel": channel}
            dic["duration"] = self._pulse_duration
            self._t0 = now
            return HasInteractedVariable(True), dic

        return HasInteractedVariable(False), {}
ggilestro commented 3 years ago

Are you trying to keep all LEDs ON at once? Perhaps there is not enough current to do that.

ThHoler commented 3 years ago

Yes, we are trying to pulse all the 20 LEDs together, with 700ms stimulus duration and 1s recurrence.

It seems the issue was indeed a lack of power coming from the RPi to the TLC. We plugged the TLC to an external power supply at 5V/2A and now the module behaves as expected with all 20 LEDs pulsing together.

ggilestro commented 3 years ago

I thought so. If you don't want to use an external power supply you may be able to get enough current using one of these: https://amzn.to/2G2LIh8

Or alternatively, you may want to divide in two nonconcurrent pulses of 10 LED each. I'll close the issue now.