Jump to content

7 posts in this topic Last Reply

Recommended Posts

Hi folks,

 

some time ago, I asked about a decent way to consolidate my IT. Thanks for your input! I posted a follow-up there, but wasn't finished at that time.

 

Now, this consolidation has come to a conclusion. I mean... for now. For... the next weeks, maybe... Don't tell my gf.

 

OS: Unraid 6.6.2

Case: Anidees AI6V2 (+ LED Strips, they boost performance! #CommonKnowledge)

Mainboard: Gigabyte X470 Aorus Ultimate Gaming

CPU: AMD Ryzen 5 2600

RAM: 2x16GB RAM, DDR4 2666.

GPU1: Sapphire Pulse Vega 56 [For Gaming VM]

GPU2: ASUS GTX 710 1GB Passive [For BIOS]

Gaming-VM-Drive: 500 GB M.2 Samsung MZ-N5E500BW 850 EVO

 

CPU-Cooler: CoolerMaster Masterliquid lite 120

Front Intake: 2x Noctua NF-A14 PWM 140mm

Top Outtake1: 1x Noctua NF-A12x15 PWM 120mm (slim, 15mm height, spare part from an old cooler I didn't need any longer)

Top Outtake2: 1x Noctua NF-F12 PWM 120mm

Rear Outtake: 1x Noctua NF-F12 PWM 120mm

 

Parity Drive: 1 x 6 TB Seagate IronWolf

Data Drive: 1 x 6 TB Seagate IronWolf

Cache: 2 x 120 GB Kingston SSD

Drive Bays: 3 currently mounted, 7 - 8 in theory.

 

Powersupply: "be quiet!" Straight Power 11 850 CM

 

PCI-E Cards: 1x USB 3.0 Extension Card, for the Gaming-VM only.

 

Information Display: 4th Gen. Amazon Kindle with PSU-attached Micro-USB Powersupply.

 

The "Kill A Watt" says:

  • 80 Watt in Idle (Drives spinning, Gaming-VM running with one virtual Monitor)
  • 70 Watt in Idle (No Drives spinning, Gaming-VM running with one virtual Monitor)
  • 220 Watt while playing Forza Horizon 4
  • 530 Watt with Prime 95 (max Heat) + Furmark 

 

Primary Use: Gaming VM, NAS, smart home central, backup master.
Likes: It's silent when idle - you only get a low-noise breeze sound from the fans.
Dislikes: Tinted acrylic display - would love a TG, and a white case.

Future Plans: Beefier CPU, more RAM

 

Impressions:

 

zeus_front.thumb.png.a2b3fc64ce6d0f159a371fb61d2033ff.png

 

zeus_sidepanel.png.9583b03ab7af7cf6c86fbb39193746ff.png

 

zeus_back.thumb.png.d55f9498bdc76676cee52967c0ff2883.png

 

zeus_sidepanel_open.png.835041def910d3a35fb3e33e9c5468aa.png

 

Gaming

 

I'm currently doing Inhouse-Streaming using Parsec. Works pretty well - imagine watching a good-quality stream on Twitch. That's basically how it's looking on my "thin-client". My thin-client is a AsRock J3455 Mini-ITX Board with Ubuntu 18.04. Would love to upgrade that to a J5005 some time soon. It's good for typing, watching Youtube and streaming. Plus, it's passive, and with a SSD, so it's totally quiet. Power Consumption is at 10W idle.

 

Information Display

 

kindle_display.thumb.png.d5929b9f2585dbda3ab509081719d6b0.png

 

I had an old Kindle laying around, and always thought of adding a display to my server. The Kindle has been rooted, Energy-saving options have been disabled, and I found a script on the web which basically allows you to fetch an image from a Webserver and display it fullscreen on the Kindle.

I then wrote a .Net Core program, which loads a template SVG I built, opens a SSH connection to the Unraid server, runs "smartctl" on the drives (without them waking up, there is a parameter for that), replaces the placeholders in the SVG, and saves it to disk under a differnt name. It then converts the SVG to PNG, and converts that PNG to the special PNG format the Kindle needs.

Finally, it's uploaded via SFTP to the unraid host, and served via a docker httpd to the Kindle script. Was some fun hacking, would love to do more in the future.

 

Conclusion

The basic system is now up and running. I'd like to re-add all the systems that were running on my server before. Mainly, that includes home-assistant with Alexa control, re-adding the Xiaomi Vacuum to the overall system. Also, I have a cable based Internet connection now, and the modem I'm using offers 4 cable tv tuners, which could be used via tvheadend. There is a lot to tinker around. If there's interest, I'll surely post some updates.

 

Thanks for reading!

Edited by AdmiralVanGilbert
Added likes, dislikes section
  • Like 1
  • Upvote 2

Share this post


Link to post

This is an incredible build. Thanks for sharing! 

I'm currently looking into building something similar: pfSense virtualisation, NAS for Storage, PLEX, Kodi or similar for streaming and some windows 10 instance for the occasional gaming. 
 

Would love to learn more about your experience about what hardware to chose and what to tinker in.
Cheers! Santa 

Edited by santa

Share this post


Link to post
On 11/23/2018 at 7:51 PM, santa said:

This is an incredible build. Thanks for sharing! 

Thanks, santa! :)

Quote

Would love to learn more about your experience about what hardware to chose and what to tinker in.
Cheers! Santa 

Well,

 

your mileage may vary. There are several reasons why I choose the components.

 

The Case

This was already existing, and for my use case, is quite sufficient. If I want to, I can add a lot of drives in total. However, because the cages are removable, I can also make some room for other stuff, e.g. the Kindle.

 

PSU

The GPU is quite hungry in terms of power - taking the rest of the system into account, and that a PSU runs best at 70-80% load, I've choosen the bequiet psu. 

 

CPU, Mainboard, RAM

This is the most important part, I guess. The Ryzen 5 2600 offers 6 cores, 12 threads for a price of roughly 160€ where I live, whereas the Ryzen 7 costs 300 €, but only adds 2 cores, 4 threads to the party. You can always overclock a non-X to X with the right cooling, so no point in taken a Ryzen 5 2600X either.

 

Which leaves the question - why no Threadripper? Because I don't need the additional power, thus I can save on the costs. However, again, depending on what you are doing, a TR4-platform might be a better choice for you. From what you wrote, if you assign 4 threads to your Windows 10 VM, there should be plenty of room left for pfsense, Kodi and stuff like that.

 

On the other hand - once you've got your hands down on unraid, and everything is working as expected, it's tempting to do a little bit more. And then a bigger platform is justified.

 

Share this post


Link to post
5 hours ago, RubberBroke said:

Love the Kindle hack.

Just what I was thinking.  While it's definitely a bit of a hack, as far as I understand your description, the end result is highly effective and a credit to your capabilities.  If you ever consider writing that up in more detail for those of us with smaller brains, it would certainly be of interest.  :)

  • Like 1

Share this post


Link to post
On 12/6/2018 at 2:29 PM, S80_UK said:

Just what I was thinking.  While it's definitely a bit of a hack, as far as I understand your description, the end result is highly effective and a credit to your capabilities.  If you ever consider writing that up in more detail for those of us with smaller brains, it would certainly be of interest.  :)

I, eh... well... *blushes*

Thanks for your kind words! Both of you!

 

Sure, a small writeup wouldn't be tooo hard, I guess...

 

The Kindle

Well, the Kindle being used is nothing too spectacular, but it was the part that consumed almost half of the time for the whole project.

It's a 4th Gen Kindle, which, as you might already have guessed from the name, runs the Firmware 4.x (don't know the exact version number).

 

For prepping the Kindle, I followed several online tutorials - most of it was taken out of this tutorial at galacticstudios. The author did a great work here. However, as outlined in the last paragraph, the writer also hit a roadblock when trying to register the cron job. I had the same issue, so at the end I had to head over to the mobileread thread, where I got everything to setup a USB-Network. Luckily, as you might remember, my thin client already runs on linux, so no problems here with crude Windows drivers. I could then ssh into the Kindle itself as root, and add the cronjob there.

 

On the now-rooted Kindle, I installed Kite, an alternative launcher for the Kindle, which allows you to run basically everything that can run under Linux. 

 

What I also took from the first link, was the script, which fetches the image from the server:

#!/bin/sh

rm -f /mnt/us/weather.png

read url </mnt/us/weatherurl
if wget $url -O /mnt/us/weather.png 2>/mnt/us/documents/recentweatherlog.txt; then
   eips -c
   eips -c
   eips -g /mnt/us/weather.png
else
   cat /mnt/us/recentweatherlog.txt >>/mnt/us/documents/weatherlog.txt
   eips -c
   eips -c
   eips -g /mnt/us/weather-image-error.png
fi

eips is the program on the kindle, which is used to display images (which are in a really special format) on the display. The script reads the url from a text-file, then tries to wget the image from a server, and stores it on the kindle. Also, logging is performed, and an error image has been provided, in case something fails.

 

This basically rounds up the Kindle setup.

 

The Server

Yeah, well - it's a bit ugly. I wanted to make it a bit more cleaner, but sometimes life happens, and you don't have time to finish stuff the way you want it. But it's working for now, and I'm sure I'll get back to it some day.

 

kindle_display.thumb.png.d5929b9f2585dbda3ab509081719d6b0.png

 

As you can see in the image from the first post, the Kindle only displays text. So, I thought it should be pretty straight forward: Design a template svg file, replace the placeholders with the actual content, convert it to a png, and push it to a webserver somewhere on the network.

 

Well... no.

 

First, I created a template SVG file in my favourite SVG editor, inkscape. It looks like this:

 

zeus-svg.thumb.PNG.7b284941669f471e24a6779dc7dda242.PNG

 

Then, because I'm a C# coder all day, I wrote a .NET Core application, which can run under Windows and Linux alike (big bonus for developing in Visual Studio, and then deploying to a Linux Docker). It reads like this:

 

using System;
using Renci.SshNet; /* reference needed: Renci.SshNet.dll */
using System.IO;

namespace Distat.Cli
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Booting Launchpad...");
            //smartctl -a /dev/sde | grep -m 1 -i Temperature_Celsius | awk '{print $10}'

            string[] drives = new string[]{ "sdf", "sdd", "sde", "sdc" };
            string[] placeholders = new string[]{ "PT", "HDD1T", "C1T", "C2T" };

            string svgContent = File.ReadAllText(@"./template/kindle-600-800.svg");

if (String.IsNullOrWhiteSpace(svgContent))
Console.WriteLine("Not loaded...");

            ConnectionInfo ConnNfo = new ConnectionInfo("192.168.178.51",22,"root",
                new AuthenticationMethod[]{

                    // Pasword based Authentication
                    new PasswordAuthenticationMethod("root","****************")

                }
            );

            //svgContent = svgContent.Replace("PT", "31").Replace("LSTUPDATETST", DateTime.Now.ToString());

            for(int i = 0; i < placeholders.Length; i++){
try{
                using (var sshclient = new SshClient(ConnNfo)){
                    sshclient.Connect();                    
using(var cmd = sshclient.CreateCommand(String.Format("smartctl -a --nocheck standby -i /dev/{0} | grep -m 1 -i Temperature_Celsius | awk '{{print $10}}'", drives[i])))
                    {
                        cmd.Execute();
                        string result = cmd.Result;
                        
                        if (String.IsNullOrWhiteSpace(result))
                            result = "Sleeping... zzZzz";
                        else
                            result = result.Replace(System.Environment.NewLine, "")  + "°C";

                        Console.WriteLine("Command>" + cmd.CommandText);
                        Console.WriteLine("Return Value = {0}", result);
                        
                        svgContent = svgContent.Replace(placeholders[i], result);
                    }            
                    sshclient.Disconnect();
                }
} catch {
}
            }

            svgContent = svgContent.Replace("LSTUPDATETST", DateTime.Now.ToString());

            Directory.CreateDirectory("./output/");

            if (File.Exists("./output/kindle.png"))
                File.Delete("./output/kindle.png");

            if (File.Exists("./output/kindle.svg"))
                File.Delete("./output/kindle.svg");
            
            File.WriteAllText("./output/kindle.svg", svgContent);

            "rsvg-convert --width 600 --height 800 --background-color white -o ./output/kindle.png ./output/kindle.svg".Bash();
            "convert ./output/kindle.png -rotate -180 ./output/kindle.png".Bash();
            "pngcrush -c 0 -ow ./output/kindle.png".Bash();

            using (var sshsftp = new SftpClient(ConnNfo)){
                sshsftp.Connect();
                sshsftp.UploadFile(new MemoryStream(File.ReadAllBytes("./output/kindle.png")), "/mnt/user/appdata/apache-www/imgs.png");
                sshsftp.Disconnect();
            }
        }
    }
}

So I guess I have to explain a few parts:

 

Everything is hardcoded. Yes. I admit. I have another EPaper sitting in our house, which waits for being allowed to display some info. Maybe next year.

 

Let's walk through the code:

string[] drives = new string[]{ "sdf", "sdd", "sde", "sdc" };
string[] placeholders = new string[]{ "PT", "HDD1T", "C1T", "C2T" };

string svgContent = File.ReadAllText(@"./template/kindle-600-800.svg");

I defined an array with all the drives that are available in the server, and another array, which holds the placeholder. Out of pure lazyness, these share the same indices. ;)

The next line reads all the content from the template svg into a simple string.

for(int i = 0; i < placeholders.Length; i++){
    try
    {
        using (var sshclient = new SshClient(ConnNfo))
        {
            sshclient.Connect();                    
            using(var cmd = sshclient.CreateCommand(String.Format("smartctl -a --nocheck standby -i /dev/{0} | grep -m 1 -i Temperature_Celsius | awk '{{print $10}}'", drives[i])))
            {
                cmd.Execute();
                string result = cmd.Result;
                
                if (String.IsNullOrWhiteSpace(result))
                    result = "Sleeping... zzZzz";
                else
                    result = result.Replace(System.Environment.NewLine, "")  + "°C";

                Console.WriteLine("Command>" + cmd.CommandText);
                Console.WriteLine("Return Value = {0}", result);
                
                svgContent = svgContent.Replace(placeholders[i], result);
            }            
            sshclient.Disconnect();
        }
    } 
    catch { }
}

Here, most of the magic happens. Using the awesome ssh.net library, I connect to the unraid machine via SSH, and run several commands on it (Note: Don't do that.)

 

Nice detail: The --nocheck standby flags doesn't wake up the drive. So it stays idle, although the cron job is updating the data every 7 minutes.

 

I then simply replace the placeholders with the real data (Note: My daily production code doesn't look like this.^^).

File.WriteAllText("./output/kindle.svg", svgContent);

"rsvg-convert --width 600 --height 800 --background-color white -o ./output/kindle.png ./output/kindle.svg".Bash();
"convert ./output/kindle.png -rotate -180 ./output/kindle.png".Bash();
"pngcrush -c 0 -ow ./output/kindle.png".Bash();

using (var sshsftp = new SftpClient(ConnNfo)){
    sshsftp.Connect();
    sshsftp.UploadFile(new MemoryStream(File.ReadAllBytes("./output/kindle.png")), "/mnt/user/appdata/apache-www/imgs.png");
    sshsftp.Disconnect();
}

Afterwards, everything is written to a new file, called kindle.svg. The ssh library also provides a simple "Bash()" - Extension to strings, with which I can run commands on a local machine. I use rsvg-convert, convert and pngcrush:

  • rsvg-convert: Does the heavy lifting from svg to png. Had some headache to get it running.
  • convert: Rotates the output. As you can see in the picture, the Kindle is placed upside down. This is for the power cord.
  • pngcrush: Does the conversion to a "Kindle"-friendly, grayscale png format. Took some time to find the right format for the png.

Finally, the image gets uploaded to the Unraid server.

 

 

On the Unraid server itself are two Docker containers running, that belong to this project:

  • Apache-PHP: The easiest to use apache server I could find. Works like a charme, and provides the image to the Kindle
  • Gogs: My go-to git repository. I use it to pull the source code to the server, and then build everything into a docker container.

The aforementioned docker container is then run via cron every 10 minutes:

#!/bin/bash
while timeout -k 10 8 docker run --rm distat; [ $? = 124 ]
do sleep 2  # Pause before retry
done

Yes, sometimes the "smartctl" - command doesn't return, and I haven't had the time to look into it. So I give the whole container a 10 second timeout. "Distat" is the name of the project - "Display Statistics". It should grow into sth. bigger, but I haven't had the time for it yet.

 

I guess I missed some bits and pieces, but I'm also a little bit sick at the moment, so my mind might not work as expected.^^

 

One more thing...

I almost forgot - there is one thing left: The power supply. One part of the hack is to change the Kindle to an "always on" kind of display. That's not how the kindle works out of the box. It normally goes to sleep, which also means that the wifi gets disconnected. So I had to turn this off.

 

This means, the Kindle needs to be attached to a 5V PSU all the time. I did this by splicing up a Micro USB cable, soldering a Fan-Connector at the other end, and then put a 5V/12V - Fan - Molex splitter in between. Now, the Kindle never switches off, because the server runs 24/7.

 

Conclusion

That's pretty much all that is to say about the project. It was fun, and I'm really looking into making it more mature - like growing it into a "home-assistant" for information displays through out the household. Don't know if that's realistic, but... Well, that's what dreams are for.

 

Thanks for reading. :)

  • Like 1

Share this post


Link to post

Wow!  Amazing write up!  Thank you.  👍

 

That was way more than I expected, and I understood quite a bit at the first attempt.  I shall definitely read a few times and have a think whether to tackle something similar.    Right now, it's academic (no Kindle available) but I am sure I could solve that pretty quicky.  The threads that you linked to are also very valuable as an insight into how this kind of approach can evolve.

 

Thanks again.  

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now