<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title><![CDATA[PINE64 - RockPro64 Projects, Ideas and Tutorials]]></title>
		<link>https://forum.pine64.org/</link>
		<description><![CDATA[PINE64 - https://forum.pine64.org]]></description>
		<pubDate>Thu, 07 May 2026 05:25:40 +0000</pubDate>
		<generator>MyBB</generator>
		<item>
			<title><![CDATA[Anyone require a apple-sleek NAS case, w/ ample passive cooling & mutliple ssds?]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=19024</link>
			<pubDate>Fri, 19 Jan 2024 20:17:25 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=27538">dairymilkbatman</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=19024</guid>
			<description><![CDATA[I hadn't realized but I had spent an inordinate amount of time on a project for my rockpro64 NAS. So much so, that I thought it maybe of use to others since, through its many iterations, it has become nicely refined, maybe to the extent of a viable product.<br />
<br />
I designed it out of a block of aluminium.<br />
it had to be passive cooled to be in a heavily dusty environment.<br />
it would be working hard, and in a hot workshop. As a result it had to have ample cooling, for chips and ram.<br />
It turned out rather sleek like, apple esc due to the nature of how I chose to passively cool it.<br />
Had to house and protect 3 SSD's.<br />
<br />
<img src="https://forum.pine64.org/images/smilies/huh.png" alt="Huh" title="Huh" class="smilie smilie_17" /> Do I have to host pics to post them here?]]></description>
			<content:encoded><![CDATA[I hadn't realized but I had spent an inordinate amount of time on a project for my rockpro64 NAS. So much so, that I thought it maybe of use to others since, through its many iterations, it has become nicely refined, maybe to the extent of a viable product.<br />
<br />
I designed it out of a block of aluminium.<br />
it had to be passive cooled to be in a heavily dusty environment.<br />
it would be working hard, and in a hot workshop. As a result it had to have ample cooling, for chips and ram.<br />
It turned out rather sleek like, apple esc due to the nature of how I chose to passively cool it.<br />
Had to house and protect 3 SSD's.<br />
<br />
<img src="https://forum.pine64.org/images/smilies/huh.png" alt="Huh" title="Huh" class="smilie smilie_17" /> Do I have to host pics to post them here?]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Logical Extensible Gizmo Organizing RockPro64 Enclosure]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=18584</link>
			<pubDate>Mon, 07 Aug 2023 03:32:42 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=26905">hoarfrosty</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=18584</guid>
			<description><![CDATA[This meticulously engineered Logical Extensible Gizmo Organizing [LEGO] single-board computer enclosure utilizes a highly modular and agile construction medium, featuring an innovative, tool-less assembly design with snap-fit components, enabling effortless customization and reconfiguration of the external layout to accommodate diverse SBC form factors and peripheral expansions, ensuring seamless integration of advanced cooling solutions and optimizing thermal dissipation to achieve enhanced computational performance, and operational stability, while remaining dynamic and aesthetically pleasing. Possible variations include (but are not limited to) "castle," "city," "space," and "technic," (for senior engineers). Many of these materials&lt;&lt; Warning: errant block components may find their way to interior walking surfaces and illicit extreme reactions of pain and Extreme-Non-Human-Resource-Approved-Language [ENHRAL] from staff &gt;&gt;<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2811" target="_blank" title="">IMG_6078.jpg</a> (Size: 441.62 KB / Downloads: 419)
<!-- end: postbit_attachments_attachment --> <!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2812" target="_blank" title="">IMG_6079.jpg</a> (Size: 411.47 KB / Downloads: 445)
<!-- end: postbit_attachments_attachment --><br />
<br />
edit: I'm far from the first person to do this, check out these old links for inspiration:<br />
<a href="https://forum.pine64.org/showthread.php?tid=332&amp;page=7&amp;highlight=lego" target="_blank" rel="noopener" class="mycode_url">https://forum.pine64.org/showthread.php?...light=lego</a><br />
<a href="https://forum.pine64.org/showthread.php?tid=2040&amp;highlight=lego" target="_blank" rel="noopener" class="mycode_url">https://forum.pine64.org/showthread.php?...light=lego</a>]]></description>
			<content:encoded><![CDATA[This meticulously engineered Logical Extensible Gizmo Organizing [LEGO] single-board computer enclosure utilizes a highly modular and agile construction medium, featuring an innovative, tool-less assembly design with snap-fit components, enabling effortless customization and reconfiguration of the external layout to accommodate diverse SBC form factors and peripheral expansions, ensuring seamless integration of advanced cooling solutions and optimizing thermal dissipation to achieve enhanced computational performance, and operational stability, while remaining dynamic and aesthetically pleasing. Possible variations include (but are not limited to) "castle," "city," "space," and "technic," (for senior engineers). Many of these materials&lt;&lt; Warning: errant block components may find their way to interior walking surfaces and illicit extreme reactions of pain and Extreme-Non-Human-Resource-Approved-Language [ENHRAL] from staff &gt;&gt;<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2811" target="_blank" title="">IMG_6078.jpg</a> (Size: 441.62 KB / Downloads: 419)
<!-- end: postbit_attachments_attachment --> <!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2812" target="_blank" title="">IMG_6079.jpg</a> (Size: 411.47 KB / Downloads: 445)
<!-- end: postbit_attachments_attachment --><br />
<br />
edit: I'm far from the first person to do this, check out these old links for inspiration:<br />
<a href="https://forum.pine64.org/showthread.php?tid=332&amp;page=7&amp;highlight=lego" target="_blank" rel="noopener" class="mycode_url">https://forum.pine64.org/showthread.php?...light=lego</a><br />
<a href="https://forum.pine64.org/showthread.php?tid=2040&amp;highlight=lego" target="_blank" rel="noopener" class="mycode_url">https://forum.pine64.org/showthread.php?...light=lego</a>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[4-pin JST-XH connector specs and staggered HDD spinup with LSI SAS 9211-4i]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=18556</link>
			<pubDate>Sun, 30 Jul 2023 11:01:59 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=27209">Gian</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=18556</guid>
			<description><![CDATA[Hello.<br />
<br />
I would like to know what is the maximum amperage of the 4-pin JST-XH connector since I am planning on powering 4 HDDs by means of it as well as whether it is possible to set up a staggered spinup configuration with the LSI SAS 9211-4i host bus adapter.<br />
<br />
If the 4-pin JST-XH connector output current allows it, the project would entail splicing together two <a href="https://pine64.com/product/rockpro64-power-cable-for-dual-sata-drives/" target="_blank" rel="noopener" class="mycode_url">Pine64 power splitters</a> somewhere between the cable input connector and the (seeming) buck converters.<br />
<br />
Any advice on a suitable 230V 50Hz AC/DC PSU would also be appreciated.<br />
<br />
Thank you.]]></description>
			<content:encoded><![CDATA[Hello.<br />
<br />
I would like to know what is the maximum amperage of the 4-pin JST-XH connector since I am planning on powering 4 HDDs by means of it as well as whether it is possible to set up a staggered spinup configuration with the LSI SAS 9211-4i host bus adapter.<br />
<br />
If the 4-pin JST-XH connector output current allows it, the project would entail splicing together two <a href="https://pine64.com/product/rockpro64-power-cable-for-dual-sata-drives/" target="_blank" rel="noopener" class="mycode_url">Pine64 power splitters</a> somewhere between the cable input connector and the (seeming) buck converters.<br />
<br />
Any advice on a suitable 230V 50Hz AC/DC PSU would also be appreciated.<br />
<br />
Thank you.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Home Server with RAID 1 SATA M2 SSDs]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=18053</link>
			<pubDate>Sun, 19 Mar 2023 13:18:09 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=26615">runyor</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=18053</guid>
			<description><![CDATA[I have been using a Raspberry Pi Model B as a home server for a decade now. Mainly as an http/cronjob server, it has never failed on me (did a SD card upgrade at some point for additional disk space). Backup was just a couple of SDs ready to replace the failing one.<br />
<br />
<br />
<br />
Now I need more CPU power so I got myself a RockPro64 4GB and plan to setup it with two M2 SSDs in RAID 1.<br />
<br />
<br />
<br />
My current inventory:<br />
<br />
- board<br />
<br />
- 60W power adapter<br />
<br />
- PCIE to M2 SATA adapter (two slots) based on the JMB582 chip<br />
<br />
<br />
<br />
Items I am waiting for:<br />
<br />
- heat sink (forgot to order it with the board)<br />
<br />
- serial to usb adapter (because apparently you cannot install Debian without it as video fails: <a href="https://youtu.be/dENsLmDhq5Y" target="_blank" rel="noopener" class="mycode_url">https://youtu.be/dENsLmDhq5Y</a>)<br />
<br />
- 2x SATA SSDs (1TB each)<br />
<br />
<br />
<br />
Tried installing Debian 12 but (as per the linked YouTube video) I think I got this <a href="https://wiki.pine64.org/wiki/ROCKPro64#No_Video_or_GPU_Acceleration_on_Debian" target="_blank" rel="noopener" class="mycode_url">https://wiki.pine64.org/wiki/ROCKPro64#N..._on_Debian</a> so now I need something to hook up to the uart (please let me know if I am wrong on this).<br />
<br />
<br />
<br />
Manjaro worked great and I was able to at least confirm the SATA adapter is loaded fine:<br />
&#36; lspci<br />
00:00.0 PCI bridge: Rockchip Electronics Co., Ltd RK3399 PCI Express Root Port<br />
01:00.0 SATA controller: Micron Technology Corp. JMB58x AHCI SATA controller]]></description>
			<content:encoded><![CDATA[I have been using a Raspberry Pi Model B as a home server for a decade now. Mainly as an http/cronjob server, it has never failed on me (did a SD card upgrade at some point for additional disk space). Backup was just a couple of SDs ready to replace the failing one.<br />
<br />
<br />
<br />
Now I need more CPU power so I got myself a RockPro64 4GB and plan to setup it with two M2 SSDs in RAID 1.<br />
<br />
<br />
<br />
My current inventory:<br />
<br />
- board<br />
<br />
- 60W power adapter<br />
<br />
- PCIE to M2 SATA adapter (two slots) based on the JMB582 chip<br />
<br />
<br />
<br />
Items I am waiting for:<br />
<br />
- heat sink (forgot to order it with the board)<br />
<br />
- serial to usb adapter (because apparently you cannot install Debian without it as video fails: <a href="https://youtu.be/dENsLmDhq5Y" target="_blank" rel="noopener" class="mycode_url">https://youtu.be/dENsLmDhq5Y</a>)<br />
<br />
- 2x SATA SSDs (1TB each)<br />
<br />
<br />
<br />
Tried installing Debian 12 but (as per the linked YouTube video) I think I got this <a href="https://wiki.pine64.org/wiki/ROCKPro64#No_Video_or_GPU_Acceleration_on_Debian" target="_blank" rel="noopener" class="mycode_url">https://wiki.pine64.org/wiki/ROCKPro64#N..._on_Debian</a> so now I need something to hook up to the uart (please let me know if I am wrong on this).<br />
<br />
<br />
<br />
Manjaro worked great and I was able to at least confirm the SATA adapter is loaded fine:<br />
&#36; lspci<br />
00:00.0 PCI bridge: Rockchip Electronics Co., Ltd RK3399 PCI Express Root Port<br />
01:00.0 SATA controller: Micron Technology Corp. JMB58x AHCI SATA controller]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Mycroft/Picroft on Rockppro64]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=17821</link>
			<pubDate>Sat, 14 Jan 2023 00:29:19 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=10017">misterc</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=17821</guid>
			<description><![CDATA[I was playing with picroft on my old RPI3 + AIY Voicehat and it was just too slow for some functions, so considering the difficulty obtaining an RPI4 w/o selling limbs, I wanted to try getting it up and running on the RockPro.<br />
<br />
One of the reasons I grabbed the RockPro was the "PI-2" gpio connector that is, more or less, compatible with the Rpi 40 pin.  I was hoping to reuse the Voicehat (which provides a nice mic array and amplified speaker output).<br />
<br />
Installing the Mycroft software was easy enough but I soon discovered that the driver that was written for the "googlevoicehat-soundcard" was, for some inane reason, held in the sound/soc/bcm area of the kernel and it's dts claimed it needed the RPI BCM processor (it's a dac/adc/amp combo it shouldn't be tied to a specific processor.<br />
<br />
The driver is in Raspi but I don't think it was ever in mainline kernel, or if it was, it was dropped before Linux 5.<br />
<br />
So, not being a kernel hacker, I've walked away from that idea.<br />
<br />
I have ordered an inexpensive USB Speakerphone and will see how that works and update here.]]></description>
			<content:encoded><![CDATA[I was playing with picroft on my old RPI3 + AIY Voicehat and it was just too slow for some functions, so considering the difficulty obtaining an RPI4 w/o selling limbs, I wanted to try getting it up and running on the RockPro.<br />
<br />
One of the reasons I grabbed the RockPro was the "PI-2" gpio connector that is, more or less, compatible with the Rpi 40 pin.  I was hoping to reuse the Voicehat (which provides a nice mic array and amplified speaker output).<br />
<br />
Installing the Mycroft software was easy enough but I soon discovered that the driver that was written for the "googlevoicehat-soundcard" was, for some inane reason, held in the sound/soc/bcm area of the kernel and it's dts claimed it needed the RPI BCM processor (it's a dac/adc/amp combo it shouldn't be tied to a specific processor.<br />
<br />
The driver is in Raspi but I don't think it was ever in mainline kernel, or if it was, it was dropped before Linux 5.<br />
<br />
So, not being a kernel hacker, I've walked away from that idea.<br />
<br />
I have ordered an inexpensive USB Speakerphone and will see how that works and update here.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Smart TV & server]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=16823</link>
			<pubDate>Sun, 12 Jun 2022 15:07:34 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=25243">storz</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=16823</guid>
			<description><![CDATA[Hello!<br />
<br />
What I would like to achieve is having a fanless server and smart TV all in one. I would like to host my personal website, torrent daemon, Nextcloud and Ampache for music. Additionally I would like to connect it to my 4K TV where I would then watch 4K movies and listen to music on my stereo system.<br />
<br />
I know that RP64 is capable of being a server but what is the story with 4K videos? An additional challenge would be crash resistance of the graphical part. What do you think of my plan?]]></description>
			<content:encoded><![CDATA[Hello!<br />
<br />
What I would like to achieve is having a fanless server and smart TV all in one. I would like to host my personal website, torrent daemon, Nextcloud and Ampache for music. Additionally I would like to connect it to my 4K TV where I would then watch 4K movies and listen to music on my stereo system.<br />
<br />
I know that RP64 is capable of being a server but what is the story with 4K videos? An additional challenge would be crash resistance of the graphical part. What do you think of my plan?]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Assembling desktop setup for newbies?]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=16813</link>
			<pubDate>Sat, 11 Jun 2022 03:27:01 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=24232">Valenoern</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=16813</guid>
			<description><![CDATA[I am thinking about buying a rockpro64, but I would specifically like to set it up as a desktop computer connected to the internet. (It would actually be a bit of a home server, but would probably see most of its use in me typing or programming things within xfce/i3.)<br />
<br />
I have experience with the pinebook pro and pinephone so I somewhat know how these computers are put together and boot.<br />
But my big problem is simply knowing what parts I need to order when the information in the shop is very sparse and sometimes doesn't even say what's included and not included.<br />
<br />
(I also tried writing to info@pine64, but the person who wrote back did not seem to be well-versed enough in English to understand what my question was.)<br />
<br />
I think that logically I probably need to have:<br />
- a computer board<br />
- an emmc<span style="text-decoration: line-through;" class="mycode_s">?</span><br />
- a wifi/bluetooth module<span style="text-decoration: line-through;" class="mycode_s">?</span> (I would mainly use this for things like a bluetooth mouse)<br />
- an enclosure<br />
- a heat sink?<br />
- an AC adapter<br />
- a SATA* ssd adapter? is that even possible, or is it only an NVMe ssd that works? (I do mainly mean inside the enclosure**.)<br />
- an hdmi cable? <span style="text-decoration: line-through;" class="mycode_s">can it use these?</span> Yes, there is an HDMI port.<br />
- <span style="text-decoration: line-through;" class="mycode_s">Is there an ethernet jack? (I think so)</span> Yes, there is an ethernet jack.<br />
<br />
I would like to know if I may have forgotten any parts or any of these things are actually included with other items.<br />
As well as what parts are shared with the pinebook pro — I'd guess this would mostly just be the emmc, as it looks like there are different specs on the power adapter.<br />
<br />
I feel like there probably should have been some kind of "ready to go" store page where you could get the minimum set of parts needed for a desktop the way you could simply order an assembled pinebook pro or pinephone. (or realistically maybe a couple of these for different use cases?)<br />
But the next best thing is a tutorial on what parts go together for what purposes.<br />
<br />
(* The only reason I ask about SATA drives is I had a previous server whose main board seemed to be dying, and it would be convenient to simply reuse its drive for the rockpro64 — specifically, I would only need it to be available as storage after boot.<br />
I would not really be upset to find out that in practice it's too much trouble or requires dangerous meddling with the firmware. I would just like to know if it's possible to do and "how hard it is" for everyone's future reference when they think about buying this computer.)<br />
<br />
(** This brings in two extra details:<br />
A) computers always have a teeny chance of getting bumped at my house, so I would want an ssd to be protected and secured to the table along with the computer box<br />
B) if it's not impossible to use a SATA drive by the time I've said that, I'd like to know how big the enclosure has to be and if this changes how I would set up a heat sink / fan / any other parts inside the box.)<br />
<br />
---<br />
<br />
Confirmed parts<br />
<br />
* computer board - <a href="https://pine64.com/product-category/rockpro64/?v=0446c16e2e66" target="_blank" rel="noopener" class="mycode_url">https://pine64.com/product-category/rock...46c16e2e66</a><br />
* emmc and/or sd card - <a href="https://pine64.com/product-category/rockpro64-accessories/page/2/" target="_blank" rel="noopener" class="mycode_url">https://pine64.com/product-category/rock...es/page/2/</a><br />
* wifi/bluetooth module - <a href="https://pine64.com/product/rockpro64-1x1-dual-band-wifi-802-11ac-bluetooth-5-0-module" target="_blank" rel="noopener" class="mycode_url">https://pine64.com/product/rockpro64-1x1...5-0-module</a><br />
<br />
---<br />
<br />
Edited to add basic facts &amp; confirmed parts 22/6/11.<br />
<br />
Sources:<br />
<a href="https://wiki.pine64.org/wiki/ROCKPro64" target="_blank" rel="noopener" class="mycode_url">https://wiki.pine64.org/wiki/ROCKPro64</a>]]></description>
			<content:encoded><![CDATA[I am thinking about buying a rockpro64, but I would specifically like to set it up as a desktop computer connected to the internet. (It would actually be a bit of a home server, but would probably see most of its use in me typing or programming things within xfce/i3.)<br />
<br />
I have experience with the pinebook pro and pinephone so I somewhat know how these computers are put together and boot.<br />
But my big problem is simply knowing what parts I need to order when the information in the shop is very sparse and sometimes doesn't even say what's included and not included.<br />
<br />
(I also tried writing to info@pine64, but the person who wrote back did not seem to be well-versed enough in English to understand what my question was.)<br />
<br />
I think that logically I probably need to have:<br />
- a computer board<br />
- an emmc<span style="text-decoration: line-through;" class="mycode_s">?</span><br />
- a wifi/bluetooth module<span style="text-decoration: line-through;" class="mycode_s">?</span> (I would mainly use this for things like a bluetooth mouse)<br />
- an enclosure<br />
- a heat sink?<br />
- an AC adapter<br />
- a SATA* ssd adapter? is that even possible, or is it only an NVMe ssd that works? (I do mainly mean inside the enclosure**.)<br />
- an hdmi cable? <span style="text-decoration: line-through;" class="mycode_s">can it use these?</span> Yes, there is an HDMI port.<br />
- <span style="text-decoration: line-through;" class="mycode_s">Is there an ethernet jack? (I think so)</span> Yes, there is an ethernet jack.<br />
<br />
I would like to know if I may have forgotten any parts or any of these things are actually included with other items.<br />
As well as what parts are shared with the pinebook pro — I'd guess this would mostly just be the emmc, as it looks like there are different specs on the power adapter.<br />
<br />
I feel like there probably should have been some kind of "ready to go" store page where you could get the minimum set of parts needed for a desktop the way you could simply order an assembled pinebook pro or pinephone. (or realistically maybe a couple of these for different use cases?)<br />
But the next best thing is a tutorial on what parts go together for what purposes.<br />
<br />
(* The only reason I ask about SATA drives is I had a previous server whose main board seemed to be dying, and it would be convenient to simply reuse its drive for the rockpro64 — specifically, I would only need it to be available as storage after boot.<br />
I would not really be upset to find out that in practice it's too much trouble or requires dangerous meddling with the firmware. I would just like to know if it's possible to do and "how hard it is" for everyone's future reference when they think about buying this computer.)<br />
<br />
(** This brings in two extra details:<br />
A) computers always have a teeny chance of getting bumped at my house, so I would want an ssd to be protected and secured to the table along with the computer box<br />
B) if it's not impossible to use a SATA drive by the time I've said that, I'd like to know how big the enclosure has to be and if this changes how I would set up a heat sink / fan / any other parts inside the box.)<br />
<br />
---<br />
<br />
Confirmed parts<br />
<br />
* computer board - <a href="https://pine64.com/product-category/rockpro64/?v=0446c16e2e66" target="_blank" rel="noopener" class="mycode_url">https://pine64.com/product-category/rock...46c16e2e66</a><br />
* emmc and/or sd card - <a href="https://pine64.com/product-category/rockpro64-accessories/page/2/" target="_blank" rel="noopener" class="mycode_url">https://pine64.com/product-category/rock...es/page/2/</a><br />
* wifi/bluetooth module - <a href="https://pine64.com/product/rockpro64-1x1-dual-band-wifi-802-11ac-bluetooth-5-0-module" target="_blank" rel="noopener" class="mycode_url">https://pine64.com/product/rockpro64-1x1...5-0-module</a><br />
<br />
---<br />
<br />
Edited to add basic facts &amp; confirmed parts 22/6/11.<br />
<br />
Sources:<br />
<a href="https://wiki.pine64.org/wiki/ROCKPro64" target="_blank" rel="noopener" class="mycode_url">https://wiki.pine64.org/wiki/ROCKPro64</a>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Umbrel Server]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=16602</link>
			<pubDate>Thu, 05 May 2022 00:14:18 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=25023">Shaeroden</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=16602</guid>
			<description><![CDATA[I bought this board in the hopes that i could spare myself the hassle of getting a raspberry pi 4 during a chip shortage on top of a scalping mania, and run <a href="https://getumbrel.com/" target="_blank" rel="noopener" class="mycode_url">umbrel</a> on the rockpro64 board.<br />
<br />
has anyone come across some good tutorials for this?  I tried going through <a href="https://thunderbiscuit.com/posts/umbrel-rockpro/" target="_blank" rel="noopener" class="mycode_url">this tutorial </a> but I <a href="https://twitter.com/thunderB__/status/1521471167147692033" target="_blank" rel="noopener" class="mycode_url">contacted the author through twitter</a> and he said the whole blog post is out of date, given umbrel's many updates since then.<br />
<br />
they make it sound so easy to set up on the raspberry pi 4, and I went ahead and overpaid for one anyways, but I'd still like to be able to run umbrel on the rockpro64, since there have been reported power delivery problems with running multiple drives on a raspberry pi.]]></description>
			<content:encoded><![CDATA[I bought this board in the hopes that i could spare myself the hassle of getting a raspberry pi 4 during a chip shortage on top of a scalping mania, and run <a href="https://getumbrel.com/" target="_blank" rel="noopener" class="mycode_url">umbrel</a> on the rockpro64 board.<br />
<br />
has anyone come across some good tutorials for this?  I tried going through <a href="https://thunderbiscuit.com/posts/umbrel-rockpro/" target="_blank" rel="noopener" class="mycode_url">this tutorial </a> but I <a href="https://twitter.com/thunderB__/status/1521471167147692033" target="_blank" rel="noopener" class="mycode_url">contacted the author through twitter</a> and he said the whole blog post is out of date, given umbrel's many updates since then.<br />
<br />
they make it sound so easy to set up on the raspberry pi 4, and I went ahead and overpaid for one anyways, but I'd still like to be able to run umbrel on the rockpro64, since there have been reported power delivery problems with running multiple drives on a raspberry pi.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[OMV 6 NAS - Image to use]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=16327</link>
			<pubDate>Fri, 18 Mar 2022 10:32:03 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=1252">g_t_j</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=16327</guid>
			<description><![CDATA[Can someone be so kind to let me know what image should I use in order to install OMV 6.0 on top?]]></description>
			<content:encoded><![CDATA[Can someone be so kind to let me know what image should I use in order to install OMV 6.0 on top?]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[SkiffOS server with Docker ,NAS, Home assistant, Jellyfin, Snikket XMPP (tutorial)]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=15944</link>
			<pubDate>Wed, 02 Feb 2022 21:44:44 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=16790">GreyLinux</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=15944</guid>
			<description><![CDATA[Hi All,<br />
<br />
So I've written another install tutorial this time using <span style="font-weight: bold;" class="mycode_b">SkiffOS</span>, the <a href="https://forum.pine64.org/showthread.php?tid=12806" target="_blank" rel="noopener" class="mycode_url">first one</a> hopefully helped a few users. I discovered SkiffOS on the Rockpro64 software release page however I was surprised to see literally no posts in the forum about it. I contacted the developer on Discord and with his endless help and patience , I have a working server running Docker and several containers.<br />
<br />
<span style="color: #008E02;" class="mycode_color"><span style="font-weight: bold;" class="mycode_b">UPDATE 19 JUNE 2022: After a lot of debugging and a lot of hard work on part <dvz_me_placeholder id="0" /> , the Rockpro64 is on the 5.18 kernel and great news , anyone who uses the Marvell 88SE9230 SATA card its now working perfectly out of the box with SkiffOS, the udev rule is no longer required as its included within the SkiffOS Rockpro64 config</span> </span><br />
<br />
<span style="font-weight: bold;" class="mycode_b"><span style="color: #008E02;" class="mycode_color">UPDATE:  <dvz_me_placeholder id="0" />  is currently working on providing pre-built images , for users who want to try SkiffOS without compiling their own build, a link will be attached when the images are available</span></span><br />
<br />
<br />
<img src="https://raw.githubusercontent.com/skiffos/SkiffOS/master/resources/images/skiff.png" loading="lazy"  alt="[Image: skiff.png]" class="mycode_img" /><br />
<br />
<br />
<a href="https://github.com/skiffos/skiffos" target="_blank" rel="noopener" class="mycode_url">SkiffOS Github page</a><br />
<br />
The Docker containers I have are <br />
<ul class="mycode_list"><li>a Simple <a href="https://github.com/ServerContainers/samba" target="_blank" rel="noopener" class="mycode_url">Samba</a> container for a NAS<br />
</li>
<li>a <a href="https://github.com/tomsquest/docker-radicale" target="_blank" rel="noopener" class="mycode_url">Radicale</a> container for caldav calendar and contacts sync<br />
</li>
<li>a <a href="https://www.home-assistant.io/installation/linux#install-home-assistant-container" target="_blank" rel="noopener" class="mycode_url">Home assistant</a> container for home automation <br />
</li>
<li>a <a href="https://hub.docker.com/r/esphome/esphome" target="_blank" rel="noopener" class="mycode_url">EspHome</a> container for managing my home automation Sonoff light switches<br />
</li>
<li>a <a href="https://jellyfin.org/docs/general/administration/installing.html#docker" target="_blank" rel="noopener" class="mycode_url">Jellyfin</a> container as a media server <br />
</li>
<li>a <a href="https://caddyserver.com/" target="_blank" rel="noopener" class="mycode_url">Caddy server </a>container for my reverse proxy and lets encrypt certs renewal <br />
</li>
<li>a <a href="https://snikket.org/service/quickstart/" target="_blank" rel="noopener" class="mycode_url">Snikket server </a>container for a xmpp server thats simple an easy to get setup <br />
</li>
<li>and finally a <a href="https://docs.portainer.io/v/ce-2.11/start/install/server/docker/linux#deployment" target="_blank" rel="noopener" class="mycode_url">Portainer</a> container to manage all my containers with a nice UI<br />
</li>
</ul>
<br />
now this sounds like a lot of containers for an arm board, but at idle which is most of the time, the Rockpro64 sits at <span style="font-weight: bold;" class="mycode_b">1% </span>CPU use. <br />
<br />
Now before I begin, SkiffOS describes itself as: <br />
<br />
<blockquote class="mycode_quote"><cite>Quote:</cite>SkiffOS is a lightweight operating system for any Linux-compatible computer, ranging from RPi, Odroid, NVIDIA Jetson, to Desktop PCs, Laptops (i.e. Apple MacBook), Phones (PinePhone), Containers, or Cloud VMs. It is:<br />
<ul class="mycode_list"><li>    Adoptable: any userspace can be imported/exported to/from container images.<br />
</li>
<li>    Familiar: uses simple Makefile and KConfig language for configuration.<br />
</li>
<li>    Flexible: supports all major OS distributions inside containers.<br />
</li>
<li>    Portable: containers can be moved between machines of similar CPU type.<br />
</li>
<li>    Reliable: changes inside user environments cannot break the host boot-up.<br />
</li>
<li>    Reproducible: a given Skiff Git tree will always produce identical output.<br />
</li>
</ul>
<br />
Uses Buildroot to produce a minimal "single-file" host OS as a standardized base cross-platform operating system "shim" for hosting containers. Most Linux platforms have widely varying requirements for kernel, firmware, and additional hardware support packages. The immutable SkiffOS host system contains everything needed to support the hardware, cleanly separated from the applications</blockquote>
<br />
The main thing to focus on, I think is that it is based on Buildroot, this is an immutable OS, so you have to compile the build on your daily machine(i.e laptop or desktop) and add any additional software packages and configuration options at build time. The benefits of an immutable system are best described above, but the main thing is "once setup" you can reproduce the build with ease and it will barely need any tinkering once its up an and running, plus it provides the security that should anything go wrong a quick reboot will restore the System back to its initial boot state.<br />
<br />
SkiffOS has a "persist" partition that stores all the files and folders that you want to<span style="font-weight: bold;" class="mycode_b"> persist</span> after a reboot. This can be where all the docker configuration files and states reside.<br />
<br />
SkiffOS also has the option for a <span style="font-weight: bold;" class="mycode_b">CORE </span>container this is a container that will provide an OS environment more familiar to most, making it easier to interact with the persist partition, you can choose between many core environments such as Alpine, Debian, Gentoo or even Ubuntu with a desktop environment. All this is best explained on the <a href="https://github.com/skiffos/skiffos" target="_blank" rel="noopener" class="mycode_url">Github</a> page, I however didn't need a core container as once setup the only interaction I need is with the containers themselves.<br />
<br />
So lets begin! to start with I would create a <span style="font-weight: bold;" class="mycode_b">Projects </span>Directory on you daily machine, preferably in you Home directory and then Git Clone the repo from Github into the projects directory.<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>mkdir Projects <br />
cd Projects/<br />
git clone https://github.com/skiffos/SkiffOS.git</code></div></div><br />
Now I created a  configuration directory that has all my docker container configs, files and scripts, I called it <span style="font-weight: bold;" class="mycode_b">my_docker_config </span>this allows me to export it as an option when compiling SkiffOS during build time but of course you could create these files and folders and copy and paste their contents one at a time to the persist partition after first boot, but this defeats the benefit of having a reproducible build. So a little work now, to create a config directory will really pay off.<br />
<br />
I have a <span style="font-weight: bold;" class="mycode_b">udev</span> rule that allows my marvell 88SE9230 Sata card to work and a fan script that starts the fan and keeps it at a constant speed, these are best explained in my other tutorial, however these are the types of things I would also want to have in my configuration directory. I also have a couple of systemd <span style="font-weight: bold;" class="mycode_b">.mount</span> files to mount my ssd's on startup similar to how an "fstab" file would work.<br />
<br />
This is how my <span style="font-weight: bold;" class="mycode_b">my_docker_config</span> directory tree looks <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>my_docker_config<br />
    └── root_overlay<br />
        ├── etc<br />
        │   ├── skiff<br />
        │   │   └── authorized_keys<br />
        │   │       └── my-key.pub<br />
        │   ├── systemd<br />
        │   │   └── system<br />
        │   │       ├── fan-speed.service<br />
        │   │       ├── mnt-ssd1.mount<br />
        │   │       ├── mnt-ssd2.mount<br />
        │   │       └── multi-user.target.wants<br />
        │   │           ├── fan-speed.service -&gt; ../fan-speed.service<br />
        │   │           ├── mnt-ssd1.mount -&gt; ../mnt-ssd1.mount<br />
        │   │           └── mnt-ssd2.mount -&gt; ../mnt-ssd2.mount<br />
        │   └── udev<br />
        │       └── rules.d<br />
        │           └── 99-marvell.rules<br />
        └── opt<br />
            ├── mydocker<br />
            │   ├── caddy<br />
            │   │   ├── caddy_data<br />
            │   │   └── Caddyfile<br />
            │   ├── docker-compose.yml<br />
            │   ├── esphome<br />
            │   │   └── config<br />
            │   ├── homeassistant<br />
            │   │   ├── automations.yaml<br />
            │   │   ├── configuration.yaml<br />
            │   │   ├── groups.yaml<br />
            │   │   └── www<br />
            │   │       ├── picone.PNG<br />
            │   │       └── pictwo.PNG<br />
            │   ├── jellyfin<br />
            │   ├── radicale<br />
            │   │   ├── config<br />
            │   │   │   └── config<br />
            │   │   ├── data<br />
            │   │   └── log<br />
            │   └── snikket<br />
            │       ├── acme_challenges<br />
            │       ├── snikket.conf<br />
            │       └── snikket_data<br />
            └── rockpro64_fan<br />
                └── fan_script.sh</code></div></div><br />
you can see I keep my docker configuration file in <span style="font-weight: bold;" class="mycode_b">mydocker </span>a fan script under "opt" and the rest of my config in "etc" this is all whats called a root overlay , all these file and directories will be overlaid upon boot and be placed in their respective folders in the live environment . You can also see I have <span style="font-weight: bold;" class="mycode_b">"authorized_keys"</span> under skiff this is where you can place your public ssh keys , so you have ssh access upon boot.<br />
<br />
<span style="color: #E82A1F;" class="mycode_color">my<span style="font-weight: bold;" class="mycode_b"> udev rule</span> named <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">99-marvell.rules</span></span> looks like this </span><br />
<span style="color: #E82A1F;" class="mycode_color"><span style="font-size: large;" class="mycode_size"><span style="font-weight: bold;" class="mycode_b">(This udev rule is no longer required it is included in SkiffOS config for the Rockpro64, its shown to inform only</span></span>):</span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1b4b", ATTR{device}=="0x9230", RUN+="/bin/bash -c 'echo %k &gt; /sys/bus/pci/drivers/ahci/bind'"</code></div></div><br />
My <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">fan_script.sh</span></span> looks like this: <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>#!/bin/bash<br />
<br />
rmmod pwm-fan<br />
echo 0 &gt; /sys/class/pwm/pwmchip1/export<br />
echo 110 &gt; /sys/class/pwm/pwmchip1/pwm0/duty_cycle<br />
echo 500 &gt; /sys/class/pwm/pwmchip1/pwm0/period<br />
echo 1 &gt; /sys/class/pwm/pwmchip1/pwm0/enable</code></div></div><br />
this will of course need a "fan speed service" to start from boot and a sym link to the service in the <span style="font-weight: bold;" class="mycode_b">multi-user.target.wants </span>directory as shown in the directory tree <br />
<br />
the service aptly named <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">fan-speed.service</span></span> looks like this :<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>[Unit]<br />
Description=change fan speed at startup <br />
After=basic.target<br />
<br />
[Service]<br />
Type=oneshot<br />
RemainAfterExit=yes<br />
ExecStart=/opt/rockpro64_fan/fan_script.sh<br />
<br />
[Install]<br />
WantedBy=multi-user.target</code></div></div><br />
one of my SSD mount files looks like this: <br />
<br />
<span style="color: #00369B;" class="mycode_color">(please note the name of the mount file must be the path to the mount point with each folder separated by a hyphen i.e <span style="font-weight: bold;" class="mycode_b">/mnt/mydrive/here</span> would be <span style="font-weight: bold;" class="mycode_b">mnt-mydrive-here.mount</span>)</span><br />
<br />
<span style="color: #00369B;" class="mycode_color">(also note that a sym link to the .mount file must also be in <span style="font-weight: bold;" class="mycode_b">multi-user.target.wants </span>directory) </span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>[Unit]<br />
Description=mounting Files from SSD<br />
<br />
[Mount]<br />
Where=/mnt/ssd1<br />
What=/dev/disk/by-label/storage1<br />
<br />
[Install]<br />
WantedBy=multi-user.target</code></div></div><br />
here is the basic <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">config</span></span> for Radicale :<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>[server]<br />
hosts = 0.0.0.0:5232<br />
<br />
max_connections = 15<br />
# 100 Megabyte<br />
max_content_length = 100000000<br />
# 30 seconds<br />
timeout = 20<br />
<br />
[auth]<br />
# Average delay after failed login attempts in seconds<br />
delay = 600<br />
<br />
type = htpasswd<br />
htpasswd_filename = /etc/radicale/users<br />
# encryption method used in the htpasswd file<br />
htpasswd_encryption = md5<br />
<br />
[storage]<br />
filesystem_folder = /data/collections</code></div></div><br />
and the <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">snikket.conf</span></span> file please edit with your email and domain name for snikket <span style="color: #C10300;" class="mycode_color">(please leave the ports commented out until later)</span> :<br />
<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code># The primary domain of your Snikket instance<br />
SNIKKET_DOMAIN=mysnikket.duckdns.org<br />
<br />
# An email address where the admin can be contacted<br />
# (also used to register your Let's Encrypt account to obtain certificates)<br />
SNIKKET_ADMIN_EMAIL=myemailaddress@mail.com<br />
<br />
#SNIKKET_TWEAK_HTTP_PORT=5080<br />
<br />
#SNIKKET_TWEAK_HTTPS_PORT=5443</code></div></div><br />
and the <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">Caddyfile</span></span> required for caddy<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>(log_common) {<br />
  log {<br />
    output file /var/log/caddy/{args.0}.access.log<br />
  }<br />
}<br />
<br />
myradicale.duckdns.org {<br />
&nbsp;&nbsp;&nbsp;&nbsp;handle_path /radicale* {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reverse_proxy localhost:5232 {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;header_up X-Script-Name /radicale<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;import log_common myradicales.duckdns.org<br />
}<br />
<br />
myhomeassistant.duckdns.org {<br />
&nbsp;&nbsp;&nbsp;&nbsp;reverse_proxy localhost:8123<br />
&nbsp;&nbsp;&nbsp;&nbsp;import log_common myhomeassistant.duckdns.org<br />
}<br />
<br />
<br />
<br />
<br />
http://mysnikket.duckdns.org,<br />
http://groups.mysnikket.duckdns.org,<br />
http://share.mysnikket.duckdns.org {<br />
&nbsp;&nbsp;&nbsp;&nbsp;reverse_proxy localhost:5080<br />
&nbsp;&nbsp;&nbsp;&nbsp;request_body {<br />
  &nbsp;&nbsp;&nbsp;&nbsp;max_size 20M<br />
  &nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
mysnikket.duckdns.org,<br />
groups.mysnikket.duckdns.org,<br />
share.mysnikket.duckdns.org {<br />
        tls /snikket/letsencrypt/live/mysnikket.duckdns.org/fullchain.pem /snikket/letsencrypt/live/mysnikket.duckdns.org/privkey.pem<br />
&nbsp;&nbsp;&nbsp;&nbsp;reverse_proxy https://localhost:5443 {<br />
&nbsp;&nbsp;&nbsp;&nbsp;    transport http {<br />
                tls_insecure_skip_verify<br />
            }<br />
        }<br />
        request_body {<br />
  &nbsp;&nbsp;&nbsp;&nbsp;max_size 20M<br />
  &nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</code></div></div><span style="color: #C10300;" class="mycode_color">(please edit the above with the domain names you have chosen for your services)</span> <br />
<br />
and finally my <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">docker-compose.yml</span></span> file for all my containers apart from Portainer which needs to access the docker daemon via a unix socket <br />
<br />
<span style="color: #C10300;" class="mycode_color">(please edit to suit your needs)</span>:<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>version: '3.7'<br />
services:<br />
<br />
  caddy:<br />
    depends_on:<br />
      - radicale<br />
      - homeassistant <br />
    image: caddy:latest<br />
    container_name: caddy_reverse_proxy<br />
    volumes:<br />
      - /mnt/persist/mydocker/caddy/caddy_data:/data<br />
      - /mnt/persist/mydocker/caddy/Caddyfile:/etc/caddy/Caddyfile<br />
      - /mnt/persist/mydocker/snikket/snikket_data:/snikket:ro <br />
      - /mnt/persist/mydocker/caddy/logs:/var/log/caddy/<br />
    environment:<br />
      - TZ=Europe/London  <br />
    restart: unless-stopped<br />
    network_mode: host<br />
<br />
  radicale:<br />
    image: tomsquest/docker-radicale<br />
    container_name: radicale<br />
    ports:<br />
      - 127.0.0.1:5232:5232<br />
    init: true<br />
    read_only: true<br />
    security_opt:<br />
      - no-new-privileges:true<br />
    cap_drop:<br />
      - ALL<br />
    cap_add:<br />
      - SETUID<br />
      - SETGID<br />
      - CHOWN<br />
      - KILL<br />
    healthcheck:<br />
      test: curl -f http://127.0.0.1:5232 || exit 1<br />
      interval: 30s<br />
      retries: 3<br />
    restart: unless-stopped<br />
    volumes:<br />
      - /mnt/persist/mydocker/radicale/data:/data<br />
      - /mnt/persist/mydocker/radicale/config:/config:ro<br />
      - /mnt/persist/mydocker/radicale/users:/etc/radicale/users<br />
      - /mnt/persist/mydocker/radicale/log:/var/log/radicale/log<br />
    environment:<br />
      - TZ=Europe/London <br />
<br />
  homeassistant:<br />
    container_name: home-assistant<br />
    image: homeassistant/home-assistant:stable<br />
    volumes:<br />
      - /mnt/persist/mydocker/homeassistant:/config<br />
    environment:<br />
      - TZ=Europe/London<br />
    restart: unless-stopped<br />
    network_mode: host<br />
<br />
  esphome:<br />
    image: esphome/esphome<br />
    volumes:<br />
      - /mnt/persist/mydocker/esphome/config:/config:rw<br />
    environment:<br />
      - TZ=Europe/London<br />
    network_mode: host<br />
    restart: unless-stopped<br />
    <br />
    <br />
  snikket_proxy:<br />
    container_name: snikket-proxy<br />
    image: snikket/snikket-web-proxy:beta<br />
    env_file: /mnt/persist/mydocker/snikket/snikket.conf<br />
    network_mode: host<br />
    volumes:<br />
      - /mnt/persist/mydocker/snikket/snikket_data:/snikket<br />
      - /mnt/persist/mydocker/snikket/acme_challenges:/var/www/html/.well-known/acme-challenge<br />
    restart: unless-stopped<br />
  snikket_certs:<br />
    container_name: snikket-certs<br />
    image: snikket/snikket-cert-manager:beta<br />
    env_file: /mnt/persist/mydocker/snikket/snikket.conf<br />
    volumes:<br />
      - /mnt/persist/mydocker/snikket/snikket_data:/snikket<br />
      - /mnt/persist/mydocker/snikket/acme_challenges:/var/www/.well-known/acme-challenge<br />
    restart: unless-stopped<br />
  snikket_portal:<br />
    container_name: snikket-portal<br />
    image: snikket/snikket-web-portal:beta<br />
    network_mode: host<br />
    env_file: /mnt/persist/mydocker/snikket/snikket.conf<br />
    restart: unless-stopped<br />
<br />
  snikket_server:<br />
    container_name: snikket<br />
    image: snikket/snikket-server:beta<br />
    network_mode: host<br />
    volumes:<br />
      - /mnt/persist/mydocker/snikket/snikket_data:/snikket<br />
    env_file: /mnt/persist/mydocker/snikket/snikket.conf<br />
    restart: unless-stopped<br />
    <br />
  samba:<br />
    build: .<br />
    image: servercontainers/samba<br />
    container_name: samba-server<br />
    restart: unless-stopped<br />
    network_mode: host<br />
    environment:<br />
      WSDD2_DISABLE: 1<br />
      AVAHI_DISABLE: 1<br />
      <br />
      ACCOUNT_user1: "user1:1000:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:DF56ET792A681E1A23E0F75695:[U          ]:LCT-4502WT5:"<br />
      UID_user1: 1020<br />
      GROUPS_user1: users<br />
<br />
      ACCOUNT_user2: "user2:1000:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:29395D100D389TEB9E295A747E420:[U          ]:LCT-521D03D5:"<br />
      UID_user2: 1021<br />
      GROUPS_user2: users<br />
      <br />
      GROUP_users: 100<br />
<br />
      SAMBA_VOLUME_CONFIG_Foldername: "[Foldername Share]; path=/shares/Foldername; valid users = user1, user2; guest ok = no; force group = users; read only = no; browseable = yes"<br />
<br />
      SAMBA_VOLUME_CONFIG_media: "[media Share]; path=/shares/media; valid users = user1, user2; guest ok = no; force group = users; read only = no; browseable = yes"<br />
    <br />
    volumes:<br />
      <br />
      - /mnt/ssd1/Foldername:/shares/Foldername<br />
      - /mnt/ssd2/media:/shares/media<br />
    <br />
  jellyfin:<br />
    image: linuxserver/jellyfin:latest<br />
    container_name: jellyfin<br />
    network_mode: host<br />
    volumes:<br />
      - /mnt/persist/mydocker/jellyfin/config:/config<br />
      - /mnt/persist/mydocker/jellyfin/cache:/cache<br />
      - /mnt/ssd2/media:/media<br />
    restart: unless-stopped<br />
    environment:<br />
      - TZ=Europe/London</code></div></div><br />
after you have your configuration directory you can move it to<span style="font-weight: bold;" class="mycode_b"> /Projects/SkiffOS/configs/apps/ </span>this will make it an exportable option in the next step.<br />
<br />
So on to the installation! <br />
<br />
Hopefully you still in the <span style="font-weight: bold;" class="mycode_b">Projects </span>directory with the freshly git cloned SkiffOS and your custom config directory moved to its place in the apps folder. As per the instruction on the SkiffOs github page you need to export all the options you want for the build, I have my board requirement,  docker compose (which also installs docker) and my custom config directory. <br />
<br />
this looks like this :<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>cd SkiffOS<br />
<br />
export SKIFF_CONFIG=pine64/rockpro64,apps/compose,apps/my_docker_config</code></div></div><br />
<span style="font-style: italic;" class="mycode_i">OK so I should mention that if you require any additional software such as "htop" installed then now is the time to select it this is done by typing</span> <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>make br/menuconfig</code></div></div><br />
<span style="color: #005DC2;" class="mycode_color"><span style="font-style: italic;" class="mycode_i">please note! all these commands will only work if you are in the the<span style="font-weight: bold;" class="mycode_b"> SkiffOS </span>directory located in the previously created <span style="font-weight: bold;" class="mycode_b">Projects</span> directory</span></span><br />
<br />
<span style="color: #444444;" class="mycode_color">T</span><span style="color: #419DC1;" class="mycode_color"><span style="color: #444444;" class="mycode_color">his make command will create an ncurses menu that will allow you to select additional software, system configs kernel options .etc</span></span><br />
<br />
The <span style="font-weight: bold;" class="mycode_b">target packages</span> option will allow you to select additional packages such as htop. <br />
<br />
The <span style="color: #C10300;" class="mycode_color"><span style="font-weight: bold;" class="mycode_b">/</span></span> key will allow you to search all the options and <span style="color: #C10300;" class="mycode_color"><span style="font-weight: bold;" class="mycode_b">space</span></span> will  allow you to select the option current highlighted . <span style="color: #C10300;" class="mycode_color">(remember to save before you exit the ncurses menu)</span><br />
<br />
Now to finish up the build process. <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>make configure<br />
<br />
make compile</code></div></div><br />
This last command will begin the compiling process, this can take some time depending on your hardware, so now is the time to get a coffee!<br />
<br />
<span style="font-style: italic;" class="mycode_i"><span style="color: #005DC2;" class="mycode_color">(please note that when you recompile,  for example after an update to SkiffOS this will be alot shorter timespan, as a previously compiled image will exist and only updated options and packages will be compiled )</span></span><br />
<br />
To copy the compiled image to an SD card or emmc <span style="color: #C10300;" class="mycode_color">(although I currently haven't tested if emmc works)</span><br />
<br />
You will need to be root to perform these next commands<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>sudo bash <br />
<br />
blkid  (this is to see  where the SD is located )<br />
<br />
export PINE64_SD=/dev/mmcblk0  ( please edit this last part to suit where you SD is located)<br />
<br />
make cmd/pine64/common/format  ( this formats the card , if you get an error with this command and your using Ubuntu please see note below)<br />
<br />
make cmd/pine64/common/install ( this will copy the build to the sd card)</code></div></div><br />
<span style="color: #00369B;" class="mycode_color"><span style="font-style: italic;" class="mycode_i">(Additional note whilst using Arch I had no issue with the format command above, however after switching to a Ubuntu based OS I noticed it failed with an error, the solution was to edit  he formatting script and add a  <span style="font-weight: bold;" class="mycode_b">P </span>to every partition  and the fatlabel )</span></span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>nano ~/Projects/SkiffOS/configs/pine64/common/scripts/format_sd.sh</code></div></div><br />
see the addition of the <span style="color: #C10300;" class="mycode_color"><span style="font-weight: bold;" class="mycode_b">p</span></span> after every curly bracket <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>echo "Formatting boot partition..."<br />
mkfs.vfat -F 32 &#36;{PINE64_SD_SFX}p1<br />
fatlabel &#36;{PINE64_SD_SFX}p1 boot<br />
<br />
echo "Formatting rootfs partition..."<br />
&#36;MKEXT4 -L "rootfs" &#36;{PINE64_SD_SFX}p2<br />
<br />
echo "Formatting persist partition..."<br />
&#36;MKEXT4 -L "persist" &#36;{PINE64_SD_SFX}p3</code></div></div><br />
now your ready to boot, but before you do I would create the <span style="font-weight: bold;" class="mycode_b"><span style="color: #008E02;" class="mycode_color">Duckdns</span></span> or other <span style="font-weight: bold;" class="mycode_b"><span style="color: #008E02;" class="mycode_color">DDNS</span></span> provider entries for the services you will expose to the internet <span style="font-style: italic;" class="mycode_i"><span style="color: #00369B;" class="mycode_color">(Please see my other tutorial for assistance)</span></span>, in my case <span style="font-weight: bold;" class="mycode_b">Home assistant</span> , <span style="font-weight: bold;" class="mycode_b">Radicale</span> and the <span style="font-weight: bold;" class="mycode_b">Snikket XMPP server,</span> so <span style="font-weight: bold;" class="mycode_b">3</span> entries in total.<br />
<br />
<span style="color: #C10300;" class="mycode_color"><span style="font-style: italic;" class="mycode_i">I will point out for completeness sake that if your following along with this tutorial and you are wanting to trial Snikket as an XMPP server it is not recommended to use a Dynamic Domain Name Service, preferably you will require a static IP address from your internet provider and you should use the recommended DNS providers suggested on the Snikket website . I have a static IP but I am using Duckdns as a free DNS which works perfectly for my use case.</span></span><br />
<br />
When you boot for the first time it may take a few minutes to provide SSH, if it doesnt provide SSH after 15 minutes,  the SSH keys provided in the config file are either incorrect or you have an issue and the SD has failed to boot. <span style="color: #00369B;" class="mycode_color"><span style="font-style: italic;" class="mycode_i">(Connecting a serial console could help you resolve your issue see <a href="https://forum.pine64.org/showthread.php?tid=6387" target="_blank" rel="noopener" class="mycode_url">here</a> )</span></span><br />
<br />
After you have successfully booted move your <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">mydocker</span></span>  config directory to the persist partition <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>mv /opt/mydocker /mnt/persist/</code></div></div><br />
now to get <span style="font-weight: bold;" class="mycode_b">Portainer </span>up and running <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker run -d -p 9443:9443 --name portainer &#92;<br />
    --restart=always &#92;<br />
    -v /var/run/docker.sock:/var/run/docker.sock &#92;<br />
    -v portainer_data:/data &#92;<br />
    cr.portainer.io/portainer/portainer-ce:latest</code></div></div><br />
You will need to setup <span style="font-weight: bold;" class="mycode_b">Portainer</span> to your requirements <span style="color: #00369B;" class="mycode_color"><span style="font-style: italic;" class="mycode_i">(This docker command will only get it installed and running)</span></span><br />
<br />
Next pull all the container images for your services <span style="font-style: italic;" class="mycode_i"><span style="color: #00369B;" class="mycode_color">(This command will only work if your in the directory where your docker-compose file is located)</span></span><br />
<br />
in my case:<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>cd /mnt/persist/mydocker/<br />
<br />
docker-compose pull ( this will pull all images and can take some time )</code></div></div><br />
So If your following along and are using the <span style="font-weight: bold;" class="mycode_b">Radicale</span> container, you will need to run a "one time" command to create a user password using htpasswd this can be done using a<span style="font-weight: bold;" class="mycode_b"> "temporary container"</span>,  the container is removed after the command completes.<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker run -v /mnt/persist/mydocker/radicale:/radicale --rm -it alpine:edge sh -c "apk add apache2-utils &amp;&amp; htpasswd -c /radicale/users calendar"<br />
<br />
(this creates user "calendar" password)( the --rm remove the container after completion )</code></div></div><br />
next if your using the <span style="font-weight: bold;" class="mycode_b">samba</span> container for a NAS and your would prefer to use password hashes rather than a plain text password, then run this docker command for each user and fill in the requested details. <br />
<br />
This will print to screen a hash for the specified user which needs to be added to the <span style="font-weight: bold;" class="mycode_b">docker-compose.yml</span> file as shown above where it says <span style="font-weight: bold;" class="mycode_b">ACCOUNT_username</span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker run -ti --rm --entrypoint create-hash.sh servercontainers/samba</code></div></div><br />
Now is the time to open the required ports on your router, <span style="font-weight: bold;" class="mycode_b">Caddy</span> only requires 80 and 443 for the reverse proxy service however <span style="font-weight: bold;" class="mycode_b">Snikket</span> requires quite a few port as shown <a href="https://github.com/snikket-im/snikket-server/blob/master/docs/advanced/firewall.md" target="_blank" rel="noopener" class="mycode_url">here</a><br />
<br />
<span style="font-weight: bold;" class="mycode_b">Snikket </span>requires a configuration file as shown above, however to use with a reverse proxy<span style="color: #00369B;" class="mycode_color"> <span style="font-style: italic;" class="mycode_i">(Because <span style="font-weight: bold;" class="mycode_b">Snikket </span>gets its own lets encrypt certificates)</span></span> you will need to change the ports in the <span style="font-weight: bold;" class="mycode_b">.conf</span> file <span style="font-style: italic;" class="mycode_i"><span style="color: #00369B;" class="mycode_color">(i.e uncomment)</span></span> to something different from 80 and 443 which are in use by the <span style="font-weight: bold;" class="mycode_b">Caddy</span> container. <br />
<br />
Before you uncomment these ports in the config I would start the <span style="font-weight: bold;" class="mycode_b">Snikket</span> containers, with out the reverse proxy running, that way the <span style="font-weight: bold;" class="mycode_b">Snikket</span> server can obtain its own lets encrypt certs without issue, the certs will renew without issue, with the newly altered ports in my case 5080 and 5443, from now on, <span style="color: #C10300;" class="mycode_color">no need to edit again</span>.<br />
<br />
Run these commands then edit the <span style="font-weight: bold;" class="mycode_b">.conf</span> file and uncomment the alternative ports. <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker-compose up snikket_proxy snikket_certs snikket_portal snikket_server<br />
<br />
ctrl + c (to stop the containers )<br />
<br />
nano /mnt/persist/mydocker/snikket/snikket.conf</code></div></div><br />
OK so your almost there, If you  have a backup of your <span style="font-weight: bold;" class="mycode_b">Homeassistant </span>install, now is the time to copy it to the homeassistant directory otherwise once started the home assistant container will be from scratch and will need to be setup from new. <br />
<br />
Finally !<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker-compose up -d</code></div></div><br />
<span style="font-style: italic;" class="mycode_i"><span style="color: #00369B;" class="mycode_color">This will start the containers in daemon mode (i.e in the background)</span></span><br />
<br />
If all is working you should see the <span style="font-weight: bold;" class="mycode_b">HomeAssistant</span> login screen, the <span style="font-weight: bold;" class="mycode_b">Radicale</span> login screen and the <span style="font-weight: bold;" class="mycode_b">Snikket</span> admin login screen at the addresses you specified with you DNS. Of course you will see all other service via their local network address/IP<br />
<br />
I would now create an admin invite for yourself to access the <span style="font-weight: bold;" class="mycode_b">Snikket</span> admin page <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker exec snikket create-invite --admin --group default</code></div></div><br />
this will generate a URL for your first admin password setup <br />
<br />
<span style="color: #C10300;" class="mycode_color">All services will need configuring/setup to suit your needs but all containers should be running and healthy, this can be checked in the Portainer UI.</span><br />
<br />
<span style="color: #2ECC40;" class="mycode_color"><span style="font-size: xx-large;" class="mycode_size">Congrats!!</span></span><br />
<br />
as a final note!<br />
<br />
<span style="font-size: large;" class="mycode_size"><span style="font-style: italic;" class="mycode_i"><span style="color: #00369B;" class="mycode_color">How to update SkiffOS,  if there has been a new release or if you have changed the config on your build / require additional packages </span></span></span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>cd Projects/SkiffOS<br />
<br />
git pull --recurse-submodules</code></div></div><br />
<span style="color: #E82A1F;" class="mycode_color"><span style="font-size: large;" class="mycode_size">Note! you may need to re-build fully if too much has changed</span></span><br />
<br />
<span style="color: #E82A1F;" class="mycode_color"><span style="font-size: large;" class="mycode_size">(it which case the <span style="font-weight: bold;" class="mycode_b">make clean</span> command should be used <span style="font-weight: bold;" class="mycode_b">before compiling</span>):</span></span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>make clean</code></div></div><br />
<span style="color: #0074D9;" class="mycode_color"><span style="color: #005DC2;" class="mycode_color">Then compile:</span></span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>make compile</code></div></div><br />
<span style="color: #005DC2;" class="mycode_color">Then push the updated image:</span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>./scripts/push_image.sh root@SKIFFOS_IP_ADDRESS</code></div></div><br />
<img src="https://forum.pine64.org/images/smilies/smile.png" alt="Smile" title="Smile" class="smilie smilie_1" />]]></description>
			<content:encoded><![CDATA[Hi All,<br />
<br />
So I've written another install tutorial this time using <span style="font-weight: bold;" class="mycode_b">SkiffOS</span>, the <a href="https://forum.pine64.org/showthread.php?tid=12806" target="_blank" rel="noopener" class="mycode_url">first one</a> hopefully helped a few users. I discovered SkiffOS on the Rockpro64 software release page however I was surprised to see literally no posts in the forum about it. I contacted the developer on Discord and with his endless help and patience , I have a working server running Docker and several containers.<br />
<br />
<span style="color: #008E02;" class="mycode_color"><span style="font-weight: bold;" class="mycode_b">UPDATE 19 JUNE 2022: After a lot of debugging and a lot of hard work on part <dvz_me_placeholder id="0" /> , the Rockpro64 is on the 5.18 kernel and great news , anyone who uses the Marvell 88SE9230 SATA card its now working perfectly out of the box with SkiffOS, the udev rule is no longer required as its included within the SkiffOS Rockpro64 config</span> </span><br />
<br />
<span style="font-weight: bold;" class="mycode_b"><span style="color: #008E02;" class="mycode_color">UPDATE:  <dvz_me_placeholder id="0" />  is currently working on providing pre-built images , for users who want to try SkiffOS without compiling their own build, a link will be attached when the images are available</span></span><br />
<br />
<br />
<img src="https://raw.githubusercontent.com/skiffos/SkiffOS/master/resources/images/skiff.png" loading="lazy"  alt="[Image: skiff.png]" class="mycode_img" /><br />
<br />
<br />
<a href="https://github.com/skiffos/skiffos" target="_blank" rel="noopener" class="mycode_url">SkiffOS Github page</a><br />
<br />
The Docker containers I have are <br />
<ul class="mycode_list"><li>a Simple <a href="https://github.com/ServerContainers/samba" target="_blank" rel="noopener" class="mycode_url">Samba</a> container for a NAS<br />
</li>
<li>a <a href="https://github.com/tomsquest/docker-radicale" target="_blank" rel="noopener" class="mycode_url">Radicale</a> container for caldav calendar and contacts sync<br />
</li>
<li>a <a href="https://www.home-assistant.io/installation/linux#install-home-assistant-container" target="_blank" rel="noopener" class="mycode_url">Home assistant</a> container for home automation <br />
</li>
<li>a <a href="https://hub.docker.com/r/esphome/esphome" target="_blank" rel="noopener" class="mycode_url">EspHome</a> container for managing my home automation Sonoff light switches<br />
</li>
<li>a <a href="https://jellyfin.org/docs/general/administration/installing.html#docker" target="_blank" rel="noopener" class="mycode_url">Jellyfin</a> container as a media server <br />
</li>
<li>a <a href="https://caddyserver.com/" target="_blank" rel="noopener" class="mycode_url">Caddy server </a>container for my reverse proxy and lets encrypt certs renewal <br />
</li>
<li>a <a href="https://snikket.org/service/quickstart/" target="_blank" rel="noopener" class="mycode_url">Snikket server </a>container for a xmpp server thats simple an easy to get setup <br />
</li>
<li>and finally a <a href="https://docs.portainer.io/v/ce-2.11/start/install/server/docker/linux#deployment" target="_blank" rel="noopener" class="mycode_url">Portainer</a> container to manage all my containers with a nice UI<br />
</li>
</ul>
<br />
now this sounds like a lot of containers for an arm board, but at idle which is most of the time, the Rockpro64 sits at <span style="font-weight: bold;" class="mycode_b">1% </span>CPU use. <br />
<br />
Now before I begin, SkiffOS describes itself as: <br />
<br />
<blockquote class="mycode_quote"><cite>Quote:</cite>SkiffOS is a lightweight operating system for any Linux-compatible computer, ranging from RPi, Odroid, NVIDIA Jetson, to Desktop PCs, Laptops (i.e. Apple MacBook), Phones (PinePhone), Containers, or Cloud VMs. It is:<br />
<ul class="mycode_list"><li>    Adoptable: any userspace can be imported/exported to/from container images.<br />
</li>
<li>    Familiar: uses simple Makefile and KConfig language for configuration.<br />
</li>
<li>    Flexible: supports all major OS distributions inside containers.<br />
</li>
<li>    Portable: containers can be moved between machines of similar CPU type.<br />
</li>
<li>    Reliable: changes inside user environments cannot break the host boot-up.<br />
</li>
<li>    Reproducible: a given Skiff Git tree will always produce identical output.<br />
</li>
</ul>
<br />
Uses Buildroot to produce a minimal "single-file" host OS as a standardized base cross-platform operating system "shim" for hosting containers. Most Linux platforms have widely varying requirements for kernel, firmware, and additional hardware support packages. The immutable SkiffOS host system contains everything needed to support the hardware, cleanly separated from the applications</blockquote>
<br />
The main thing to focus on, I think is that it is based on Buildroot, this is an immutable OS, so you have to compile the build on your daily machine(i.e laptop or desktop) and add any additional software packages and configuration options at build time. The benefits of an immutable system are best described above, but the main thing is "once setup" you can reproduce the build with ease and it will barely need any tinkering once its up an and running, plus it provides the security that should anything go wrong a quick reboot will restore the System back to its initial boot state.<br />
<br />
SkiffOS has a "persist" partition that stores all the files and folders that you want to<span style="font-weight: bold;" class="mycode_b"> persist</span> after a reboot. This can be where all the docker configuration files and states reside.<br />
<br />
SkiffOS also has the option for a <span style="font-weight: bold;" class="mycode_b">CORE </span>container this is a container that will provide an OS environment more familiar to most, making it easier to interact with the persist partition, you can choose between many core environments such as Alpine, Debian, Gentoo or even Ubuntu with a desktop environment. All this is best explained on the <a href="https://github.com/skiffos/skiffos" target="_blank" rel="noopener" class="mycode_url">Github</a> page, I however didn't need a core container as once setup the only interaction I need is with the containers themselves.<br />
<br />
So lets begin! to start with I would create a <span style="font-weight: bold;" class="mycode_b">Projects </span>Directory on you daily machine, preferably in you Home directory and then Git Clone the repo from Github into the projects directory.<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>mkdir Projects <br />
cd Projects/<br />
git clone https://github.com/skiffos/SkiffOS.git</code></div></div><br />
Now I created a  configuration directory that has all my docker container configs, files and scripts, I called it <span style="font-weight: bold;" class="mycode_b">my_docker_config </span>this allows me to export it as an option when compiling SkiffOS during build time but of course you could create these files and folders and copy and paste their contents one at a time to the persist partition after first boot, but this defeats the benefit of having a reproducible build. So a little work now, to create a config directory will really pay off.<br />
<br />
I have a <span style="font-weight: bold;" class="mycode_b">udev</span> rule that allows my marvell 88SE9230 Sata card to work and a fan script that starts the fan and keeps it at a constant speed, these are best explained in my other tutorial, however these are the types of things I would also want to have in my configuration directory. I also have a couple of systemd <span style="font-weight: bold;" class="mycode_b">.mount</span> files to mount my ssd's on startup similar to how an "fstab" file would work.<br />
<br />
This is how my <span style="font-weight: bold;" class="mycode_b">my_docker_config</span> directory tree looks <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>my_docker_config<br />
    └── root_overlay<br />
        ├── etc<br />
        │   ├── skiff<br />
        │   │   └── authorized_keys<br />
        │   │       └── my-key.pub<br />
        │   ├── systemd<br />
        │   │   └── system<br />
        │   │       ├── fan-speed.service<br />
        │   │       ├── mnt-ssd1.mount<br />
        │   │       ├── mnt-ssd2.mount<br />
        │   │       └── multi-user.target.wants<br />
        │   │           ├── fan-speed.service -&gt; ../fan-speed.service<br />
        │   │           ├── mnt-ssd1.mount -&gt; ../mnt-ssd1.mount<br />
        │   │           └── mnt-ssd2.mount -&gt; ../mnt-ssd2.mount<br />
        │   └── udev<br />
        │       └── rules.d<br />
        │           └── 99-marvell.rules<br />
        └── opt<br />
            ├── mydocker<br />
            │   ├── caddy<br />
            │   │   ├── caddy_data<br />
            │   │   └── Caddyfile<br />
            │   ├── docker-compose.yml<br />
            │   ├── esphome<br />
            │   │   └── config<br />
            │   ├── homeassistant<br />
            │   │   ├── automations.yaml<br />
            │   │   ├── configuration.yaml<br />
            │   │   ├── groups.yaml<br />
            │   │   └── www<br />
            │   │       ├── picone.PNG<br />
            │   │       └── pictwo.PNG<br />
            │   ├── jellyfin<br />
            │   ├── radicale<br />
            │   │   ├── config<br />
            │   │   │   └── config<br />
            │   │   ├── data<br />
            │   │   └── log<br />
            │   └── snikket<br />
            │       ├── acme_challenges<br />
            │       ├── snikket.conf<br />
            │       └── snikket_data<br />
            └── rockpro64_fan<br />
                └── fan_script.sh</code></div></div><br />
you can see I keep my docker configuration file in <span style="font-weight: bold;" class="mycode_b">mydocker </span>a fan script under "opt" and the rest of my config in "etc" this is all whats called a root overlay , all these file and directories will be overlaid upon boot and be placed in their respective folders in the live environment . You can also see I have <span style="font-weight: bold;" class="mycode_b">"authorized_keys"</span> under skiff this is where you can place your public ssh keys , so you have ssh access upon boot.<br />
<br />
<span style="color: #E82A1F;" class="mycode_color">my<span style="font-weight: bold;" class="mycode_b"> udev rule</span> named <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">99-marvell.rules</span></span> looks like this </span><br />
<span style="color: #E82A1F;" class="mycode_color"><span style="font-size: large;" class="mycode_size"><span style="font-weight: bold;" class="mycode_b">(This udev rule is no longer required it is included in SkiffOS config for the Rockpro64, its shown to inform only</span></span>):</span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1b4b", ATTR{device}=="0x9230", RUN+="/bin/bash -c 'echo %k &gt; /sys/bus/pci/drivers/ahci/bind'"</code></div></div><br />
My <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">fan_script.sh</span></span> looks like this: <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>#!/bin/bash<br />
<br />
rmmod pwm-fan<br />
echo 0 &gt; /sys/class/pwm/pwmchip1/export<br />
echo 110 &gt; /sys/class/pwm/pwmchip1/pwm0/duty_cycle<br />
echo 500 &gt; /sys/class/pwm/pwmchip1/pwm0/period<br />
echo 1 &gt; /sys/class/pwm/pwmchip1/pwm0/enable</code></div></div><br />
this will of course need a "fan speed service" to start from boot and a sym link to the service in the <span style="font-weight: bold;" class="mycode_b">multi-user.target.wants </span>directory as shown in the directory tree <br />
<br />
the service aptly named <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">fan-speed.service</span></span> looks like this :<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>[Unit]<br />
Description=change fan speed at startup <br />
After=basic.target<br />
<br />
[Service]<br />
Type=oneshot<br />
RemainAfterExit=yes<br />
ExecStart=/opt/rockpro64_fan/fan_script.sh<br />
<br />
[Install]<br />
WantedBy=multi-user.target</code></div></div><br />
one of my SSD mount files looks like this: <br />
<br />
<span style="color: #00369B;" class="mycode_color">(please note the name of the mount file must be the path to the mount point with each folder separated by a hyphen i.e <span style="font-weight: bold;" class="mycode_b">/mnt/mydrive/here</span> would be <span style="font-weight: bold;" class="mycode_b">mnt-mydrive-here.mount</span>)</span><br />
<br />
<span style="color: #00369B;" class="mycode_color">(also note that a sym link to the .mount file must also be in <span style="font-weight: bold;" class="mycode_b">multi-user.target.wants </span>directory) </span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>[Unit]<br />
Description=mounting Files from SSD<br />
<br />
[Mount]<br />
Where=/mnt/ssd1<br />
What=/dev/disk/by-label/storage1<br />
<br />
[Install]<br />
WantedBy=multi-user.target</code></div></div><br />
here is the basic <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">config</span></span> for Radicale :<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>[server]<br />
hosts = 0.0.0.0:5232<br />
<br />
max_connections = 15<br />
# 100 Megabyte<br />
max_content_length = 100000000<br />
# 30 seconds<br />
timeout = 20<br />
<br />
[auth]<br />
# Average delay after failed login attempts in seconds<br />
delay = 600<br />
<br />
type = htpasswd<br />
htpasswd_filename = /etc/radicale/users<br />
# encryption method used in the htpasswd file<br />
htpasswd_encryption = md5<br />
<br />
[storage]<br />
filesystem_folder = /data/collections</code></div></div><br />
and the <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">snikket.conf</span></span> file please edit with your email and domain name for snikket <span style="color: #C10300;" class="mycode_color">(please leave the ports commented out until later)</span> :<br />
<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code># The primary domain of your Snikket instance<br />
SNIKKET_DOMAIN=mysnikket.duckdns.org<br />
<br />
# An email address where the admin can be contacted<br />
# (also used to register your Let's Encrypt account to obtain certificates)<br />
SNIKKET_ADMIN_EMAIL=myemailaddress@mail.com<br />
<br />
#SNIKKET_TWEAK_HTTP_PORT=5080<br />
<br />
#SNIKKET_TWEAK_HTTPS_PORT=5443</code></div></div><br />
and the <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">Caddyfile</span></span> required for caddy<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>(log_common) {<br />
  log {<br />
    output file /var/log/caddy/{args.0}.access.log<br />
  }<br />
}<br />
<br />
myradicale.duckdns.org {<br />
&nbsp;&nbsp;&nbsp;&nbsp;handle_path /radicale* {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reverse_proxy localhost:5232 {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;header_up X-Script-Name /radicale<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;import log_common myradicales.duckdns.org<br />
}<br />
<br />
myhomeassistant.duckdns.org {<br />
&nbsp;&nbsp;&nbsp;&nbsp;reverse_proxy localhost:8123<br />
&nbsp;&nbsp;&nbsp;&nbsp;import log_common myhomeassistant.duckdns.org<br />
}<br />
<br />
<br />
<br />
<br />
http://mysnikket.duckdns.org,<br />
http://groups.mysnikket.duckdns.org,<br />
http://share.mysnikket.duckdns.org {<br />
&nbsp;&nbsp;&nbsp;&nbsp;reverse_proxy localhost:5080<br />
&nbsp;&nbsp;&nbsp;&nbsp;request_body {<br />
  &nbsp;&nbsp;&nbsp;&nbsp;max_size 20M<br />
  &nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
mysnikket.duckdns.org,<br />
groups.mysnikket.duckdns.org,<br />
share.mysnikket.duckdns.org {<br />
        tls /snikket/letsencrypt/live/mysnikket.duckdns.org/fullchain.pem /snikket/letsencrypt/live/mysnikket.duckdns.org/privkey.pem<br />
&nbsp;&nbsp;&nbsp;&nbsp;reverse_proxy https://localhost:5443 {<br />
&nbsp;&nbsp;&nbsp;&nbsp;    transport http {<br />
                tls_insecure_skip_verify<br />
            }<br />
        }<br />
        request_body {<br />
  &nbsp;&nbsp;&nbsp;&nbsp;max_size 20M<br />
  &nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</code></div></div><span style="color: #C10300;" class="mycode_color">(please edit the above with the domain names you have chosen for your services)</span> <br />
<br />
and finally my <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">docker-compose.yml</span></span> file for all my containers apart from Portainer which needs to access the docker daemon via a unix socket <br />
<br />
<span style="color: #C10300;" class="mycode_color">(please edit to suit your needs)</span>:<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>version: '3.7'<br />
services:<br />
<br />
  caddy:<br />
    depends_on:<br />
      - radicale<br />
      - homeassistant <br />
    image: caddy:latest<br />
    container_name: caddy_reverse_proxy<br />
    volumes:<br />
      - /mnt/persist/mydocker/caddy/caddy_data:/data<br />
      - /mnt/persist/mydocker/caddy/Caddyfile:/etc/caddy/Caddyfile<br />
      - /mnt/persist/mydocker/snikket/snikket_data:/snikket:ro <br />
      - /mnt/persist/mydocker/caddy/logs:/var/log/caddy/<br />
    environment:<br />
      - TZ=Europe/London  <br />
    restart: unless-stopped<br />
    network_mode: host<br />
<br />
  radicale:<br />
    image: tomsquest/docker-radicale<br />
    container_name: radicale<br />
    ports:<br />
      - 127.0.0.1:5232:5232<br />
    init: true<br />
    read_only: true<br />
    security_opt:<br />
      - no-new-privileges:true<br />
    cap_drop:<br />
      - ALL<br />
    cap_add:<br />
      - SETUID<br />
      - SETGID<br />
      - CHOWN<br />
      - KILL<br />
    healthcheck:<br />
      test: curl -f http://127.0.0.1:5232 || exit 1<br />
      interval: 30s<br />
      retries: 3<br />
    restart: unless-stopped<br />
    volumes:<br />
      - /mnt/persist/mydocker/radicale/data:/data<br />
      - /mnt/persist/mydocker/radicale/config:/config:ro<br />
      - /mnt/persist/mydocker/radicale/users:/etc/radicale/users<br />
      - /mnt/persist/mydocker/radicale/log:/var/log/radicale/log<br />
    environment:<br />
      - TZ=Europe/London <br />
<br />
  homeassistant:<br />
    container_name: home-assistant<br />
    image: homeassistant/home-assistant:stable<br />
    volumes:<br />
      - /mnt/persist/mydocker/homeassistant:/config<br />
    environment:<br />
      - TZ=Europe/London<br />
    restart: unless-stopped<br />
    network_mode: host<br />
<br />
  esphome:<br />
    image: esphome/esphome<br />
    volumes:<br />
      - /mnt/persist/mydocker/esphome/config:/config:rw<br />
    environment:<br />
      - TZ=Europe/London<br />
    network_mode: host<br />
    restart: unless-stopped<br />
    <br />
    <br />
  snikket_proxy:<br />
    container_name: snikket-proxy<br />
    image: snikket/snikket-web-proxy:beta<br />
    env_file: /mnt/persist/mydocker/snikket/snikket.conf<br />
    network_mode: host<br />
    volumes:<br />
      - /mnt/persist/mydocker/snikket/snikket_data:/snikket<br />
      - /mnt/persist/mydocker/snikket/acme_challenges:/var/www/html/.well-known/acme-challenge<br />
    restart: unless-stopped<br />
  snikket_certs:<br />
    container_name: snikket-certs<br />
    image: snikket/snikket-cert-manager:beta<br />
    env_file: /mnt/persist/mydocker/snikket/snikket.conf<br />
    volumes:<br />
      - /mnt/persist/mydocker/snikket/snikket_data:/snikket<br />
      - /mnt/persist/mydocker/snikket/acme_challenges:/var/www/.well-known/acme-challenge<br />
    restart: unless-stopped<br />
  snikket_portal:<br />
    container_name: snikket-portal<br />
    image: snikket/snikket-web-portal:beta<br />
    network_mode: host<br />
    env_file: /mnt/persist/mydocker/snikket/snikket.conf<br />
    restart: unless-stopped<br />
<br />
  snikket_server:<br />
    container_name: snikket<br />
    image: snikket/snikket-server:beta<br />
    network_mode: host<br />
    volumes:<br />
      - /mnt/persist/mydocker/snikket/snikket_data:/snikket<br />
    env_file: /mnt/persist/mydocker/snikket/snikket.conf<br />
    restart: unless-stopped<br />
    <br />
  samba:<br />
    build: .<br />
    image: servercontainers/samba<br />
    container_name: samba-server<br />
    restart: unless-stopped<br />
    network_mode: host<br />
    environment:<br />
      WSDD2_DISABLE: 1<br />
      AVAHI_DISABLE: 1<br />
      <br />
      ACCOUNT_user1: "user1:1000:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:DF56ET792A681E1A23E0F75695:[U          ]:LCT-4502WT5:"<br />
      UID_user1: 1020<br />
      GROUPS_user1: users<br />
<br />
      ACCOUNT_user2: "user2:1000:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:29395D100D389TEB9E295A747E420:[U          ]:LCT-521D03D5:"<br />
      UID_user2: 1021<br />
      GROUPS_user2: users<br />
      <br />
      GROUP_users: 100<br />
<br />
      SAMBA_VOLUME_CONFIG_Foldername: "[Foldername Share]; path=/shares/Foldername; valid users = user1, user2; guest ok = no; force group = users; read only = no; browseable = yes"<br />
<br />
      SAMBA_VOLUME_CONFIG_media: "[media Share]; path=/shares/media; valid users = user1, user2; guest ok = no; force group = users; read only = no; browseable = yes"<br />
    <br />
    volumes:<br />
      <br />
      - /mnt/ssd1/Foldername:/shares/Foldername<br />
      - /mnt/ssd2/media:/shares/media<br />
    <br />
  jellyfin:<br />
    image: linuxserver/jellyfin:latest<br />
    container_name: jellyfin<br />
    network_mode: host<br />
    volumes:<br />
      - /mnt/persist/mydocker/jellyfin/config:/config<br />
      - /mnt/persist/mydocker/jellyfin/cache:/cache<br />
      - /mnt/ssd2/media:/media<br />
    restart: unless-stopped<br />
    environment:<br />
      - TZ=Europe/London</code></div></div><br />
after you have your configuration directory you can move it to<span style="font-weight: bold;" class="mycode_b"> /Projects/SkiffOS/configs/apps/ </span>this will make it an exportable option in the next step.<br />
<br />
So on to the installation! <br />
<br />
Hopefully you still in the <span style="font-weight: bold;" class="mycode_b">Projects </span>directory with the freshly git cloned SkiffOS and your custom config directory moved to its place in the apps folder. As per the instruction on the SkiffOs github page you need to export all the options you want for the build, I have my board requirement,  docker compose (which also installs docker) and my custom config directory. <br />
<br />
this looks like this :<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>cd SkiffOS<br />
<br />
export SKIFF_CONFIG=pine64/rockpro64,apps/compose,apps/my_docker_config</code></div></div><br />
<span style="font-style: italic;" class="mycode_i">OK so I should mention that if you require any additional software such as "htop" installed then now is the time to select it this is done by typing</span> <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>make br/menuconfig</code></div></div><br />
<span style="color: #005DC2;" class="mycode_color"><span style="font-style: italic;" class="mycode_i">please note! all these commands will only work if you are in the the<span style="font-weight: bold;" class="mycode_b"> SkiffOS </span>directory located in the previously created <span style="font-weight: bold;" class="mycode_b">Projects</span> directory</span></span><br />
<br />
<span style="color: #444444;" class="mycode_color">T</span><span style="color: #419DC1;" class="mycode_color"><span style="color: #444444;" class="mycode_color">his make command will create an ncurses menu that will allow you to select additional software, system configs kernel options .etc</span></span><br />
<br />
The <span style="font-weight: bold;" class="mycode_b">target packages</span> option will allow you to select additional packages such as htop. <br />
<br />
The <span style="color: #C10300;" class="mycode_color"><span style="font-weight: bold;" class="mycode_b">/</span></span> key will allow you to search all the options and <span style="color: #C10300;" class="mycode_color"><span style="font-weight: bold;" class="mycode_b">space</span></span> will  allow you to select the option current highlighted . <span style="color: #C10300;" class="mycode_color">(remember to save before you exit the ncurses menu)</span><br />
<br />
Now to finish up the build process. <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>make configure<br />
<br />
make compile</code></div></div><br />
This last command will begin the compiling process, this can take some time depending on your hardware, so now is the time to get a coffee!<br />
<br />
<span style="font-style: italic;" class="mycode_i"><span style="color: #005DC2;" class="mycode_color">(please note that when you recompile,  for example after an update to SkiffOS this will be alot shorter timespan, as a previously compiled image will exist and only updated options and packages will be compiled )</span></span><br />
<br />
To copy the compiled image to an SD card or emmc <span style="color: #C10300;" class="mycode_color">(although I currently haven't tested if emmc works)</span><br />
<br />
You will need to be root to perform these next commands<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>sudo bash <br />
<br />
blkid  (this is to see  where the SD is located )<br />
<br />
export PINE64_SD=/dev/mmcblk0  ( please edit this last part to suit where you SD is located)<br />
<br />
make cmd/pine64/common/format  ( this formats the card , if you get an error with this command and your using Ubuntu please see note below)<br />
<br />
make cmd/pine64/common/install ( this will copy the build to the sd card)</code></div></div><br />
<span style="color: #00369B;" class="mycode_color"><span style="font-style: italic;" class="mycode_i">(Additional note whilst using Arch I had no issue with the format command above, however after switching to a Ubuntu based OS I noticed it failed with an error, the solution was to edit  he formatting script and add a  <span style="font-weight: bold;" class="mycode_b">P </span>to every partition  and the fatlabel )</span></span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>nano ~/Projects/SkiffOS/configs/pine64/common/scripts/format_sd.sh</code></div></div><br />
see the addition of the <span style="color: #C10300;" class="mycode_color"><span style="font-weight: bold;" class="mycode_b">p</span></span> after every curly bracket <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>echo "Formatting boot partition..."<br />
mkfs.vfat -F 32 &#36;{PINE64_SD_SFX}p1<br />
fatlabel &#36;{PINE64_SD_SFX}p1 boot<br />
<br />
echo "Formatting rootfs partition..."<br />
&#36;MKEXT4 -L "rootfs" &#36;{PINE64_SD_SFX}p2<br />
<br />
echo "Formatting persist partition..."<br />
&#36;MKEXT4 -L "persist" &#36;{PINE64_SD_SFX}p3</code></div></div><br />
now your ready to boot, but before you do I would create the <span style="font-weight: bold;" class="mycode_b"><span style="color: #008E02;" class="mycode_color">Duckdns</span></span> or other <span style="font-weight: bold;" class="mycode_b"><span style="color: #008E02;" class="mycode_color">DDNS</span></span> provider entries for the services you will expose to the internet <span style="font-style: italic;" class="mycode_i"><span style="color: #00369B;" class="mycode_color">(Please see my other tutorial for assistance)</span></span>, in my case <span style="font-weight: bold;" class="mycode_b">Home assistant</span> , <span style="font-weight: bold;" class="mycode_b">Radicale</span> and the <span style="font-weight: bold;" class="mycode_b">Snikket XMPP server,</span> so <span style="font-weight: bold;" class="mycode_b">3</span> entries in total.<br />
<br />
<span style="color: #C10300;" class="mycode_color"><span style="font-style: italic;" class="mycode_i">I will point out for completeness sake that if your following along with this tutorial and you are wanting to trial Snikket as an XMPP server it is not recommended to use a Dynamic Domain Name Service, preferably you will require a static IP address from your internet provider and you should use the recommended DNS providers suggested on the Snikket website . I have a static IP but I am using Duckdns as a free DNS which works perfectly for my use case.</span></span><br />
<br />
When you boot for the first time it may take a few minutes to provide SSH, if it doesnt provide SSH after 15 minutes,  the SSH keys provided in the config file are either incorrect or you have an issue and the SD has failed to boot. <span style="color: #00369B;" class="mycode_color"><span style="font-style: italic;" class="mycode_i">(Connecting a serial console could help you resolve your issue see <a href="https://forum.pine64.org/showthread.php?tid=6387" target="_blank" rel="noopener" class="mycode_url">here</a> )</span></span><br />
<br />
After you have successfully booted move your <span style="text-decoration: underline;" class="mycode_u"><span style="font-weight: bold;" class="mycode_b">mydocker</span></span>  config directory to the persist partition <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>mv /opt/mydocker /mnt/persist/</code></div></div><br />
now to get <span style="font-weight: bold;" class="mycode_b">Portainer </span>up and running <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker run -d -p 9443:9443 --name portainer &#92;<br />
    --restart=always &#92;<br />
    -v /var/run/docker.sock:/var/run/docker.sock &#92;<br />
    -v portainer_data:/data &#92;<br />
    cr.portainer.io/portainer/portainer-ce:latest</code></div></div><br />
You will need to setup <span style="font-weight: bold;" class="mycode_b">Portainer</span> to your requirements <span style="color: #00369B;" class="mycode_color"><span style="font-style: italic;" class="mycode_i">(This docker command will only get it installed and running)</span></span><br />
<br />
Next pull all the container images for your services <span style="font-style: italic;" class="mycode_i"><span style="color: #00369B;" class="mycode_color">(This command will only work if your in the directory where your docker-compose file is located)</span></span><br />
<br />
in my case:<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>cd /mnt/persist/mydocker/<br />
<br />
docker-compose pull ( this will pull all images and can take some time )</code></div></div><br />
So If your following along and are using the <span style="font-weight: bold;" class="mycode_b">Radicale</span> container, you will need to run a "one time" command to create a user password using htpasswd this can be done using a<span style="font-weight: bold;" class="mycode_b"> "temporary container"</span>,  the container is removed after the command completes.<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker run -v /mnt/persist/mydocker/radicale:/radicale --rm -it alpine:edge sh -c "apk add apache2-utils &amp;&amp; htpasswd -c /radicale/users calendar"<br />
<br />
(this creates user "calendar" password)( the --rm remove the container after completion )</code></div></div><br />
next if your using the <span style="font-weight: bold;" class="mycode_b">samba</span> container for a NAS and your would prefer to use password hashes rather than a plain text password, then run this docker command for each user and fill in the requested details. <br />
<br />
This will print to screen a hash for the specified user which needs to be added to the <span style="font-weight: bold;" class="mycode_b">docker-compose.yml</span> file as shown above where it says <span style="font-weight: bold;" class="mycode_b">ACCOUNT_username</span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker run -ti --rm --entrypoint create-hash.sh servercontainers/samba</code></div></div><br />
Now is the time to open the required ports on your router, <span style="font-weight: bold;" class="mycode_b">Caddy</span> only requires 80 and 443 for the reverse proxy service however <span style="font-weight: bold;" class="mycode_b">Snikket</span> requires quite a few port as shown <a href="https://github.com/snikket-im/snikket-server/blob/master/docs/advanced/firewall.md" target="_blank" rel="noopener" class="mycode_url">here</a><br />
<br />
<span style="font-weight: bold;" class="mycode_b">Snikket </span>requires a configuration file as shown above, however to use with a reverse proxy<span style="color: #00369B;" class="mycode_color"> <span style="font-style: italic;" class="mycode_i">(Because <span style="font-weight: bold;" class="mycode_b">Snikket </span>gets its own lets encrypt certificates)</span></span> you will need to change the ports in the <span style="font-weight: bold;" class="mycode_b">.conf</span> file <span style="font-style: italic;" class="mycode_i"><span style="color: #00369B;" class="mycode_color">(i.e uncomment)</span></span> to something different from 80 and 443 which are in use by the <span style="font-weight: bold;" class="mycode_b">Caddy</span> container. <br />
<br />
Before you uncomment these ports in the config I would start the <span style="font-weight: bold;" class="mycode_b">Snikket</span> containers, with out the reverse proxy running, that way the <span style="font-weight: bold;" class="mycode_b">Snikket</span> server can obtain its own lets encrypt certs without issue, the certs will renew without issue, with the newly altered ports in my case 5080 and 5443, from now on, <span style="color: #C10300;" class="mycode_color">no need to edit again</span>.<br />
<br />
Run these commands then edit the <span style="font-weight: bold;" class="mycode_b">.conf</span> file and uncomment the alternative ports. <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker-compose up snikket_proxy snikket_certs snikket_portal snikket_server<br />
<br />
ctrl + c (to stop the containers )<br />
<br />
nano /mnt/persist/mydocker/snikket/snikket.conf</code></div></div><br />
OK so your almost there, If you  have a backup of your <span style="font-weight: bold;" class="mycode_b">Homeassistant </span>install, now is the time to copy it to the homeassistant directory otherwise once started the home assistant container will be from scratch and will need to be setup from new. <br />
<br />
Finally !<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker-compose up -d</code></div></div><br />
<span style="font-style: italic;" class="mycode_i"><span style="color: #00369B;" class="mycode_color">This will start the containers in daemon mode (i.e in the background)</span></span><br />
<br />
If all is working you should see the <span style="font-weight: bold;" class="mycode_b">HomeAssistant</span> login screen, the <span style="font-weight: bold;" class="mycode_b">Radicale</span> login screen and the <span style="font-weight: bold;" class="mycode_b">Snikket</span> admin login screen at the addresses you specified with you DNS. Of course you will see all other service via their local network address/IP<br />
<br />
I would now create an admin invite for yourself to access the <span style="font-weight: bold;" class="mycode_b">Snikket</span> admin page <br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>docker exec snikket create-invite --admin --group default</code></div></div><br />
this will generate a URL for your first admin password setup <br />
<br />
<span style="color: #C10300;" class="mycode_color">All services will need configuring/setup to suit your needs but all containers should be running and healthy, this can be checked in the Portainer UI.</span><br />
<br />
<span style="color: #2ECC40;" class="mycode_color"><span style="font-size: xx-large;" class="mycode_size">Congrats!!</span></span><br />
<br />
as a final note!<br />
<br />
<span style="font-size: large;" class="mycode_size"><span style="font-style: italic;" class="mycode_i"><span style="color: #00369B;" class="mycode_color">How to update SkiffOS,  if there has been a new release or if you have changed the config on your build / require additional packages </span></span></span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>cd Projects/SkiffOS<br />
<br />
git pull --recurse-submodules</code></div></div><br />
<span style="color: #E82A1F;" class="mycode_color"><span style="font-size: large;" class="mycode_size">Note! you may need to re-build fully if too much has changed</span></span><br />
<br />
<span style="color: #E82A1F;" class="mycode_color"><span style="font-size: large;" class="mycode_size">(it which case the <span style="font-weight: bold;" class="mycode_b">make clean</span> command should be used <span style="font-weight: bold;" class="mycode_b">before compiling</span>):</span></span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>make clean</code></div></div><br />
<span style="color: #0074D9;" class="mycode_color"><span style="color: #005DC2;" class="mycode_color">Then compile:</span></span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>make compile</code></div></div><br />
<span style="color: #005DC2;" class="mycode_color">Then push the updated image:</span><br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>./scripts/push_image.sh root@SKIFFOS_IP_ADDRESS</code></div></div><br />
<img src="https://forum.pine64.org/images/smilies/smile.png" alt="Smile" title="Smile" class="smilie smilie_1" />]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[3D Printed 2 SSD Vertical NAS Case]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=15538</link>
			<pubDate>Sun, 12 Dec 2021 22:52:47 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=23733">tenox</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=15538</guid>
			<description><![CDATA[Made this vertical 2 SSD nas case that houses stock PCIe SATA card and 2 SSDs.<br />
<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPEG Image" border="0" alt=".jpeg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2539" target="_blank" title="">PINENAS.jpeg</a> (Size: 320.79 KB / Downloads: 735)
<!-- end: postbit_attachments_attachment --><br />
<br />
<br />
<a href="https://www.thingiverse.com/thing:5162344" target="_blank" rel="noopener" class="mycode_url">https://www.thingiverse.com/thing:5162344</a><br />
<br />
Still working on the top lid]]></description>
			<content:encoded><![CDATA[Made this vertical 2 SSD nas case that houses stock PCIe SATA card and 2 SSDs.<br />
<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPEG Image" border="0" alt=".jpeg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2539" target="_blank" title="">PINENAS.jpeg</a> (Size: 320.79 KB / Downloads: 735)
<!-- end: postbit_attachments_attachment --><br />
<br />
<br />
<a href="https://www.thingiverse.com/thing:5162344" target="_blank" rel="noopener" class="mycode_url">https://www.thingiverse.com/thing:5162344</a><br />
<br />
Still working on the top lid]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[RockPro64 programing GPIO by Rust]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=15407</link>
			<pubDate>Thu, 25 Nov 2021 02:43:54 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=8848">yanagawa3</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=15407</guid>
			<description><![CDATA[Hello,<br />
For the Rust developer, I show the gpio led blinking program code for RockPro64.<br />
You can use GPIO pin number or BCM number.<br />
<br />
In project<br />
cargo build --release<br />
sudo target/release/gpio<br />
<br />
LED connected Pin 16 blinks.<br />
<br />
cargo.toml<br />
<br />
------------------------------------<br />
[dependencies]<br />
sysfs_gpio = "0.6"<br />
<br />
------------------------------------<br />
main.rs<br />
-----------------------------------------------------------------------------------<br />
use sysfs_gpio::{Direction, Pin};<br />
use std::thread:<img src="https://forum.pine64.org/images/smilies/confused.png" alt="Confused" title="Confused" class="smilie smilie_13" />leep;<br />
use std::time:<img src="https://forum.pine64.org/images/smilies/biggrin.png" alt="Big Grin" title="Big Grin" class="smilie smilie_4" />uration;<br />
<br />
// Define GPIO arrays<br />
static BOARD_TO_ROCK: &amp;'static [u8] = &amp;[0, 0, 0, 52, 0, 53, 0, 152, 148, 0, 147, 54, 120, 50, 0, 33, 36, 0, 149, 40, 0, 39, 153, 41, 42, 0, 45, 43, 44, 155, 0, 156, 124, 125, 0, 122, 126, 121, 123, 0, 127];<br />
static ROCK_VALID_CHANNELS: &amp;'static [u8] = &amp;[52, 53, 152, 54, 50, 33, 40, 39, 41, 43, 155, 156, 125, 122, 121, 148, 147, 120, 36, 149, 153, 42, 45, 44, 124, 126, 123, 127];<br />
static BCM_TO_ROCK: &amp;'static [u8] = &amp;[43, 44, 52, 53, 152, 155, 156, 45, 42, 39, 40, 41, 124, 125, 148, 147, 124, 54, 120, 122, 123, 127, 33, 36, 149, 153, 121, 50];<br />
<br />
fn main() {<br />
    let my_pin_num = 16;//gpio number: 36<br />
    let my_led = Pin::new(BOARD_TO_ROCK[my_pin_num].into()); // Pin 16 GPIO1_A4 gpio number 36 depends on chip, etc.<br />
    //let my_bcm_num = 23;//gpio number: 36<br />
    //let my_led = Pin::new(BCM_TO_ROCK[my_bcm_num].into()); // Pin 16 GPIO1_A4 gpio number 36 depends on chip, etc.<br />
    my_led.with_exported(|| {<br />
        my_led.set_direction(Direction::Out).unwrap();<br />
        loop {<br />
            my_led.set_value(0).unwrap();<br />
            sleep(Duration::from_millis(100));<br />
            my_led.set_value(1).unwrap();<br />
            sleep(Duration::from_millis(100));<br />
            println!("Blinking GPIO1_A4(36) Pin 16");<br />
        }<br />
    }).unwrap();<br />
}<br />
<br />
-------------------------------------------------------------------------------------------------]]></description>
			<content:encoded><![CDATA[Hello,<br />
For the Rust developer, I show the gpio led blinking program code for RockPro64.<br />
You can use GPIO pin number or BCM number.<br />
<br />
In project<br />
cargo build --release<br />
sudo target/release/gpio<br />
<br />
LED connected Pin 16 blinks.<br />
<br />
cargo.toml<br />
<br />
------------------------------------<br />
[dependencies]<br />
sysfs_gpio = "0.6"<br />
<br />
------------------------------------<br />
main.rs<br />
-----------------------------------------------------------------------------------<br />
use sysfs_gpio::{Direction, Pin};<br />
use std::thread:<img src="https://forum.pine64.org/images/smilies/confused.png" alt="Confused" title="Confused" class="smilie smilie_13" />leep;<br />
use std::time:<img src="https://forum.pine64.org/images/smilies/biggrin.png" alt="Big Grin" title="Big Grin" class="smilie smilie_4" />uration;<br />
<br />
// Define GPIO arrays<br />
static BOARD_TO_ROCK: &amp;'static [u8] = &amp;[0, 0, 0, 52, 0, 53, 0, 152, 148, 0, 147, 54, 120, 50, 0, 33, 36, 0, 149, 40, 0, 39, 153, 41, 42, 0, 45, 43, 44, 155, 0, 156, 124, 125, 0, 122, 126, 121, 123, 0, 127];<br />
static ROCK_VALID_CHANNELS: &amp;'static [u8] = &amp;[52, 53, 152, 54, 50, 33, 40, 39, 41, 43, 155, 156, 125, 122, 121, 148, 147, 120, 36, 149, 153, 42, 45, 44, 124, 126, 123, 127];<br />
static BCM_TO_ROCK: &amp;'static [u8] = &amp;[43, 44, 52, 53, 152, 155, 156, 45, 42, 39, 40, 41, 124, 125, 148, 147, 124, 54, 120, 122, 123, 127, 33, 36, 149, 153, 121, 50];<br />
<br />
fn main() {<br />
    let my_pin_num = 16;//gpio number: 36<br />
    let my_led = Pin::new(BOARD_TO_ROCK[my_pin_num].into()); // Pin 16 GPIO1_A4 gpio number 36 depends on chip, etc.<br />
    //let my_bcm_num = 23;//gpio number: 36<br />
    //let my_led = Pin::new(BCM_TO_ROCK[my_bcm_num].into()); // Pin 16 GPIO1_A4 gpio number 36 depends on chip, etc.<br />
    my_led.with_exported(|| {<br />
        my_led.set_direction(Direction::Out).unwrap();<br />
        loop {<br />
            my_led.set_value(0).unwrap();<br />
            sleep(Duration::from_millis(100));<br />
            my_led.set_value(1).unwrap();<br />
            sleep(Duration::from_millis(100));<br />
            println!("Blinking GPIO1_A4(36) Pin 16");<br />
        }<br />
    }).unwrap();<br />
}<br />
<br />
-------------------------------------------------------------------------------------------------]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[[Turorial] NAS Using A Recycled 1U Proliant Case]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=14035</link>
			<pubDate>Sun, 30 May 2021 23:54:09 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=22076">ColPanic_</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=14035</guid>
			<description><![CDATA[So you've just gotten your brand new Rockpro64, you're looking for a clean way to stuff a PSU and 4 HDD backplane into a box that fits nicely onto your network rack. What do you do? Well, you could get the official NAS case. But that only fits 2 disks, and how are you supposed to mount that on your rack? With a shelf?? Inconceivable.<br />
<br />
Well here's what I did:<br />
<br />
Having bought my home network equipment off eBay, I know retired enterprise gear can be had for dirt cheap. So after some quick searching, I came across the perfect 1U case, that happened to also have a Proliant DL160 server inside it. Perfect for a weekend project.<br />
<br />
Steps to NAS:<br />
<br />
1. Gut the server, leaving only the PSU, backplane, and fans.<br />
<br />
2. If the original SAS controller works, great! If not, find a Rockpro64 compatible PCIe SAS controller. I used an IBM H1110.<br />
<br />
3. An ATX breakout board is handy for connecting to the case's power supply. But if you prefer crimping wires, you do you. As a side note, I'm using the ATX supply to directly control power to the Rockpro. The Rockpro actually has some solder points for power control, near the power buttons, which could be used to improve on my design. I'll be adding that mod myself whenever I get around to it.<br />
<br />
4. You'll need an Arduino as well, I like using Nano's. They're super cheap and fit nicely into just about any project.<br />
<br />
5. Shove it all into the case. It should look something like this:<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2363" target="_blank" title="">Inside-2.jpg</a> (Size: 34.2 KB / Downloads: 749)
<!-- end: postbit_attachments_attachment --><br />
<br />
6. You'll need to wire all those pieces together. I'm driving the LEDs directly from the Arduino GPIO, because <span style="text-decoration: line-through;" class="mycode_s">I'm lazy </span>I like to live dangerously. If you're more concerned about damaging your AVR than I am, you might want to buffer each of those LED lines with a transistor. I decided to wire the fancy buttons and blinkenlights on the front panel up to the Arduino for my NAS. You can put together a wiring harness to mate with the case connector, or be like me and shove your wires directly into the case connector. They're not going anywhere:<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2362" target="_blank" title="">DL160_Case.jpg</a> (Size: 72.19 KB / Downloads: 644)
<!-- end: postbit_attachments_attachment --><br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2364" target="_blank" title="">DL160-Harness.jpg</a> (Size: 21.06 KB / Downloads: 611)
<!-- end: postbit_attachments_attachment --><br />
<br />
7. The front panel has a 2 row, 2.5mm pitch female connector for the USB connections. Hack a connector onto couple USB cables. You can then use the hacked cables as an adapter between the case USB connector and the Rockpro USB inputs. My DL160 case only supports USB 2.0, so I can't take full advantage of the Rockpro's USB interface:<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2365" target="_blank" title="">USB-Harness.jpg</a> (Size: 15.87 KB / Downloads: 642)
<!-- end: postbit_attachments_attachment --><br />
<br />
8. If you don't like your NAS to sound like a jet plane taking off, take out some fans. I've left just one fan. It's keeping everything nice and cool, and runs almost silently. To maintain proper airflow, stuff some packing foam in place of the old fans:<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2366" target="_blank" title="">Fans.jpg</a> (Size: 28.55 KB / Downloads: 655)
<!-- end: postbit_attachments_attachment --><br />
<br />
9. You'll need to get some code onto the Arduino. This script handles power up/shutdown; fan control; and, just to look like a real enterprise grade server, blinking some lights:<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>#include &lt;Arduino.h&gt;<br />
#include &lt;elapsedMillis.h&gt;<br />
<br />
#define PIN_UART_RX     1<br />
#define PIN_PSU_ON      2<br />
#define PIN_PANEL_PWR   3<br />
#define PIN_SYS_LED_P1  4<br />
#define PIN_NIC_LED_P   5<br />
#define PIN_HDD_LED_P   6<br />
#define PIN_BOARD_PWR   7<br />
#define PIN_FAN_PWM     9<br />
#define PIN_PWR_LED_GP_RN   12<br />
#define PIN_PWR_LED_GN_RP   13<br />
#define PIN_SYS_LED_G_N     19<br />
#define PIN_SYS_LED_R_N     20<br />
#define PIN_SYS_LED_P2      21<br />
<br />
#define PWR_STATE_OFF           0   //PSU powered off<br />
#define PWR_STATE_STARTUP       1   //PSU starting up, power button is still held<br />
#define PWR_STATE_ON            2   //PSU powered up, button no longer held<br />
#define PWR_STATE_SHUTDOWN_TMR  3   //PSU powered up, shutdown timer is counting<br />
#define PWR_STATE_SHUTDOWN      4   //PSU shut down, button is still held<br />
#define PWR_STATE_BOARD_OFF     5   //board shut down, PSU still on<br />
<br />
#define BLINK_STATE_READY   0   //LED is not blinking<br />
#define BLINK_STATE_ON      1   //LED is off<br />
#define BLINK_STATE_OFF     2   //LED is on<br />
<br />
#define UART_STATE_OK       0<br />
#define UART_STATE_ERR      1<br />
<br />
#define UART_WATCHDOG_TIMEOUT   10000<br />
<br />
uint8_t power_state = PWR_STATE_OFF;<br />
_Bool uart_state = UART_STATE_OK;<br />
<br />
int blink_chance = 0;<br />
<br />
elapsedMillis event_timer;<br />
elapsedMillis uart_watchdog;<br />
elapsedMillis blink_timer;<br />
<br />
void setup() {<br />
<br />
    // Configure PWM on pin 9 (and 10) @ 25 kHz.<br />
    TCCR1A = 0;<br />
    TCCR1B = 0;<br />
    TCNT1  = 0;<br />
    TCCR1A = _BV(COM1A1)<br />
           | _BV(COM1B1)<br />
           | _BV(WGM11);<br />
    TCCR1B = _BV(WGM13)<br />
           | _BV(CS10);<br />
    ICR1   = 320;<br />
<br />
    pinMode(PIN_UART_RX, INPUT);<br />
    pinMode(PIN_PSU_ON, INPUT);<br />
    pinMode(PIN_PANEL_PWR, INPUT_PULLUP);<br />
    pinMode(PIN_SYS_LED_P1, OUTPUT);<br />
    pinMode(PIN_NIC_LED_P, OUTPUT);<br />
    pinMode(PIN_HDD_LED_P, OUTPUT);<br />
    pinMode(PIN_BOARD_PWR, INPUT);<br />
    pinMode(PIN_FAN_PWM, OUTPUT);<br />
    pinMode(PIN_PWR_LED_GP_RN, OUTPUT);<br />
    pinMode(PIN_PWR_LED_GN_RP, OUTPUT);<br />
    pinMode(PIN_SYS_LED_G_N, OUTPUT);<br />
    pinMode(PIN_SYS_LED_R_N, OUTPUT);<br />
    pinMode(PIN_SYS_LED_P2, OUTPUT);<br />
<br />
    digitalWrite(PIN_SYS_LED_P1, HIGH);<br />
    digitalWrite(PIN_NIC_LED_P, LOW);<br />
    digitalWrite(PIN_HDD_LED_P, LOW);<br />
    analogWrite(PIN_FAN_PWM, 0);<br />
    digitalWrite(PIN_PWR_LED_GP_RN, LOW);<br />
    digitalWrite(PIN_PWR_LED_GN_RP, LOW);<br />
    digitalWrite(PIN_SYS_LED_G_N, LOW);<br />
    digitalWrite(PIN_SYS_LED_R_N, HIGH);<br />
    digitalWrite(PIN_SYS_LED_P2, HIGH);<br />
<br />
    Serial.setTimeout(50);<br />
}<br />
<br />
void loop() {<br />
    <br />
/*********************************************<br />
 * handle system power<br />
 *********************************************/<br />
    switch(power_state)<br />
    {<br />
<br />
        case PWR_STATE_OFF:<br />
        {<br />
            if( (digitalRead(PIN_PANEL_PWR) == LOW) || (PIN_BOARD_PWR == HIGH) )<br />
            {<br />
                power_state = PWR_STATE_STARTUP;<br />
                pinMode(PIN_PSU_ON, OUTPUT);<br />
                digitalWrite(PIN_PSU_ON, LOW);<br />
<br />
                //turn on the GREEN power LED<br />
                pinMode(PIN_PWR_LED_GP_RN, OUTPUT);<br />
                pinMode(PIN_PWR_LED_GN_RP, OUTPUT);<br />
                digitalWrite(PIN_PWR_LED_GN_RP, LOW);<br />
                digitalWrite(PIN_PWR_LED_GP_RN, HIGH);<br />
<br />
                event_timer = 0;<br />
            }<br />
        } break;<br />
        case PWR_STATE_STARTUP:<br />
        {<br />
            //after button is released, wait 5 seconds for startup<br />
            if( (digitalRead(PIN_PANEL_PWR) == HIGH) &amp;&amp; (event_timer &gt; 5000) )<br />
            {<br />
                power_state = PWR_STATE_ON;<br />
            }<br />
        } break;<br />
        case PWR_STATE_ON:<br />
        {<br />
            if(digitalRead(PIN_PANEL_PWR) == LOW)<br />
            {<br />
                event_timer = 0;<br />
                power_state = PWR_STATE_SHUTDOWN_TMR;<br />
            }<br />
            else if(digitalRead(PIN_BOARD_PWR) == LOW)<br />
            {<br />
                event_timer = 0;<br />
                power_state = PWR_STATE_BOARD_OFF;<br />
            }<br />
        } break;<br />
        case PWR_STATE_SHUTDOWN_TMR:<br />
        {<br />
            if(digitalRead(PIN_PANEL_PWR) == LOW)<br />
            {<br />
                //button was held for 3 seconds, shut down<br />
                if( event_timer &gt; 3000 )<br />
                {<br />
                    //tri-state the power pin<br />
                    power_state = PWR_STATE_SHUTDOWN;<br />
                    pinMode(PIN_PSU_ON, INPUT);<br />
<br />
                    //turn on the RED power LED<br />
                    digitalWrite(PIN_PWR_LED_GP_RN, LOW);<br />
                    digitalWrite(PIN_PWR_LED_GN_RP, HIGH);<br />
<br />
                    //disconnect UART<br />
                    Serial.end();<br />
                    pinMode(PIN_UART_RX, INPUT);<br />
                }<br />
            }<br />
            else<br />
            {<br />
                power_state = PWR_STATE_ON;<br />
            }<br />
        } break;<br />
        case PWR_STATE_SHUTDOWN:<br />
        {<br />
            //wait until button is released<br />
            if(digitalRead(PIN_PANEL_PWR) == HIGH)<br />
            {<br />
                power_state = PWR_STATE_OFF;<br />
            }<br />
        } break;<br />
        case PWR_STATE_BOARD_OFF:<br />
        {<br />
            if(digitalRead(PIN_BOARD_PWR) == LOW)<br />
            {<br />
                //board was off for 3 seconds, shut down<br />
                if( event_timer &gt; 3000 )<br />
                {<br />
                    //tri-state the power pin<br />
                    power_state = PWR_STATE_SHUTDOWN;<br />
                    pinMode(PIN_PSU_ON, INPUT);<br />
<br />
                    //turn on the RED power LED<br />
                    digitalWrite(PIN_PWR_LED_GP_RN, LOW);<br />
                    digitalWrite(PIN_PWR_LED_GN_RP, HIGH);<br />
<br />
                    //disconnect UART<br />
                    Serial.end();<br />
                    pinMode(PIN_UART_RX, INPUT);<br />
                }<br />
            }<br />
            else<br />
            {<br />
                power_state = PWR_STATE_ON;<br />
            }<br />
        } break;<br />
        default:<br />
            break;<br />
    }<br />
<br />
/*********************************************<br />
 * blink Eth0 LED<br />
 *********************************************/<br />
    // 100 megabit blinks fastest<br />
    #define BLINK_MAX_CHANCE    100000000<br />
    #define BLINK_MILLIS        30<br />
<br />
    static uint8_t blink_state = BLINK_STATE_READY;<br />
<br />
    if( (power_state == PWR_STATE_ON) &amp;&amp;<br />
        (uart_state == UART_STATE_OK) )<br />
    {<br />
        switch(blink_state)<br />
        {<br />
            case BLINK_STATE_READY:<br />
            {<br />
                // blink the eth0 LED randomly, with a frequency based<br />
                // on the network traffic<br />
                if( (blink_chance != 0) &amp;&amp;<br />
                    ( (random(0,BLINK_MAX_CHANCE) &lt; blink_chance) || <br />
                    (random(0,2000) == 0) ) <br />
                    )<br />
                {<br />
                    blink_state = BLINK_STATE_ON;<br />
                    blink_timer = 0;<br />
                    digitalWrite(PIN_NIC_LED_P, HIGH);<br />
                }<br />
            } break;<br />
            case BLINK_STATE_ON:<br />
            {<br />
                if(blink_timer &gt; BLINK_MILLIS)<br />
                {<br />
                    blink_state = BLINK_STATE_OFF;<br />
                    blink_timer = 0;<br />
                    digitalWrite(PIN_NIC_LED_P, LOW);<br />
                }<br />
            } break;<br />
            case BLINK_STATE_OFF:<br />
            {<br />
                if(blink_timer &gt; (BLINK_MILLIS/2))<br />
                {<br />
                    blink_state = BLINK_STATE_READY;<br />
                }<br />
            } break;<br />
            default:<br />
            {<br />
                    blink_state = BLINK_STATE_READY;<br />
                    blink_timer = 0;<br />
                    digitalWrite(PIN_NIC_LED_P, LOW);<br />
            }<br />
        }<br />
    }<br />
<br />
    #undef BLINK_MAX_CHANCE <br />
    #undef BLINK_TICKS         <br />
<br />
/*********************************************<br />
 * handle UART commands<br />
 *********************************************/<br />
    #define UART_IDENTIFIER_IDX  0<br />
    #define UART_CMD_IDX         9<br />
<br />
    /* enable/reset UART */<br />
<br />
    // keep UART off until board is powered up<br />
    if(power_state != PWR_STATE_ON)<br />
    {<br />
        uart_watchdog = 0;<br />
    }<br />
    // no commands received in timeout period, reset UART<br />
    else if(uart_watchdog &gt; UART_WATCHDOG_TIMEOUT)<br />
    {<br />
        Serial.end();<br />
        Serial.begin(115200);<br />
        uart_watchdog = 0;<br />
<br />
        uart_state = UART_STATE_ERR;<br />
<br />
        //set warning LED<br />
        digitalWrite(PIN_SYS_LED_G_N, HIGH);<br />
        digitalWrite(PIN_SYS_LED_R_N, LOW);<br />
    }<br />
<br />
    /* monitor the console output */<br />
<br />
    if (Serial.available() &gt; 0) {<br />
<br />
        String command;<br />
        int argument = 0;<br />
<br />
        // monitor the serial port for the identifier string<br />
        command = Serial.readStringUntil('&#92;n');<br />
<br />
        if(command.substring(UART_IDENTIFIER_IDX, UART_CMD_IDX)<br />
            .equals("SimonSays"))<br />
        {<br />
            // reset the watchdog timer<br />
            uart_watchdog = 0;<br />
<br />
            uart_state = UART_STATE_OK;<br />
<br />
            // system OK<br />
            digitalWrite(PIN_SYS_LED_R_N, HIGH);<br />
            digitalWrite(PIN_SYS_LED_G_N, LOW);<br />
<br />
            // parse the command<br />
            command.remove(UART_IDENTIFIER_IDX, UART_CMD_IDX);<br />
            command.trim();<br />
<br />
            int arg_idx = command.lastIndexOf(' ')+1;<br />
            if(arg_idx &gt; 0)<br />
            {<br />
                argument = command.substring(arg_idx).toInt();<br />
                command.remove(arg_idx);<br />
                command.trim();<br />
            }<br />
<br />
        }<br />
<br />
        /* commands */<br />
<br />
        // change the fan speed<br />
        if(command.equals("SET FAN"))<br />
        {<br />
            analogWrite(PIN_FAN_PWM, argument);<br />
        }<br />
<br />
        // blink the eth0 LED<br />
        else if(command.equals("SET NET LED"))<br />
        {<br />
            blink_chance = argument;<br />
        }<br />
    }<br />
<br />
    #undef UART_IDENTIFIER_IDX<br />
    #undef UART_CMD_IDX<br />
}</code></div></div><br />
10. Now you'll need something on the Rockpro side to help with fan and light control. This script uses a wake word to send commands to the Arduino over the UART console. You'll want to add this python script to systemd so it runs automagically on boot: <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>#!/usr/bin/python3<br />
<br />
import io<br />
import time<br />
import psutil<br />
import os<br />
<br />
net_traffic_prev = 0<br />
<br />
os.system("stty -F /dev/ttyS2 ospeed 115200")<br />
<br />
while True:<br />
    temp_file1 = open("/sys/class/thermal/thermal_zone0/temp", "r")<br />
    temp_file2 = open("/sys/class/thermal/thermal_zone1/temp", "r")<br />
<br />
    temp1 = temp_file1.readline()<br />
    temp2 = temp_file2.readline()<br />
<br />
    temp_file1.close()<br />
    temp_file2.close()<br />
<br />
    if temp1 &gt; temp2:<br />
        hightemp = int(temp1)<br />
    else:<br />
        hightemp = int(temp2)<br />
<br />
    if hightemp &lt; 40000:<br />
        pwm = 0<br />
    else:<br />
        pwm = int(( (hightemp-40000) / 40000 ) * 255)<br />
<br />
    net_counters = psutil.net_io_counters()<br />
    net_traffic_curr = (net_counters.bytes_sent + net_counters.bytes_recv)<br />
    net_traffic = str(net_traffic_curr - net_traffic_prev)<br />
    net_traffic_prev = net_traffic_curr<br />
<br />
    uart_out = open("/dev/ttyS2", "w")<br />
    uart_out.writelines("&#92;nSimonSays SET FAN "+str(pwm)+"&#92;n")<br />
    uart_out.writelines("&#92;nSimonSays SET NET LED "+str(net_traffic)+"&#92;n")<br />
    uart_out.close()<br />
<br />
    time.sleep(2)</code></div></div><br />
11. Now put it onto your rack, and make all your friends jealous of your new ultra-quiet ultra-low-power 1U server!<br />
<br />
<br />
I promised earlier that I'd post my project if I got some useful help from this forum, so I hope I didn't disappoint!<br />
<br />
My scripts and schematic can also be found on <a href="https://github.com/WreckGitRalph/Rockpro64_Scripts" target="_blank" rel="noopener" class="mycode_url">my github page.</a>  Feel free to send me a pull request with any suggestions.<br />
<br />
<img src="https://forum.pine64.org/images/smilies/biggrin.png" alt="Big Grin" title="Big Grin" class="smilie smilie_4" />]]></description>
			<content:encoded><![CDATA[So you've just gotten your brand new Rockpro64, you're looking for a clean way to stuff a PSU and 4 HDD backplane into a box that fits nicely onto your network rack. What do you do? Well, you could get the official NAS case. But that only fits 2 disks, and how are you supposed to mount that on your rack? With a shelf?? Inconceivable.<br />
<br />
Well here's what I did:<br />
<br />
Having bought my home network equipment off eBay, I know retired enterprise gear can be had for dirt cheap. So after some quick searching, I came across the perfect 1U case, that happened to also have a Proliant DL160 server inside it. Perfect for a weekend project.<br />
<br />
Steps to NAS:<br />
<br />
1. Gut the server, leaving only the PSU, backplane, and fans.<br />
<br />
2. If the original SAS controller works, great! If not, find a Rockpro64 compatible PCIe SAS controller. I used an IBM H1110.<br />
<br />
3. An ATX breakout board is handy for connecting to the case's power supply. But if you prefer crimping wires, you do you. As a side note, I'm using the ATX supply to directly control power to the Rockpro. The Rockpro actually has some solder points for power control, near the power buttons, which could be used to improve on my design. I'll be adding that mod myself whenever I get around to it.<br />
<br />
4. You'll need an Arduino as well, I like using Nano's. They're super cheap and fit nicely into just about any project.<br />
<br />
5. Shove it all into the case. It should look something like this:<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2363" target="_blank" title="">Inside-2.jpg</a> (Size: 34.2 KB / Downloads: 749)
<!-- end: postbit_attachments_attachment --><br />
<br />
6. You'll need to wire all those pieces together. I'm driving the LEDs directly from the Arduino GPIO, because <span style="text-decoration: line-through;" class="mycode_s">I'm lazy </span>I like to live dangerously. If you're more concerned about damaging your AVR than I am, you might want to buffer each of those LED lines with a transistor. I decided to wire the fancy buttons and blinkenlights on the front panel up to the Arduino for my NAS. You can put together a wiring harness to mate with the case connector, or be like me and shove your wires directly into the case connector. They're not going anywhere:<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2362" target="_blank" title="">DL160_Case.jpg</a> (Size: 72.19 KB / Downloads: 644)
<!-- end: postbit_attachments_attachment --><br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2364" target="_blank" title="">DL160-Harness.jpg</a> (Size: 21.06 KB / Downloads: 611)
<!-- end: postbit_attachments_attachment --><br />
<br />
7. The front panel has a 2 row, 2.5mm pitch female connector for the USB connections. Hack a connector onto couple USB cables. You can then use the hacked cables as an adapter between the case USB connector and the Rockpro USB inputs. My DL160 case only supports USB 2.0, so I can't take full advantage of the Rockpro's USB interface:<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2365" target="_blank" title="">USB-Harness.jpg</a> (Size: 15.87 KB / Downloads: 642)
<!-- end: postbit_attachments_attachment --><br />
<br />
8. If you don't like your NAS to sound like a jet plane taking off, take out some fans. I've left just one fan. It's keeping everything nice and cool, and runs almost silently. To maintain proper airflow, stuff some packing foam in place of the old fans:<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2366" target="_blank" title="">Fans.jpg</a> (Size: 28.55 KB / Downloads: 655)
<!-- end: postbit_attachments_attachment --><br />
<br />
9. You'll need to get some code onto the Arduino. This script handles power up/shutdown; fan control; and, just to look like a real enterprise grade server, blinking some lights:<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>#include &lt;Arduino.h&gt;<br />
#include &lt;elapsedMillis.h&gt;<br />
<br />
#define PIN_UART_RX     1<br />
#define PIN_PSU_ON      2<br />
#define PIN_PANEL_PWR   3<br />
#define PIN_SYS_LED_P1  4<br />
#define PIN_NIC_LED_P   5<br />
#define PIN_HDD_LED_P   6<br />
#define PIN_BOARD_PWR   7<br />
#define PIN_FAN_PWM     9<br />
#define PIN_PWR_LED_GP_RN   12<br />
#define PIN_PWR_LED_GN_RP   13<br />
#define PIN_SYS_LED_G_N     19<br />
#define PIN_SYS_LED_R_N     20<br />
#define PIN_SYS_LED_P2      21<br />
<br />
#define PWR_STATE_OFF           0   //PSU powered off<br />
#define PWR_STATE_STARTUP       1   //PSU starting up, power button is still held<br />
#define PWR_STATE_ON            2   //PSU powered up, button no longer held<br />
#define PWR_STATE_SHUTDOWN_TMR  3   //PSU powered up, shutdown timer is counting<br />
#define PWR_STATE_SHUTDOWN      4   //PSU shut down, button is still held<br />
#define PWR_STATE_BOARD_OFF     5   //board shut down, PSU still on<br />
<br />
#define BLINK_STATE_READY   0   //LED is not blinking<br />
#define BLINK_STATE_ON      1   //LED is off<br />
#define BLINK_STATE_OFF     2   //LED is on<br />
<br />
#define UART_STATE_OK       0<br />
#define UART_STATE_ERR      1<br />
<br />
#define UART_WATCHDOG_TIMEOUT   10000<br />
<br />
uint8_t power_state = PWR_STATE_OFF;<br />
_Bool uart_state = UART_STATE_OK;<br />
<br />
int blink_chance = 0;<br />
<br />
elapsedMillis event_timer;<br />
elapsedMillis uart_watchdog;<br />
elapsedMillis blink_timer;<br />
<br />
void setup() {<br />
<br />
    // Configure PWM on pin 9 (and 10) @ 25 kHz.<br />
    TCCR1A = 0;<br />
    TCCR1B = 0;<br />
    TCNT1  = 0;<br />
    TCCR1A = _BV(COM1A1)<br />
           | _BV(COM1B1)<br />
           | _BV(WGM11);<br />
    TCCR1B = _BV(WGM13)<br />
           | _BV(CS10);<br />
    ICR1   = 320;<br />
<br />
    pinMode(PIN_UART_RX, INPUT);<br />
    pinMode(PIN_PSU_ON, INPUT);<br />
    pinMode(PIN_PANEL_PWR, INPUT_PULLUP);<br />
    pinMode(PIN_SYS_LED_P1, OUTPUT);<br />
    pinMode(PIN_NIC_LED_P, OUTPUT);<br />
    pinMode(PIN_HDD_LED_P, OUTPUT);<br />
    pinMode(PIN_BOARD_PWR, INPUT);<br />
    pinMode(PIN_FAN_PWM, OUTPUT);<br />
    pinMode(PIN_PWR_LED_GP_RN, OUTPUT);<br />
    pinMode(PIN_PWR_LED_GN_RP, OUTPUT);<br />
    pinMode(PIN_SYS_LED_G_N, OUTPUT);<br />
    pinMode(PIN_SYS_LED_R_N, OUTPUT);<br />
    pinMode(PIN_SYS_LED_P2, OUTPUT);<br />
<br />
    digitalWrite(PIN_SYS_LED_P1, HIGH);<br />
    digitalWrite(PIN_NIC_LED_P, LOW);<br />
    digitalWrite(PIN_HDD_LED_P, LOW);<br />
    analogWrite(PIN_FAN_PWM, 0);<br />
    digitalWrite(PIN_PWR_LED_GP_RN, LOW);<br />
    digitalWrite(PIN_PWR_LED_GN_RP, LOW);<br />
    digitalWrite(PIN_SYS_LED_G_N, LOW);<br />
    digitalWrite(PIN_SYS_LED_R_N, HIGH);<br />
    digitalWrite(PIN_SYS_LED_P2, HIGH);<br />
<br />
    Serial.setTimeout(50);<br />
}<br />
<br />
void loop() {<br />
    <br />
/*********************************************<br />
 * handle system power<br />
 *********************************************/<br />
    switch(power_state)<br />
    {<br />
<br />
        case PWR_STATE_OFF:<br />
        {<br />
            if( (digitalRead(PIN_PANEL_PWR) == LOW) || (PIN_BOARD_PWR == HIGH) )<br />
            {<br />
                power_state = PWR_STATE_STARTUP;<br />
                pinMode(PIN_PSU_ON, OUTPUT);<br />
                digitalWrite(PIN_PSU_ON, LOW);<br />
<br />
                //turn on the GREEN power LED<br />
                pinMode(PIN_PWR_LED_GP_RN, OUTPUT);<br />
                pinMode(PIN_PWR_LED_GN_RP, OUTPUT);<br />
                digitalWrite(PIN_PWR_LED_GN_RP, LOW);<br />
                digitalWrite(PIN_PWR_LED_GP_RN, HIGH);<br />
<br />
                event_timer = 0;<br />
            }<br />
        } break;<br />
        case PWR_STATE_STARTUP:<br />
        {<br />
            //after button is released, wait 5 seconds for startup<br />
            if( (digitalRead(PIN_PANEL_PWR) == HIGH) &amp;&amp; (event_timer &gt; 5000) )<br />
            {<br />
                power_state = PWR_STATE_ON;<br />
            }<br />
        } break;<br />
        case PWR_STATE_ON:<br />
        {<br />
            if(digitalRead(PIN_PANEL_PWR) == LOW)<br />
            {<br />
                event_timer = 0;<br />
                power_state = PWR_STATE_SHUTDOWN_TMR;<br />
            }<br />
            else if(digitalRead(PIN_BOARD_PWR) == LOW)<br />
            {<br />
                event_timer = 0;<br />
                power_state = PWR_STATE_BOARD_OFF;<br />
            }<br />
        } break;<br />
        case PWR_STATE_SHUTDOWN_TMR:<br />
        {<br />
            if(digitalRead(PIN_PANEL_PWR) == LOW)<br />
            {<br />
                //button was held for 3 seconds, shut down<br />
                if( event_timer &gt; 3000 )<br />
                {<br />
                    //tri-state the power pin<br />
                    power_state = PWR_STATE_SHUTDOWN;<br />
                    pinMode(PIN_PSU_ON, INPUT);<br />
<br />
                    //turn on the RED power LED<br />
                    digitalWrite(PIN_PWR_LED_GP_RN, LOW);<br />
                    digitalWrite(PIN_PWR_LED_GN_RP, HIGH);<br />
<br />
                    //disconnect UART<br />
                    Serial.end();<br />
                    pinMode(PIN_UART_RX, INPUT);<br />
                }<br />
            }<br />
            else<br />
            {<br />
                power_state = PWR_STATE_ON;<br />
            }<br />
        } break;<br />
        case PWR_STATE_SHUTDOWN:<br />
        {<br />
            //wait until button is released<br />
            if(digitalRead(PIN_PANEL_PWR) == HIGH)<br />
            {<br />
                power_state = PWR_STATE_OFF;<br />
            }<br />
        } break;<br />
        case PWR_STATE_BOARD_OFF:<br />
        {<br />
            if(digitalRead(PIN_BOARD_PWR) == LOW)<br />
            {<br />
                //board was off for 3 seconds, shut down<br />
                if( event_timer &gt; 3000 )<br />
                {<br />
                    //tri-state the power pin<br />
                    power_state = PWR_STATE_SHUTDOWN;<br />
                    pinMode(PIN_PSU_ON, INPUT);<br />
<br />
                    //turn on the RED power LED<br />
                    digitalWrite(PIN_PWR_LED_GP_RN, LOW);<br />
                    digitalWrite(PIN_PWR_LED_GN_RP, HIGH);<br />
<br />
                    //disconnect UART<br />
                    Serial.end();<br />
                    pinMode(PIN_UART_RX, INPUT);<br />
                }<br />
            }<br />
            else<br />
            {<br />
                power_state = PWR_STATE_ON;<br />
            }<br />
        } break;<br />
        default:<br />
            break;<br />
    }<br />
<br />
/*********************************************<br />
 * blink Eth0 LED<br />
 *********************************************/<br />
    // 100 megabit blinks fastest<br />
    #define BLINK_MAX_CHANCE    100000000<br />
    #define BLINK_MILLIS        30<br />
<br />
    static uint8_t blink_state = BLINK_STATE_READY;<br />
<br />
    if( (power_state == PWR_STATE_ON) &amp;&amp;<br />
        (uart_state == UART_STATE_OK) )<br />
    {<br />
        switch(blink_state)<br />
        {<br />
            case BLINK_STATE_READY:<br />
            {<br />
                // blink the eth0 LED randomly, with a frequency based<br />
                // on the network traffic<br />
                if( (blink_chance != 0) &amp;&amp;<br />
                    ( (random(0,BLINK_MAX_CHANCE) &lt; blink_chance) || <br />
                    (random(0,2000) == 0) ) <br />
                    )<br />
                {<br />
                    blink_state = BLINK_STATE_ON;<br />
                    blink_timer = 0;<br />
                    digitalWrite(PIN_NIC_LED_P, HIGH);<br />
                }<br />
            } break;<br />
            case BLINK_STATE_ON:<br />
            {<br />
                if(blink_timer &gt; BLINK_MILLIS)<br />
                {<br />
                    blink_state = BLINK_STATE_OFF;<br />
                    blink_timer = 0;<br />
                    digitalWrite(PIN_NIC_LED_P, LOW);<br />
                }<br />
            } break;<br />
            case BLINK_STATE_OFF:<br />
            {<br />
                if(blink_timer &gt; (BLINK_MILLIS/2))<br />
                {<br />
                    blink_state = BLINK_STATE_READY;<br />
                }<br />
            } break;<br />
            default:<br />
            {<br />
                    blink_state = BLINK_STATE_READY;<br />
                    blink_timer = 0;<br />
                    digitalWrite(PIN_NIC_LED_P, LOW);<br />
            }<br />
        }<br />
    }<br />
<br />
    #undef BLINK_MAX_CHANCE <br />
    #undef BLINK_TICKS         <br />
<br />
/*********************************************<br />
 * handle UART commands<br />
 *********************************************/<br />
    #define UART_IDENTIFIER_IDX  0<br />
    #define UART_CMD_IDX         9<br />
<br />
    /* enable/reset UART */<br />
<br />
    // keep UART off until board is powered up<br />
    if(power_state != PWR_STATE_ON)<br />
    {<br />
        uart_watchdog = 0;<br />
    }<br />
    // no commands received in timeout period, reset UART<br />
    else if(uart_watchdog &gt; UART_WATCHDOG_TIMEOUT)<br />
    {<br />
        Serial.end();<br />
        Serial.begin(115200);<br />
        uart_watchdog = 0;<br />
<br />
        uart_state = UART_STATE_ERR;<br />
<br />
        //set warning LED<br />
        digitalWrite(PIN_SYS_LED_G_N, HIGH);<br />
        digitalWrite(PIN_SYS_LED_R_N, LOW);<br />
    }<br />
<br />
    /* monitor the console output */<br />
<br />
    if (Serial.available() &gt; 0) {<br />
<br />
        String command;<br />
        int argument = 0;<br />
<br />
        // monitor the serial port for the identifier string<br />
        command = Serial.readStringUntil('&#92;n');<br />
<br />
        if(command.substring(UART_IDENTIFIER_IDX, UART_CMD_IDX)<br />
            .equals("SimonSays"))<br />
        {<br />
            // reset the watchdog timer<br />
            uart_watchdog = 0;<br />
<br />
            uart_state = UART_STATE_OK;<br />
<br />
            // system OK<br />
            digitalWrite(PIN_SYS_LED_R_N, HIGH);<br />
            digitalWrite(PIN_SYS_LED_G_N, LOW);<br />
<br />
            // parse the command<br />
            command.remove(UART_IDENTIFIER_IDX, UART_CMD_IDX);<br />
            command.trim();<br />
<br />
            int arg_idx = command.lastIndexOf(' ')+1;<br />
            if(arg_idx &gt; 0)<br />
            {<br />
                argument = command.substring(arg_idx).toInt();<br />
                command.remove(arg_idx);<br />
                command.trim();<br />
            }<br />
<br />
        }<br />
<br />
        /* commands */<br />
<br />
        // change the fan speed<br />
        if(command.equals("SET FAN"))<br />
        {<br />
            analogWrite(PIN_FAN_PWM, argument);<br />
        }<br />
<br />
        // blink the eth0 LED<br />
        else if(command.equals("SET NET LED"))<br />
        {<br />
            blink_chance = argument;<br />
        }<br />
    }<br />
<br />
    #undef UART_IDENTIFIER_IDX<br />
    #undef UART_CMD_IDX<br />
}</code></div></div><br />
10. Now you'll need something on the Rockpro side to help with fan and light control. This script uses a wake word to send commands to the Arduino over the UART console. You'll want to add this python script to systemd so it runs automagically on boot: <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>#!/usr/bin/python3<br />
<br />
import io<br />
import time<br />
import psutil<br />
import os<br />
<br />
net_traffic_prev = 0<br />
<br />
os.system("stty -F /dev/ttyS2 ospeed 115200")<br />
<br />
while True:<br />
    temp_file1 = open("/sys/class/thermal/thermal_zone0/temp", "r")<br />
    temp_file2 = open("/sys/class/thermal/thermal_zone1/temp", "r")<br />
<br />
    temp1 = temp_file1.readline()<br />
    temp2 = temp_file2.readline()<br />
<br />
    temp_file1.close()<br />
    temp_file2.close()<br />
<br />
    if temp1 &gt; temp2:<br />
        hightemp = int(temp1)<br />
    else:<br />
        hightemp = int(temp2)<br />
<br />
    if hightemp &lt; 40000:<br />
        pwm = 0<br />
    else:<br />
        pwm = int(( (hightemp-40000) / 40000 ) * 255)<br />
<br />
    net_counters = psutil.net_io_counters()<br />
    net_traffic_curr = (net_counters.bytes_sent + net_counters.bytes_recv)<br />
    net_traffic = str(net_traffic_curr - net_traffic_prev)<br />
    net_traffic_prev = net_traffic_curr<br />
<br />
    uart_out = open("/dev/ttyS2", "w")<br />
    uart_out.writelines("&#92;nSimonSays SET FAN "+str(pwm)+"&#92;n")<br />
    uart_out.writelines("&#92;nSimonSays SET NET LED "+str(net_traffic)+"&#92;n")<br />
    uart_out.close()<br />
<br />
    time.sleep(2)</code></div></div><br />
11. Now put it onto your rack, and make all your friends jealous of your new ultra-quiet ultra-low-power 1U server!<br />
<br />
<br />
I promised earlier that I'd post my project if I got some useful help from this forum, so I hope I didn't disappoint!<br />
<br />
My scripts and schematic can also be found on <a href="https://github.com/WreckGitRalph/Rockpro64_Scripts" target="_blank" rel="noopener" class="mycode_url">my github page.</a>  Feel free to send me a pull request with any suggestions.<br />
<br />
<img src="https://forum.pine64.org/images/smilies/biggrin.png" alt="Big Grin" title="Big Grin" class="smilie smilie_4" />]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[PINE AIO - All in ones - 28" 32" 1080p, + touch screen ones]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=13699</link>
			<pubDate>Sun, 25 Apr 2021 07:10:17 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=11626">mitcoes</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=13699</guid>
			<description><![CDATA[I realized that there is no 32" AIO offer in  the market when my sister asked me to select one for her.<br />
There was some Intel and AMD based ones from Lenovo and HP with good price / performance and I opted for an HP one with an AMD APU inside.<br />
<br />
The Dunning-Kruger effect 1999, nobel price 2000 applied to this, is that not company engineers no tinkers think people, would prefer AIO, as other options are cheaper and easier to repair, and yet AIO are a sell success being most of the models overpriced a lot.<br />
<br />
I think that a cheap offer of Pine AIO would be a success<br />
<br />
With replaceable PCBs to upgrade them if needed in the future - in 28" 16:9, as it seems is the most demanded market size, plus 32" that I think is the most valuable size for a desktop, and even 40" ones to be TV and big desktop  - real AIO - <br />
<br />
And bigger Smart TVs with replaceable PCB and the latest Android TV  AOSP - It is rare that a TVbox with Android would upgrade to the next version - .<br />
<br />
And 28" and 32" touch screen ones for bars, restaurants, and many other businesses to act as a register case and split the screen for internet leisure if it is not an everyday full business, or whatever.<br />
<br />
GNU/Linux, Chromium OS or Android in ARM are good solutions for most of the SOHO and business tasks, and they are usually much cheaper than X86, adding a PCB and some connections to an already good a cheap 1080p (or 720p if price difference is noticeable or 4k if price difference is not or offering price gamma if the market is big enough) 32" monitor - no need to be IPS, VA, is cheaper and good enough - and 28" and 40 50 and 60 with TV tuners - at least as an option - would be a great product to have in the market. <br />
<br />
Even if is just because they produce less noise than their competence.<br />
<br />
Last but not least, integrated 1080p webcam, with a privacy switch, can start a landline phone replacement to a TV based video call replacement if paired with a free or cheap service provided by asterisk installing IP phone software on every device with this 1080p webcams - not only this AIOs -  and I think that a one-year free trial, plus perhaps a 12 USD/year price, would make that service a good business with a great value add for customers - not just pine hardware ones -. <br />
<br />
Thanks for reading this suggestion.]]></description>
			<content:encoded><![CDATA[I realized that there is no 32" AIO offer in  the market when my sister asked me to select one for her.<br />
There was some Intel and AMD based ones from Lenovo and HP with good price / performance and I opted for an HP one with an AMD APU inside.<br />
<br />
The Dunning-Kruger effect 1999, nobel price 2000 applied to this, is that not company engineers no tinkers think people, would prefer AIO, as other options are cheaper and easier to repair, and yet AIO are a sell success being most of the models overpriced a lot.<br />
<br />
I think that a cheap offer of Pine AIO would be a success<br />
<br />
With replaceable PCBs to upgrade them if needed in the future - in 28" 16:9, as it seems is the most demanded market size, plus 32" that I think is the most valuable size for a desktop, and even 40" ones to be TV and big desktop  - real AIO - <br />
<br />
And bigger Smart TVs with replaceable PCB and the latest Android TV  AOSP - It is rare that a TVbox with Android would upgrade to the next version - .<br />
<br />
And 28" and 32" touch screen ones for bars, restaurants, and many other businesses to act as a register case and split the screen for internet leisure if it is not an everyday full business, or whatever.<br />
<br />
GNU/Linux, Chromium OS or Android in ARM are good solutions for most of the SOHO and business tasks, and they are usually much cheaper than X86, adding a PCB and some connections to an already good a cheap 1080p (or 720p if price difference is noticeable or 4k if price difference is not or offering price gamma if the market is big enough) 32" monitor - no need to be IPS, VA, is cheaper and good enough - and 28" and 40 50 and 60 with TV tuners - at least as an option - would be a great product to have in the market. <br />
<br />
Even if is just because they produce less noise than their competence.<br />
<br />
Last but not least, integrated 1080p webcam, with a privacy switch, can start a landline phone replacement to a TV based video call replacement if paired with a free or cheap service provided by asterisk installing IP phone software on every device with this 1080p webcams - not only this AIOs -  and I think that a one-year free trial, plus perhaps a 12 USD/year price, would make that service a good business with a great value add for customers - not just pine hardware ones -. <br />
<br />
Thanks for reading this suggestion.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Small Ceph Cluster]]></title>
			<link>https://forum.pine64.org/showthread.php?tid=13597</link>
			<pubDate>Fri, 09 Apr 2021 06:55:57 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://forum.pine64.org/member.php?action=profile&uid=7498">digitaldaz</a>]]></dc:creator>
			<guid isPermaLink="false">https://forum.pine64.org/showthread.php?tid=13597</guid>
			<description><![CDATA[<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font"><!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2290" target="_blank" title="">baddass1.jpg</a> (Size: 269.3 KB / Downloads: 582)
<!-- end: postbit_attachments_attachment -->For a long time I have wanted to build a small but highly available piece of storage.</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">The RockPro64 has finally allowed me to achieve this and, as a bonus, quite neatly using some Sun F80s I managed to get off ebay.</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">I'll write a full tutorial later, for now I just wanted to share the achievement.</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font"><img src="http://www.dmmhosting.co.uk/images/baddass1.jpg" loading="lazy"  alt="[Image: baddass1.jpg]" class="mycode_img" />root@ceph2:~# ceph -s</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">  cluster:</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">    id:    e86bf5ce-cd1e-4449-8051-dc1b45ed0ead</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">    health: HEALTH_OK</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">  services:</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">    mon: 3 daemons, quorum ceph1,ceph2,ceph3</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">    mgr: ceph2(active), standbys: ceph3, ceph1</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">    osd: 12 osds: 12 up, 12 in</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">  data:</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">Happy days <img src="https://forum.pine64.org/images/smilies/smile.png" alt="Smile" title="Smile" class="smilie smilie_1" /></span></span></span><br />
<br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font"><img src="http://dmmhosting.co.uk/images/baddass1.jpg" loading="lazy"  alt="[Image: baddass1.jpg]" class="mycode_img" /></span></span></span>]]></description>
			<content:encoded><![CDATA[<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font"><!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://forum.pine64.org/images/attachtypes/image.png" title="JPG Image" border="0" alt=".jpg" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=2290" target="_blank" title="">baddass1.jpg</a> (Size: 269.3 KB / Downloads: 582)
<!-- end: postbit_attachments_attachment -->For a long time I have wanted to build a small but highly available piece of storage.</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">The RockPro64 has finally allowed me to achieve this and, as a bonus, quite neatly using some Sun F80s I managed to get off ebay.</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">I'll write a full tutorial later, for now I just wanted to share the achievement.</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font"><img src="http://www.dmmhosting.co.uk/images/baddass1.jpg" loading="lazy"  alt="[Image: baddass1.jpg]" class="mycode_img" />root@ceph2:~# ceph -s</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">  cluster:</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">    id:    e86bf5ce-cd1e-4449-8051-dc1b45ed0ead</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">    health: HEALTH_OK</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">  services:</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">    mon: 3 daemons, quorum ceph1,ceph2,ceph3</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">    mgr: ceph2(active), standbys: ceph3, ceph1</span></span></span><br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">    osd: 12 osds: 12 up, 12 in</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">  data:</span></span></span><br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font">Happy days <img src="https://forum.pine64.org/images/smilies/smile.png" alt="Smile" title="Smile" class="smilie smilie_1" /></span></span></span><br />
<br />
<br />
<span style="color: #333333;" class="mycode_color"><span style="font-size: small;" class="mycode_size"><span style="font-family: Tahoma, Verdana, Arial, sans-serif;" class="mycode_font"><img src="http://dmmhosting.co.uk/images/baddass1.jpg" loading="lazy"  alt="[Image: baddass1.jpg]" class="mycode_img" /></span></span></span>]]></content:encoded>
		</item>
	</channel>
</rss>