シェルスクリプトマガジン

Raspberry Piを100%活用しよう(Vol.70掲載)

著者:米田聡

小型コンピュータボード「Raspberry Pi」(ラズパイ)向けにさまざまな拡張ボードが発売されています。その拡張ボードとラズパイを組み合わせれば、ラズパイでいろいろなことが簡単に試せます。第3回は、電源断でもラズパイを正常終了するための拡張基板を扱います。

シェルスクリプトマガジン Vol.70は以下のリンク先でご購入できます。

図3 ADRSZUPコマンドのソースコード(ADRSZUP.c)

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <string.h>
#include <time.h>
#include <linux/reboot.h>

#define TRUE    1
#define FALSE   0

/* エラーメッセージ */
void error(char *s)
{
    fputs(s, stderr);
}

/* GPIO初期化 */
int initGpio(unsigned int gpio)
{
    char buf[256];
    int i, fd;

    // export
    fd = open("/sys/class/gpio/export", O_WRONLY);
    if( fd < 0 ) {
        error("/sys/class/gpio/export cant be opened \n");
        return FALSE;
    }
    sprintf(buf,"%d",gpio);
    write(fd, buf, strlen(buf));
    close(fd);

    // direction
    sprintf(buf, "/sys/class/gpio/gpio%d/direction", gpio);
    for( i=0; i < 10000; i++) {
        fd = open(buf,O_WRONLY );
        if(fd >= 0) break;
    }
    if(fd < 0) {
        error("Direction cant opened\n");
        return FALSE;
    }
    sprintf(buf,"in");
    write(fd, buf, strlen(buf));
    close(fd);
    
    // High -> Low falling edge
    sprintf(buf, "/sys/class/gpio/gpio%d/edge", gpio);
    for( i=0; i < 10000; i++) {
        fd = open(buf,O_WRONLY );
        if(fd >= 0) break;
    }
    if( fd < 0 ) {
        error("Edge cant opended\n");
        return FALSE;
    }
    sprintf(buf, "falling");
    write(fd, buf, strlen(buf));
    close(fd);

    return TRUE;
}

/* GPIO開放 */
int deinitGpio(unsigned int gpio)
{
    char buf[256];
    int fd;

    sprintf(buf, "%d", gpio);
    fd = open("/sys/class/gpio/unexport", O_WRONLY);
    if(fd < 0 ){
        error("GPIO cant opened");
        return FALSE;
    }
    write(fd, buf, strlen(buf));
    close(fd);

    return TRUE;
}

/* シャットダウン */
int shutdown(void)
{
    sync();
    sync();
    return reboot(LINUX_REBOOT_CMD_POWER_OFF);
}

#define PWDN_GPIO  6    // 電源断通知GPIO番号

int main(int argc, char *argv[])
{
    int retval = 0;
    int fd;
    char buf[256];
    char c;

    if(! initGpio(PWDN_GPIO) ) {
        error("GPIO cant be initialized\n");
        return 0;
    }

    // GPIOオープン
    sprintf(buf,"/sys/class/gpio/gpio%d/value", PWDN_GPIO );
    fd = open(buf, O_RDONLY);
    if(fd < 0) {
        error("Value cant opened");
        return 0;
    }
    // 空読み
    read(fd, &c, 1);
    while(1) {
        struct pollfd pfd;
        pfd.fd = fd;
        pfd.events = POLLPRI;
        pfd.revents = 0;
        // GPIO fallingイベント待ち
        lseek(fd, 0, SEEK_SET);
        int r = poll(&pfd, 1, -1);
        fputs("Power down detected\nWait for 5 seconds\n", stdout);
        // 5秒待つ
        sleep(5);
        read(fd, &c, 1);
        if( c == '0' ) {
            close(fd);
            deinitGpio(PWDN_GPIO);
            fputs("Shutdown now\n",stdout);
            retval = shutdown();
            break;
        }
    }
    return retval;
}

図4 /etc/systemd/system/adrszup.serviceファイルの内容

[Unit]
Description=Auto shutdown process for ADRSZUP

[Service]
Type=simple
Restart=no
User=root
ExecStart=/usr/local/bin/ADRSZUP

[Install]
WantedBy=multi-user.target