Table of Contents

About

This document expands on the large screen display microscope but instead builds a portable microscope with a small RCA connected screen as well as taking care to implement the button as a means to take snapshots and various other tweaks.

Requirements

Setting up RCA Composite Video Output

Car LCD screens seem to be suitable for many applications given their very low cost as well as their suitable resolution. Furthermore, the screen is very light such that it can be wall-mounted.

The only modification necessary to the Raspberry Pi is to switch from HDMI output to composite output. This involves soldering two additional pins to the Raspberry Pi board and creating a lead, preferably with an RCA video jack. After the software modification, given the already existing setup described in this document, the Raspberry Pi seems to boot up perfectly and display video on the LCD screen seamlessly without any extra setup needed.

Setting up the Raspberry Pi

One of the annoyances has been that the Raspberry Pi Zero is to be powered using $5V$. Even though the Raspberry Pi Zero can be powered with $3.3V$ via the $5V$ pin, $5V$ is still needed in order to operate USB devices connected to the Raspberry Pi; in this case, the microscope. This would mean that two separate power regulators would have to be used which makes the project too complex. To work around this issue, a step down has been used and a pass-through has been designed in order to supply the LCD screen with $12V$ whilst pulling $5V$ to power on the Raspberry Pi using the pin header.

This is a very simple circuit as can be seen from the following sketch:

        +----------+----------------+
-> 12V             |                  12V -> to LCD car screen
        +-------+--|----------------+
                |  |
                +--|--+-------+-----+
                |  | Stepdown |       5V -> to Raspberry Pi pin header
                +--+----------+-----+

Putting it all together, some single use straps are necessary to hold all the cables in place while letting only two connectors dangling: the power jack and the micro USB to USB cable to connect the microscope.

Factoring in the cost of all components, the whole project could sum up to the cheapest LCD microscope on the market but with the extra value that each separate component can at a later time be reused, or even used at the same time for different purposes (ie: displaying an oscilloscope signal as well) thereby offering lots more wiggle room for further developments. That being said, the back of the LCD screen could be simplified and the cables could perhaps be cut down to size but it would reduce the re-usability of the components. In any case, all the circuitry is connected at the back of the LCD screen and the LCD is wall-mounted such that the cables cannot be seen anyway.

Microscope Software Setup

Nicely enough, the cheap microscope has two buttons: one for changing the light setting and another button that can be mapped manually to whatever function the user wants to have. One idea is to make Linux read the button state and when pressed, take a snapshot of the microscope image and then save it on a network share for further inspection or processing.

The first problem is that while ffmpeg will be used to render the microscope image to the screen, when the button is pressed, ffmpeg would have to be used again to take the screenshot but that will not work because the device will be kept busy by the first instance of ffmpeg. To work around that issue, the microscope stream is multiplexed from /dev/video0 to /dev/video50 and /dev/video51 using v4l2loopback as described on the Linux FUSS page. A service file is created at /etc/systemd/system/microscope_clone.service with the following contents:

[Unit]
Description=Microscope Clone
After=multi-user.target
Before=microscope.service microscope_button.service

[Service]
ExecStart=/usr/bin/ffmpeg -hide_banner -loglevel quiet -f v4l2 -i /dev/video0 -codec copy -f v4l2 /dev/video50 -codec copy -f v4l2 /dev/video51
Restart=always
RestartSec=10
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=microscope
User=root
Group=root
Environment=PATH=/usr/bin/:/usr/local/bin/

[Install]
WantedBy=microscope.target

Next, ffmpeg can be used to read the microscope image and then render it directly to the framebuffer. To that end, a service file is created at /etc/systemd/system/microscope.service with the following contents:

[Unit]
Description=Microscope
After=microscope_clone.service
Before=microscope_button.service

[Service]
ExecStart=/usr/bin/ffmpeg -hide_banner -loglevel quiet -f v4l2 -video_size 640x420 -i /dev/video50 -pix_fmt rgb565le -f fbdev /dev/fb0
Restart=always
RestartSec=10
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=microscope
User=root
Group=root
Environment=PATH=/usr/bin/:/usr/local/bin/

[Install]
WantedBy=microscope.target

And now a third service file is created that will be responsible for reading the microscope button and then saving the image onto a network share. In order to do that, Samba can be installed, perhaps even by using Samba standalone templates and then a Samba share created that will store the images.

Typically, the kernel will recognize any button as well and will create a device file for the button from which events can be later read.

usb 1-1: Found UVC 1.00 device USB2.0 UVC PC Camera (05e3:0515)
input: USB2.0 UVC PC Camera: USB2.0 UV as /devices/platform/soc/20980000.usb/usb1/1-1/1-1:1.0/input/input0

There are multiple ways to read the microscope button, but perhaps the most accessible is to use evtest in order to check the button device for input events along with a simple polling bash script that will read the input button.

First, create the following filesystem directory structure:

 root
  +--- etc
        +
        |
        +--- camera_button
                  +
                  |
                  +--- 1.d
                  |
                  +--- 0.d

by executing the command:

mkdir -p /etc/camera_button/{0,1}.d

Now the directory /etc/camera_button/1.d will hold bash scripts that will be run whenever the button is pressed, and /etc/camera_button/0.d will hold bash scripts that will be run whenever the button is released. For that to work, a script is needed that is created at /etc/camera_button/uvcbutton.sh with the following contents:

#!/bin/sh
###########################################################################
##  Copyright (C) Wizardry and Steamworks 2023 - License: GNU GPLv3      ##
###########################################################################
 
###########################################################################
##                            CONFIGURATION                              ##
###########################################################################
 
# A directory containing scripts to execute on button down.
A_DIRECTORY=/etc/camera_button/1.d
# A directory containing scripts to execute on button up.
B_DIRECTORY=/etc/camera_button/0.d
# The input device for the camera button.
BUTTON_INPUT_DEVICE="/dev/input/by-id/usb-05e3_USB2.0_UVC_PC_Camera_USB2.0_UVC_PC_Camera-event-if00"
 
###########################################################################
##                              INTERNALS                                ##
###########################################################################
 
a() {
    for SCRIPT in `find $A_DIRECTORY -type f -executable`; do
        bash $SCRIPT
    done
}
 
b() {
    for SCRIPT in `find $B_DIRECTORY -type f -executable`; do
        bash $SCRIPT
    done
}
 
A='*KEY_CAMERA*value 1*'
B='*KEY_CAMERA*value 0*'
 
evtest --grab $BUTTON_INPUT_DEVICE | \
while read LINE; do
    #DEBUG
    #echo $LINE
    # function a (press) and b (release) are forked
    case $LINE in
        ($A) a & ;;
        ($B) b & ;;
    esac
done

The section under the CONFIGURATION section has to be modified and, in particular, to set the BUTTON_INPUT_DEVICE variable to the path of the input device generated by the kernel.

The script is then started using a SystemD service file (the third one created in this guide), that is created at /etc/systemd/system/microscope_button.service and has the following contents:

[Unit]
Description=Microscope Button
After=multi-user.target microscope_clone.service microscope.service

[Service]
ExecStart=/etc/camera_button/uvcbutton.sh
Restart=always
RestartSec=10
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=microscope
User=root
Group=root
Environment=PATH=/usr/bin/:/usr/local/bin/

[Install]
WantedBy=microscope.target

The service file will run the script placed at /etc/camera_button/uvcbutton.sh and will then run scripts inside /etc/camera_button/1.d respectively /etc/camera_button/0.d when the button is pressed or released. In order to take the snapshot, a script is placed at /etc/camera_button/1.d/snapshot.sh with the following contents:

#!/bin/sh
/usr/bin/ffmpeg \
    -hide_banner \
    -loglevel quiet \
    -f v4l2 \
    -video_size 640x480 \
    -i /dev/video51 \
    -pix_fmt rgb565le \
    -vframes 1 \
    /mnt/images/snapshot-`date +%s`.jpg

When the script runs due to pressing the button on the microphone, a file is created inside /mnt/images/ following the naming convention snapshot-TIMESTAMP.jpg where TIMESTAMP is the epoch time in unix format. It follows that /mnt/images should then be shared on the network by using Samba, perhaps using the following Samba share configuration:

[images]
    comment = Microscope Images
    path = /mnt/images
    valid users = root pi
    # Create file under this user - useful for seamlessly copying files.
    force user = pi
    force group = pi
    read only = No
    create mask = 0664
    force create mode = 0664
    force directory mode = 0755

where pi is the Raspberry Pi username (note that the pi user would have to be added to Samba by using the smbpasswd command).

In total, there should be three files created inside /etc/systemd/system/, namely microscope_clone.service that will be responsible for duplexing the video device, microscope.service that will be responsible for reading from the microscope and then rendering the image to the Linux frame buffer and a final script named microscope_button.service that will take care to take a snapshot of the microscope when the microscope button is clicked.

The directory /etc/systemd/system/microscope.target.wants is created to store links to the three service files.

ln -sf /etc/systemd/system/{microscope_clone,microscope,microscope_button}.service /etc/systemd/system/microscope.target.wants

Finally, the microscope target file is created at /etc/systemd/system/microscope.target with the following contents:

[Unit]
Description=Microscope Target
Requires=multi-user.target
After=multi-user.target
AllowIsolate=yes

and the default target is changed to the microscope target by issuing:

systemctl set-default microscope.target

In doing so, this ensures that the microscope target executes all the three service files after all other services have been started.

Optimizations

Although pitched as "performance" yet pertaining more to "safety", recent Raspbian distribution releases include an option named "Overlay File System Enable/disable read-only file system", that can be accessed from the "Performance Options" menu via the raspi-config tool.

The purpose of this setting is to extend a memory-only read-only overlay on top of the entire filesystem thereby making the filesystem read-only. Generally this is a good idea because IoT devices are typically more prone to be abused in terms of power with the likelihood that the device will be powered down or up by pulling the cable being very high. Over time, bad powercycling will eventually lead to the deterioration of the filesystem and given that this project is finalized, there is not reason to allow further writes.