[PYTHON] Try to put LED in your own PC (slightly)

Hello.

Recently, I'm Yukari, who is also an LED shop in the professional field. Well, according to the recent fashion. Oiler also wanted to "make the PC shine with LEDs". I tried to do it.

result

In such a situation! Dsc03705.jpg

Preparation

It is OK if you collect materials that meet the following conditions and do it with acupoint alchemy.

――A case where you can put LEDs inside and you can see the LEDs from the outside. --Appropriate matrix LED -Maybe it will suffocate, so it is a proper heat dissipation means

Well, I was looking for a case with a "glass or acrylic front". I made a case of NZXT's H510i Elite. Well, I also introduced a simple water cooling called "kraken M22" so that the exhaust heat of the CPU can be taken out.

LED selection

There are multiple types of "HUB75 surface mount full color matrix LEDs" that are popular these days, depending on the size of the bud. The number after the "P" in the model number is the size of the brim. The case selected this time seems to be able to hold two fans of 12 cm to 14 cm on the front. Select a size that looks like it.

For example, if it is 32x64 of P5, the size is "16cm x 32cm", which is a little big. Well, if you tabulate the typical resolution and size, it looks like this.

pitch size size
P2 64x128 12.8cm x 25.6cm
P2.5 32x64 8.0cm x 16.0cm
P3 32x64 9.6cm x 19.2cm
P4 32x64 12.8cm x 25.6cm
P5 32x64 16.0cm x 32.0cm
P6.67 32x64 21.3cm x 42.6cm

Well, this time, I decided to use the top "P2 64x128". It's more fun to have resolution, maybe. If the short side is 64 dots or more, there is a high possibility that the address line "E" will be required. Please be careful when selecting the board, which will be described later.

Raspberry Pi and interface preparation

I use a Raspberry Pi to make it shine.

This time, I chose "Raspberry Pi4 4GB". The case is appropriate, but since GPIO is used, the official case is strict. Dsc03691.jpg

Next is the LED interface board. This time I decided to use the old Adafruit board, which I had left over. Dsc03690.jpg

However! , The following two modifications are required.

--Hardware PWM drive --Addition of address line "E"

Well, referring to this description, it feels like adding two lines and one pattern cut. There are two patterns of address E line, so first check with a tester etc. which panel you are using. The unused address E line falls to GND.

https://github.com/hzeller/rpi-rgb-led-matrix

Wiring is properly fastened with polyimide. Dsc03689.jpg

assembly

First of all, the front side looks like this. Dsc03697.jpg

The back is fixed to the stay for the 12cm fan with M3 screws. Dsc03698.jpg

It will look like this when you insert it. Dsc03684.jpg

This alone is lonely, so I laid an LED on the bottom as well. Dsc03685.jpg

This guy is fixed with a magnet and mounted at the four corners. Dsc03686.jpg

This magnet is sold at the LED shop next to Sengoku Densho in Akihabara. Dsc03687.jpg

https://www.akiba-led.jp/product/1197

The LED connects the LED on the bottom side to the Raspberry Pi, and from there it connects to the front LED with a chain. This is purely wiring convenience.

I decided to take power from SATA. Well, it's a long-standing promise, but the power supply for personal computers is generally "yellow: 12V", "red: 5V", "orange: 3.3V", and "black: GND". With RaspberryPi4 and LED, it seems to use 5A or more, but it will be okay because it is a PC power supply (appropriate). Dsc03693.jpg

The interface between the Raspberry Pi and the PC uses LAN. The PC side is USB LAN. For USB, use the conversion connector for the case from the header of the motherboard. Dsc03694.jpg

The connection around the Raspberry Pi looks like this. Dsc03695.jpg

After that, push the Raspberry Pi into the gap behind the power supply of H510i Elite, and it is completed. Dsc03696.jpg

Built-in completion

Well, in other words, this PC. It's a "dual configuration PC" that lives in two environments. Well from the outside it looks like a normal PC. Dsc03682.jpg

The back is also an ordinary PC. Dsc03683.jpg

It is not batting with the genuine NZXT control mechanism "HUE2". Dsc03688.jpg

While keeping things up to this point, it's "no modification of the case".

Preparation on the PC side

On the PC side, monitoring software is required. This time, I used "Open Hardware Monitor". This software has a Web server function, so I can grasp the situation from the outside. It is a strategy to get the JSON used for rendering on the Raspberry Pi side. https://forest.watch.impress.co.jp/docs/review/383668.html

However, the above software does not support recent architecture. Actually, I am using "LibreHardwareMonitor" of Nora build from the following URL. http://takkun87.blog40.fc2.com/blog-category-6.html

If you enable the web server, set the F / W appropriately, and get the following JSON from the outside, it is successful. http://[IPアドレス]:8085/data.json

The content looks like this (partial excerpt):

data.json


{
    "id": 0,
    "Text": "Sensor",
    "Min": "Min",
    "Value": "Value",
    "Max": "Max",
    "ImageURL": "",
    "Children": [
        {
            "id": 1,
            "Text": "DESKTOP-HOGEHOGE",
            "Min": "",
            "Value": "",
            "Max": "",
            "ImageURL": "images_icon/computer.png ",
            "Children": [
                {
                    "id": 2,
                    "Text": "ASRock Z390 Phantom Gaming 6",
                    "Min": "",
                    "Value": "",
                    "Max": "",
                    "ImageURL": "images_icon/mainboard.png ",
                    "Children": [
                        {
                            "id": 3,
                            "Text": "Nuvoton NCT6791D",
                            "Min": "",
                            "Value": "",
                            "Max": "",
                            "ImageURL": "images_icon/chip.png ",
                            "Children": [
                                {
                                    "id": 4,
                                    "Text": "Voltages",
                                    "Min": "",
                                    "Value": "",
                                    "Max": "",
                                    "ImageURL": "images_icon/voltage.png ",
                                    "Children": [
                                        {
                                            "id": 5,
                                            "Text": "Vcore",
                                            "Min": "1.392 V",
                                            "Value": "1.392 V",
                                            "Max": "1.408 V",
                                            "SensorId": "/lpc/nct6791d/voltage/0",
                                            "Type": "Voltage",
                                            "ImageURL": "images/transparent.png ",
                                            "Children": []
                                        },
                                        {
                                            "id": 6,
                                            "Text": "Voltage #2",
                                            "Min": "1.680 V",
                                            "Value": "1.688 V",
                                            "Max": "1.688 V",
                                            "SensorId": "/lpc/nct6791d/voltage/1",
                                            "Type": "Voltage",
                                            "ImageURL": "images/transparent.png ",
                                            "Children": []
                                        },
                                        {
                                            "id": 7,
                                            "Text": "AVCC",
                                            "Min": "3.488 V",
                                            "Value": "3.488 V",
                                            "Max": "3.488 V",
                                            "SensorId": "/lpc/nct6791d/voltage/2",
                                            "Type": "Voltage",
                                            "ImageURL": "images/transparent.png ",
                                            "Children": []
                                        },
                                        {
                                            "id": 8,
                                            "Text": "3VCC",
                                            "Min": "3.488 V",
                                            "Value": "3.488 V",
                                            "Max": "3.488 V",
                                            "SensorId": "/lpc/nct6791d/voltage/3",
                                            "Type": "Voltage",
                                            "ImageURL": "images/transparent.png ",
                                            "Children": []
                                        },
                                        {
                                            "id": 9,
                                            "Text": "Voltage #5",
                                            "Min": "1.016 V",
                                            "Value": "1.032 V",
                                            "Max": "1.032 V",
                                            "SensorId": "/lpc/nct6791d/voltage/4",
                                            "Type": "Voltage",
                                            "ImageURL": "images/transparent.png ",
                                            "Children": []
                                        },
                                        {
                                            "id": 10,
                                            "Text": "Voltage #6",
                                            "Min": "1.088 V",
                                            "Value": "1.088 V",

After that, let's assign an appropriate local IP to the USB LAN and Raspberry Pi of the PC to secure communication.

Raspberry Pi side preparation

What to do is like this

--Addition of library for LED panel --Program --Definition of RAMDISK and SYSTEMD

For the LED panel library, refer to the URL below. https://qiita.com/eucalyhome/items/e871e297bfd527ccaf2c

program

I implemented "status display" and "life game (busyness changes depending on temperature)". Since the LEDs are chained together, the first 128 dots are for life games, and the latter 128 dots are for vertical writing status display.

ledstat.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PIL import Image, ImageDraw, ImageOps, ImageFont, ImageFilter, ImageChops, ImageColor
from rgbmatrix import RGBMatrix, RGBMatrixOptions
import time, re, copy, os, codecs, random, datetime, json
import numpy as np
from scipy import signal

class ledworker(object):
    def ledinit(self):
        self.options = RGBMatrixOptions()
        self.options.hardware_mapping = "adafruit-hat-pwm"
        self.options.led_rgb_sequence = "RGB"
        self.options.rows = 64
        self.options.chain_length = 8
        self.options.parallel = 1
        self.options.pwm_bits = 11
        self.options.brightness = 50
        self.options.pwm_lsb_nanoseconds = 130
        self.options.gpio_slowdown = 1
        self.options.panel_type = "FM6126A"
        self.matrix = RGBMatrix(options = self.options)

    def ledoutput(self,handle):
        self.matrix.SetImage(handle)

    def imagenew(self):
        imagecolorspace = 'RGB'
        imagecolorfill = (0, 0, 80)
        imagesizex = 256
        imagesizey = 64
        image = Image.new(imagecolorspace, (imagesizex, imagesizey), imagecolorfill)
        return (image)

class lifeworker(object):
    def initstate(self, width, height, init_alive_prob=0.5):
        N = width*height
        v = np.array(np.random.rand(N) + init_alive_prob, dtype=int)
        return v.reshape(height, width)

    def addstate(self, F, width, height, init_alive_prob=0.5):
        N = width*height
        v = np.array(np.random.rand(N) + init_alive_prob, dtype=int).reshape(height, width)
        v = F + v
        return v

    def countneighbor(self, F):
        mask = np.ones((3, 3), dtype=int)
        return signal.correlate2d(F, mask, mode="same", boundary="wrap")

    def nextgeneration(self, F):
        N = self.countneighbor(F)
        G = (N == 3) + F * (N == 4)
        return G

    def toimage(self, F):
        nparray = np.array(F, dtype=np.uint8)*255
        image = Image.fromarray(np.uint8(nparray)).convert("1")
        return image

class monitorworker(object):
    def getdata(self):
        datafile_hwdata = '/ramdisk/hwdata.json'
        outdata = {}
        outdata['cpuload'] = 0
        outdata['cputemp'] = 0
        outdata['gpuload'] = 0
        outdata['gputemp'] = 0
        outdata['cpuclock'] = 0

        if not os.path.exists(datafile_hwdata):
            return (outdata)
        try:
            with open(datafile_hwdata) as f:
                hwdata = json.load(f)
        except:
            return (outdata)

        for primarykey in hwdata['Children'][0]['Children']:
            if 'cpu' in primarykey['ImageURL']:
                for secondarykey in primarykey['Children']:
                    if 'Clocks' in secondarykey['Text']:
                        for thirdkey in secondarykey['Children']:
                            if 'CPU Core #1' in thirdkey['Text']:
                                outdata['cpuclock'] =re.sub("[^0-9\.]","",thirdkey['Value'])
                    if 'Load' in secondarykey['Text']:
                        for thirdkey in secondarykey['Children']:
                            if 'CPU Total' in thirdkey['Text']:
                                outdata['cpuload'] =re.sub("[^0-9\.]","",thirdkey['Value'])
            if 'nvidia' in primarykey['ImageURL']:
                for secondarykey in primarykey['Children']:
                    if 'Temperatures' in secondarykey['Text']:
                        outdata['gputemp'] = re.sub("[^0-9\.]","",secondarykey['Children'][0]['Value'])
                    if 'Load' in secondarykey['Text']:
                        outdata['gpuload'] = re.sub("[^0-9\.]","",secondarykey['Children'][0]['Value'])
            if 'mainboard' in primarykey['ImageURL']:
                for secondarykey in primarykey['Children'][0]['Children']:
                    if 'Temperatures' in secondarykey['Text']:
                        outdata['cputemp'] = re.sub("[^0-9\.]","",secondarykey['Children'][0]['Value'])
        outdata['cpuload'] = "{0:5.1f}".format(float(outdata['cpuload']))
        outdata['gpuload'] = "{0:5.1f}".format(float(outdata['gpuload']))
        outdata['cpuclock'] = "{0:4d}".format(int(outdata['cpuclock']))
        return (outdata)

def writetext(draw,hpos,message,color,font):
    w, h = draw.textsize(str(message), font)
    w = 48 - w
    draw.text((w,hpos), str(message), color,font=font)

def main():
    led = ledworker()
    led.ledinit()
    image = led.imagenew()
    fontdata = "./GDhwGoJA-OTF112b2.otf"

    life = lifeworker()
    lifedatablue = life.initstate(128, 64, init_alive_prob=0.08)
    lifedatagreen = life.initstate(128, 64, init_alive_prob=0.08)

    monitor = monitorworker()
    outdata = monitor.getdata()

    imagebasel = Image.open('/data/ledstat/nzx_log_w.png').convert("RGB")
    imagebasel = imagebasel.transpose(Image.ROTATE_180)
    imagebaseh = copy.copy(imagebasel)
    imagebaseh = imagebaseh.transpose(Image.ROTATE_90)
    imageh = copy.copy(imagebasel)
    image = Image.new('RGB', (256, 64), (0, 0, 0))
    image.paste(imageh,(128, 0))
    imagehtemp = copy.copy(imagebaseh)
    draw = ImageDraw.Draw(imagehtemp)
    font = ImageFont.truetype(fontdata, 16)
    fontsmall = ImageFont.truetype(fontdata, 7)

    addcount = 0
    while True:
        imageblue = life.toimage(lifedatablue)
        imagegreen = life.toimage(lifedatagreen)
        imagel = copy.copy(imagebasel)
        imagel.paste(Image.new("RGB", imagel.size, (0,255,255)), mask=imageblue)
        imagel.paste(Image.new("RGB", imagel.size, (0,0,255)), mask=imagegreen)
        image.paste(imagel,(128, 0))

        led.ledoutput(image)

        lifedatablue = life.nextgeneration(lifedatablue)
        lifedatagreen = life.nextgeneration(lifedatagreen)
        time.sleep(0.05)
        addcount = addcount + 1
        if addcount > 20:
            addcount = 0

            outdatatemp = monitor.getdata()
            if outdatatemp['cputemp'] != 0:
                outdata = outdatatemp
            blueseed = (float(outdata['cputemp'])-40) / 200
            greenseed = (float(outdata['gputemp'])-40) / 200
            if blueseed < 0:
                blueseed = 0
            if greenseed < 0:
                greenseed = 0
            lifedatablue = life.addstate(lifedatablue,128, 64, init_alive_prob=blueseed)
            lifedatagreen = life.addstate(lifedatagreen,128, 64, init_alive_prob=greenseed)
            lifedatablue = life.nextgeneration(lifedatablue)
            lifedatagreen = life.nextgeneration(lifedatagreen)

            imagehtemp.paste(imagebaseh,(0, 0))
            draw.text((0,0), "CPU", (0, 255, 255),font=font)
            writetext(draw,16,outdata['cpuload'],(255, 255, 255),font)
            draw.text((48,24), "%", (0, 255, 255),font=fontsmall)
            writetext(draw,32,outdata['cputemp'],(255, 255, 255),font)
            draw.text((48,40), "'C", (0, 255, 255),font=fontsmall)
            writetext(draw,48,outdata['cpuclock'],(255, 255, 255),font)
            draw.text((48,56), "MHz", (0, 255, 255),font=fontsmall)
            draw.text((0,74), "GPU", (0, 0, 255),font=font)
            writetext(draw,90,outdata['gpuload'],(255, 255, 255),font)
            draw.text((48,98), "%", (0, 0, 255),font=fontsmall)
            writetext(draw,106,outdata['gputemp'],(255, 255, 255),font)
            draw.text((48,116), "'C", (0, 0, 255),font=fontsmall)
            imageh = copy.copy(imagehtemp)
            imageh = imageh.transpose(Image.ROTATE_90)
            image.paste(imageh,(0, 0))

if __name__ == '__main__':
    main()

For the implementation of Life Game, I referred to the following URL. https://qiita.com/sage-git/items/c6c175887faa4cf737fb

The prerequisite files are as follows 128x64 background logo: /data/ledstat/nzx_log_w.png Data.json for PC status: /ramdisk/data.json Font: ./GDhwGoJA-OTF112b2.otf

The font is the one on the highway, if you like. https://forest.watch.impress.co.jp/library/software/gdhighway/

Next, a suitable program to get JSON from your PC

getdata.py


import urllib.request
import json
import time

url = 'http://[ip address]:8085/data.json'
datafile = '/ramdisk/hwdata.json'

req = urllib.request.Request(url)

while True:
    try:
        with urllib.request.urlopen(req) as res:
            body = json.load(res)
        fw = open(datafile,'w')
        json.dump(body,fw,indent=4)
        fw.close()
    except:
        time.sleep(30)
    time.sleep(2)

It works with Python3 Note, well, unscrupulous.

RAMDISK definition is easy. First, create a 777 directory with "/ ramdisk /" as root. Add "tmpfs / ramdisk tmpfs defaults, size = 16m 0 0" to FSTAB. Just "mount -a" or reboot. If you df and have "tmpfs 16384 84 16300 1% / ramdisk", it's OK.

Registration to systemd looks like the following.

/etc/system/getdata.service


[Unit]
Description=getdata

[Service]
ExecStart=/usr/bin/python3 /data/ledstat/getdata.py
Restart=always
Type=simple
User=pi

[Install]
WantedBy=multi-user.target

/etc/systemd/system/ledstat.service


[Unit]
Description=ledstat

[Service]
WorkingDirectory=/data/ledstat
ExecStart=/usr/bin/python /data/ledstat/ledstat.py
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

Then, from "systemctl daemon -reload"

systemctl start getdata
systemctl enable getdata
systemctl start ledstat
systemctl enable ledstat

With a feeling. If an error occurs, check the status.

Operation check

The front panel looks like this! .. Dsc03709.jpg

The bottom panel looks like this if you're not busy. Dsc03700.jpg

When I'm busy and the temperature rises, I get confused. Dsc03710.jpg

It's quite fun w.

Try to do it

I mean. I wonder if I could make something like oil.

By the way, I don't see another computer while playing games. It's covered with LED panels, so the temperature seems to rise. In the first place, the LED is generating heat.

There is nothing good to think about seriously. It's good because it's fun! .. It's beautiful! ..

Manzoku! ..

Recommended Posts

Try to put LED in your own PC (slightly)
Try to put data in MongoDB
Try HeloWorld in your own language (with How to & code)
Try to log in to Netflix automatically using python on your PC
Try to make your own AWS-SDK with bash
[Road to intermediate Python] Define in in your own class
[Linux] How to put your IP in a variable
Try sorting your own objects with priority queue in Python
How to use pyenv and pyenv-virtualenv in your own way
Try to make a blackjack strategy by reinforcement learning (③ Reinforcement learning in your own OpenAI Gym environment)
[Python] logging in your own module
How to create your own Transform
Try implementing k-NN on your own
Flow from installing Ubuntu to installing chainer by making your own PC
Bridge ROS to your own protocol
Introduction to Deep Learning (2) --Try your own nonlinear regression with Chainer-
Cython to try in the shortest
When using the zap library in your own Logger with Golang, how to put the hierarchy above Logger in Caller
Put your own image data in Deep Learning and play with it
Add your own content view to mitmproxy
Try logging in to qiita with Python
Create your own Linux commands in Python
[LLDB] Create your own command in Python
Easily use your own functions in Python
Migrate your own CMS data to WordPress
Try to reproduce NumPy's add.at in Julia
Make your own PC for deep learning
To import your own module with jupyter
How to install your own (root) CA
[V11 ~] A memorandum to put in Misskey
Try text mining your diary in Python
Get your own IP address in Python
Try making your own CorDapp. (Until States creation)
First steps to try Google CloudVision in Python
Put Linux in your Chromebook and use R ...
Try to calculate a statistical problem in Python
3.14 π day, so try to output in Python
Try auto to automatically price Enums in Python 3.6
Try to separate Controllers using Blueprint in Flask
Argument implementation (with code) in your own language
3 steps to put Python + mecab in yum only
Try to calculate RPN in Python (for beginners)
How to create data to put in CNN (Chainer)
To add a module to python put in Julialang
Annotate your own data to train Mask R-CNN
Import your own modules in Grasshopper's Python development
Try to imitate Rails' _method parameter in webapp2
Sample to put Python Kivy in one file
Steps to install your own library with pip