[Turorial] NAS Using A Recycled 1U Proliant Case
#1
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.

Well here's what I did:

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.

Steps to NAS:

1. Gut the server, leaving only the PSU, backplane, and fans.

2. If the original SAS controller works, great! If not, find a Rockpro64 compatible PCIe SAS controller. I used an IBM H1110.

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.

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.

5. Shove it all into the case. It should look something like this:
   

6. You'll need to wire all those pieces together. I'm driving the LEDs directly from the Arduino GPIO, because I'm lazy 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:
   
   

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:
   

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:
   

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:
Code:
#include <Arduino.h>
#include <elapsedMillis.h>

#define PIN_UART_RX     1
#define PIN_PSU_ON      2
#define PIN_PANEL_PWR   3
#define PIN_SYS_LED_P1  4
#define PIN_NIC_LED_P   5
#define PIN_HDD_LED_P   6
#define PIN_BOARD_PWR   7
#define PIN_FAN_PWM     9
#define PIN_PWR_LED_GP_RN   12
#define PIN_PWR_LED_GN_RP   13
#define PIN_SYS_LED_G_N     19
#define PIN_SYS_LED_R_N     20
#define PIN_SYS_LED_P2      21

#define PWR_STATE_OFF           0   //PSU powered off
#define PWR_STATE_STARTUP       1   //PSU starting up, power button is still held
#define PWR_STATE_ON            2   //PSU powered up, button no longer held
#define PWR_STATE_SHUTDOWN_TMR  3   //PSU powered up, shutdown timer is counting
#define PWR_STATE_SHUTDOWN      4   //PSU shut down, button is still held
#define PWR_STATE_BOARD_OFF     5   //board shut down, PSU still on

#define BLINK_STATE_READY   0   //LED is not blinking
#define BLINK_STATE_ON      1   //LED is off
#define BLINK_STATE_OFF     2   //LED is on

#define UART_STATE_OK       0
#define UART_STATE_ERR      1

#define UART_WATCHDOG_TIMEOUT   10000

uint8_t power_state = PWR_STATE_OFF;
_Bool uart_state = UART_STATE_OK;

int blink_chance = 0;

elapsedMillis event_timer;
elapsedMillis uart_watchdog;
elapsedMillis blink_timer;

void setup() {

    // Configure PWM on pin 9 (and 10) @ 25 kHz.
    TCCR1A = 0;
    TCCR1B = 0;
    TCNT1  = 0;
    TCCR1A = _BV(COM1A1)
           | _BV(COM1B1)
           | _BV(WGM11);
    TCCR1B = _BV(WGM13)
           | _BV(CS10);
    ICR1   = 320;

    pinMode(PIN_UART_RX, INPUT);
    pinMode(PIN_PSU_ON, INPUT);
    pinMode(PIN_PANEL_PWR, INPUT_PULLUP);
    pinMode(PIN_SYS_LED_P1, OUTPUT);
    pinMode(PIN_NIC_LED_P, OUTPUT);
    pinMode(PIN_HDD_LED_P, OUTPUT);
    pinMode(PIN_BOARD_PWR, INPUT);
    pinMode(PIN_FAN_PWM, OUTPUT);
    pinMode(PIN_PWR_LED_GP_RN, OUTPUT);
    pinMode(PIN_PWR_LED_GN_RP, OUTPUT);
    pinMode(PIN_SYS_LED_G_N, OUTPUT);
    pinMode(PIN_SYS_LED_R_N, OUTPUT);
    pinMode(PIN_SYS_LED_P2, OUTPUT);

    digitalWrite(PIN_SYS_LED_P1, HIGH);
    digitalWrite(PIN_NIC_LED_P, LOW);
    digitalWrite(PIN_HDD_LED_P, LOW);
    analogWrite(PIN_FAN_PWM, 0);
    digitalWrite(PIN_PWR_LED_GP_RN, LOW);
    digitalWrite(PIN_PWR_LED_GN_RP, LOW);
    digitalWrite(PIN_SYS_LED_G_N, LOW);
    digitalWrite(PIN_SYS_LED_R_N, HIGH);
    digitalWrite(PIN_SYS_LED_P2, HIGH);

    Serial.setTimeout(50);
}

void loop() {
   
/*********************************************
* handle system power
*********************************************/
    switch(power_state)
    {

        case PWR_STATE_OFF:
        {
            if( (digitalRead(PIN_PANEL_PWR) == LOW) || (PIN_BOARD_PWR == HIGH) )
            {
                power_state = PWR_STATE_STARTUP;
                pinMode(PIN_PSU_ON, OUTPUT);
                digitalWrite(PIN_PSU_ON, LOW);

                //turn on the GREEN power LED
                pinMode(PIN_PWR_LED_GP_RN, OUTPUT);
                pinMode(PIN_PWR_LED_GN_RP, OUTPUT);
                digitalWrite(PIN_PWR_LED_GN_RP, LOW);
                digitalWrite(PIN_PWR_LED_GP_RN, HIGH);

                event_timer = 0;
            }
        } break;
        case PWR_STATE_STARTUP:
        {
            //after button is released, wait 5 seconds for startup
            if( (digitalRead(PIN_PANEL_PWR) == HIGH) && (event_timer > 5000) )
            {
                power_state = PWR_STATE_ON;
            }
        } break;
        case PWR_STATE_ON:
        {
            if(digitalRead(PIN_PANEL_PWR) == LOW)
            {
                event_timer = 0;
                power_state = PWR_STATE_SHUTDOWN_TMR;
            }
            else if(digitalRead(PIN_BOARD_PWR) == LOW)
            {
                event_timer = 0;
                power_state = PWR_STATE_BOARD_OFF;
            }
        } break;
        case PWR_STATE_SHUTDOWN_TMR:
        {
            if(digitalRead(PIN_PANEL_PWR) == LOW)
            {
                //button was held for 3 seconds, shut down
                if( event_timer > 3000 )
                {
                    //tri-state the power pin
                    power_state = PWR_STATE_SHUTDOWN;
                    pinMode(PIN_PSU_ON, INPUT);

                    //turn on the RED power LED
                    digitalWrite(PIN_PWR_LED_GP_RN, LOW);
                    digitalWrite(PIN_PWR_LED_GN_RP, HIGH);

                    //disconnect UART
                    Serial.end();
                    pinMode(PIN_UART_RX, INPUT);
                }
            }
            else
            {
                power_state = PWR_STATE_ON;
            }
        } break;
        case PWR_STATE_SHUTDOWN:
        {
            //wait until button is released
            if(digitalRead(PIN_PANEL_PWR) == HIGH)
            {
                power_state = PWR_STATE_OFF;
            }
        } break;
        case PWR_STATE_BOARD_OFF:
        {
            if(digitalRead(PIN_BOARD_PWR) == LOW)
            {
                //board was off for 3 seconds, shut down
                if( event_timer > 3000 )
                {
                    //tri-state the power pin
                    power_state = PWR_STATE_SHUTDOWN;
                    pinMode(PIN_PSU_ON, INPUT);

                    //turn on the RED power LED
                    digitalWrite(PIN_PWR_LED_GP_RN, LOW);
                    digitalWrite(PIN_PWR_LED_GN_RP, HIGH);

                    //disconnect UART
                    Serial.end();
                    pinMode(PIN_UART_RX, INPUT);
                }
            }
            else
            {
                power_state = PWR_STATE_ON;
            }
        } break;
        default:
            break;
    }

/*********************************************
* blink Eth0 LED
*********************************************/
    // 100 megabit blinks fastest
    #define BLINK_MAX_CHANCE    100000000
    #define BLINK_MILLIS        30

    static uint8_t blink_state = BLINK_STATE_READY;

    if( (power_state == PWR_STATE_ON) &&
        (uart_state == UART_STATE_OK) )
    {
        switch(blink_state)
        {
            case BLINK_STATE_READY:
            {
                // blink the eth0 LED randomly, with a frequency based
                // on the network traffic
                if( (blink_chance != 0) &&
                    ( (random(0,BLINK_MAX_CHANCE) < blink_chance) ||
                    (random(0,2000) == 0) )
                    )
                {
                    blink_state = BLINK_STATE_ON;
                    blink_timer = 0;
                    digitalWrite(PIN_NIC_LED_P, HIGH);
                }
            } break;
            case BLINK_STATE_ON:
            {
                if(blink_timer > BLINK_MILLIS)
                {
                    blink_state = BLINK_STATE_OFF;
                    blink_timer = 0;
                    digitalWrite(PIN_NIC_LED_P, LOW);
                }
            } break;
            case BLINK_STATE_OFF:
            {
                if(blink_timer > (BLINK_MILLIS/2))
                {
                    blink_state = BLINK_STATE_READY;
                }
            } break;
            default:
            {
                    blink_state = BLINK_STATE_READY;
                    blink_timer = 0;
                    digitalWrite(PIN_NIC_LED_P, LOW);
            }
        }
    }

    #undef BLINK_MAX_CHANCE
    #undef BLINK_TICKS        

/*********************************************
* handle UART commands
*********************************************/
    #define UART_IDENTIFIER_IDX  0
    #define UART_CMD_IDX         9

    /* enable/reset UART */

    // keep UART off until board is powered up
    if(power_state != PWR_STATE_ON)
    {
        uart_watchdog = 0;
    }
    // no commands received in timeout period, reset UART
    else if(uart_watchdog > UART_WATCHDOG_TIMEOUT)
    {
        Serial.end();
        Serial.begin(115200);
        uart_watchdog = 0;

        uart_state = UART_STATE_ERR;

        //set warning LED
        digitalWrite(PIN_SYS_LED_G_N, HIGH);
        digitalWrite(PIN_SYS_LED_R_N, LOW);
    }

    /* monitor the console output */

    if (Serial.available() > 0) {

        String command;
        int argument = 0;

        // monitor the serial port for the identifier string
        command = Serial.readStringUntil('\n');

        if(command.substring(UART_IDENTIFIER_IDX, UART_CMD_IDX)
            .equals("SimonSays"))
        {
            // reset the watchdog timer
            uart_watchdog = 0;

            uart_state = UART_STATE_OK;

            // system OK
            digitalWrite(PIN_SYS_LED_R_N, HIGH);
            digitalWrite(PIN_SYS_LED_G_N, LOW);

            // parse the command
            command.remove(UART_IDENTIFIER_IDX, UART_CMD_IDX);
            command.trim();

            int arg_idx = command.lastIndexOf(' ')+1;
            if(arg_idx > 0)
            {
                argument = command.substring(arg_idx).toInt();
                command.remove(arg_idx);
                command.trim();
            }

        }

        /* commands */

        // change the fan speed
        if(command.equals("SET FAN"))
        {
            analogWrite(PIN_FAN_PWM, argument);
        }

        // blink the eth0 LED
        else if(command.equals("SET NET LED"))
        {
            blink_chance = argument;
        }
    }

    #undef UART_IDENTIFIER_IDX
    #undef UART_CMD_IDX
}

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:
Code:
#!/usr/bin/python3

import io
import time
import psutil
import os

net_traffic_prev = 0

os.system("stty -F /dev/ttyS2 ospeed 115200")

while True:
    temp_file1 = open("/sys/class/thermal/thermal_zone0/temp", "r")
    temp_file2 = open("/sys/class/thermal/thermal_zone1/temp", "r")

    temp1 = temp_file1.readline()
    temp2 = temp_file2.readline()

    temp_file1.close()
    temp_file2.close()

    if temp1 > temp2:
        hightemp = int(temp1)
    else:
        hightemp = int(temp2)

    if hightemp < 40000:
        pwm = 0
    else:
        pwm = int(( (hightemp-40000) / 40000 ) * 255)

    net_counters = psutil.net_io_counters()
    net_traffic_curr = (net_counters.bytes_sent + net_counters.bytes_recv)
    net_traffic = str(net_traffic_curr - net_traffic_prev)
    net_traffic_prev = net_traffic_curr

    uart_out = open("/dev/ttyS2", "w")
    uart_out.writelines("\nSimonSays SET FAN "+str(pwm)+"\n")
    uart_out.writelines("\nSimonSays SET NET LED "+str(net_traffic)+"\n")
    uart_out.close()

    time.sleep(2)

11. Now put it onto your rack, and make all your friends jealous of your new ultra-quiet ultra-low-power 1U server!


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!

My scripts and schematic can also be found on my github page.  Feel free to send me a pull request with any suggestions.

Big Grin
  Reply
#2
Thumbs Up 
This is a very cool project and a good reuse of old server ! Thanks for the tutorial. I will also try to copy your set up to make a nas from a retired server. Blush
Just one piece of advice : if it is not done yet, you should put a heatsink on your rockpro64
  Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Anyone require a apple-sleek NAS case, w/ ample passive cooling & mutliple ssds? dairymilkbatman 0 219 01-19-2024, 02:17 PM
Last Post: dairymilkbatman
  3D Printed 2 SSD Vertical NAS Case tenox 1 2,125 12-19-2021, 01:30 PM
Last Post: CounterPillow
  NAS/fileserver project—RP64 in 1U ATX/ITX case (comments welcome!) Skirmisher 10 13,460 04-09-2021, 01:37 AM
Last Post: MNtinkerer
  Ideas to use extra space in NAS case? _hg_ 1 3,262 10-04-2020, 11:42 PM
Last Post: zer0sig
Star Detailed NAS Case assembly (ameriDroid) Luke 15 24,242 10-04-2020, 01:04 AM
Last Post: Humid Stylus

Forum Jump:


Users browsing this thread: 1 Guest(s)