Introduction: Dual Screen Raspberry Pi Project
The Nintendo DS is a nostalgic console by every mean necessary. I released November 21st 2004 and it sold better than most consoles in history. That is the inspiration of my project. Today Im going to explain how to make a project like the Nintendo DS and it may seem complicated at first but overall its not too bad. I'm going to go over a step by step guide of what parts you need and how to make it.
Supplies
Hardware:
1 Raspberry (5,4B,3, even 2 would work here)
Hosyond 7 inch IPS LCD Touch Screen Display Panel
GeeekPi 7-Inch Screen for Raspberry Pi, 1024x600 IPS LCD Display
USB Stick (for OS)
Software:
Raspberry Pi Image Loader
Raspberry pi os lite (64 bit)
Evtest
python3
some of your own
Step 1: PARTS
First you need to get the supplies, the Hoyond is around 40 dollars and if that is too steep however the screen on the top is only 27 dollars (geekPI) this example I used a Pi 4 2GB but any pi would mostly do. now that you have the screens. Now that we all the pieces its time to....
Step 2: PUT TOGETHER
Put it all together! The GeekPi uses hdmi and includes an adapter in there for you right out of the box. There is two mini HDMI ports plug it in to the port closer to the USB ports. From my experience the cable and adapter is cheap and finicky. I would suggest securing it somehow that it shows a stable signal or use a mini HDMI to HDMI cable from online. Some should possibly even come in the packaging of the PI itself. I didn't list it because while not the greatest you do have access to one.
Step 3: CONNECT SCREEN
Connect the other screen! Now the other screen is a little different this uses DSI technology (no pun intended). This uses a ribbon cable to securely connect from the raspberry pi to the screen to give it data. The power however is a little interesting it uses the fist and 3rd pin on the side of the USBs to power meaning it is powered by the PI itself its a plug and play system itself. (make sure the ribbon cable is plugged in the right way if plugged in the wrong way on either port will cause it to not work or may even cause serious damage to your screen and PI.
Step 4: IMAGER
Now that we have connected the screens we need software now in order to do this we need the Raspberry Pi Image loader. You can find it by searching up Raspberry Pi Image loader but here is a link directly to it https://www.raspberrypi.com/news/raspberry-pi-imager-imaging-utility/. You can use other software like rufus however I have found Raspberry Pi's official Image loader to be more efficient. It support all types of operating systems like windows and Mac. Give it an install and check it out.
Step 5: IMAGER PART:2
Now that we have the Image loader we want to click on the Choose OS button. Then scroll down to Raspberry Pi OS (other). Under there the first result should be Raspberry Pi OS Lite (64-bit). Choose this one and select under Raspberry Pi device you want to choose the board you are working with. In this case I will choose Raspberry Pi 4 because that is the board I'm working with. If you have a USB that can support the raspberry pi put it into your computer and click the choose storage button. now finally hit next and it will pop up some config settings go through what it asks during the prompt and then hit the button on the bottom right to continue and your USB should be now Imaging your raspberry Pi OS. Once the process and done and verified go to the next step
Step 6: EVTEST
Now plug that stick into your Pi you will see a rainbow screen that is normal but then right after it will boot up into the PI OS lite (64-bit). After you will be prompted to then put in a password and username but afterwards you need to install the software. The first one being Evtest this is to test what ports are what when you are debugging. do sudo apt update && sudo apt install Evtest to update missing packages and install Evtest. to run Evtest just do sudo Evtest this will show all the "events" (inputs) and its names.
Step 7: Python + Commands
You need python to write the code so how you go about doing that sudo apt install python3-pip remember whenever you download a new package do sudo apt update. You want to install the library Pil this is an python imaging library. To install it do pip install Pillow you will make a new file by doing touch square_touch.py (or whatever you want to name it) and when that's done do nano square_touch.py so now you are finally able to write some code. You want to import the Pil library, OS, and sys library.
Step 8: Main Functions + Find Screen
We have 3 main functions in this code. One to find the touch part of the screen we can do this by first find the event. You want to make a for loop that encompasses all of the events without going overboard but enough for the touch screen to be discoverable. Without it, it wont know what path its looking for
info = open(f"../../sys/class/input/event{devnum}/device/name").read().lower()
this reads that path then finds keywords or letters in that name to find the right name of the device this is used by the command if "ft" in info: and then the file path is found. once it is it then is set to a variable to be open later on.
inputPath = InputDevice(path)
Step 9: Mismatch Screen RGB
So the major problem I have faced when working with these screens are the fact that they don't use the right RGB. You see RGB888 is the standard 888 representing 8 bits for Red 8 for Green 8 for Blue. Balanced as life should be. This is also known as True color or 24 bit color. However the screen manufacturer wanted to cut corners posible. They ended up cutting cost in the color department. This is because these screens are not 888 but 565. 5 for red 6 for Green 5 for Blue. Pil as you can probably image uses 888 but no problem lets fix that problem. Theres this little package named numpy. With a mixture of that library, bit shifting and bit masking we are able to make something that was originally supposed to be 888 but make it 565.
RR = (RGB888[:, :, 0] >> 3).astype(np.uint16) << 11
GG = (RGB888[:, :, 1] >> 2).astype(np.uint16) << 5
BB = (RGB888[:, :, 2] >> 3).astype(np.uint16)
the astype function is a useful little function it takes an array and from that it converts it into a data type of your choosing and in this code its being converted into an unsigned 16 bit integer. The array is shown below:
RGB888 = np.array(RGB888Image)
but first we need to use what you have all been waiting for PIL. PIL is an imager library for the pi and its going to be our key to start out with a simple 888 color. What this line does it essentially makes a new block of color or a window so to speak.
RGB888Image = Image.frombytes('RGB', (SCREEN_WIDTH, SCREEN_HEIGHT), rgb888Data)
you can add to that image which is exactly what I did using a nested for loop for x and y. The is was my favorite because i figured out if we are already making a buffer full of pixels then we should be able to replace pixels with ones I put on with a simple nested for loop. now we have beautiful squares to print on the screen that we can change its width height start x and start y.
for x in range(X,width):
for y in range(Y,height):
RGB888Image.putpixel((x,y), color)
All of this code moves to the Frame Buffer explained in the next step
Step 10: The Frame Buffer
The Frame Buffer is a powerful part of all graphics systems. The Frame Buffer is the address in hardware that the code tells it "Hey give me graphics and I will print it on the screen" in this case the Frame Buffer of the linux id Fb0 this data that you shift and mask gets sent to fb0 for all the graphical goodness. Now we have the frame work now we need to work with it.
Step 11: Finally Working With the Frame Work
What we need to do first is read the values from the touch screen. First we make a loop with inputPath.ReadLoop() this is from the Evtest.inputDevice in which it reads from the path given earlier being inputPath and continuously reads the data from that path. (for event in inputPath.read_loop():) In order to do that we need take the absolute positions of the touch by using Evtest, Evtest has a two event codes used here ABS_X and ABS_Y this is used to take the value of the the X and Y value.
if event.code == ecodes.ABS_X:
input_x = event.value
if event.code == ecodes.ABS_Y:
input_y = event.value
We turn them into variables and then we turn it into values to compare the square against. The formula for distance is (X2 - X1) squared plus (y2-y1) squared hence why we have dx being input_x - square_x and input_y and square_y we square dx by doing dx*dx and then we do the same for dy. then we compare it to the square length which you set to 40 and if that's the case you create another square to show on the screen.
dx = input_x - square_x
dy = input_y - square_y
dist = (dx*dx + dy*dy) ** 0.5
These event codes gets its function from the library ecodes. once that is finished you now code as many squares as you want. What i did was use interval every time it was clicked and allow the square and the click radius to move and I set it manually


