Mini-pc case

 

目次

1.仕様........................................................................1

2.ソフトウエア設定...............................................................1

初期設定(インストール).........................................................1

・イメージファイルの書き込み(インストール).....................................2

wifi設定.............................................................2

/boot/config.txt LCDUART設定.............................................2

/boot/cmdline.txt UART設定..................................................2

・キーボード設定.............................................................2

TABキー.................................................................2

touch panel調整 ファイル.....................................................3

LCDバックライト...........................................................3

3.オンボードATTINY85..........................................................3

接続図.....................................................................5

ATTINY85ソフト..........................................................5

Raspberry PI UARTプログラム................................................6

/etc/battery.conf()........................................................6

4.GPIO......................................................................12

5.USBASPによるATTINY85書き換え...............................................12

6.keybind.c...................................................................13

7.電源.......................................................................15

免責事項.......................................................................16

 

 

mini-pc caseはキーボード、2.8インチLCD、リチウムイオンバッテリーを含むRaspberry pi zero w用ケースです。単体で超小型のパソコンとして利用出来ます。

 

1.仕様

LCD 2.8インチ(SPI) タッチパネル 640x480(縮小表示) 320x240(物理解像度)

bluetooth mini keyboard

リチウムバッテリー 4000ma(駆動時間 約15時間)

Attiny85(バッテリー電圧取得用 UART接続)

GPIOオーディオ

 

2.ソフトウエア設定

初期設定(インストール)

  初期設定、マイクロSDへ書き込みとwifi設定を行います。

  wifiが接続されるとssh接続が可能です。

 

・イメージファイルの書き込み(インストール)

  カスタム済みのrasbian.img(付属DVD) ddコマンド等を利用し、マイクロSDへ書き込みを行います。

  4Gバイト以上のマイクロSDが利用出来ます。

 

wifi設定

  設定ファイルは/etc/wpa_supplicant/wpa_supplicant.confです。

  ssidは、wifiルータのSsIDpskは接続時のwifiパスワードです。

 

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev

update_config=1

country=JP

 

network={

        ssid="xxxxxx" ←----- SSID

        scan_ssid=1

        psk="xxxxx"   ←----- WIFI password

        key_mgmt=WPA-PSK

}

/boot/config.txt  LCDUART設定

 

framebuffer_ignore_alpha=1

framebuffer_swap=1

hdmi_force_hotplug=0

hdmi_cvt=640 480 60 1 0 0 0

hdmi_group=2

hdmi_mode=7

display_rotate=3

 

#dtparam=i2c_baudrate=9600

#core_freq=250

enable_uart=1

dtoverlay=pi3-miniuart-bt

dtoverlay=pwm-2chan,pin=18,func=2,pin2=13,func2=4

 

# touch screen

dtoverlay=ads7846,penirq=27,speed=10000,keep_vref_on=1,penirq_pull=2,xohms=150

 

/boot/cmdline.txt  UART設定

  “console=serial0,115200”を削除し、UARTアクセスを有効化。

 

console=tty1 root=PARTUUID=6c586e13-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash

 

・キーボード設定

 

  タスクバーのアイコンよりペアリンクさせます。キーを入力後、最後にエンターキーを入力する必要が

  あります。

 

TABキー

  物理的にTabキーが存在していないので、利用できるよう以下のファイルに設定します。

  FNキー + 1(数字の1キー)により、Tabキーが入力されます。

  他のキーに変更したい場合は、xevを起動し、キーコードを調べ、180の数値を変更します。

  Cmdキー(134)に変更する場合は、180134に書き換え、Xを再起動するかOSを再起動します。

 

  /etc/keybind.xmodmap

 

    keycode 180 = Tab

 

  ログイン時に自動実行できるよう以下のファイルに設定します。

 

  xmodmapコマンドで新たなキーバインドを設定しても、Bluetoothキーボードが少電力モードから

  復帰し再接続されるたびにキーバインドは初期化されてしまいます。

  少電力モードから復帰後、再設定されるように設定します。

 

  .config/lxsession/LXDE-pi/autostart @keybindコマンドを追加し、自動起動させます。

  このコマンドはキーボードの再接続時、xmodmapを再実行します。

 

  @lxpanel --profile LXDE-pi

  @pcmanfm --desktop --profile LXDE-pi

  @xscreensaver -no-splash

  @keybind

 

  keybindコマンドは/dev/input/event1を監視し、xmodmapを再実行します。

touch panel調整 ファイル

  以下のファイルの"358 3946 413 3990"部分の数値調整で可能。

 

/usr/share/X11/xorg.conf.d/99-calibration.conf

 

Section "InputClass"

        Identifier      "calibration"

        MatchProduct    "ADS7846 Touchscreen"

        Option  "Calibration" "358 3946 413 3990"

        Option  "SwapAxes"      "0"

EndSection

 

LCDバックライト

  バックライト(LED)GPIO17 に接続されています。gpioコマンド等で、オン/オフが可能です。

 

  gpio -g write 17 0/1

 

  輝度コントロールを行いたい場合、GPIO17を高速でオンオフを繰り返し、オフ回数を増やし、オフ時間を伸ばす

  ことで輝度を下げることは可能です。プログラムからオンオフによるPWM的な動作をさせることで輝度を調整は

  可能ですが、電力消費的には?です。

 

3.オンボードATTINY85

 

  Raspberry piはアナログ入力を持たない為、attiny85(8MHz動作)はそれを補強します。

  ソケット上にあるため、取り外し再プログラミング可能です。

  Raspberry piattiny85間は、UART接続(9600bps)で接続されています。

  電源供給はRaspberry pi 3.3vピンより供給されます。

 

getty停止 UART利用の為、シリアルを利用しているgettyサービスを停止します。

sudo systemctl stop serial-getty@ttyAMA0.service

sudo systemctl disable serial-getty@ttyAMA0.service

 

attiny85 接続ピン接続(UART)

 

   RX 1

   TX 0

 

  arduino ideでプログラミングする場合はSoftwareSerialを利用します。

 

     SoftwareSerial mySerial(1, 0); // RX, TX

 

 

 

Raspberry pi側はGPIO14 15、通常のハードウエアUARTピンに接続されます。


 

接続図

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ATTINY85ソフト

 

基準電圧3.3vで、1/2に分圧されたリチウムイオンバッテリーの電圧を読み取り、UARTにより、Raspberry pi側へデータを送信します。以下のプログラムが既にUPLOADされています。

 

#include <stdio.h>

#include <SoftwareSerial.h>

 

//#define A1 1

#define DTIME 1000

 

SoftwareSerial mySerial(1, 0); // RX, TX(avr side)

 

void setup() {

  //pinMode(0, OUTPUT);

  mySerial.begin(9600);

  //Serial.begin(9600);

  pinMode(2, INPUT);

}

 

 

void loop() {

  delay(DTIME);

 

  char buf[12];

  memset(buf,0,12);

  int i = analogRead( A1 );

  double f = i * 3.3 / 1023.0 * 2;

  //sprintf(buf, "%3.2lf%%", f);

  printDouble(f,100);

}

 

void printDouble( double val, unsigned int precision){

// prints val with number of decimal places determine by precision

// NOTE: precision is 1 followed by the number of zeros for the desired number of decimial places

// example: printDouble( 3.1415, 100); // prints 3.14 (two decimal places)

 

   mySerial.print (int(val));  //prints the int part

   mySerial.print("."); // print the decimal point

   unsigned int frac;

   if(val >= 0)

     frac = (val - int(val)) * precision;

   else

      frac = (int(val)- val ) * precision;

   int frac1 = frac;

   while( frac1 /= 10 )

       precision /= 10;

   precision /= 10;

   while(  precision /= 10)

       mySerial.print("0");

 

   mySerial.println(frac,DEC) ;

}

 

Raspberry PI UARTプログラム

 

voltage.cATTINT85よりUART経由で送信されるリチウムイオンバッテリーの電圧を受信し、

特定の電圧でコマンド(shutdown)を実行します。

 

/etc/battery.confを参照し、動作変更が可能。

 

  volt=3.40                      3.4vでコマンド起動。

  command=shutdown -h now        volt指定電圧でコマンド実行

  logfile=/var/log/battery.log   電圧の記録ログファイル指定

  interval=10            サンプリング間隔()

 

サンプリングした電圧10回の平均値を電圧として、コマンドを起動します。

 

/etc/battery.conf()

 

  初期値は以下の設定となる。つまり、volt=xxを書かなかった場合、3.40になります。

  他のパラメータも同様でcommand=xxxを書かなかった場合は、shutdown -h nowになります。

 

volt=3.40

command=shutdown -h now

logfile=/var/log/battery.log

interval=10

 

・設置方法(インストール)

 

プログラムの設置は、/etc/rc.localに “&”付きで書き加えるか、難しく設定したければ

service用の設定ファイルを作成し、serviceとして起動します。

 

 

コンパイル

 

  gcc voltage.c -o voltage

 

 

/* getting battery voltage from attiny85 */

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <termios.h>

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <time.h>

#include <stdlib.h>

 

/* baudrate settings are defined in <asm/termbits.h>, which is

included by <termios.h> */

#define BAUDRATE B9600           

/* change this definition for the correct port */

#define MODEMDEVICE "/dev/serial0"

#define _POSIX_SOURCE 1 /* POSIX compliant source */

 

#define FALSE 0

#define TRUE 1

 

volatile int STOP=FALSE;

 

/* config file splited by = . don't use space */

#define STR_MAX 256

#define CONFIG_FILE "/etc/battery.conf"

 

void writelog(char* file,char* str);

int getParam(char* kind, char* param);

 

double d[10];

int start = 0;

 

int main()

{

    int fd,c, res;

    struct termios oldtio,newtio;

    char buf[STR_MAX];

 

    char volt[STR_MAX];

    char command[STR_MAX];

    char interval[STR_MAX];

    char logfile[STR_MAX];

    double shutdown_volt;

    int acquisition_interval;

 

    /* read battery-config.txt */

    getParam("volt", volt);

    if(strlen(volt) == 0){

        printf("voltage is empty in config file, default shutdown voltage is 3.4v\n");

 shutdown_volt = 3.4;

    }else{

        shutdown_volt = atof(volt);

 printf("shutdown voltage is %f\n",shutdown_volt);

    }

    getParam("command", command);

    if(strlen(command) == 0){

        printf("command is empty in config file, default is shutdown -h now\n");

 sprintf(command, "shutdown -h now");

    }else{

        printf("command is %s\n",command);

    }

    getParam("interval", interval);

    if(strlen(interval) == 0){

        printf("interval is empty in config file, default acqusistion interval is 10sec\n");

 acquisition_interval=10;

    }else{

        acquisition_interval = atoi(interval);

 printf("acquisition interval is %d\n",acquisition_interval);

    }

    getParam("logfile", logfile);

    if(strlen(logfile) == 0){

        printf("logfile is empty in config file, default log file is /var/log/battery.log\n");

 sprintf(logfile,"/var/log/battery.log");

    }else{

        printf("log file is %s\n", logfile);

    }

 

    /*

      Open modem device for reading and writing and not as controlling tty

      because we don't want to get killed if linenoise sends CTRL-C.

    */

    fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );

    if (fd <0) {perror(MODEMDEVICE); return -1; }

 

    tcgetattr(fd,&oldtio); /* save current serial port settings */

    bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */

 

    /*

      BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.

      CRTSCTS : output hardware flow control (only used if the cable has

                all necessary lines. See sect. 7 of Serial-HOWTO)

      CS8     : 8n1 (8bit,no parity,1 stopbit)

      CLOCAL  : local connection, no modem contol

      CREAD   : enable receiving characters

    */

     newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

 

    /*

      IGNPAR  : ignore bytes with parity errors

      ICRNL   : map CR to NL (otherwise a CR input on the other computer

                will not terminate input)

      otherwise make device raw (no other input processing)

    */

    newtio.c_iflag = IGNPAR | ICRNL;

 

    /*

      Raw output.

    */

    newtio.c_oflag = 0;

 

    /*

      ICANON  : enable canonical input

      disable all echo functionality, and don't send signals to calling program

    */

    newtio.c_lflag = ICANON;

 

    /*

      initialize all control characters

      default values can be found in /usr/include/termios.h, and are given

      in the comments, but we don't need them here

    */

    newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */

    newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */

    newtio.c_cc[VERASE]   = 0;     /* del */

    newtio.c_cc[VKILL]    = 0;     /* @ */

    newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */

    newtio.c_cc[VTIME]    = 0;     /* inter-character timer unused */

    newtio.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */

    newtio.c_cc[VSWTC]    = 0;     /* '\0' */

    newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */

    newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */

    newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */

    newtio.c_cc[VEOL]     = 0;     /* '\0' */

    newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */

    newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */

    newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */

    newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */

    newtio.c_cc[VEOL2]    = 0;     /* '\0' */

 

    /*

      now clean the modem line and activate the settings for the port

    */

    tcflush(fd, TCIFLUSH);

    tcsetattr(fd,TCSANOW,&newtio);

 

    /*

      terminal settings done, now handle input

      In this example, inputting a 'z' at the beginning of a line will

      exit the program.

    */

    time_t last_t;

    int start = 0;

    int vol[3];

    char tmp[6];

    int n = 0;

    while (STOP==FALSE) {     /* loop until we have a terminating condition */

        /* read blocks program execution until a line terminating character is

        input, even if more than 255 chars are input. If the number

        of characters read is smaller than the number of chars available,

        subsequent reads will return the remaining chars. res will be set

        to the actual number of characters actually read */

        memset(buf,0,255);

        res = read(fd,buf,255);

        /* current time */

        time_t t = time(NULL);

 /* log voltage at interval */

 double dt = difftime(t , last_t);

 if(dt < acquisition_interval){

     continue;

 }

 int i;

 for(i = 0; i< res; i++){

     if(buf[i] == 0x0a){

         buf[i] = 0x00;

     }

     if(buf[i] == 0x0d){

         buf[i] = 0x00;

     }

 }

 if(strlen(buf) > 0){

     char tbuf[256];

     char vol_str[4];

     char* ct = ctime(&t);

     if (ct[strlen(ct)-1] == '\n') ct[strlen(ct)-1] = '\0';

     sprintf(tbuf, "%s:%s", ct, buf);

     /* n is buffer number count up by 60 sec until 10 and turn back 0 */

     sprintf(vol_str, "%s", buf);

     d[n] = atof(vol_str);

     /* caliculate level of 10 value */

     if(start == 1){

         double lv = 0;

  for(int i = 0; i < 10; i++){

             lv +=  d[i];

  }

  lv = lv / 10;

  printf("%s:%f:%f\n", buf, lv, d[n]);

         /* 3.4volt have remaining time is about 1h */

  if(lv <= shutdown_volt && start ==1 ){

      char str[STR_MAX];

      sprintf(str,"command execute:%s",command);

      writelog(logfile, str);

      sprintf(str,"level voltage of 10 time:%f",lv);

      writelog(logfile, str);

      system(command);

  }

     }

     ++n;

     if(n >=10){

       start = 1;

       n = 0;

     }

 

     for(i = 0; i< res; i++){

         if(tbuf[i] == 0x0a){

             tbuf[i] = 0x20;

         }

         if(tbuf[i] == 0x0d){

             tbuf[i] = 0x20;

         }

     }

     printf("%s\n",tbuf);

     writelog(logfile, tbuf);

 }

  /* copy time_t */

 memcpy(&last_t, &t, sizeof(time_t));

    }

    /* restore the old port settings */

    tcsetattr(fd,TCSANOW,&oldtio);

}

 

void writelog(char* file, char* str){

    FILE* fp;

    if((fp = fopen(file, "a")) != NULL){

        fputs(str,fp);

        fputs("\n",fp);

        fclose(fp);

    }else{

        printf("you don't have permissions on /var/log\n");

    }

}

 

int getParam(char* kind, char* param){

  //char str[STR_MAX], param[STR_MAX];

  char str[STR_MAX];

  FILE* fin;

  if ((fin = fopen(CONFIG_FILE, "r")) == NULL) {

    printf("fin error:[%s]\n", CONFIG_FILE);

    return -1; /* system error */

  }

 

  int i = 0;

  int j = 0;

  for(;;) {

    if (fgets(str, STR_MAX, fin) == NULL) {

      /* EOF */

      fclose(fin);

      return -3; /* not found keyword */

    }

    if (!strncmp(str, kind, strlen(kind))) {

      while (str[i++] != '=') {

 ;

      }

      while (str[i] != '\n') {

 param[j++] = str[i++];

      }

      param[j] = '\0';

      printf("param:[%s]\n", param);

      fclose(fin);

      return -3; /* not found keyword */

    }

    if (!strncmp(str, kind, strlen(kind))) {

      while (str[i++] != '=') {

 ;

      }

      while (str[i] != '\n') {

 param[j++] = str[i++];

      }

      param[j] = '\0';

      printf("param:[%s]\n", param);

      fclose(fin);

      return 0;

    }

  }

  fclose(fin);

}

 

 


4.GPIO

 

Raspberry pi zero側のGPIOピンで、LCD、その他の接続されていないピンは利用可能です。

I2Cは右上あたりにあるピンホールSCL1,SDA1に引き出されているので接続可能です。SPI0SPI1LCD表示、タッチパネルで利用されており、UARTATTINY85と接続されています。

 

 

 

 

5.USBASPによるATTINY85書き換え

ATTINY85はソケットから取り外し可能なので、USBAPSなどの書き込み機を利用して再プログラミングが可能です。

ボードのATTINY85の2,3番ピンからケーブルを引き出す等して他の機器に接続してアナログデータを取得する等

が可能です。ハンダやケースの穴あけなどは必要です。UARTを利用する場合、voltage.cの再プログラミンも必要です。書き込み機は数百円でamazone等から購入出来ます。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

6.keybind.c

  キーボードにTABキーが見当たらない為、xmodmapで別のキーにTABキーを割り当てしています。

  割り当ては、少電力復帰時に初期化されるため、/dev/input/event1を監視し、再度、コマンドを実行します。

 

  g++でコンパイルします。

 

  g++ keybind.c -o  keybind

 

 

#include <sys/types.h>

#include <dirent.h>

#include <errno.h>

#include <vector>

#include <string>

#include <iostream>

#include <fstream>

#include <stdlib.h>

#include <stdio.h>

 

#include <sys/stat.h>

#include <unistd.h>

 

using namespace std;

 

bool keybdstate();

inline bool exists(const std::string& name);

int getProcIdByName(string procName);

 

int main()

{

    /* prevention multiple startup */

    /*

    int keybind_pid = getProcIdByName("keybind");

    if(keybind_pid >= 0)

    {

 printf("keybind pid = %d\n", keybind_pid);

 return -1;

    }

    */

    /* main loop */

    while(1){

 if(keybdstate() == false){

     sleep(3);

     continue;

 }

 

 int x_pid = getProcIdByName("Xorg");

 if( x_pid >= 0){

     for(int i = 0; i < 3; i++){

  setenv("DISPLAY", ":0.0", 1);

  system("/usr/bin/xmodmap /etc/keybind.xmodmap");

  printf("Find Xorg proc No. %d keybind started /etc/keybind.xmodmap\n", x_pid );

  sleep(10);

     }

 }

 sleep(3);

    }

}

 

/* bluetooth keyboard check /dev/event1

   state change from false to true return true

*/

 

bool keybdstate()

{

    static bool kb = false;

    if(exists("/dev/input/event1") == true){

 if(kb == false){

     kb = true;

     return true;

 }

 kb = true;

    }else{

 kb = false;

    }

    return false;

}

 

inline bool exists(const std::string& name)

{

  struct stat buffer;  

  return (stat (name.c_str(), &buffer) == 0);

}

 

int getProcIdByName(string procName)

{

    int pid = -1;

 

    // Open the /proc directory

    DIR *dp = opendir("/proc");

    if (dp != NULL)

    {

        // Enumerate all entries in directory until process found

        struct dirent *dirp;

        while (pid < 0 && (dirp = readdir(dp)))

        {

            // Skip non-numeric entries

            int id = atoi(dirp->d_name);

            if (id > 0)

            {

                // Read contents of virtual /proc/{pid}/cmdline file

                string cmdPath = string("/proc/") + dirp->d_name + "/cmdline";

                ifstream cmdFile(cmdPath.c_str());

                string cmdLine;

                getline(cmdFile, cmdLine);

                if (!cmdLine.empty())

                {

      cout << cmdLine << endl;

                    // Keep first cmdline item which contains the program path

                    size_t pos = cmdLine.find('\0');

                    if (pos != string::npos)

                        cmdLine = cmdLine.substr(0, pos);

                    // Keep program name only, removing the path

                    pos = cmdLine.rfind('/');

                    if (pos != string::npos)

                        cmdLine = cmdLine.substr(pos + 1);

                    // Compare against requested process name

                    if (procName == cmdLine)

                        pid = id;

                }

            }

        }

    }

 

    closedir(dp);

 

    return pid;

}

 

 

 

 

 

 

 

 

 

 

 

7.電源

Raspberry pishutdownコマンドで電源OFF状態にしてもUSB等色々な機器が電源OFF状態になりません。

リチウムイオン電池は低電圧保護(2.4V)で電流を流さなくなりますが、電池運用の場合、次回起動時の動作時間が短くなります。もしくは、バッテリーが空になって充電するまで起動しません。停止時は、側面にあるメインスイッチで、shutdown後に完全停止する運用をおすすめします。


 

免責事項

 

ハードウエア、ソフトウエアなどの障害によって生じた損害については一切責任を負いません。

 

1