Now that the neopixels are working again, I've spent the last few days continuing programming the tricorder python script, and trying to figure out how I can optimize some processes.
1) Raspberry Pi's task manager (kind of)Section titled 1) Raspberry Pi's task manager (kind of)
The following simple command can be used in the console to check CPU/memory utilization and running processes:
2) Make use of touch sensor's interrupt signalSection titled 2) Make use of touch sensor's interrupt signal
Until now I have queried the touch signals in a while loop at millisecond intervals, as it's also done in Adafruit's examples for the MPR121 module. Unfortunately this loop is quite demanding for the CPU, the Raspberry Pi Zero had a very high load just because of this loop. I suspect that this also affects the sound output, since the sound is not played smoothly due to "buffer underrun".
Luckily the MPR121 breakout has a dedicated IRQ (interruption) pin. When no button is touched this IRQ pin signal is high (3.3V). When a button is touched the IRQ signal drops down to low (0V) and stays low until the signal is read via I²C by Raspberry Pi.
I didn't know if you can connect MPR121's IRQ pin directly to a GPIO of the Raspberry Pi, or if you should put resistors in between for safety. Likewise, I didn't know whether an additional connection to a “ground” pin of the Raspberry is required.
After useless research on the internet I decided to just try several variants (with/without 330Ω resistor at the GPIO-PIN and with/without additional ground connection with 3.3KΩ resistor). After the tests I can say: A direct connection from the IRQ pin of the MPR121 to the GPIO of the Raspberry is no problem. The IRQ signal should have a maximum of 3.3V, so this shouldn't be a problem for Raspi's GPIO input.
I want to use two touch breakouts (one for the touch buttons in the upper shell, one for buttons in the lower part/door). So I also need two free GPIOs to read the touch interrupt signals. Currently in my setup GPIO16 and GPIO26 are unused and available.
- At the top of /boot/config.txt I added the GPIOs as input with pull down:
# Touch sensor IRQ (interrupt) signals # GPIO 16 = board A ; GPIO 26 = board B # ip = input, pu = pull up gpio=16=ip,pu gpio=26=ip,pu
Then I made some larger changes in my Python scripts:
- Added new functions for touch delays to avoid input spamming (before I tried to add a bouncetime in the GPIO event, but this prevented the MPR121 IRQ status from being reset after reading via I2C). I later reprogrammed these functions to use threading to prevent that this "waiting time" slows down other functions. Example code excerpt:
# Define global delay variables var_touch_a_delay = False # Functions for input delay to prevent input signal spam def fn_touch_a_delay(arg): t = threading.currentThread() while getattr(t, "do_run", True): global var_touch_a_delay if var_touch_a_delay == True: time.sleep(0.3) var_touch_a_delay = False else: time.sleep(0.3) # Define threads for delay t_touch_a_delay = threading.Thread(target=fn_touch_a_delay, args=("task",)) t_touch_a_delay.do_run = True t_touch_a_delay.start()
- Added new functions to read the MPR121 signals and defined actions for detected touch events (calling other functions). Within these functions I also make use of the touch delay functions. Example code excerpt:
# Read touch inputs from MPR121 module A def fn_touch_a(): var_touched_a = mpr121_a.touched_pins global var_touch_a_delay if var_touch_a_delay == False: # delay to prevent input spam var_touch_a_delay = True # Call other functions depending on pressed button if var_touched_a: fn_button_a_0() if var_touched_a: fn_button_a_1() if var_touched_a: fn_button_a_2() # ...
- Defined GPIO settings and event triggers, e.g. like this:
# Pin 16 = IRQ signal (interrupt) of touch board A # GPIO is HIGH when nothing is touched and gets low when buttons are touched # Set pin to be an input pin and set initial value to be pulled up GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Pin 16 callback fucnction def fn_touch_a_irq_callback(channel): if GPIO.input(channel) == GPIO.LOW: fn_touch_a() # Pin 16 event detection GPIO.add_event_detect(16, GPIO.BOTH, callback=fn_touch_a_irq_callback)
And then I defined the individual functions for each button, which were previously contained in the large while loop (when button X is pressed: play sound Y and video Z...).
Here a few notes to myself:
- Global variables are defined outside of functions
- To be able to access a global variable from within a function, it must be named once again with the keyword "global" at the top of the function. After that, the keyword "global" isn't required anymore within the function to access or change the variable value.
- To change threading functions from within the button functions "global" isn't required, the variables can be called directly.
3) VLC error and warning messagesSection titled 3) VLC error and warning messages
I'm using Python-VLC in my script to play video files on the screen with VLC media player. When I execute my script via the SSH console, the following warnings and error messages are shown:
libdvdnav: Using dvdnav version 6.0.0 libdvdread: Encrypted DVD support unavailable. ********************************************** ** No css library available. See ** ** /usr/share/doc/libdvdread4/README.css ** ** for more information ** ********************************************** libdvdread: Couldn't find device name. libdvdread:DVDOpenFilePath:findDVDFile /VIDEO_TS/VIDEO_TS.IFO failed libdvdread:DVDOpenFilePath:findDVDFile /VIDEO_TS/VIDEO_TS.BUP failed libdvdread: Can't open file VIDEO_TS.IFO. libdvdnav: vm: failed to read VIDEO_TS.IFO
Following these instructions I was able to get rid of the warning about the missing CSS library:
sudo apt install libdvd-pkg sudo dpkg-reconfigure libdvd-pkg
Note: The execution of the 2nd line took approx. 5 minutes without any feedback in the console.
As for the other warnings from libdvdread, I've been researching the internet for a while, but so far without success. I don't quite understand why this is showing at all, because there is no DVD drive and I'm not trying to open VIDEO_TS files.
I already tried changing some parameters in the Python script and changing the order of the VLC initialization command lines, but that didn't fix the messages yet. The message is also displayed if I comment out all the Neopixel code and do not call the script with "sudo". So at least it doesn't seem to be the "sudo". I need to do more research and further testing on this.
But another error message is definitely displayed because of "sudo: As soon as a video is played with play(), this message appears in the console:
error: XDG_RUNTIME_DIR not set in the environment.
I currently have to run the script with "sudo" as root admin because of the Neopixels.
The media player VLC, on the other hand, is not designed to run with sudo. Apparently the VLC player is missing environment variables that are shared/set only for the pi user.
I researched this problem for a while on the internet and decided to first test the possibilities of getting my script to work without "sudo", i.e. whether the Neopixels can also be controlled via SPI.