├── .gitignore ├── README.md ├── base_board ├── README.md ├── img │ ├── test_cut_params.jpg │ └── v1.jpg └── nafuda_board_v1.ai ├── bootup ├── README.md ├── bootup.sh ├── check_bad_password.py ├── check_vsd.sh ├── const.sh ├── enable_usb_gadget.sh ├── exec_stop_post.sh ├── mount_vsd_ro.sh ├── mount_vsd_rw.sh ├── nafuda-bootup.service ├── reset_hostname.sh ├── reset_password.sh └── virtual_sd_builder │ ├── build_img.sh │ └── skel │ ├── README.txt │ ├── docs │ └── 0_THIS_DOCS_ARE_PROBABLY_NOT_THE_LATEST_VERSION.txt │ ├── img │ ├── 0_info.png │ ├── 1_gadget_sponsor_mercari.png │ └── __CONVERT_TO_QR__.png │ └── wpa_supplicant.conf.sample ├── docs ├── DIY_NAFUDA.md ├── EXTRA_INFO.md ├── FOR_HACKER.md ├── G_ETHER_WITH_WINDOWS.md ├── HOW_TO_LOGIN.md ├── LICENSE.md ├── PIN_ASSIGN.md ├── README.md ├── TROUBLESHOOT.md └── assets │ ├── back_image.jpg │ ├── cable_epd_side.jpg │ ├── cable_rpz_side.jpg │ ├── connect_center_usb_port.jpg │ ├── connect_center_usb_port_before.jpg │ ├── connect_nafuda_to_pc.jpg │ ├── connect_outside_usb_port.jpg │ ├── create_file_1.jpg │ ├── create_file_2.jpg │ ├── create_file_3.jpg │ ├── delete_img.jpg │ ├── detach_cable.jpg │ ├── downside_image.jpg │ ├── eject_nafuda.jpg │ ├── eject_nafuda_win.jpg │ ├── front_image.jpg │ ├── g_ether_with_windows │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 16.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png │ ├── g_serial_win_1.png │ ├── g_serial_win_2.png │ ├── g_serial_win_3.png │ ├── g_serial_win_4.png │ ├── g_serial_win_5.png │ ├── g_serial_win_6.png │ ├── g_serial_win_7.png │ ├── img_copy.jpg │ ├── nafuda_drive.jpg │ ├── nafuda_drive_img_dir.jpg │ ├── nafuda_drive_img_dir_win.jpg │ ├── nafuda_drive_win.jpg │ ├── pc_usb_port.jpg │ ├── plugin_usb_battery.jpg │ ├── pull_out_usb_battery.jpg │ ├── usbc_converter_sample.jpg │ ├── usbc_converter_sample2.jpg │ ├── windows_create_file.jpg │ └── windows_rename_file.jpg ├── lib ├── README.md ├── epd4in2.py ├── epd4in2_mock.py ├── epdif.py └── nafuda.py ├── resource ├── motd ├── mount_vsd_ro ├── mount_vsd_rw ├── show_img └── show_txt ├── setup_script ├── README.md ├── reset_all.sh ├── setup_boot_partition.sh └── setup_from_baseinstall.sh ├── show_img ├── README.md └── show_img.py ├── show_txt ├── README.md └── show_txt.py ├── simple_nafuda ├── README.md ├── exec_stop_post.sh ├── img │ ├── bcon-logo.png │ └── texture.png ├── main.py ├── settings.json └── simple-nafuda.service ├── simple_nafuda_server ├── .gitignore ├── README.md ├── composer.json ├── composer.lock └── public │ ├── .htaccess │ ├── bc2018 │ └── image_store_dir │ ├── form.php │ └── index.php ├── simple_sample ├── README.md └── startup.sh └── weather ├── README.md ├── main.py └── weather.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | official_lib 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | electronic badge 2018 2 | ====== 3 | 4 | builderscon tokyo 2018で配布される「電子名札」の向けのソフトウェアおよび情報です。 5 | 6 | 電子名札はraspberry pi zero whをベースに作成されています。 7 | 8 | 9 | ## 電子名札の簡単画像設定1 ウェブ経由アップロード 10 | 11 | > ※ この機能は会期終了にともない、デフォルトでオフになりました。`simple_nafuda_server`をセットアップし、`simple_nafuda`の`settings.json`を設定する必要があります。 12 | 13 | 1. 名札を起動します(USBケーブルを、基盤部分に挿してください、LEDが点滅しはじめて、約1分ほどで起動します) 14 | 2. スライドショーが開始し、しばらくすると、QRコードの画面が表示されます。 15 | 3. スマホでスキャンします。 16 | 4. 画像アップロード画面が表示されるので、ならんでいる入力欄に画像を設定して、アップロードボタンをおし、好きな画像をアップロードします。自分のソーシャルアカウントの画像などをスクショするのはどうでしょうか? 17 | 5. アップロードを一通りしたら、名札のUSBを抜き、再度接続します(再起動します) 18 | 6. ネットワークがある環境(デフォルトでは、会場のWifiが設定済みです)で名札が起動すると、自動的に画像をダウンロードし、スライドショーが開始します。(枚数が多いと時間がかかるので、ご注意) 19 | 20 | > ※ QRコードは変更することもできますが、PCと接続する(パスワードリセットか、ホスト名リセット)が必要です。むやみに人にスキャンさせないようにしましょう。 21 | 22 | ## 電子名札の簡単画像設定1 PCにUSB接続して、アップロード 23 | 24 | 1. 名札にとりつけられたUSBケーブルを取り外します。 25 | -
(画像リンク)ラズパイ側からケーブルを抜く

26 | -
(画像リンク)バッテリーからケーブルを抜く

27 | -
(画像リンク)板からケーブルをとりはずす

28 | 2. 取り外したケーブルで、名札とPCをを接続してください。その時にPCへと接続するUSBポートは右側(中央寄り)です。 29 | -
画像リンク)PCとの接続概要

30 | 3. 名札の起動を待ちます。名札が起動完了後、PCにNAFUDAドライブとして認識されます。 31 | -
(画像リンク)NAFUDAドライブ(Mac)

32 | -
画像リンク)NAFUDAドライブ(Windows)

33 | 4. NAFUDAドライブ中の`img` ディレクトリに好きな画像(拡張子png,jpeg形式)をコピーし、不要な画像を消したり、imgディレクトリから外してください。 34 | -
(画像リンク)imgディレクトリの様子

35 | -
(画像リンク)imgディレクトリの様子(Windows)

36 | -
(画像リンク)ファイルコピーの様子

37 | -
(画像リンク)不要画像削除の様子

38 | 5. コピーが終わったら、ドライブを一般的なUSBメモリ同様にイジェクト操作し、少し待ってLEDの点滅がないことを確認してから電子名札とPCのUSBケーブルを抜いてください。 39 | -
(画像リンク)取り出しの様子(Mac)

40 | -
(画像リンク)取り出しの様子(Windows)

41 | 6. 名札とUSBバッテリーをつなぎ、名札を起動してください。 42 | -
(画像リンク)バッテリーにケーブルを挿す

43 | -
(画像リンク)ラズパイにケーブルを挿す

44 | -
(画像リンク)ケーブルの取り回し

45 | 7. imgに保存した画像が、スライドショー表示されます。名札をエンジョイしてください! 46 | - 初期設定では起動直後に、数秒NAFUDAの情報表示をおこなわれ、その後にスライドショーが始まります。 47 | 48 | > ※ Type-C to Aの変換コネクタは少量ですが、運営よりお貸しできます。 49 | 50 | > ※ 画像のファイル名は英数小文字で設定ください。 51 | 52 | > ※ 画像ファイルは長辺1000px以下で作成ください。 53 | 54 | > ※ `img`ディレクトリを含む`NAFUDA`ドライブの中身は初期化される場合があります、かならずPC側に元ファイルを保持してください。 55 | 56 | > ※ 想定駆動時間はバッテリー満充電時は約6時間ですが、利用状況や画像の種類などによって大きく変動します。 57 | 58 | > ※ imgディレクトリに最初から入っている画像は削除しても`NAFUDA`ドライブを所定の手続きで初期化することで戻せますが、ソフトウェアのアップデートで画像が変わる事があります。 59 | 60 | 61 | ## 動画によるデモ 62 | 63 | - [電子名札 スライドショー起動](https://www.youtube.com/watch?v=tByA1lBPJD4) 64 | - [電子名札 USB接続時 NAFUDAドライブ操作](https://www.youtube.com/watch?v=ldZi0VksX1o) 65 | - [電子名札 ウェブ経由画像アップロードデモ](https://www.youtube.com/watch?v=RRAVv2eyS_Y) 66 | 67 | 68 | *** 69 | 70 | ## ドキュメント 71 | 72 | - [README.md 最初にみてください](docs/README.md) 73 | - [Raspbery Pi へのログイン方法](docs/HOW_TO_LOGIN.md) 74 | - [トラブルシュート](docs/TROUBLESHOOT.md) 75 | - [LICENSE](docs/LICENSE.md) 76 | - [自分で名札を作る方へ、作りかたや部品について](docs/DIY_NAFUDA.md) 77 | - [その他ドキュメント](docs/) 78 | 79 | 80 | ## サンプルアプリ 81 | 82 | - [simple_sample - 一番簡単、Hello,World!](simple_sample/README.md) 83 | - [simple nafuda - 名札アプリ(デフォルト導入済み)](simple_nafuda/) 84 | - [show_img - 画像を表示](show_img/) 85 | - [show_txt - ファイル、あるいは標準入力からのテキストを表示](show_txt/) 86 | - [weather - 今日のお天気](weather/) 87 | 88 | 89 | ## プルリクお待ちしています! 90 | 91 | ドキュメントの誤りや、致命的なバグの修正などがあればお気軽にプルリクください。 92 | 93 | また、あなたが新しいアプリケーションを作成したらぜひおしえてください! 94 | 95 | 96 | ## ガジェットスポンサー 97 | 98 | 実物としての「電子名札」はbuilderscon tokyo 2018 サポーターチケットのノベルティです。 99 | 100 | 電子名札の実機制作・配布においては [株式会社メルカリ](https://about.mercari.com/) 様にご協賛をいただきました、ありがとうございます! 101 | 102 | || 103 | |:---:| 104 | | ![mercari](bootup/virtual_sd_builder/skel/img/1_gadget_sponsor_mercari.png) | 105 | |謎ガジェットにご理解のあるmercari様| 106 | 107 | 108 | - [メルカリが、builderscon tokyo 2018の謎ガジェット「電子名札」のスポンサーに! \#builderscon \- Mercari Engineering Blog](https://tech.mercari.com/entry/2018/08/14/120000) 109 | - [電子名札開発での異常な努力 または私は如何にして心配するのを止めて『謎ガジェット』を作るようになったか \- builderscon::blog](https://blog.builderscon.io/entry/2018/08/09/100000) 110 | 111 | -------------------------------------------------------------------------------- /base_board/README.md: -------------------------------------------------------------------------------- 1 | ベースボード 2 | ========== 3 | 4 | ベースボードをレーザーカッターで加工するためのデータです。 5 | 6 | ![](img/v1.jpg) 7 | 8 | > ※ このデータ(`nafuda_board_v1.ai`)はbuilderscon tokyo 2018での配布物(上記画像)から、「gadget sponsor mercari」を消したものになります。 9 | 10 | # 試作環境 11 | 12 | - trotec speedy 300 13 | 14 | 試作時のパラメーターは以下を参照ください 15 | 16 | ![](img/test_cut_params.jpg) 17 | 18 | > ※ 量産時の機材、パラメタはございません。 19 | 20 | 21 | # ライセンス 22 | 23 | CC BY-SA 24 | 25 | > buildersconロゴ(b ロゴ)を利用しています。 26 | -------------------------------------------------------------------------------- /base_board/img/test_cut_params.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/base_board/img/test_cut_params.jpg -------------------------------------------------------------------------------- /base_board/img/v1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/base_board/img/v1.jpg -------------------------------------------------------------------------------- /base_board/nafuda_board_v1.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/base_board/nafuda_board_v1.ai -------------------------------------------------------------------------------- /bootup/README.md: -------------------------------------------------------------------------------- 1 | # bootup script 2 | 3 | 起動時に以下のようなことを行うコードです。 4 | 5 | - パスワード再生成・再設定 6 | - ホスト名再生性・再設定 7 | - NAFUDA ドライブの初期化 8 | - `wpa_supplicant.conf`のコピー 9 | - `g_ether`,`g_mass_storage`,`g_serial`などの起動切り分け 10 | - 起動直後、各種情報を表示 11 | - 緊急脱出ハッチ 12 | - 上記完了後、`simple-nafuda`サービス起動 13 | 14 | systemdに`simple-bootup` サービスとして登録されており、`bootup.sh`がエントリポイントです。 15 | 16 | 17 | # スイッチ群 18 | 19 | NAFUDAドライブ直下にファイルを作成することで、起動時の動作を変更できます。 20 | 21 | たとえば、`enable_g_ether`オプションを有効にする場合は以下のようにできます。 22 | 23 | ```bash 24 | # MacやLinuxの場合、NAFUDAドライブは/Volumes/NAFUDAにマウントされているものとする 25 | $ touch /Volumes/NAFUDA/enable_g_ether 26 | ``` 27 | 28 | FinderやExplorerでも作成は可能ですが、拡張子をまちがってつけないように注意してください。 29 | 30 | ## `reset_passwd` 31 | 32 | passwordをリセットし、`default_password.txt`というファイルを作成します。 33 | 34 | ## `i_love_common_password` 35 | 36 | パスワードを`raspberrypi`に設定します。 37 | 38 | > ※ 必要がなければ、セキュリティ的に危険なのでつかわないでください。 39 | 40 | ## `reset_hostname` 41 | 42 | `hostname`をランダムなものに変更します。`hosts`も変更します。 43 | 44 | `default_password.txt`というファイルを作成します。 45 | 46 | ## `i_love_common_hostname` 47 | 48 | ホスト名を`raspberrypi`に設定します。 49 | 50 | > ※ 他にraspberrypiや名札のいるネットワークではおすすめしません 51 | 52 | 53 | ## `wpa_supplicant.conf` 54 | 55 | `/etc/wpa_supplicant/wpa_supplicant.conf` にコピーします。 56 | 57 | サンプルはNAFUDAドライブの直下に`wpa_supplicant.conf.sample`として設置されています 58 | 59 | > ※ ラズパイの典型的な、`/boot/wpa_supplicant.conf` に制作した時と同じです 60 | 61 | ## `id_rsa.pub` 62 | 63 | お手持ちの公開鍵を登録する時に使うことができます、内容を`/home/pi/authorized_keys` に追記します。 64 | 65 | ## `enable_g_ether` 66 | 67 | ファイルがあれば、起動後に`modprobe g_ether`などを実行します。 68 | 69 | > ※ 再起動すると、上記ファイルは削除されます。 70 | 71 | > ※ なければ、g_mass_storageを有効にします 72 | 73 | ## `enable_g_serial` 74 | 75 | ファイルがあれば、起動後に`modprobe g_serial`などを実行します。 76 | 77 | > ※ 再起動すると、上記ファイルは削除されます。 78 | 79 | > ※ なければ、g_mass_storageを有効にします 80 | 81 | ## `disable_simple_nafuda` 82 | 83 | ファイルがあれば、`simple-nafuda`を起動しません。 84 | 85 | ## `disable_startup_info` 86 | 87 | ファイルがあれば、起動時にIPやパスワードの情報表示をディスプレイにおこないません。 88 | 89 | ## `startup.sh` 90 | 91 | `startup.sh`があれば、起動後に`/bin/bash`の引数として実行します。 92 | 93 | 詳しい使い方の一部は`simple_sample`にも記載されています。 94 | 95 | 96 | ## `vsd_rebuild` 97 | 98 | 強制的にNAFUDAドライブを初期化します。 99 | 100 | たとえば、git pullした後にNAFUDAドライブの内容を最新にしたい場合に実行します。 101 | 102 | > ※ NAFUDAドライブにはいっていたファイルは消失します。 103 | 104 | > ※ これはNAFUDAシステムの完全初期化ではありません。NAFUDAドライブの外にあるファイルや、ID/PASSが初期化されるわけではありません。 105 | -------------------------------------------------------------------------------- /bootup/bootup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | if [ 0 -ne ${EUID:-${UID}} ] 4 | then 5 | echo "You need to be root to perform this command." 6 | exit 1 7 | fi 8 | 9 | ### Escape hatch 10 | 11 | if [ -e /boot/startup.sh ] 12 | then 13 | /bin/bash /boot/startup.sh | tee /boot/startup.log 14 | fi 15 | 16 | . const.sh 17 | 18 | # mount vsd (NAFUDA drive) or rebuild vsd. 19 | set +e 20 | ${SCRIPT_DIR}/check_vsd.sh 21 | 22 | # mount failed. 23 | # VSDがないときは、g_etherにフォールバックして終了する。 24 | if [ 0 -eq `mount | grep -c ${VSD_BASE_DIR}` ] 25 | then 26 | modprobe g_ether 27 | ifconfig usb0 up 28 | ifconfig usb0 169.254.123.45/16 # IP固定 29 | IPADDR_LIST=`ip -f inet -o addr show |grep -v 127.0.0.1 |cut -d\ -f 2,7 |cut -d/ -f 1` 30 | 31 | STARUP_TEXT="NAFUDA SAFE MODE 32 | VSD MOUNT FAILED! 33 | 34 | =INFO= 35 | ip: ${IPADDR_LIST} 36 | default password: `cat /boot/default_passwd.txt` 37 | usb gadget mode: g_ether 38 | " 39 | # dump to hw serial 40 | echo "${STARUP_TEXT}" > /dev/ttyAMA0 41 | 42 | # show text to e-paper display 43 | echo "${STARUP_TEXT}" | ${SCRIPT_DIR}/../show_txt/show_txt.py - 44 | 45 | exit 1 46 | fi 47 | set -e 48 | 49 | ### 50 | ### enable usb gadget 51 | ### 52 | G_MODE=`${SCRIPT_DIR}/enable_usb_gadget.sh` 53 | 54 | 55 | # 56 | ${SCRIPT_DIR}/reset_password.sh 57 | 58 | 59 | # 60 | ${SCRIPT_DIR}/reset_hostname.sh 61 | 62 | 63 | ### 64 | ### copy wpa_supplicant.conf 65 | ### 66 | 67 | if [ -e ${VSD_BASE_DIR}/wpa_supplicant.conf ] 68 | then 69 | if [ -e /etc/wpa_supplicant/wpa_supplicant.conf ] 70 | then 71 | cp /etc/wpa_supplicant/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf.old 72 | fi 73 | ${VSD_RW} 74 | mv ${VSD_BASE_DIR}/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf 75 | ${VSD_RO} 76 | sudo systemctl restart networking.service 77 | sudo systemctl daemon-reload 78 | sudo systemctl restart dhcpcd 79 | sleep 5 # wait for dhcp 80 | fi 81 | 82 | 83 | ### 84 | ### add id_rsa.pub 85 | ### 86 | 87 | if [ -e ${VSD_BASE_DIR}/id_rsa.pub ] 88 | then 89 | if [ ! -d /home/pi/.ssh ] 90 | then 91 | mkdir /home/pi/.ssh 92 | chown pi:pi /home/pi/.ssh 93 | chmod 700 /home/pi/.ssh 94 | fi 95 | 96 | cat ${VSD_BASE_DIR}/id_rsa.pub >> /home/pi/.ssh/authorized_keys 97 | chown pi:pi /home/pi/.ssh/authorized_keys 98 | chmod 700 /home/pi/.ssh/authorized_keys 99 | 100 | ${VSD_RW} 101 | rm ${VSD_BASE_DIR}/id_rsa.pub 102 | ${VSD_RO} 103 | fi 104 | 105 | 106 | ### 107 | ### show info to e-paper display 108 | ### 109 | 110 | # 一応同時起動を避けるため 111 | set +e 112 | systemctl stop simple-nafuda 113 | set -e 114 | 115 | IPADDR_LIST=`ip -f inet -o addr show |grep -v 127.0.0.1 |cut -d\ -f 2,7 |cut -d/ -f 1` 116 | COMMIT_ID=`git -C /home/pi/electronic_badge_2018 log --oneline |head -1` 117 | 118 | DEFAULT_PASSWORD="default_passwd.txt missing" 119 | if [ -e "${VSD_BASE_DIR}/default_passwd.txt" ] 120 | then 121 | DEFAULT_PASSWORD=`cat ${VSD_BASE_DIR}/default_passwd.txt` 122 | fi 123 | 124 | set +e 125 | HOSTNAME=`hostname` 126 | set -e 127 | 128 | STARUP_TEXT="NAFUDA 129 | commit: ${COMMIT_ID} 130 | 131 | =INFO= 132 | ip: ${IPADDR_LIST} 133 | default password: ${DEFAULT_PASSWORD} 134 | usb gadget mode: ${G_MODE} 135 | default hostname: ${HOSTNAME} 136 | 137 | ====== 138 | builderscon 139 | discover something new!! 140 | " 141 | 142 | echo "${STARUP_TEXT}" 143 | 144 | if [ ! -e ${VSD_BASE_DIR}/disable_startup_info ] 145 | then 146 | # dump to hw serial 147 | set +e 148 | echo "${STARUP_TEXT}" > /dev/ttyAMA0 149 | set -e 150 | 151 | # show text to e-paper display 152 | echo "${STARUP_TEXT}" | ${SCRIPT_DIR}/../show_txt/show_txt.py - 153 | fi 154 | 155 | 156 | ### 157 | ### start program 158 | ### 159 | 160 | if [ -e ${VSD_BASE_DIR}/startup.sh ] 161 | then 162 | # execute user program 163 | exec /bin/bash ${VSD_BASE_DIR}/startup.sh | tee /boot/startup2.log 164 | else 165 | # mass storageモードの場合は simple-nafudaを起動する 166 | if [ ${G_MODE} = "g_mass_storage" ] 167 | then 168 | systemctl start simple-nafuda 169 | fi 170 | 171 | # 消費電力を抑える 172 | /opt/vc/bin/tvservice --off 173 | fi 174 | -------------------------------------------------------------------------------- /bootup/check_bad_password.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import re 4 | import crypt 5 | 6 | line = "" 7 | with open('/etc/shadow') as f: 8 | for l in f.readlines(): 9 | 10 | if re.match('^pi:\$', l): 11 | line = l 12 | break 13 | 14 | if 0 == len(line): 15 | print('good(not found pi)') 16 | exit(0) # nothing to do. 17 | 18 | hash = line.split(":")[1] 19 | 20 | _, id, salt, _encrypted = hash.split("$") 21 | 22 | encrypted_password = crypt.crypt("raspberry", salt="$%s$%s" % (id, salt)) 23 | 24 | # print(encrypted_password) 25 | # print(hash) 26 | 27 | if encrypted_password == hash: 28 | # found default password! 29 | print("bad") 30 | else: 31 | # not found default password, good boy 32 | print("good") 33 | -------------------------------------------------------------------------------- /bootup/check_vsd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | if [ 0 -ne ${EUID:-${UID}} ] 4 | then 5 | echo "You need to be root to perform this command." 6 | exit 1 7 | fi 8 | 9 | . const.sh 10 | 11 | # スタック回避のため 12 | set +e 13 | 14 | ## fsck、失敗したら削除 15 | if [ -e ${VSD_IMG_PATH} ] 16 | then 17 | fsck.vfat -a "${VSD_IMG_PATH}" 18 | if [ $? -ne 0 ] || [ $? -ne 1 ] 19 | then 20 | echo "fsck failed. force rm disk." 21 | rm -f "${VSD_IMG_PATH}" 22 | fi 23 | fi 24 | 25 | # try mount vsd 26 | ${VSD_RO} 27 | 28 | DO_VSD_REBUILD=0 29 | 30 | # check mount success 31 | if [ 0 -eq `mount | grep -c ${VSD_BASE_DIR}` ] 32 | then 33 | echo "mount failed." 34 | DO_VSD_REBUILD=1 35 | fi 36 | 37 | # force rebuild switch 1 38 | if [ -e "/boot/vsd_rebuild" ] 39 | then 40 | rm "/boot/vsd_rebuild" 41 | echo "boot vsd_rebuild switch found." 42 | DO_VSD_REBUILD=1 43 | fi 44 | 45 | # force rebuild switch 2 46 | if [ -e "${VSD_BASE_DIR}/vsd_rebuild" ] 47 | then 48 | # vsd_rebuildは後のrebuildで消えるはずなので削除しない 49 | echo "vsd vsd_rebuild switch found." 50 | DO_VSD_REBUILD=1 51 | fi 52 | 53 | # force rebuild switch 2 54 | if [ ! -e "${VSD_BASE_DIR}/img" ] 55 | then 56 | # img ディレクトリがなかったらrebuild(ちょっと大げさかもしれない 57 | echo "img dir not found" 58 | DO_VSD_REBUILD=1 59 | fi 60 | 61 | # Rebuild execute when need. 62 | if [ ${DO_VSD_REBUILD} -eq 1 ] 63 | then 64 | REBUILD_IMG_RESULT="`${SCRIPT_DIR}/virtual_sd_builder/build_img.sh 2>&1`" 65 | 66 | if [ ! $? -eq 0 ] 67 | then 68 | echo "VSD REBUILD FAILED ${REBUILD_IMG_RESULT}" 69 | echo "VSD REBUILD FAILED ${REBUILD_IMG_RESULT}" | show_txt - 70 | exit 1 71 | fi 72 | 73 | ${VSD_RO} 74 | 75 | # check mount success 76 | if [ 0 -eq `mount | grep -c ${VSD_BASE_DIR}` ] 77 | then 78 | # broken 79 | echo "VSD MOUNT FAILED" 80 | echo "VSD MOUNT FAILED" | show_txt - 81 | exit 1 82 | fi 83 | 84 | echo "REBUILDED" 85 | fi 86 | 87 | # スタック回避のため 88 | set -e 89 | 90 | echo "vsd mount success" 91 | -------------------------------------------------------------------------------- /bootup/const.sh: -------------------------------------------------------------------------------- 1 | #!bin/bash 2 | 3 | SCRIPT_DIR=$(cd $(dirname $BASH_SOURCE); pwd) 4 | 5 | VSD_BASE_DIR=/mnt/virtual_sd 6 | VSD_IMG_PATH=/home/pi/virtual_sd.img 7 | 8 | VSD_RW=${SCRIPT_DIR}/mount_vsd_rw.sh 9 | VSD_RO=${SCRIPT_DIR}/mount_vsd_ro.sh 10 | 11 | -------------------------------------------------------------------------------- /bootup/enable_usb_gadget.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | if [ 0 -ne ${EUID:-${UID}} ] 4 | then 5 | echo "You need to be root to perform this command." 6 | exit 1 7 | fi 8 | 9 | . const.sh 10 | 11 | # すでになんらかのusb gadget moduleがロードされていれば処理しない 12 | ALREADY_LOADED_MOD=`lsmod|grep -e ^g_mass_storage -e ^g_ether -e ^g_serial|cut -f 1 --delim=" "` 13 | 14 | if [ ${#ALREADY_LOADED_MOD} -gt 0 ] 15 | then 16 | echo ${ALREADY_LOADED_MOD} 17 | exit 18 | fi 19 | 20 | G_MODE="" 21 | if [ -e ${VSD_BASE_DIR}/enable_g_ether ] 22 | then 23 | G_MODE="g_ether" 24 | ${VSD_RW} 25 | /bin/rm ${VSD_BASE_DIR}/enable_g_ether 26 | ${VSD_RO} 27 | modprobe g_ether 28 | ifconfig usb0 up 29 | ifconfig usb0 169.254.123.45/16 # IP固定 30 | fi 31 | 32 | if [ -e ${VSD_BASE_DIR}/enable_g_serial ] 33 | then 34 | G_MODE="g_serial" 35 | ${VSD_RW} 36 | /bin/rm ${VSD_BASE_DIR}/enable_g_serial 37 | ${VSD_RO} 38 | modprobe g_serial 39 | systemctl start getty@ttyGS0.service 40 | fi 41 | 42 | if [ -z "${G_MODE}" ] 43 | then 44 | G_MODE="g_mass_storage" 45 | modprobe g_mass_storage file=${VSD_IMG_PATH} stall=0 removable=1 46 | sudo sysctl -w vm.dirty_expire_centisecs=50 2>&1 > /dev/null 47 | sudo sysctl -w vm.dirty_writeback_centisecs=100 2>&1 > /dev/null 48 | fi 49 | 50 | echo "${G_MODE}" 51 | -------------------------------------------------------------------------------- /bootup/exec_stop_post.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # the script will run when error exit 4 | # 5 | 6 | SCRIPT_DIR=$(cd $(dirname $0); pwd) 7 | VSD_BASE_DIR=/mnt/virtual_sd 8 | 9 | # 10 | #STOPPED 11 | #success 12 | #killed 13 | #TERM 14 | # 15 | #STOPPED 16 | #exit-code 17 | #exited 18 | #1 19 | 20 | if [ "${SERVICE_RESULT}" = "success" ] 21 | then 22 | exit 23 | fi 24 | 25 | sudo /home/pi/electronic_badge_2018/bootup/mount_vsd_rw.sh 26 | echo "exit with error" > ${VSD_BASE_DIR}/boot-stop.log 27 | journalctl -b >> ${VSD_BASE_DIR}/boot-stop.log 28 | sudo /home/pi/electronic_badge_2018/bootup/mount_vsd_ro.sh 29 | 30 | echo "== exit with error ==" > ${SCRIPT_DIR}/boot-stop.log 31 | journalctl -u "nafuda-bootup" >> ${SCRIPT_DIR}/boot-stop.log 32 | 33 | cat ${SCRIPT_DIR}/boot-stop.log | \ 34 | grep -v "\-\- Logs begin" | \ 35 | grep -v "pam_unix(sudo:session)" | \ 36 | grep -v "bootup/mount_vsd_" | \ 37 | sed "s/nafuda-bootup.service: //" | \ 38 | sed "s/.*\]: //" | \ 39 | tail -20 | /home/pi/electronic_badge_2018/show_txt/show_txt.py - 40 | -------------------------------------------------------------------------------- /bootup/mount_vsd_ro.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | if [ 0 -ne ${EUID:-${UID}} ] 4 | then 5 | echo "You need to be root to perform this command." 6 | exit 1 7 | fi 8 | 9 | VSD_BASE_DIR=/mnt/virtual_sd 10 | VSD_IMG_PATH=/home/pi/virtual_sd.img 11 | 12 | set +e 13 | if [ ! 0 -eq `mount | grep -c ${VSD_BASE_DIR}` ] 14 | then 15 | umount ${VSD_BASE_DIR} 16 | fi 17 | mount -t vfat -o loop,sync,ro,noatime,dmask=000,fmask=111,iocharset=utf8,noauto ${VSD_IMG_PATH} ${VSD_BASE_DIR} 18 | set -e 19 | -------------------------------------------------------------------------------- /bootup/mount_vsd_rw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | if [ 0 -ne ${EUID:-${UID}} ] 4 | then 5 | echo "You need to be root to perform this command." 6 | exit 1 7 | fi 8 | 9 | VSD_BASE_DIR=/mnt/virtual_sd 10 | VSD_IMG_PATH=/home/pi/virtual_sd.img 11 | 12 | if [ ! 0 -eq `mount | grep -c ${VSD_BASE_DIR}` ] 13 | then 14 | umount ${VSD_BASE_DIR} 15 | fi 16 | mount -t vfat -o loop,sync,rw,noatime,dmask=000,fmask=111,iocharset=utf8,noauto ${VSD_IMG_PATH} ${VSD_BASE_DIR} 17 | -------------------------------------------------------------------------------- /bootup/nafuda-bootup.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NAFUDA bootup 3 | After=multi-user.target 4 | 5 | [Service] 6 | WorkingDirectory=/home/pi/electronic_badge_2018/bootup 7 | ExecStart=/home/pi/electronic_badge_2018/bootup/bootup.sh 8 | ExecStopPost=/home/pi/electronic_badge_2018/bootup/exec_stop_post.sh 9 | Type=oneshot 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | 14 | ## how to install 15 | # sudo cp nafuda-bootup.service /etc/systemd/system/ 16 | # sudo systemctl daemon-reload 17 | # sudo systemctl status nafuda-bootup 18 | # (check output 19 | # sudo systemctl enable nafuda-bootup 20 | # -------------------------------------------------------------------------------- /bootup/reset_hostname.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | # generate and reset hostname when found /mnt/virtual_sd/reset_hostname or default hostname. 3 | # generated hostname will be store in /mnt/virtual_sd/default_hostname.txt 4 | 5 | # force reset 6 | # $ sudo reset_hostname.sh -f 7 | 8 | if [ 0 -ne ${EUID:-${UID}} ] 9 | then 10 | echo "You need to be root to perform this command." 11 | exit 1 12 | fi 13 | 14 | . const.sh 15 | 16 | # どうしてもhostnameをraspberryに固定したい人用 17 | if [ -e ${VSD_BASE_DIR}/i_love_common_hostname ] 18 | then 19 | echo "raspberrypi" > /etc/hostname 20 | # add hosts 21 | # echo "\n127.0.0.1 raspberrypi" >> /etc/hosts 22 | /bin/hostname "raspberrypi" 23 | /bin/systemctl restart avahi-daemon.service 24 | exit 25 | fi 26 | 27 | 28 | ## 29 | ## check commands 30 | ## 31 | 32 | DO_RENAME=0 33 | 34 | # force switch check 35 | if [ $# -gt 0 ] && [ "$1" = "-f" ] 36 | then 37 | DO_RENAME=1 38 | fi 39 | 40 | # default hostname check 41 | if [ "raspberrypi" = `cat /etc/hostname` ] 42 | then 43 | echo "host name is default(raspberrypi)" 44 | DO_RENAME=1 45 | fi 46 | 47 | # switch check 48 | if [ -e ${VSD_BASE_DIR}/reset_hostname ] 49 | then 50 | echo "reset hostname requested" 51 | ${VSD_RW} 52 | /bin/rm ${VSD_BASE_DIR}/reset_hostname 53 | ${VSD_RO} 54 | 55 | DO_RENAME=1 56 | fi 57 | 58 | # exit when command not found. 59 | if [ ! $DO_RENAME -eq 1 ] 60 | then 61 | echo "hostname not changed." 62 | exit 63 | fi 64 | 65 | 66 | ## 67 | ## do reset 68 | ## 69 | 70 | ${VSD_RW} 71 | 72 | # ランダムHOSTNAMEを生成し、保存 73 | set +e # tweak: for openssl put "unable to write 'random state'" with systemd. FIY https://www.openssl.org/docs/faq.html#USER2 74 | NEW_HOSTNAME="nafuda-`/usr/bin/openssl rand -hex 2`" 75 | set -e 76 | echo "${NEW_HOSTNAME}" > ${VSD_BASE_DIR}/default_hostname.txt 77 | 78 | cp ${VSD_BASE_DIR}/default_hostname.txt /boot/default_hostname.txt 79 | 80 | # change hostname config 81 | cat ${VSD_BASE_DIR}/default_hostname.txt > /etc/hostname 82 | # add hosts 83 | echo "" >> /etc/hosts 84 | echo "127.0.0.1 `cat ${VSD_BASE_DIR}/default_hostname.txt`" >> /etc/hosts 85 | 86 | ${VSD_RO} 87 | 88 | # apply settings. 89 | /bin/hostname "${NEW_HOSTNAME}" 90 | /bin/systemctl restart avahi-daemon.service 91 | 92 | echo "hostname changed." 93 | -------------------------------------------------------------------------------- /bootup/reset_password.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | # generate and reset password when found /mnt/virtual_sd/reset_passwd or default pasword. 3 | # generated passwd will be store in /mnt/virtual_sd/default_passwd.txt 4 | 5 | # force reset 6 | # $ sudo reset_password.sh -f 7 | 8 | if [ 0 -ne ${EUID:-${UID}} ] 9 | then 10 | echo "You need to be root to perform this command." 11 | exit 1 12 | fi 13 | 14 | . const.sh 15 | 16 | # どうしてもpasswdをraspberryに固定したい人用 17 | if [ -e ${VSD_BASE_DIR}/i_love_common_password ] 18 | then 19 | echo "pi:raspberrypi" | /usr/sbin/chpasswd 20 | echo "password reset, use 'raspberrypi'" 21 | exit 22 | fi 23 | 24 | 25 | ## 26 | ## check commands 27 | ## 28 | 29 | DO_PASSWD_RESET=0 30 | 31 | # force switch check 32 | if [ $# -gt 0 ] && [ "$1" = "-f" ] 33 | then 34 | DO_PASSWD_RESET=1 35 | fi 36 | 37 | # check switch 38 | if [ -e ${VSD_BASE_DIR}/reset_passwd ] 39 | then 40 | DO_PASSWD_RESET=1 41 | ${VSD_RW} 42 | /bin/rm ${VSD_BASE_DIR}/reset_passwd 43 | ${VSD_RO} 44 | fi 45 | 46 | # check another switch 47 | if [ -e /boot/reset_passwd ] 48 | then 49 | DO_PASSWD_RESET=1 50 | /bin/rm /boot/reset_passwd 51 | fi 52 | 53 | # check default password. 54 | if [ "good" != `${SCRIPT_DIR}/check_bad_password.py` ] 55 | then 56 | DO_PASSWD_RESET=1 57 | fi 58 | 59 | # exit when command not found. 60 | if [ ! ${DO_PASSWD_RESET} -eq 1 ] 61 | then 62 | echo "password not changed." 63 | exit 64 | fi 65 | 66 | 67 | ### 68 | ### do reset. 69 | ### 70 | 71 | ${VSD_RW} 72 | 73 | # ランダム文字列をパスワード用に生成する 74 | set +e # tweak: for openssl put "unable to write 'random state'" with systemd. FIY https://www.openssl.org/docs/faq.html#USER2 75 | /usr/bin/openssl rand -base64 6 > /boot/default_passwd.txt 76 | set -e 77 | 78 | cp /boot/default_passwd.txt ${VSD_BASE_DIR}/default_passwd.txt 79 | /bin/echo "pi:`/bin/cat ${VSD_BASE_DIR}/default_passwd.txt`" | /usr/sbin/chpasswd 80 | 81 | ${VSD_RO} 82 | 83 | echo "password changed." 84 | -------------------------------------------------------------------------------- /bootup/virtual_sd_builder/build_img.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | # create new NAFUDA drive image and copy template files. 3 | 4 | if [ 0 -ne ${EUID:-${UID}} ] 5 | then 6 | echo "You need to be root to perform this command." 7 | exit 1 8 | fi 9 | 10 | BASE_DIR=`dirname "${0}"` 11 | 12 | . ${BASE_DIR}/../const.sh 13 | 14 | # remove old image. 15 | set +e 16 | if [ ! 0 -eq `mount | grep -c ${VSD_BASE_DIR}` ] 17 | then 18 | umount ${VSD_BASE_DIR} 19 | if [ ! $? -eq 0 ] 20 | then 21 | echo "unmount failed" 22 | fuser -muv ${VSD_BASE_DIR} 23 | echo "unmount failed disk busy `fuser -muv ${VSD_BASE_DIR}`" | show_txt - 24 | exit 1 25 | fi 26 | fi 27 | 28 | if [ -e ${VSD_IMG_PATH} ] 29 | then 30 | rm ${VSD_IMG_PATH} 31 | fi 32 | set -e 33 | 34 | # create loopback image. 35 | /bin/dd if=/dev/zero of=${VSD_IMG_PATH} bs=1MB count=64 36 | 37 | # format and set disk label 38 | /sbin/mkfs.vfat -n "NAFUDA" ${VSD_IMG_PATH} 39 | 40 | # make mount point 41 | if [ ! -e ${VSD_BASE_DIR} ] 42 | then 43 | /bin/mkdir ${VSD_BASE_DIR} 44 | fi 45 | 46 | ${VSD_RW} 47 | 48 | # copy default files 49 | /bin/cp -r ${BASE_DIR}/skel/* ${VSD_BASE_DIR} 50 | 51 | # copy docs(snapshot) 52 | /bin/cp -r ${BASE_DIR}/../../docs/* ${VSD_BASE_DIR}/docs/ 53 | 54 | # copy simple sample 55 | /bin/cp -r ${BASE_DIR}/../../simple_sample ${VSD_BASE_DIR}/ 56 | 57 | # recovery exists files from boot partition 58 | set +e 59 | /bin/cp -r /boot/default_* ${VSD_BASE_DIR} 60 | set -e 61 | 62 | /bin/umount ${VSD_BASE_DIR} 63 | 64 | exit 0 65 | -------------------------------------------------------------------------------- /bootup/virtual_sd_builder/skel/README.txt: -------------------------------------------------------------------------------- 1 | Please see 2 | 3 | https://github.com/builderscon/electronic_badge_2018 4 | 5 | thanks. 6 | -------------------------------------------------------------------------------- /bootup/virtual_sd_builder/skel/docs/0_THIS_DOCS_ARE_PROBABLY_NOT_THE_LATEST_VERSION.txt: -------------------------------------------------------------------------------- 1 | このディレクトリ内のdocsは最新版とはかぎりません! 2 | (git pullしても更新されません!) 3 | 4 | 最新版はこちら 5 | 6 | https://github.com/builderscon/electronic_badge_2018 7 | -------------------------------------------------------------------------------- /bootup/virtual_sd_builder/skel/img/0_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/bootup/virtual_sd_builder/skel/img/0_info.png -------------------------------------------------------------------------------- /bootup/virtual_sd_builder/skel/img/1_gadget_sponsor_mercari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/bootup/virtual_sd_builder/skel/img/1_gadget_sponsor_mercari.png -------------------------------------------------------------------------------- /bootup/virtual_sd_builder/skel/img/__CONVERT_TO_QR__.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/bootup/virtual_sd_builder/skel/img/__CONVERT_TO_QR__.png -------------------------------------------------------------------------------- /bootup/virtual_sd_builder/skel/wpa_supplicant.conf.sample: -------------------------------------------------------------------------------- 1 | ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev 2 | update_config=1 3 | country=JP 4 | ap_scan=1 5 | 6 | network={ 7 | ssid="WIFI SSID" 8 | psk="WIFI PASS" 9 | # scan_ssid=1 10 | } 11 | 12 | # network={ 13 | # ssid="OTHER WIFI SSID" 14 | # psk="OTHER WIFI PASS" 15 | # } 16 | -------------------------------------------------------------------------------- /docs/DIY_NAFUDA.md: -------------------------------------------------------------------------------- 1 | 自分で電子名札を作る方法 2 | ===================== 3 | 4 | 会期中に配布した電子名札をご自身で自作してみたい場合の情報です。 5 | 6 | # 部品を揃える 7 | 8 | - Raspberry pi zero wh 9 | - 販売店例: https://raspberry-pi.ksyic.com/main/index/pdp.id/406/pdp.open/406 10 | - Waveshare 400x300, 4.2inch E-Ink display module 11 | - 販売店例: https://www.robotshop.com/jp/ja/400x300-42-e-ink-display-module.html 12 | - 販売店例: https://www.waveshare.com/4.2inch-e-paper-module.htm 13 | - 販売店例: https://www.sengoku.co.jp/mod/sgk_cart/detail.php?code=EEHD-58US 14 | - microSD (配布品においては8GB) 15 | - 板(MDFなどで制作) 16 | - 配布時のものと意匠が少々異なりますが、`base_board` 内のデータを参照ください。伝導体でない素材であれば、なにでも構わないと思われます 17 | - バッテリー 18 | - 配布時のものと異なりますが、ダイソーの300円のモバイルバッテリーが形状として同一です 19 | - バッテリーホールド用のゴムバンド 20 | - 2本、幅6mm、折径80mm 21 | - 販売店例: https://www.amazon.co.jp/dp/B002P8YO84 22 | - USBケーブル 23 | - 配布したものは50cmのもの、充電専用ケーブルや、その他一部ケーブルはPCとラズパイで認識ができない場合があります。配布したものはセリアで販売されていた JAN:4560425569125 です 24 | - ネジ m2.6 6mm 25 | - 長ナット 15mm (ラズパイ固定用) 26 | - 長ナット 10mm (EPD固定用) 27 | - ネジロック剤(たとえばロックタイトなど、無くてもよい) 28 | 29 | # 組み立て 30 | 31 | 板にゴムバンドを取り付け、EPDとラズパイをネジとナットで板に固定し、EPD添付のケーブルを [`PIN_ASSIGN.md`](PIN_ASSIGN.md)のように接続します。 32 | 33 | > ※ ネジで固定時にネジロックを塗布するのはお好みで 34 | 35 | バッテリーをゴムバンドにはさみこむようにして固定し、USBケーブルを準備します。 36 | 37 | microSDを[`setup_script/README.md`](../setup_script/README.md)を参考に、別のPCで制作します。 38 | 39 | 制作したmicroSDを差し込み、起動し、[`setup_script/README.md`](../setup_script/README.md)を参考に初期設定してください。 40 | 41 | それで完成となります。 42 | 43 | 44 | # 注意 45 | 46 | 初期に導入されている「ネットワーク経由での画像アップロード(QRコードをスキャンして更新するもの)」は別途それらを動かすサーバーの用意が必要です。 47 | 48 | > ※ 会期中運用していたサーバーは終了いたします 49 | 50 | それをセットアップした後、`simple_nafuda`のコード内にあるURLをかきかえる必要があります。 51 | 52 | なお、サーバー側[`simple_nafuda_server`](../simple_nafuda_server) がそれですが、これのサポートは予定しておりません。 53 | 54 | 是非ご自身でもっと良い連携システムをつくってみてください! 55 | -------------------------------------------------------------------------------- /docs/EXTRA_INFO.md: -------------------------------------------------------------------------------- 1 | # 追加情報 2 | 3 | 以下はすべて無保証です。 4 | 5 | 6 | ## NAFUDAドライブの構造について 7 | 8 | 電子名札は`g_mass_storage`を用いることで、`/home/pi/virtual_sd.img`のloop deviceをMass Storage deviceとしてUSBポートへ公開しています。 9 | 10 | そのloop deviceは、別途 `/mnt/virtual_sd`にread onlyでマウントされており、起動時に`/home/pi/electronic_badge_2018/simple_nafuda`プログラムから読み込まれています。 11 | 12 | > ※ ラズパイとPC(母艦)両方から同時にマウントされるため、結構危険なやり方です。最低限の対処としてroでマウントしています。ラズパイからrwでもマウントできますが自己責任でお願いいたします。 13 | 14 | > ※ PCからファイルを書き込んでも、Linuxから見えるファイルには即時反映はされるとはかぎりません。再起動やumount/mountをして読み込み直す必要があります。 15 | 16 | 17 | ## ディレクトリ構成 18 | 19 | Raspberry pi側からから見たディレクトリ構成は以下の通りです。 20 | 21 | - `/home/pi/electronic_badge_2018` 各種コード、詳しくはそれぞれの内容をご確認ください 22 | - `/home/pi/virtual_sd.img` NAFUDAドライブの実態(`/mnt/virtual_sd`にループバックでマウントされます) 23 | - `/mnt/virtual_sd` NAFUDAドライブのイメージをマウントしたもの 24 | - `/mnt/virtual_sd/img` 名札に表示する画像ファイルが保存された、NAFUDAドライブのimgディレクトリ 25 | 26 | 27 | ## NAFUDAドライブをLinuxから読み書きするためには 28 | 29 | NAFUDAドライブは `/home/pi/virtual_sd.img` が実体で、readonlyでマウントされています。マウントは`bootup/bootup.sh`で行われています。 30 | 31 | rw(書き込み可)でマウントするには、`sudo ~/electronic_badge_2018/bootup/mount_vsd_rw.sh`を実行してください。 32 | 33 | ro(書き込み不可)でマウントするには、`sudo ~/electronic_badge_2018/bootup/mount_vsd_ro.sh`を実行してください。 34 | 35 | > ※ PCでNAFUDAドライブをマウントしつつ読み書きするとFSが壊れたりします。rwに変更する場合は自己責任でお願いいたします。 36 | 37 | ## E-paper仕様、オフィシャルライブラリ 38 | 39 | オリジナルのライブラリは以下からダウンロードできます。 40 | 41 | https://www.waveshare.com/wiki/4.2inch_e-Paper_Module 42 | 43 | > python以外に、C言語向けのサンプルコード・ライブラリがあります。 44 | 45 | > オリジナルのpythonライブラリは、python2用です。 46 | 47 | ## disk sizeについて 48 | 49 | 初期状態ではSDカードの容量を「一杯まで」つかっていません。 50 | 51 | ``` 52 | pi@raspberrypi:~ $ df -h 53 | Filesystem Size Used Avail Use% Mounted on 54 | /dev/root 1.7G 1.1G 471M 70% / 55 | ``` 56 | 57 | > ※ 大きい容量だと、microSDを焼くのがすごい時間がかかるからです! 58 | 59 | コマンドで拡張することができますが、以下は自己責任でお願いいたします。 60 | 61 | #### 方法1 62 | 63 | 以下のコマンドを実行するとサイズを広げる事ができます 64 | 65 | ```bash 66 | $ raspi-config --expand-rootfs 67 | ``` 68 | 69 | #### 方法2 70 | 71 | あるいは、`/boot/cmdline.txt`の最後に以下を追記し、再起動することでmicroSDのフルサイズである8GBまでパーティーションを拡大できます。 72 | 73 | ``` 74 | init=/usr/lib/raspi-config/init_resize.sh 75 | ``` 76 | 77 | ## 初期状態でBLEなどBluetoothが動作しない件について 78 | 79 | 起動時間の短縮と、ハードウェアシリアルを有効化する為にBTのserviceを一部無効にしています。 80 | 81 | そのため、`hcitool lescan`などを実行した際に以下のエラーが発生する事があります。 82 | 83 | ``` 84 | Could not open device: No such device 85 | ``` 86 | 87 | BLEを利用するには、ログインして`/boot/config.txt`ファイルの以下の該当行を消すかコメントアウトし、 88 | 89 | ``` 90 | enable_uart=1 91 | dtoverlay=pi3-miniuart-bt 92 | ``` 93 | 94 | 以下を実行して、一部サービスを有効化してください。 95 | 96 | ``` 97 | $ sudo systemctl enable hciuart.service 98 | ``` 99 | -------------------------------------------------------------------------------- /docs/FOR_HACKER.md: -------------------------------------------------------------------------------- 1 | # ハックしたい皆様へ 2 | 3 | もともと保証もないですが、ここからはさらに無保証です! 4 | 5 | ## コード 6 | 7 | 詳しくは 以下レポジトリを見てください。 8 | 9 | https://github.com/builderscon/electronic_badge_2018 10 | 11 | > ※ ログインができるなら、まずは`git pull`して更新することをおすすめします 12 | 13 | 14 | ## まずはログイン 15 | 16 | ログイン方法は`HOW_TO_LOGIN.md`を参照ください。 17 | 18 | 19 | ### ログインできない方へ 20 | 21 | ログインせず、NAFUDAドライブにプログラムをおいて起動する方法があります。 22 | 23 | `simple_nafuda`を参照してください。 24 | 25 | 26 | ## python以外で電子ペーパーを制御したい方 27 | 28 | オフィシャルのドキュメント、ライブラリはこちらから閲覧できます。 29 | 30 | [https://www.waveshare.com/wiki/4.2inch_e-Paper_Module](`https://www.waveshare.com/wiki/4.2inch_e-Paper_Module`) 31 | 32 | ただし、基本はCのライブラリなので、C以外で制御するならば以下の方法となります。 33 | 34 | - C拡張をつくる 35 | - データシートをみながら、自分で指定のSPI通信を実装する 36 | - 名札のサンプルプログラムにふくまれる `show_txt`, `show_img` コマンドラインツールを自分のプログラムから実行して代用する 37 | 38 | 基本的には、三番目の方法をおすすめします。 39 | 40 | 画像を生成して、どこかtmpなどに保存し、`show_img`にそのファイルを指定して実行し、表示する、のが一番簡単です。 41 | 42 | 43 | ## 「どうやってアプリをつくるか?」 44 | 45 | 46 | ### `simple_nafuda`、 `simple_sample`などを参考にしてアプリを作ります 47 | 48 | (TBD:寄稿希望) 49 | 50 | 51 | ### 自動起動を設定 52 | 53 | 一番簡単な方法は以下ファイルを作成し、起動する行を記載することです。 54 | 55 | `/mnt/virtual_sd/starup.sh` 56 | 57 | > ※ `/mnt/virtual_sd`はroでマウントされているので、rwでマウントしなおすか、NAFUDAドライブ経由で保存して再起動してください。 58 | 59 | > ※ `startup.sh`を作成すると、`simple_nafuda`が起動しなくなります 60 | 61 | 62 | ### もっと細かい起動方法、systemdをつかう 63 | 64 | > ※ ここでは細かいsystemdの説明はいたしません。 65 | 66 | `/mnt/virtual_sd/starup.sh` 67 | 68 | を空で作成して`simple-nafuda`の起動抑制し、独自のsystemd serviceを追加します。 69 | 70 | > `simple_nafuda`はsystemdに`system-nafuda` serviceとして登録されています。 71 | 72 | その際には、 73 | 74 | ``` 75 | After=nafuda-bootup 76 | ``` 77 | 78 | とserviceの設定に追記しておくと、`nafuda-bootup`サービス完了後に自動的にサービスが起動します。 79 | 80 | > ※ simple-nafudaはnafuda-bootupから起動される都合上、Afterの指定をしていません。 81 | 82 | -------------------------------------------------------------------------------- /docs/G_ETHER_WITH_WINDOWS.md: -------------------------------------------------------------------------------- 1 | g_gadgetをもちいて、Windowsから名札へssh接続する例 2 | ======================================== 3 | 4 | あくまでもこちらの操作は「このようにやればできる事がある」という例で、必ずできるとは限りません。 5 | 6 | 別の機器のドライバを手動で当てるため、自己責任でお願いいたします。 7 | 8 | ## 名札を接続する 9 | 10 | Windowsに名札を接続し、認識した後に「USB シリアル デバイス」として認識される 11 | 12 | > ※ もしそうでなく「USB Ethernet/RNDIS Gadget」として正しく認識されていれば、これらの操作は不要なのでsshの項目までスキップしてください。 13 | 14 | ## ドライバの取得 15 | 16 | http://www.catalog.update.microsoft.com/home.aspx 17 | 18 | こちらのページより、RNDIS gadgetで検索。 19 | 20 | ![](assets/g_ether_with_windows/1.png) 21 | 22 | - Acer Incorporated. - Other hardware - USB Ethernet/RNDIS Gadget Windows 7,Windows 8,Windows 8.1 and later drivers Drivers (Other Hardware) 23 | - 2010/01/13 24 | - 1.0.0.0 25 | - 21 KB 26 | 27 | の項目のダウンロードをクリック。 28 | 29 | ![](assets/g_ether_with_windows/2.png) 30 | ![](assets/g_ether_with_windows/3.png) 31 | 32 | ダウンロードしたcabファイルをひらき、中の2ファイルを適当な所にコピーする(たとえば、デスクトップに新しくフォルダをつくり、その中になど) 33 | 34 | ![](assets/g_ether_with_windows/4.png) 35 | 36 | ## ドライバのインストール(更新) 37 | 38 | デバイスマネージャをひらき、USB シリアルデバイスとして認識された「USB シリアルデバイス」を右クリックし、「ドライバーの更新」をクリック 39 | 40 | ![](assets/g_ether_with_windows/5.png) 41 | 42 | ドライバーの検索方法は「コンピューターを参照して、ドライバーソフトウェアを検索」を選ぶ 43 | 44 | ![](assets/g_ether_with_windows/6.png) 45 | 46 | 「参照」をおす 47 | 48 | ![](assets/g_ether_with_windows/7.png) 49 | 50 | 先程ファイルをコピーしたディレクトリを選ぶ 51 | 52 | ![](assets/g_ether_with_windows/8.png) 53 | 54 | 「次へ」をクリック 55 | 56 | ![](assets/g_ether_with_windows/9.png) 57 | 58 | うまく行けば、ここで「USB Ethernet/RNDIS Gadget」として認識する 59 | 60 | ![](assets/g_ether_with_windows/10.png) 61 | ![](assets/g_ether_with_windows/11.png) 62 | 63 | ipconfigにも「イーサネット アダプター イーサネット2」などと現れる(この確認は不要) 64 | 65 | ![](assets/g_ether_with_windows/12.png) 66 | 67 | ## ssh接続 68 | 69 | Tera Termなどで、`169.254.123.45`に接続する 70 | 71 | ![](assets/g_ether_with_windows/13.png) 72 | 73 | > ※ 特にドライバーインストール直後、Windowsと接続した後つないだ後はIPが変わる事がある。もしこのIPで接続ができなければ、もう一度名札を再起動し、もう一度enable_g_etherをつくり、さらにPCと接続して再起動させ、再度接続を試行する。 74 | 75 | > ※ 再起動した直後もつながらないことが多い、1〜3回くらいTera Termで接続を試行しているうちにつながる事が多い。 76 | 77 | 接続ができると、このような警告が出た後、ID/PASSを入力できるようになる。 78 | 79 | ![](assets/g_ether_with_windows/14.png) 80 | ![](assets/g_ether_with_windows/15.png) 81 | ![](assets/g_ether_with_windows/16.png) 82 | 83 | sshでつながれば、同様にsftpやscpなどもつかえるので、WinSCPなどを使い、名札の中にファイル転送などもできる。 84 | 85 | -------------------------------------------------------------------------------- /docs/HOW_TO_LOGIN.md: -------------------------------------------------------------------------------- 1 | # 名札へのログイン方法 2 | 3 | 名札のラズパイ(Raspberry Pi Zeo wh)にログインするための方法を紹介します。 4 | 5 | こちらはアドバンスドな方法となりますので、自己責任の作業でお願いいたします。 6 | 7 | ## ログインするには? 8 | 9 | 添付の物品だけで行う場合は以下の方法があります。 10 | 11 | 1. usb gadget etherを用いてsshログイン 12 | 2. wifiでsshログイン 13 | 3. usb gadget serialでログイン 14 | 15 | > ※ Windowsの場合、1の`g_ether`はあまりおすすめできません。 16 | 17 | ## 共通情報 18 | 19 | ### ID/PASS 20 | 21 | まず、NAFUDAドライブの中のファイルを確認し、パスワードを別の所にメモしてください。 22 | 23 | - ログインユーザー名 : `pi` 24 | - ログインパスワード : NAFUDAドライブ内 `default_passwd.txt`に記載されています。 25 | - ホスト名(Bonjourで利用可能) : NAFUDAドライブ内 `default_hostname.txt`に記載されています。 26 | 27 | > ※ パスワードは起動時の情報表示でも表示されます。通常設定だと数秒後`simple-nafuda`が起動するので、スマホで撮影などしてください。 28 | 29 | > ※ 通常の設定においてはパスワードは初回の起動時に生成されるため、共通のマスターパスワードは存在しません。 30 | 31 | ### rootについて 32 | 33 | rootでログインはできません。 34 | 35 | `pi` ユーザーはパスワード無しで`sudo`ができます。 36 | 37 | rootのシェルが必要な場合には、`sudo su`をしてください。 38 | 39 | ### 「パスワードを忘れた」 40 | 41 | - まず、ログインIDは`pi`です。 42 | - NAFUDAドライブの`default_passwd.txt`に初期パスワードは保存されています、こちらをためしてください。 43 | - 名札として起動が可能なら、NAFUDAドライブに`reset_passwd`という空のファイルを作成し、名札を再起動してください。 44 | - いずれもうまくいかない場合は、別ドキュメントの「初期化」を参照してください。 45 | 46 | 47 | ### 「名札を取り外す」の意味 48 | 49 | 電子名札をNAFUDAドライブとして認識するようにPCに接続した後は、 50 | PCでアンマウントや、イジェクトや、「取り外し」操作をした後、LEDの点滅なないことを確認してからUSBケーブルを抜いてください。 51 | 52 | アンマウントをせずに抜き差しをすると、NAFUDAドライブが壊れる事があります。 53 | 54 | > ※ 何も操作せずに抜き差しすると、PC上で警告も表示されます。 55 | 56 | 57 | ## 1. usb gadget etherを用いてsshログイン 58 | 59 | > ※ このモードにした際には、利便性のために情報表示画面で停止し、名札は起動しません。`sudo systemctl start simple-nafuda`で起動することができます。 60 | 61 | NAFUDAドライブの直下に`enable_g_ether`というファイルを作ります(中身は空でかまいません)。 62 | 63 | ``` 64 | Macでの例 65 | $ touch /Volumes/NAFUDA/enable_g_ether 66 | ``` 67 | 68 | > ※ テキストエディタやExplorerなどで作成する場合は、拡張子が自動的に付与されないように注意してください。 69 | 70 | > ※ 正しく設定されていると、起動時の情報表示で`usb gadget mode: g_ether`と表示されます。 71 | 72 | 名札を取り外して、再度PCに接続してください。しばらくするとPCにネットワークインターフェイスとして認識されます。 73 | 74 | > ※ Windowsの場合はネットワークインターフェイスとして認識させるのに様々な手法があり「公式」な手法はありません。 75 | > [ドライバーを導入して認識させる例をこちらに記載いたします](G_ETHER_WITH_WINDOWS.md)が、うまくいかない事もあります。 76 | > うまくいかない場合は、`g_ether windows ssh`などでネットを検索して、お手元のWindowsで正しく動作する方法を探してみてください。 77 | 78 | 79 | 80 | `169.254.123.45`のIPアドレスにsshしてください 81 | 82 | ``` 83 | 例 84 | $ ssh pi@169.254.123.45 85 | ``` 86 | 87 | > ※ `g_ether`時にはIPアドレスを固定しています。もし、複数の名札をおもちの場合は注意してください。 88 | 89 | > ※ 接続するPCでDHCPなどを用意したり、インターネット共有を用いる場合は `~/electronic_badge_2018/bootup/bootup.sh`の`ifconfig usb0 169.254.123.45/16 # IP固定`行をコメントアウトや削除してください。 90 | 91 | もし、上記IPアドレスに接続ができず、pingなども通らない場合は以下を確認してください。 92 | 93 | - ただしく `enable_g_ether` ファイルが作成できているか(NAFUDAドライブとして認識される場合はできていない) 94 | - 接続しているUSBポートが正しく内側か 95 | - USBケーブルが充電専用でないか 96 | - (Macの場合)コントロールパネル>ネットワークにて `RNDIS/Ethernet Gadget`が認識されているか 97 | - OS側でドライバのインストールが正しくできているか 98 | 99 | > ※ 起動時に自動的に`enable_g_*`は削除され、次の起動時にはUSBドライブとして認識するモードへ戻ります。 100 | 101 | 102 | ## 2. wifiでsshログイン 103 | 104 | > ※ WifiのAP(アクセスポイント)がいわゆる「プライバシーセパレータ」有効などで、接続している端末同士の通信を禁止している場合は接続ができません。 105 | 106 | > ※ Wifiが別途ブラウザでのアクセスを必要とするような場合は、以下方法では接続できません。 107 | 108 | > ※ Wifiの提供があるカンファレンス会場では、自前のWifiアクセスポイントはできるだけオフにしましょう。 109 | 110 | お手持ちのスマホやPCなどをインターネット共有やテザリングモードにして、Wifi APとして有効にします。 111 | 112 | 電子名札をドライブとしてマウントし、`wpa_supplicant.conf.sample` ファイルを `wpa_supplicant.conf`としてコピーし、エディタで内容を編集します。 113 | 114 | ``` 115 | ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev 116 | update_config=1 117 | country=JP 118 | ap_scan=1 119 | 120 | network={ 121 | ssid="ここにあなたの接続したいWifiのSSID" 122 | psk="ここにあなたの接続したWifiのパスワード" 123 | # scan_ssid=1 # ステルスAPの場合に必要な場合があります 124 | } 125 | ``` 126 | 127 | 保存した後名札を再起動してください。再起動後、設定ファイルは`/etc/wpa_supplicant`に移動され、名札は自動的にWifiに接続します。 128 | 129 | 適切なネットワークであれば生成されたホスト名をBonjourで検索できるため、ホスト名でもログインできるようになります。 130 | `nafuda888`の場合`nafuda888.local`で接続ができるようになります。 131 | 132 | あるいは、起動時の情報表示に表示される`ip:`欄で `wlan0`に付与されたIPアドレスを確認してください。 133 | 134 | > ※ Wifiの状況によっては、起動時までにip取得が間に合わず、表示されないことがあります 135 | 136 | > ※ Wifiはどのモードにおいても有効になり、手軽にオフにする方法やフライトモードは提供していません。必要ならば設定を消すなどしてください。 137 | 138 | > ※ ステルスAPなどの場合で接続ができなければ、`scan_ssid`行のコメントをはずしてみてください。 139 | 140 | ## 3. usb gadget serialでログイン 141 | 142 | > ※ このモードにした際には、利便性のために情報表示画面で停止し、名札は起動しません。`sudo systemctl start simple-nafuda`で起動することができます。 143 | 144 | `g_ether`同様に`enable_g_serial`ファイルを作成し、名札をPCにつなぎ直し、再起動します。 145 | 146 | 後述の各種方法で接続した後、Enterなどを一度叩くとログインプロンプトなどが表示されます。 147 | 148 | ### Macの場合 149 | 150 | `/dev/cu.usbmodem1421`などのファイルができますので、`ls /dev/cu*`で探してください。 151 | 152 | デバイスファイルがみつかれば、`cu`コマンドや`screen`コマンドなどで接続が可能です。 153 | 154 | screenを利用する場合には、以下のようにします。(cuはLinuxの場合を参考にしてください) 155 | 156 | ``` 157 | $ screen /dev/cu.usbmodem1421 158 | 159 | # 必要があってbaud を指定するなら例 160 | $ screen /dev/cu.usbmodem1421 9600 161 | ``` 162 | 163 | > ※ screenの終了は、デフォルトではctrl+a , ctrl-k で「Really kill this window y/n」と質問されるので、yをおします。 164 | 165 | > ※ screen等は切断したタイミングによって、端末入出力オプションが崩れたままになることがあります。ターミナルソフトを終了するか、「(Enter) `reset`(Enter) `clear`(Enter)」等と(表示されなくとも)つづけてコマンドを入力することで、多くの場合復帰できます。 166 | 167 | ### Linuxの場合 168 | 169 | `cu`や`screen`などを用いてください。`/dev/ttyACM0`は環境によって異なる場合があります。 170 | 171 | `Connected.`などと表示されたら、一回Enterを打ってください。 172 | 173 | ``` 174 | $ sudo cu -l /dev/cu.ttyACM0 175 | Password: 176 | Connected. 177 | 178 | Raspbian GNU/Linux 9 nafuda-xxxx ttyGS0 179 | nafuda-xxxx login: 180 | 181 | # 必要があってbaud を指定するなら例 182 | $ sudo cu -l /dev/cu.ttyACM0 115200 183 | ``` 184 | 185 | > ※ cuの終了は、`~.`とタイプしてEnterです。 186 | 187 | ### Windowsの場合 188 | 189 | デバイスマネージャからCOMポートを確認し、Teratermなど通信ターミナルをもちいてください 190 | 191 | - [`enable_g_serial`を作成し、名札を取り外す](assets/g_serial_win_1.png) 192 | - [再度さしこむと、Serialとして認識開始(ドライブは表示されません0](assets/g_serial_win_2.png) 193 | - [認識完了し、COMポート番号が表示される](assets/g_serial_win_3.png) 194 | - [デバイスマネージャからもCOMポート番号は確認可能です](assets/g_serial_win_4.png) 195 | - [Tera Termなどをつかい、COMポート番号を指定して接続](assets/g_serial_win_5.png) 196 | - [ログインプロンプトが表示されるので、ID/PASSを入力してログイン](assets/g_serial_win_6.png) 197 | - [ログイン成功の様子](assets/g_serial_win_7.png) 198 | 199 | > ※ Tera Termは[こちらなどからダウンロードできます](https://forest.watch.impress.co.jp/library/software/utf8teraterm/) 200 | 201 | > ※ 起動時に自動的に`enable_g_*`は削除され、次の起動時にはUSBドライブとして認識するモードへ戻ります。 202 | 203 | 204 | ## 補足:アドバンスドな方法、USB to Serial(UART)を接続する 205 | 206 | USB シリアル変換ケーブル(amazonなどで1000円くらい)を接続することで、OSのブートアップ中から確認できるように設定がなされています。 207 | 208 | かならず3.3Vのものを使用し、TX,RX,GNDのみを接続して電源ラインは接続しないことをおすすめします。 209 | 210 | > 例 https://www.switch-science.com/catalog/1196/ 211 | 212 | > 設定例 https://learn.adafruit.com/adafruits-raspberry-pi-lesson-5-using-a-console-cable/overview 213 | 214 | 215 | ## 補足:g_etherなどのモードを固定したい場合 216 | 217 | もし再起動しても`g_mass_storage`モードにもどしたくない場合は、microSDを別のPCでマウントし`boot`ドライブの`cmdline.txt`の`modules-load`指定に、`,g_ether`などと追記することで固定ができます。 218 | 219 | ``` 220 | # 修正前 221 | 省略) modules-load=dwc2 222 | # 修正後 g_ether例 223 | 省略) modules-load=dwc2,g_ether 224 | ``` 225 | 226 | その上で、`boot`ドライブに`startup.sh`を作成し、以下をを追記します。 227 | 228 | #### g_etherの場合 229 | 230 | ```bash 231 | ifconfig usb0 up 232 | ifconfig usb0 169.254.123.45/16 # IP固定 233 | ``` 234 | 235 | #### g_serialの場合 236 | 237 | ``` 238 | systemctl start getty@ttyGS0.service 239 | ``` 240 | 241 | > ※ 起動時、`bootup.sh`が呼ばれた時点で`g_mass_storage`,`g_ether`,`g_serial`いずれかのmoduleがロードされていた場合には、`bootup.sh`内でモジュールをロードしません。 242 | 243 | > ※ `g_multi`, `g_cdc`などは十分な検証ができませんでしたので対象外としています。もし指定したい場合には`bootup.sh`を修正するなどご自身でトライしてみてください。 244 | 245 | もし、この固定を戻したい場合には、再度microSDをマウントし、`cmdline.txt`と`startup.sh`の記述を削ってください。 246 | 247 | -------------------------------------------------------------------------------- /docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | # LICENSE 2 | 3 | ## 電子名札のmicroSDにプリインストールされたOSなどソフトウェア群 4 | 5 | > ※ これらは配布された名札のmicroSD、およびReleasesのイメージに関わります。 6 | > ※ 当時配布されたものと、最新版は異なる場合があります。 7 | 8 | 電子名札のmicroSDにインストールされた環境は、以下のバージョンのraspbianの上に構築されています。 9 | 10 | ``` 11 | Raspbian Buster Lite 12 | Minimal image based on Debian Buster 13 | Version:September 2019 14 | Release date:2019-09-26 15 | ``` 16 | 17 | それらのソフトウェアは以下をたどり、ダウンロード、ソースコードの取得が可能です。 18 | 19 | - https://www.raspbian.org/ 20 | - https://www.raspberrypi.org/downloads/raspbian/ 21 | 22 | 内容されているソフトウェアのライセンスは、ログイン後に以下ファイル群から確認できます。 23 | 24 | `/usr/share/doc/*/copyright` 25 | 26 | ## `lib` 以下の一部ファイルについて 27 | 28 | - `epd4in2.py` オフィシャルのライブラリを修正し、再配布 29 | - `epdif.py` オフィシャルのライブラリからの再配布 30 | 31 | オフィシャルのファイルは以下からもダウンロードできます。 32 | 33 | - https://www.waveshare.com/wiki/File:4.2inch_e-paper_module_code.7z 34 | - https://www.waveshare.com/wiki/4.2inch_e-Paper_Module 35 | 36 | 37 | ## 上記以外の`~/electronic_badge_2018`以下の電子名札のソフトウェア、ドキュメント、データ 38 | 39 | 上記を除き、本レポジトリにふくまれる「ソースコード」はそれぞれのファイルに記載されたライセンスとなります。 40 | 41 | > 特別な表記のないかぎり、MITライセンスです。 42 | 43 | README.md, 及びdocs内の「ドキュメント」は特別な表記のないかぎりCC BY-SA 4.0です。 44 | 45 | 名札サンプル画像などの「画像ファイル」については 46 | 47 | - mercari のロゴが含まれた画像(`gadget_sponsor_mercari.png`)は再配布禁止です。 48 | - builderscon ロゴ(とテキスト)で構成された画像はCC BY-SAです。 49 | -------------------------------------------------------------------------------- /docs/PIN_ASSIGN.md: -------------------------------------------------------------------------------- 1 | ピンアサイン 2 | ========== 3 | 4 | ケーブルが抜けた際には、こちらの情報を元にしてさしなおしてください。 5 | 6 | ## E-paper コネクタ側 7 | 8 | ![](assets/cable_epd_side.jpg) 9 | 10 | > こちらはコネクタのリブ(突起)の見える面です 11 | 12 | |1|2|3|4|5|6|7|8| 13 | |---|---|---|---|---|---|---|---| 14 | |紫|白|緑|橙|黄|青|黒|赤| 15 | |BUSY|RST|DC|CS|CLK|DIN|GND|3.3V| 16 | 17 | この配置は、リブのある面を上から見たものです。 18 | 19 | e-paper ディスプレイを画面の裏を見たとき、リブのある面が見えるように接続してください。 20 | 21 | ## Raspberry pi ピンアサイン 22 | 23 | ![](assets/cable_rpz_side.jpg) 24 | 25 | > ピン周りが見やすいように、板から取り外して撮影しています。 26 | 27 | | | | | | | | | | | | | | | | | | | | | | 28 | |---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| 29 | | | | | | | | | |紫| |緑|橙| | | | | | | | | 30 | | 2| 4| 6| 8|10|12|14|16|18|20|22|24|26|28|30|32|34|36|38|40| 31 | | 1| 3| 5| 7| 9|11|13|15|17|19|21|23|25|27|29|31|33|35|37|39| 32 | |赤| | | | |白| | | |青| | 黃| | | | | | | |黒| 33 | 34 | この配置は、ピンを差し込む面(チップなどが見える面)をみながら、microSDスロットを左にして見たものです。 35 | 36 | > ※ Raspberry piのGPIOピンの意味は、`raspberry pi zero wh gpio`で検索したり、公式サイトなどを参照ください。 37 | 38 | > ※ 「遠く離れた」1番から3.3V、39番からGNDをとっているのは、ただわかりやすさの都合です。 -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # NAFUDA 2 | 3 | 電子名札の取扱説明書です。 4 | 5 | ## このドキュメントは最新版ではないかもしれません 6 | 7 | 最新版のドキュメントは以下のURLにアクセスしてご確認ください。 8 | 9 | > ※ あなたが今GitHubのサイトでみているならば最新版です。 10 | 11 | https://github.com/builderscon/electronic_badge_2018 12 | 13 | 14 | ## 最初にご注意いただきたいこと 15 | 16 | - 添付のモバイルバッテリーは絶対に充電しながらラズパイへ給電しないでください。 17 | - 電子名札は精密機械です。濡らしたり、落としたり、力をかけたりしないでください。 18 | - 尖った部分があり、静電気は大敵です。取扱には注意してください。(あるいはケースを自作しましょう) 19 | - microSDや各種ケーブルは接着固定等はされていません、抜けないように注意して取扱ください。(あるいはテープなどで止めましょう) 20 | 21 | 22 | ## 同梱物内容 23 | 24 | - ベースボード(木の板) 25 | - Raspberry pi zero wh(取り付け済み) 26 | - e-Paper ディスプレイ(取り付け済み) 27 | - e-Paper <-> Raspberry pi 接続ケーブル(取り付け済み) 28 | - microSD 8GB(取り付け済み) 29 | - microSD、SD変換アダプタ(同梱) 30 | - USB充電専用ケーブル(白、同梱) 31 | - USB通信ケーブル(白あるいは黒、同梱) 32 | 33 | ※ 名札として利用するために首からさげるストラップは、受付時に配布される通常のストラップをご利用いただくか、あるいは適当な紐をご用意ください。 34 | 35 | 36 | ## 各部解説 37 | 38 | ![](assets/front_image.jpg) 39 | 40 | ![](assets/back_image.jpg) 41 | 42 | 43 | ## サポートにつきまして 44 | 45 | ビルダーズコン運営により、初期不良のみ対応をさせていただきます。 46 | (木の板以外は汎用部品ですので、補修パーツを購入し、ご自身での修理が可能です) 47 | 48 | builderscon tokyo 2018当日に動作するかを確認し、問題があれば受付スタッフにお声がけください。 49 | 50 | ハック方法などのアドバンスドなご相談は、制作者が個人的に対応致します。 51 | 52 | 53 | ## セキュリティについて 54 | 55 | この名札は初回起動時にパスワードを生成しますが、起動直後の情報画面にはそれが表示されます。安易に人に見せないようにご注意ください。 56 | 57 | ネットワーク経由での画像アップロードを実現するQRコードはを他人にスキャンされないようにご注意ください。 58 | 59 | NAFUDAは便利さのために、「高いセキュア」ではありません、機密情報はいれないようにご注意ください。 60 | 61 | 62 | パスワードは初期生成されますが、sshホスト鍵はすべての名札で共通です(製造上の都合です、これは不特定多数がログインできるマスターキーではありません。ただし、IP乗っ取りなどで別の名札に接続しようとしていることに気づけ無いかも知れません)可能ならば、ログインして再生成をお願いいたします。 63 | 64 | ``` 65 | # 再生成例 66 | $ sudo rm /etc/ssh/ssh_host_*key* 67 | $ sudo dpkg-reconfigure openssh-server 68 | ``` 69 | 70 | 71 | ## 画面に表示される `default_passwd.txt` について 72 | 73 | ユーザーがログインして、passwdコマンドでpiユーザーのパスワードを変更した場合、それに追随はされません。(昔の、初期のパスワードがでつづけます) 74 | 75 | 76 | 77 | ## 電源のオンオフ 78 | 79 | ![](assets/connect_center_usb_port_before.jpg) 80 | 81 | 82 | 基盤部分にUSBケーブルを挿すと電源オン、抜くとオフです。初期状態での初回起動には約1〜2分かかります。(4~5分たっても起動しない、LEDの点滅が1分近くおこらない場合、なにかがおかしいと思われます) 83 | 84 | 基盤部分(Raspberry pi zero wh部分)には2つのmicro USBコネクタがあり、左側(外に近い側)が電源供給専用、右側(中央寄り側)がPC等との通信兼用ポートです。どちらにUSB電源を接続しても電源がオンとなります。 85 | 86 | 電源がはいると緑色のLEDが点灯します。LEDは(デフォルトでは)microSDアクセス時に点滅します。つかない場合は、USBバッテリーが放電していないか、microSDが抜けていないかをご確認ください。 87 | 88 | 電源投入後、しばらく(〜数分)まつとプログラムが起動し、電子ペーパーに画像などが表示されます。初期状態ではロゴが繰り返し表示されます。もし起動しない場合にはmicroSDの初期化が必要です。(詳しくは`FOR_HACKER.md`を参照ください) 89 | 90 | 電源を切るには、LEDが点滅していないことを確認した上でUSB電源を抜きます。 91 | 92 | ※ 適切な手段をせず(たとえばshutdown -h nowせず)いきなり電源を抜くのは(製作者も)どうかと思いますが、コストの都合です。ぜひご自身でシャットダウンスイッチを実装などしてみてください! 93 | 94 | 95 | ## 電子名札の簡単画像設定 96 | 97 | 1. USBメモリドライブ(USB Mass Storage Class)を認識できるPC等に電子名札を接続してください。その時にPCへと接続するUSBポートは右側(中央寄り)です。 98 | 2. 名札の起動を待ちます(名札が起動完了後、認識されます) 99 | 3. PCに`NAFUDA`という名称のドライブが現れますので、その中の`img` ディレクトリに好きな画像(png,jpeg形式)をコピーしてください。 100 | 4. コピーが終わったら、ドライブを一般的なUSBメモリ同様にイジェクト操作し、少し待ってLEDの点滅がないことを確認してから電子名札とPCのUSBケーブルを抜いてください。 101 | 5. USBバッテリーなどの電源をつなぎ、名札を起動してください。(電子名札として動作させる際には、左側(外側)のUSBポートにつなぐことをおすすめします。 102 | 6. imgに保存した画像が、スライドショー表示されます。名札をエンジョイしてください! 103 | 104 | > ※ 画像のファイル名は英数小文字で設定ください。 105 | 106 | > ※ 画像ファイルは長辺1000px以下を設定ください。 107 | 108 | > ※ `img`ディレクトリを含む`NAFUDA`ドライブの中身は初期化される場合があります、かならずPC側に元ファイルを保持してください。 109 | 110 | > ※ 想定駆動時間はバッテリー満充電時は約6時間ですが、利用状況や画像の種類などによって大きく変動します。 111 | 112 | 113 | ### 仕様など 114 | 115 | - 名札の電子ペーパー解像度は 300x400 pxです(縦の場合)。 116 | - 電子ペーパーはモノクロ(2値)です。 117 | - 初期に動作している`simple-nafuda` サンプルプログラムは、画像がディスプレイサイズを超過する場合は自動的に縮小し、サイズが不足する場合は中央寄せします。カラーやグレイスケール画像はディザリングで2値に変換します。 118 | - スライドショーのインターバル速度は設定変更できません。 119 | - (というより、シリアルデータ転送に時間がかかるのでこれが最速です。より高速な描画をする場合にはC言語を用いたサンプルプログラムを検討してください!詳しくは`FOR_HACKER.md`を参照ください。) 120 | 121 | 122 | 123 | ## 電子名札をハックせんとするハッカーの皆さんへ 124 | 125 | 名札をハックしたい!という方は、[`HOW_TO_LOGIN.md`](HOW_TO_LOGIN.md)や[`FOR_HACKER.md`](FOR_HACKER.md)を確認ください。ログイン方法などを記載しています。 126 | 127 | それでは楽しい電子名札、謎ガジェットライフを! 128 | -------------------------------------------------------------------------------- /docs/TROUBLESHOOT.md: -------------------------------------------------------------------------------- 1 | トラブルシュート 2 | ============== 3 | 4 | ## FAQ 5 | 6 | - ケーブルをさしてもLEDが光らない → microSDが刺さっているか見てください 7 | - ケーブルをPCとつないでも、認識しない → 接続するPCのポートをかえてみてください、再起動してみてください。 8 | - ケーブルをPCとつないでも、認識しない → ケーブルは最初からツイているものですか?お手持ちのUSBケーブルを使っている場合、それは充電専用ではないですか?もしケーブルをなくした場合には、スタッフにお声がけください、予備があればお渡しいたします。 9 | - USBとPCをつなぎ、 NAFUDAドライブにファイルをコピーしたが、ファイルが消える → ファイルコピーした後20秒たってから取り外し作業をおこなってください。環境によっては書き込みが遅延するようです。あるいはすこし調整しましたので、最新ファームに更新してみてください。 10 | 11 | ## 画面に表示がされない、名札スライドショーが開始されない 12 | 13 | - ラズパイのLEDが光っていることを確認してください、光っていない場合は、電源とmicroSDの接続を確認してください。 14 | - 電源(USBバッテリー)が点灯していることを確認してください、点灯していない場合はバッテリー切れの可能性があります。 15 | - e-paperとraspberry piのケーブルがぬけていないか確認してください。抜けている場合は適切に挿しなおしてください。 16 | - E-paperのコネクタが接触不良をおこしていることがあります、E-paper側のコネクタを抜き差ししてみてください(固く、取り外しが難しいので、注意して行ってください)。 17 | - 起動には1~3分程度かかります。 18 | - 起動しているが、表示上わからない場合があります。ログインしてみてください。 19 | - `img`内の画像を変えてみてください(画像読み込みに失敗している可能性があります)。 20 | 21 | 22 | ## NAFUDAドライブをPCで認識しない 23 | 24 | - 起動には1〜3分かかります。USBをPCと接続した後、名札が起動するまで待ってください。 25 | - ただしく中央よりのUSBポートに接続していることを確認してください。 26 | - USBケーブルの相性かもしれません、別のUSBケーブルを試してください。 27 | 28 | 29 | ## NAFUDAドライブにエラーがあると表示される(特にWindowsにおいて) 30 | 31 | > ※ microSDを直接PCにつないだ場合は、このようなフォーマットをおこなうと起動しなくなります。 32 | 33 | - OSの指示に従ってスキャンや復旧を行ってください。(特に、最初の一回は出る可能性が高いです) 34 | - もしスキャンに失敗した場合は、名札を取り外して再度接続してください。NAFUDAドライブが壊れている場合は、起動中に自動的に復旧を試みます。 35 | - もしフォーマットをした場合、「NAFUDAドライブを初期化する」を元にNAFUDAドライブを初期状態にもどすことはできます。 36 | 37 | 38 | # 初期化 39 | 40 | ## NAFUDAドライブを初期化する 41 | 42 | NAFUDAドライブに`vsd_rebuild`というファイルを作成して、名札を再起動してください。 43 | 44 | 初期化されると、サンプルファイルなどもあたらしく生成されます。 45 | 46 | > ※ 詳細は`/bootup/README.md`の`vsd_rebuild`項目を参照してください。 47 | 48 | > ※ これはNAFUDAシステムの完全初期化ではありません。NAFUDAドライブの外にあるファイルや、ID/PASSが初期化されるわけではありません。 49 | 50 | NAFUDAドライブをPCで初期化(フォーマット)すると、正しく動作しなくなる可能性があります。 51 | 52 | 53 | ### TIPS: ファイルの作成の仕方がわからない、うまく制作できない場合 54 | 55 | シェルからtouchしたり、テキストエディタで新規作成したファイルを保存してもかまいませんが、`vsd_rebuild`のファイルは中身は何でもよいので、適当なファイルをコピーしてリネームしても構いません。 56 | 57 | たとえば、Macの場合はNAFUDAドライブをひらき、適当なファイル(ここでは`README.md`)を複製することでも作れます。 58 | 59 | > ※ ファイルである必要があります、ディレクトリを複製してはいけません。 60 | 61 | ![](assets/create_file_1.jpg) 62 | 63 | 複製してできた「〜 のコピー」の名前を変更します 64 | 65 | ![](assets/create_file_2.jpg) 66 | 67 | ![](assets/create_file_3.jpg) 68 | 69 | ここでは拡張子が残らないように注意してください。 70 | 71 | 72 | Windowsの場合は、NAFUDAドライブの中を右クリックして、新規作成>テキストファイルとして作成し、 73 | 74 | ![](assets/windows_create_file.jpg) 75 | 76 | 名前を変更するとよいでしょう。 77 | 78 | ![](assets/windows_rename_file.jpg) 79 | 80 | ここでは拡張子がつかないように注意してください。 81 | 82 | 83 | ## パスワード、ホスト名、NAFUDAドライブを初期化する 84 | 85 | NAFUDAドライブ直下に`startup.sh`を作成し、 86 | 87 | ```bash 88 | # たとえば、MacでmicroSDをマウントした場合 89 | $ vi /Volumes/NAFUDA/startup.sh 90 | ``` 91 | 92 | 以下の内容を記述します。 93 | 94 | ```bash 95 | bash /home/pi/electronic_badge_2018/setup_script/reset_all.sh 96 | shutdown -r now 97 | ``` 98 | 99 | 名札を再起動すると、ホスト名、パスワード、NAFUDAドライブがリセットされ、初期のようにスライドショーがはじまるはずです。また、上記のstartup.shも削除されます。 100 | 101 | 初期化されると、サンプルファイルなどもあたらしく生成されます。 102 | 103 | > 初期化と再起動をおこなうので、3分程度はお待ちください。 104 | 105 | > このstartup.shはNAFUDAドライブ内にあるので、初期化とともに消えます。 106 | 107 | 108 | ### NAFUDAドライブがマウントできない場合 109 | 110 | microSDを電子名札からぬき、PCにmicroSDを接続します。 111 | 112 | `boot`という名称のドライブが現れますので、その直下に`startup.sh`を作成し、以下の内容を記述します。 113 | 114 | ```bash 115 | bash /home/pi/electronic_badge_2018/setup_script/reset_all.sh 116 | ``` 117 | 118 | microSDをPCから正しく取り外し、名札に再接続して再起動をすると、ホスト名、パスワード、NAFUDAドライブがリセットされます。 119 | 120 | リセットされたことを確認したら、ログインしたり、もう一度PCにmicroSDを接続して操作するなどして`startup.sh`を削除してください。 121 | 122 | 初期化されると、サンプルファイルなどもあたらしく生成されます。 123 | 124 | > 削除をしないと、起動の度にリセットがかかりつづけます。 125 | 126 | 127 | ## microSD全体をイメージから初期化する 128 | 129 | `reset_all.sh`を用いた方法では、NAFUDAドライブ以外のファイルをリセットすることはできません。 130 | 131 | microSDを配布時の状態に完全にリセットするには、イメージをダウンロードしてmicroSDに書き込みする必要があります。 132 | 133 | イメージは[Releases](https://github.com/builderscon/electronic_badge_2018/releases)からダウンロードできます。 134 | 135 | Etcherやddなどで書き込みをしてください。 136 | 137 | [Etcher はこちらからDLできます](https://etcher.io/)。 138 | 139 | Etcherを起動し、ダウンロードしたイメージを'select image'から指定し、Select driveから接続しているmicroSDリーダーを指定し、「Flash!」を推すと書き込みが開始されます。 140 | 141 | 書き込み前に、ドライブのアンマウントをしていなければ、パスワードを聞かれます。 142 | 143 | 書き込み完了後、ドライブを抜き、名札にさしこんで正しく焼けたかご確認ください。 144 | 145 | > ※ 当日であれば、製作者(uzulla)に声をかけてもらえれば、可能な範囲でmicroSDを焼きます。 146 | 147 | 148 | ## イメージからではなく初期化する 149 | 150 | > ※ raspbian を自分でインストール・設定できる程度の知識が必要です。 151 | 152 | raspbianイメージを焼いて、そこから手順を踏むことでゼロからの再セットアップが可能です。 153 | 154 | `setup_script/README.md`を参照して作業してください。 155 | 156 | -------------------------------------------------------------------------------- /docs/assets/back_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/back_image.jpg -------------------------------------------------------------------------------- /docs/assets/cable_epd_side.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/cable_epd_side.jpg -------------------------------------------------------------------------------- /docs/assets/cable_rpz_side.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/cable_rpz_side.jpg -------------------------------------------------------------------------------- /docs/assets/connect_center_usb_port.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/connect_center_usb_port.jpg -------------------------------------------------------------------------------- /docs/assets/connect_center_usb_port_before.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/connect_center_usb_port_before.jpg -------------------------------------------------------------------------------- /docs/assets/connect_nafuda_to_pc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/connect_nafuda_to_pc.jpg -------------------------------------------------------------------------------- /docs/assets/connect_outside_usb_port.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/connect_outside_usb_port.jpg -------------------------------------------------------------------------------- /docs/assets/create_file_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/create_file_1.jpg -------------------------------------------------------------------------------- /docs/assets/create_file_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/create_file_2.jpg -------------------------------------------------------------------------------- /docs/assets/create_file_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/create_file_3.jpg -------------------------------------------------------------------------------- /docs/assets/delete_img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/delete_img.jpg -------------------------------------------------------------------------------- /docs/assets/detach_cable.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/detach_cable.jpg -------------------------------------------------------------------------------- /docs/assets/downside_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/downside_image.jpg -------------------------------------------------------------------------------- /docs/assets/eject_nafuda.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/eject_nafuda.jpg -------------------------------------------------------------------------------- /docs/assets/eject_nafuda_win.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/eject_nafuda_win.jpg -------------------------------------------------------------------------------- /docs/assets/front_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/front_image.jpg -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/1.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/10.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/11.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/12.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/13.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/14.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/15.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/16.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/2.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/3.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/4.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/5.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/6.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/7.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/8.png -------------------------------------------------------------------------------- /docs/assets/g_ether_with_windows/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_ether_with_windows/9.png -------------------------------------------------------------------------------- /docs/assets/g_serial_win_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_serial_win_1.png -------------------------------------------------------------------------------- /docs/assets/g_serial_win_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_serial_win_2.png -------------------------------------------------------------------------------- /docs/assets/g_serial_win_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_serial_win_3.png -------------------------------------------------------------------------------- /docs/assets/g_serial_win_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_serial_win_4.png -------------------------------------------------------------------------------- /docs/assets/g_serial_win_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_serial_win_5.png -------------------------------------------------------------------------------- /docs/assets/g_serial_win_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_serial_win_6.png -------------------------------------------------------------------------------- /docs/assets/g_serial_win_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/g_serial_win_7.png -------------------------------------------------------------------------------- /docs/assets/img_copy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/img_copy.jpg -------------------------------------------------------------------------------- /docs/assets/nafuda_drive.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/nafuda_drive.jpg -------------------------------------------------------------------------------- /docs/assets/nafuda_drive_img_dir.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/nafuda_drive_img_dir.jpg -------------------------------------------------------------------------------- /docs/assets/nafuda_drive_img_dir_win.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/nafuda_drive_img_dir_win.jpg -------------------------------------------------------------------------------- /docs/assets/nafuda_drive_win.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/nafuda_drive_win.jpg -------------------------------------------------------------------------------- /docs/assets/pc_usb_port.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/pc_usb_port.jpg -------------------------------------------------------------------------------- /docs/assets/plugin_usb_battery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/plugin_usb_battery.jpg -------------------------------------------------------------------------------- /docs/assets/pull_out_usb_battery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/pull_out_usb_battery.jpg -------------------------------------------------------------------------------- /docs/assets/usbc_converter_sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/usbc_converter_sample.jpg -------------------------------------------------------------------------------- /docs/assets/usbc_converter_sample2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/usbc_converter_sample2.jpg -------------------------------------------------------------------------------- /docs/assets/windows_create_file.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/windows_create_file.jpg -------------------------------------------------------------------------------- /docs/assets/windows_rename_file.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/docs/assets/windows_rename_file.jpg -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | ライブラリ 2 | ========= 3 | 4 | Python3で、E-paper displayを簡単にあつかう単純なライブラリです。 5 | 6 | 7 | # nafuda ライブラリ 8 | 9 | 簡単にe-paper Displayに画像を表示するためのヘルパー関数群です。 10 | 11 | ``` 12 | sys.path.append('/path/to/lib') 13 | from nafuda import Nafuda 14 | 15 | nafuda = Nafuda() 16 | 17 | # 文字列表示 18 | nafuda.draw_text("Hello world", orientation=90) 19 | # 画像表示 20 | nafuda.draw_image_file(file_path, orientation=90) 21 | ``` 22 | 23 | ## 実機以外での開発テクニック 24 | 25 | ### 疑似画面 26 | 27 | `PSEUDO_EPD_MODE`環境変数がある場合は、ライブラリmockのものがよみこまれ、表示すべき画像は`PIL.show()`で表示されます。 28 | 29 | > ※ つまり、PCで動作させたときにはPCの画像ビューワーなどで画像ファイルが開く等します。 30 | 31 | 32 | ### `nafuda.draw_text`のフォント指定 33 | 34 | show_txtなど、`nafuda.draw_text`を用いる場合には、別途フォントパスの指定が必要です。 TTFやOTFなどが指定できます。 35 | 36 | ``` 37 | # たとえばMacなら… 38 | nafuda.draw_text(input_text, font_path="/System/Library/Fonts/ヒラギノ角ゴシック W0.ttc", font_pt=16, orientation=90) 39 | ``` 40 | 41 | > ※ 指定をしない場合は、`/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf`が読み込まれます。 42 | 43 | ### `show_txt`を利用するプログラムをつくる場合 44 | 45 | `EPD_FONT_PATH`環境変数で外部からフォントパスを指定できます。 46 | 47 | ```bash 48 | $ export EPD_FONT_PATH="/System/Library/Fonts/ヒラギノ角ゴシック W0.ttc" 49 | $ show_txt.py some.txt 50 | ``` 51 | 52 | ## サンプル 53 | 54 | ### `nafuda.draw_text()` 55 | 56 | テキストを描画します。 57 | 58 | ``` 59 | nafuda.draw_text( 60 | input_text, # 入力テキスト 61 | orientation=90, # 角度(90は縦(名札状態)、0は横) 62 | font_pt=16, # フォントサイズ、pt(省略可能 63 | font_path=font_path # フォントファイルのパス (省略可能 64 | ) 65 | ``` 66 | 67 | ### `nafuda.draw_image_file()` 68 | 69 | 画像ファイルを表示します 70 | 71 | ``` 72 | nafuda.draw_image_file( 73 | file_path, # 画像のファイルパス 74 | orientation=90 # 角度(90は縦(名札状態)、0は横) 75 | ) 76 | ``` 77 | 78 | ### `nafuda.draw_image_buffer()` 79 | 80 | Pillow(PIL)のimageインスタンスを表示します。生成したイメージを表示します。 81 | 82 | ``` 83 | nafuda.draw_image_buffer( 84 | image_buffer, 85 | orientation=90 # 角度(90は縦(名札状態)、0は横) 86 | ) 87 | ``` 88 | 89 | 90 | -------------------------------------------------------------------------------- /lib/epd4in2.py: -------------------------------------------------------------------------------- 1 | # 2 | # epd4in2.py for python3 3 | # 4 | # Original epd4in2.py was not support python3. 5 | # This is port for python3. 6 | # 7 | # LICENSE : 8 | # 9 | # Copyright (C) Aug 4 2018, Junichi Ishida 10 | # 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 12 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation 13 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 14 | # to permit persons to whom the Software is furnished to do so, subject to the following conditions: 15 | # 16 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of 17 | # the Software. 18 | # 19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 20 | # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | # 25 | 26 | # 27 | # You can download the original files from here. 28 | # https://www.waveshare.com/wiki/File:4.2inch_e-paper_module_code.7z 29 | # and documents. 30 | # https://www.waveshare.com/wiki/4.2inch_e-Paper_Module 31 | # 32 | # Original's license 33 | ## 34 | # @filename : epd4in2.py 35 | # @brief : Implements for e-paper library 36 | # @author : Yehui from Waveshare 37 | # 38 | # Copyright (C) Waveshare September 9 2017 39 | # 40 | # Permission is hereby granted, free of charge, to any person obtaining a copy 41 | # of this software and associated documnetation files (the "Software"), to deal 42 | # in the Software without restriction, including without limitation the rights 43 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 44 | # copies of the Software, and to permit persons to whom the Software is 45 | # furished to do so, subject to the following conditions: 46 | # 47 | # The above copyright notice and this permission notice shall be included in 48 | # all copies or substantial portions of the Software. 49 | # 50 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 51 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 52 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 53 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 54 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 55 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 56 | # THE SOFTWARE. 57 | # 58 | 59 | import epdif 60 | from PIL import Image 61 | 62 | import RPi.GPIO as GPIO 63 | 64 | # Display resolution 65 | EPD_WIDTH = 400 66 | EPD_HEIGHT = 300 67 | 68 | # GDEW042T2 commands 69 | PANEL_SETTING = 0x00 70 | POWER_SETTING = 0x01 71 | POWER_OFF = 0x02 72 | POWER_OFF_SEQUENCE_SETTING = 0x03 73 | POWER_ON = 0x04 74 | POWER_ON_MEASURE = 0x05 75 | BOOSTER_SOFT_START = 0x06 76 | DEEP_SLEEP = 0x07 77 | DATA_START_TRANSMISSION_1 = 0x10 78 | DATA_STOP = 0x11 79 | DISPLAY_REFRESH = 0x12 80 | DATA_START_TRANSMISSION_2 = 0x13 81 | LUT_FOR_VCOM = 0x20 82 | LUT_WHITE_TO_WHITE = 0x21 83 | LUT_BLACK_TO_WHITE = 0x22 84 | LUT_WHITE_TO_BLACK = 0x23 85 | LUT_BLACK_TO_BLACK = 0x24 86 | PLL_CONTROL = 0x30 87 | TEMPERATURE_SENSOR_COMMAND = 0x40 88 | TEMPERATURE_SENSOR_SELECTION = 0x41 89 | TEMPERATURE_SENSOR_WRITE = 0x42 90 | TEMPERATURE_SENSOR_READ = 0x43 91 | VCOM_AND_DATA_INTERVAL_SETTING = 0x50 92 | LOW_POWER_DETECTION = 0x51 93 | TCON_SETTING = 0x60 94 | RESOLUTION_SETTING = 0x61 95 | GSST_SETTING = 0x65 96 | GET_STATUS = 0x71 97 | AUTO_MEASUREMENT_VCOM = 0x80 98 | READ_VCOM_VALUE = 0x81 99 | VCM_DC_SETTING = 0x82 100 | PARTIAL_WINDOW = 0x90 101 | PARTIAL_IN = 0x91 102 | PARTIAL_OUT = 0x92 103 | PROGRAM_MODE = 0xA0 104 | ACTIVE_PROGRAMMING = 0xA1 105 | READ_OTP = 0xA2 106 | POWER_SAVING = 0xE3 107 | 108 | 109 | class EPD: 110 | def __init__(self): 111 | self.reset_pin = epdif.RST_PIN; 112 | self.dc_pin = epdif.DC_PIN; 113 | self.busy_pin = epdif.BUSY_PIN; 114 | self.width = EPD_WIDTH; 115 | self.height = EPD_HEIGHT; 116 | 117 | lut_vcom0 = [ 118 | 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 119 | 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, 120 | 0x00, 0x0A, 0x01, 0x00, 0x00, 0x01, 121 | 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, 122 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 123 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 124 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 125 | ] 126 | 127 | lut_ww = [ 128 | 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, 129 | 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, 130 | 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, 131 | 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, 132 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 133 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 134 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 135 | ] 136 | 137 | lut_bw = [ 138 | 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, 139 | 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, 140 | 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, 141 | 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, 142 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 143 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 144 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 145 | ] 146 | 147 | lut_bb = [ 148 | 0x80, 0x17, 0x00, 0x00, 0x00, 0x02, 149 | 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, 150 | 0x80, 0x0A, 0x01, 0x00, 0x00, 0x01, 151 | 0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02, 152 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 153 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 154 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 155 | ] 156 | 157 | lut_wb = [ 158 | 0x80, 0x17, 0x00, 0x00, 0x00, 0x02, 159 | 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, 160 | 0x80, 0x0A, 0x01, 0x00, 0x00, 0x01, 161 | 0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02, 162 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 163 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 164 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 165 | ] 166 | 167 | def digital_write(self, pin, value): 168 | epdif.epd_digital_write(pin, value) 169 | 170 | def digital_read(self, pin): 171 | return epdif.epd_digital_read(pin) 172 | 173 | def delay_ms(self, delaytime): 174 | epdif.epd_delay_ms(delaytime) 175 | 176 | def send_command(self, command): 177 | self.digital_write(self.dc_pin, GPIO.LOW) 178 | # the parameter type is list but not int 179 | # so use [command] instead of command 180 | epdif.spi_transfer([command]) 181 | 182 | def send_data(self, data): 183 | self.digital_write(self.dc_pin, GPIO.HIGH) 184 | # the parameter type is list but not int 185 | # so use [data] instead of data 186 | epdif.spi_transfer([data]) 187 | 188 | def init(self): 189 | if (epdif.epd_init() != 0): 190 | return -1 191 | self.reset() 192 | self.send_command(POWER_SETTING) 193 | self.send_data(0x03) # VDS_EN, VDG_EN 194 | self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] 195 | self.send_data(0x2b) # VDH 196 | self.send_data(0x2b) # VDL 197 | self.send_data(0xff) # VDHR 198 | self.send_command(BOOSTER_SOFT_START) 199 | self.send_data(0x17) 200 | self.send_data(0x17) 201 | self.send_data(0x17) # 07 0f 17 1f 27 2F 37 2f 202 | self.send_command(POWER_ON) 203 | self.wait_until_idle() 204 | self.send_command(PANEL_SETTING) 205 | self.send_data(0xbf) # KW-BF KWR-AF BWROTP 0f 206 | self.send_data(0x0b) 207 | self.send_command(PLL_CONTROL) 208 | self.send_data(0x3c) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ 209 | return 0 210 | 211 | def wait_until_idle(self): 212 | while (self.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle 213 | self.delay_ms(100) 214 | 215 | def reset(self): 216 | self.digital_write(self.reset_pin, GPIO.LOW) # module reset 217 | self.delay_ms(200) 218 | self.digital_write(self.reset_pin, GPIO.HIGH) 219 | self.delay_ms(200) 220 | 221 | def set_lut(self): 222 | self.send_command(LUT_FOR_VCOM) # vcom 223 | for count in range(0, 44): 224 | self.send_data(self.lut_vcom0[count]) 225 | 226 | self.send_command(LUT_WHITE_TO_WHITE) # ww -- 227 | for count in range(0, 42): 228 | self.send_data(self.lut_ww[count]) 229 | 230 | self.send_command(LUT_BLACK_TO_WHITE) # bw r 231 | for count in range(0, 42): 232 | self.send_data(self.lut_bw[count]) 233 | 234 | self.send_command(LUT_WHITE_TO_BLACK) # wb w 235 | for count in range(0, 42): 236 | self.send_data(self.lut_bb[count]) 237 | 238 | self.send_command(LUT_BLACK_TO_BLACK) # bb b 239 | for count in range(0, 42): 240 | self.send_data(self.lut_wb[count]) 241 | 242 | def get_frame_buffer(self, image): 243 | buf = [0] * int(self.width * self.height / 8) 244 | # Set buffer to value of Python Imaging Library image. 245 | # Image must be in mode 1. 246 | image_monocolor = image.convert('1') 247 | imwidth, imheight = image_monocolor.size 248 | if imwidth != self.width or imheight != self.height: 249 | raise ValueError('Image must be same dimensions as display \ 250 | ({0}x{1}).'.format(self.width, self.height)) 251 | 252 | pixels = image_monocolor.load() 253 | for y in range(self.height): 254 | for x in range(self.width): 255 | # Set the bits for the column of pixels at the current position. 256 | if pixels[x, y] != 0: 257 | buf[int((x + y * self.width) / 8)] |= 0x80 >> (x % 8) 258 | return buf 259 | 260 | def display_frame(self, frame_buffer): 261 | self.send_command(RESOLUTION_SETTING) 262 | self.send_data(self.width >> 8) 263 | self.send_data(self.width & 0xff) 264 | self.send_data(self.height >> 8) 265 | self.send_data(self.height & 0xff) 266 | 267 | self.send_command(VCM_DC_SETTING) 268 | self.send_data(0x12) 269 | 270 | self.send_command(VCOM_AND_DATA_INTERVAL_SETTING) 271 | self.send_command(0x97) # VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7 272 | 273 | if (frame_buffer != None): 274 | self.send_command(DATA_START_TRANSMISSION_1) 275 | for i in range(0, int(self.width * self.height / 8)): 276 | self.send_data(0xFF) # bit set: white, bit reset: black 277 | self.delay_ms(2) 278 | self.send_command(DATA_START_TRANSMISSION_2) 279 | for i in range(0, int(self.width * self.height / 8)): 280 | self.send_data(frame_buffer[i]) 281 | self.delay_ms(2) 282 | 283 | self.set_lut() 284 | 285 | self.send_command(DISPLAY_REFRESH) 286 | self.delay_ms(100) 287 | self.wait_until_idle() 288 | 289 | ## 290 | # @brief: After this command is transmitted, the chip would enter the 291 | # deep-sleep mode to save power. 292 | # The deep sleep mode would return to standby by hardware reset. 293 | # The only one parameter is a check code, the command would be 294 | # executed if check code = 0xA5. 295 | # You can use reset() to awaken or init() to initialize 296 | ## 297 | def sleep(self): 298 | self.send_command(VCOM_AND_DATA_INTERVAL_SETTING) 299 | self.send_data(0x17) # border floating 300 | self.send_command(VCM_DC_SETTING) # VCOM to 0V 301 | self.send_command(PANEL_SETTING) # 302 | self.delay_ms(100) 303 | 304 | self.send_command(POWER_SETTING) # VG&VS to 0V fast 305 | self.send_data(0x00) 306 | self.send_data(0x00) 307 | self.send_data(0x00) 308 | self.send_data(0x00) 309 | self.send_data(0x00) 310 | self.delay_ms(100) 311 | 312 | self.send_command(POWER_OFF) # power off 313 | self.wait_until_idle() 314 | self.send_command(DEEP_SLEEP) # deep sleep 315 | self.send_data(0xA5) 316 | 317 | ### END OF FILE ### 318 | -------------------------------------------------------------------------------- /lib/epd4in2_mock.py: -------------------------------------------------------------------------------- 1 | # 2 | # epd4in2_mock.py 3 | # 4 | # dumb mock for epd4in2 5 | # 6 | # LICENSE : 7 | # 8 | # Copyright (C) Aug 4 2018, Junichi Ishida 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 11 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation 12 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 13 | # to permit persons to whom the Software is furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of 16 | # the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 19 | # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | # 24 | 25 | class EPD: 26 | 27 | def init(self): 28 | return 0 29 | 30 | def get_frame_buffer(self, frame_buffer): 31 | return frame_buffer 32 | 33 | def display_frame(self, frame_buffer): 34 | # PIL.show() 35 | frame_buffer.show() 36 | -------------------------------------------------------------------------------- /lib/epdif.py: -------------------------------------------------------------------------------- 1 | ## 2 | # @filename : epdif.py 3 | # @brief : EPD hardware interface implements (GPIO, SPI) 4 | # @author : Yehui from Waveshare 5 | # 6 | # Copyright (C) Waveshare July 10 2017 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documnetation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in 16 | # all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | # THE SOFTWARE. 25 | # 26 | 27 | import spidev 28 | import RPi.GPIO as GPIO 29 | import time 30 | 31 | # Pin definition 32 | RST_PIN = 17 33 | DC_PIN = 25 34 | CS_PIN = 8 35 | BUSY_PIN = 24 36 | 37 | # SPI device, bus = 0, device = 0 38 | SPI = spidev.SpiDev(0, 0) 39 | 40 | 41 | def epd_digital_write(pin, value): 42 | GPIO.output(pin, value) 43 | 44 | 45 | def epd_digital_read(pin): 46 | return GPIO.input(BUSY_PIN) 47 | 48 | 49 | def epd_delay_ms(delaytime): 50 | time.sleep(delaytime / 1000.0) 51 | 52 | 53 | def spi_transfer(data): 54 | SPI.writebytes(data) 55 | 56 | 57 | def epd_init(): 58 | GPIO.setmode(GPIO.BCM) 59 | GPIO.setwarnings(False) 60 | GPIO.setup(RST_PIN, GPIO.OUT) 61 | GPIO.setup(DC_PIN, GPIO.OUT) 62 | GPIO.setup(CS_PIN, GPIO.OUT) 63 | GPIO.setup(BUSY_PIN, GPIO.IN) 64 | SPI.max_speed_hz = 2000000 65 | SPI.mode = 0b00 66 | return 0; 67 | 68 | ### END OF FILE ### 69 | -------------------------------------------------------------------------------- /lib/nafuda.py: -------------------------------------------------------------------------------- 1 | # 2 | # nafuda.py 3 | # 4 | # nafuda library 5 | # 6 | # LICENSE : 7 | # 8 | # Copyright (C) Aug 4 2018, Junichi Ishida 9 | # 10 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 11 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation 12 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 13 | # to permit persons to whom the Software is furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of 16 | # the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 19 | # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | # 24 | 25 | 26 | import os 27 | import textwrap 28 | from PIL import Image 29 | from PIL import ImageDraw 30 | from PIL import ImageFont 31 | 32 | # for development in PC. 33 | if "PSEUDO_EPD_MODE" in os.environ: 34 | import epd4in2_mock as epd4in2 35 | else: 36 | import epd4in2 37 | 38 | EPD_WIDTH = 400 39 | EPD_HEIGHT = 300 40 | 41 | # VL-Gothic-Regular.ttf with 16pt 42 | DISP_COLS = 37 43 | DISP_ROWS = 20 44 | 45 | 46 | # for test memo 47 | # echo 12345678901234567890123456789012345678901234567890 | ./show_txt.py - 48 | # echo -e "1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n" | ./show_txt.py - 49 | 50 | 51 | class Nafuda: 52 | 53 | def __init__(self): 54 | # init epd lib 55 | self.epd = epd4in2.EPD() 56 | self.epd.init() 57 | 58 | def draw_text( 59 | self, 60 | text, 61 | orientation=0, 62 | font_path='/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf', 63 | font_pt=16, 64 | max_col=DISP_COLS, 65 | max_row=DISP_ROWS): 66 | 67 | font = ImageFont.truetype(font_path, font_pt) 68 | 69 | if orientation != 0 and orientation % 90 == 0: 70 | w = EPD_HEIGHT 71 | h = EPD_WIDTH 72 | else: 73 | w = EPD_WIDTH 74 | h = EPD_HEIGHT 75 | 76 | image = Image.new('1', (w, h), 1) # 1: clear the frame 77 | draw = ImageDraw.Draw(image) 78 | 79 | # XXX NEED NICE width CALC!!! 80 | 81 | # 72pt=96px -> 300pt(400px) 225pt(300px) 82 | # so, 12pt 25 x 18.75 chars 83 | # (really?) 84 | 85 | lines_count = 0 86 | print_str_list = [] 87 | for line in text.split("\n"): 88 | wrapped_lines = textwrap.wrap(line, max_col) 89 | lines_count += len(wrapped_lines) 90 | print_str_list.append("\n".join(wrapped_lines)) 91 | if max_row < lines_count: 92 | break 93 | 94 | print_str = "\n".join(print_str_list) 95 | # print(print_str) 96 | 97 | draw.text( 98 | (0, 0), 99 | print_str, 100 | font=font, 101 | fill=0) 102 | 103 | self.draw_image_buffer(image, orientation) 104 | 105 | def draw_image_file(self, path, orientation=0): 106 | image_buffer = Image.open(path) 107 | 108 | self.draw_image_buffer(image_buffer, orientation) 109 | 110 | def draw_image_buffer(self, image_buffer, orientation=0): 111 | 112 | # remove Alpha 113 | if len(image_buffer.split()) > 3: 114 | # make blank image 115 | _image_buffer = Image.new('RGB', image_buffer.size, "#FFFFFF") 116 | 117 | # draw image to center 118 | _image_buffer.paste(image_buffer, (0, 0), image_buffer.split()[3]) 119 | 120 | # swap 121 | image_buffer = _image_buffer 122 | 123 | # rotate image buffer 124 | if orientation != 0: 125 | # rotate image (FYI 90=vertical(nafuda mode), 0=holizontal) 126 | image_buffer = image_buffer.rotate(orientation, 0, True) 127 | 128 | # resize image buffer 129 | w, h = image_buffer.size 130 | if EPD_WIDTH < w or EPD_HEIGHT < h: 131 | # resize 132 | image_buffer.thumbnail((EPD_WIDTH, EPD_HEIGHT)) 133 | 134 | # make blank image 135 | _image_buffer = Image.new('1', (EPD_WIDTH, EPD_HEIGHT), 1) 136 | 137 | # draw image to center 138 | _image_buffer.paste(image_buffer, self.get_offset_for_centering((EPD_WIDTH, EPD_HEIGHT), image_buffer.size)) 139 | 140 | # swap 141 | image_buffer = _image_buffer 142 | 143 | # centering 144 | w, h = image_buffer.size 145 | if EPD_WIDTH > w or EPD_HEIGHT > h: 146 | # make blank image 147 | _image_buffer = Image.new('1', (EPD_WIDTH, EPD_HEIGHT), 1) 148 | 149 | # draw image to center 150 | _image_buffer.paste(image_buffer, self.get_offset_for_centering((EPD_WIDTH, EPD_HEIGHT), image_buffer.size)) 151 | 152 | # swap 153 | image_buffer = _image_buffer 154 | 155 | self.epd.display_frame(self.epd.get_frame_buffer(image_buffer)) 156 | 157 | @staticmethod 158 | def get_offset_for_centering(canvas_size, img_size): 159 | x = 0 160 | y = 0 161 | # print(canvas_size) 162 | # print(canvas_size[0]) 163 | # print(img_size) 164 | # print(img_size[0]) 165 | 166 | if canvas_size[0] > img_size[0]: 167 | x = int((canvas_size[0] - img_size[0]) / 2) 168 | if canvas_size[1] > img_size[1]: 169 | y = int((canvas_size[1] - img_size[1]) / 2) 170 | 171 | return x, y 172 | 173 | # XXX NEED NICE width CALC!!! 174 | # def get_font_wh_px(self, font_path, text, canvas_w, canvas_h): 175 | # pt = 0 176 | # max_pt = 256 177 | # 178 | # while True: 179 | # # too bulldoze method. but e-paper is more than slow. 180 | # pt = pt + 1 181 | # if max_pt < pt: 182 | # break 183 | # 184 | # font = ImageFont.truetype(font_path, pt) 185 | # font_w, font_h = font.getsize(text) 186 | # 187 | # if font_w > canvas_w: 188 | # break 189 | # if font_h > canvas_h: 190 | # break 191 | # 192 | # return pt - 1 193 | -------------------------------------------------------------------------------- /resource/motd: -------------------------------------------------------------------------------- 1 | 2 | The programs included with the Debian GNU/Linux system are free software; 3 | the exact distribution terms for each program are described in the 4 | individual files in /usr/share/doc/*/copyright. 5 | 6 | Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent 7 | permitted by applicable law. 8 | 9 | == 10 | ___ ____ _ _ ___ _ _ __ 11 | F __". F ___J F L L] F __". FJ L] FJ 12 | J |--\ L J |___: J \| L J (___| J |__| L J L 13 | | | J | | _____| | |\ | J\___ \ | __ | | | 14 | F L__J | F L____: F L\\ J .--___) \ F L__J J F J 15 | J______/FJ________LJ__L \\__LJ\______JJ__L J__LJ____L 16 | |______F |________||__L J__| J______F|__L J__||____| 17 | _ _ _ ____ _ _ ___ _ 18 | F L L] /.\ F ___J FJ L] F __". /.\ 19 | J \| L //_\\ J |___: J | | L J |--\ L //_\\ 20 | | |\ | / ___ \ | _____|| | | | | | J | / ___ \ 21 | F L\\ J / L___J \ F |____JF L__J J F L__J | / L___J \ 22 | J__L \\__LJ__L J__LJ__F J\______/FJ______/FJ__L J__L 23 | |__L J__||__L J__||__| J______F |______F |__L J__| 24 | 25 | at first, please update software and documents. 26 | $ git -C ~/electronic_badge_2018/ pull 27 | 28 | Docs: ~/electronic_badge_2018/docs 29 | (On web : https://github.com/builderscon/electronic_badge_2018 ) 30 | 31 | If you want to stop the nafuda slideshow. 32 | $ sudo systemctl stop simple-nafuda 33 | -------------------------------------------------------------------------------- /resource/mount_vsd_ro: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo /home/pi/electronic_badge_2018/bootup/mount_vsd_ro.sh -------------------------------------------------------------------------------- /resource/mount_vsd_rw: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo /home/pi/electronic_badge_2018/bootup/mount_vsd_rw.sh -------------------------------------------------------------------------------- /resource/show_img: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec /home/pi/electronic_badge_2018/show_img/show_img.py $@ 4 | -------------------------------------------------------------------------------- /resource/show_txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec /home/pi/electronic_badge_2018/show_txt/show_txt.py $@ 4 | -------------------------------------------------------------------------------- /setup_script/README.md: -------------------------------------------------------------------------------- 1 | setup script 2 | ============ 3 | 4 | 電子名札用にmicroSDを最初からビルドするための手順や、サポートスクリプトです。 5 | 6 | > ※ 配布状態に戻すためのものです。 7 | 8 | 9 | ## 1. raspbian をmicroSDに焼く 10 | 11 | 一般的な Raspberry pi への raspbianと同じ作業となります。 12 | 13 | ここでは省略します。 14 | 15 | ## 2. `boot`パーティションの初期設定 16 | 17 | 焼いたmicroSDを一度ぬいて、もう一度さして `boot`ドライブをマウントし、内容を編集します。 18 | 19 | Macの場合は、以下のように`setup_boot_partition.sh`を走らせると自動的にアンマウントまで実施されます。 このとき、以下のようにWifiのパスワードを環境変数にいれてください(`wpa_supplicant.conf`を生成します)。 20 | 21 | > ※ この後の手順で、ラズパイはインターネットにつながっている必要があります。Wifi以外の別途のネットワークが確保できれば不要です。 22 | 23 | ```bash 24 | $ export WPA_SSID="YOUR SSID" 25 | $ export WPA_PSK="\"YOUR PASSWORD\"" 26 | # 上記の `\"` は平文のパスワードで設定するのには重要です 27 | 28 | $ cd electronic_badge_2018/setup_script 29 | $ ./setup_boot_partition.sh 30 | 31 | # 以下成功時出力例 32 | Volume boot on disk2s1 unmounted 33 | DONE 34 | ``` 35 | 36 | Mac以外の場合は、`setup_boot_partition.sh`を参考にして手動で作業してください。 37 | 38 | 39 | ## 3. 起動し、sshなどでログイン 40 | 41 | ここではまだraspbianのデフォルトID/PASSである`pi`/`raspberry`でログインできます。 42 | 43 | ログインしたら以下のスクリプトを実行します。 44 | 45 | ```bash 46 | $ cd /boot 47 | $ ./nafuda-setup.sh 48 | ``` 49 | 50 | ここで 51 | 52 | - apt update 53 | - apt upgrade 54 | - git clone simple-nafuda 55 | - systemdへのサービス登録 56 | 57 | などを行います。 58 | 59 | 状態にもよりますが、2〜30分以上の時間がかかりますので、気長に待ってください。 60 | 61 | 62 | ## 4. Wifi接続情報などを必要ならば削除 63 | 64 | 必要ならば`/etc/wpa_supplicant/wpa_supplicant.conf`を消します。 65 | 66 | > ※ この時点でのmicroSDが「配布状態」となります。 67 | 68 | 69 | ## 5. 再起動 70 | 71 | 再起動すると、パスワード、hostnameなどが生成され、完了です。 72 | 73 | ここからは`docs/README.md`を参照してください。 74 | 75 | 76 | # 実MicroSDからimageを吸い出すときのメモ 77 | 78 | MacにMicroSDを接続して、クローンするためにイメージを作る。 79 | 80 | ### 状況を確認 81 | 82 | > diskX については、mountコマンドの結果や抜き差しなどによって特定する。 83 | 84 | ``` 85 | $ sudo fdisk /dev/disk7 86 | Password: 87 | Disk: /dev/disk7 geometry: 958/255/63 [15400960 sectors] 88 | Signature: 0xAA55 89 | Starting Ending 90 | #: id cyl hd sec - cyl hd sec [ start - size] 91 | ------------------------------------------------------------------------ 92 | 1: 0C 64 0 1 - 1023 3 32 [ 8192 - 524288] Win95 FAT32L 93 | 2: 83 1023 3 32 - 1023 3 32 [ 532480 - 3858432] Linux files* 94 | 3: 00 0 0 0 - 0 0 0 [ 0 - 0] unused 95 | 4: 00 0 0 0 - 0 0 0 [ 0 - 0] unused 96 | ``` 97 | 98 | ### 吸い出す長さを決定する 99 | 100 | 8GBのDiskでもパーティーションが2GBなら2GBでよいし、そうしたい。(そういうことをかんがえなければ、長さの指定は不要、イメージは大きくなるが確実) 101 | 102 | fdiskでセクタ数を計算し、それに512(バイト)を書けて長さを特定する。 103 | 104 | 上記、最後のパーティションをみて、 `532480 + 3858432 = 4390912` が(有意な)最終セクタだと判断する。 105 | 106 | セクタサイズは512なので、`4390912 * 512 = 2248146944` が吸い出すデータ量。 107 | 108 | > この計算方法は適当なので、あんまり信用しないこと、後述のcountをふやせば解決することも多い 109 | 110 | 吸い出す長さが間違っていてもエラーはでないし、特に足りてないときは「後できづくことになる」ので気をつけないといけない。 111 | 112 | ### 実読み込み 113 | 114 | わかりやすく512byteごとの読み込み等とかすると無限に時間がかかるので、1mくらい一気に読み込みをさせたい。`if=rdisk`なのは、Macの場合こちらが速度が出るから。 115 | 116 | `2248146944 / 1024/1024 = 2144` 。ここから適当な余裕をみて2200とした 117 | 118 | > 繰り返しになるが、countは、きっちりにすると(自分の計算ミスで)エラーすることがあるので、計算ミスを考えるとちょっと多めにしておいてもいい。まあ色々試してみてほしい。 119 | 120 | ``` 121 | sudo dd if=/dev/rdisk2 of=~/rp.img bs=1m count=2200 122 | ``` 123 | 124 | ### 書き込み 125 | 126 | 生成された`~/rp.img`を別のsdに書き込む。 127 | 128 | ddでもできるが、すなおにEtcherを使ったほうが楽。 129 | 130 | 131 | -------------------------------------------------------------------------------- /setup_script/reset_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # virtual_sd(NAFUDAドライブ)を削除し、ID/PASSをraspbian初期のものにする。 3 | # 再度再起動後にID/PASSなどを生成したい場合につかう 4 | 5 | if [ 0 -ne ${EUID:-${UID}} ] 6 | then 7 | echo "You need to be root to perform this command." 8 | exit 1 9 | fi 10 | 11 | echo "pi:raspberry" | /usr/sbin/chpasswd 12 | echo "raspberrypi" > /etc/hostname 13 | /bin/hostname "raspberrypi" 14 | 15 | umount /mnt/virtual_sd 16 | rm /home/pi/virtual_sd.img 17 | -------------------------------------------------------------------------------- /setup_script/setup_boot_partition.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | # このスクリプトは、raspbianをmicroSDに焼いた後、母艦で実行するためのものです。 4 | 5 | # BOOT_DIRは、Macだと/Volumes/bootにマウントされる。 6 | # Windowsの場合は`boot`という名前のドライブが認識される 7 | BOOT_DIR=/Volumes/boot 8 | 9 | 10 | # BOOT ドライブがあるか確認(なかったら終了させるため) 11 | ls ${BOOT_DIR} > /dev/null 12 | 13 | 14 | # このスクリプトはラズパイで動作扠せるためのものではないので、ホスト名で確認 15 | if [ "raspberrypi" = `hostname` ] 16 | then 17 | echo "this scirpt must be execute in PC (not raspberrypi)" 18 | exit 1 19 | fi 20 | 21 | 22 | # cmdline.txtを編集 23 | # - remove `quiet init=/usr/lib/raspi-config/init_resize.sh` 24 | # - add `modules-load=dwc2` 25 | 26 | cp ${BOOT_DIR}/cmdline.txt ${BOOT_DIR}/cmdline.txt.orig 27 | cat ${BOOT_DIR}/cmdline.txt.orig | \ 28 | sed -e 's/quiet init=\/usr\/lib\/raspi-config\/init_resize.sh/modules-load=dwc2/g' > \ 29 | ${BOOT_DIR}/cmdline.txt 30 | 31 | 32 | # config.txt に以下テキストを追記 33 | ADD_CONFIG=" 34 | # for e-paper display 35 | dtparam=spi=on 36 | 37 | # usb gadget 38 | dtoverlay=dwc2 39 | 40 | # for serial 41 | enable_uart=1 42 | dtoverlay=pi3-miniuart-bt 43 | " 44 | echo "${ADD_CONFIG}" >> ${BOOT_DIR}/config.txt 45 | 46 | 47 | # sshd を自動起動するように、bootドライブにsshファイルを設置(raspbianの機能) 48 | touch ${BOOT_DIR}/ssh 49 | 50 | 51 | # wifi接続情報を保存 52 | set +u 53 | # Wifi ID/PASS環境変数があれば 54 | if [ ! -z "${WPA_SSID}" ] && [ ! -z "${WPA_PSK}" ] 55 | then 56 | # Wifiのコンフィグを生成して、保存 57 | WPA_CONFIG="ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev 58 | update_config=1 59 | country=JP 60 | 61 | network={ 62 | ssid=\"${WPA_SSID}\" 63 | psk=${WPA_PSK} 64 | } 65 | " 66 | echo "${WPA_CONFIG}" > ${BOOT_DIR}/wpa_supplicant.conf 67 | else 68 | # なくても処理は完了するが、注意書き出力 69 | echo "No WPA_SSID env. skip generate wpa_supplicant.conf" 70 | echo " example:" 71 | echo " export WPA_SSID=\"adsf\"" 72 | echo " export WPA_PSK=\"\\\"adsf\\\"\"" 73 | echo " (\\\" is important)" 74 | fi 75 | set -u 76 | 77 | 78 | # ラズパイにログインしてから実行する初期化スクリプトを`boot`にコピーしておく 79 | cp setup_from_baseinstall.sh ${BOOT_DIR}/nafuda_setup.sh 80 | 81 | 82 | # microSD のとりはずし 83 | diskutil unmount ${BOOT_DIR} 84 | 85 | echo DONE 86 | -------------------------------------------------------------------------------- /setup_script/setup_from_baseinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | # このスクリプトはラズパイの依存ライブラリをインストールし、nafudaソフトウェアをインストールするものです。 4 | # ラズパイがインターネットに接続された状態で実行する必要があります。 5 | 6 | if [ "raspberrypi" != `hostname` ] 7 | then 8 | echo "this scirpt must be execute in raspberrypi" 9 | exit 1 10 | fi 11 | 12 | # install deps 13 | sudo apt -y update 14 | sudo apt -y upgrade 15 | 16 | sudo apt -y install git vim python-pil python3-pil python-spidev python3-spidev fonts-freefont-ttf fonts-vlgothic python-rpi.gpio python3-rpi.gpio python3-pip python-pip 17 | sudo pip3 install qrcode requests 18 | 19 | sudo update-alternatives --set editor /usr/bin/vim.basic 20 | 21 | # install nafuda codes 22 | set +e 23 | rm -rf /home/pi/electronic_badge_2018 24 | set -e 25 | export GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" 26 | git -C /home/pi clone https://github.com/builderscon/electronic_badge_2018.git 27 | 28 | /home/pi/electronic_badge_2018/show_img/show_img.py /home/pi/electronic_badge_2018/bootup/virtual_sd_builder/skel/img/0_info.png 29 | 30 | sudo cp /home/pi/electronic_badge_2018/bootup/nafuda-bootup.service /etc/systemd/system/ 31 | sudo cp /home/pi/electronic_badge_2018/simple_nafuda/simple-nafuda.service /etc/systemd/system/ 32 | 33 | sudo systemctl daemon-reload 34 | sudo systemctl enable nafuda-bootup 35 | 36 | set +e 37 | sudo mv /etc/motd /etc/motd.default 38 | sudo cp /home/pi/electronic_badge_2018/resource/motd /etc/motd 39 | 40 | sudo cp /home/pi/electronic_badge_2018/resource/show_txt /usr/bin/ 41 | sudo cp /home/pi/electronic_badge_2018/resource/show_img /usr/bin/ 42 | sudo cp /home/pi/electronic_badge_2018/resource/mount_vsd_ro /usr/bin/ 43 | sudo cp /home/pi/electronic_badge_2018/resource/mount_vsd_rw /usr/bin/ 44 | 45 | set -e 46 | 47 | # boot speedup tweaks 48 | sudo systemctl disable hciuart.service 49 | sudo sed -i -e 's/root\=PARTUUID\=4d3ee428\-02/root\=\/dev\/mmcblk0p2/' /boot/cmdline.txt 50 | sudo sed -i -e 's/rootwait/rootwait quiet/' /boot/cmdline.txt 51 | 52 | 53 | echo "DONE" 54 | echo "don't forget wipe wpa_supplicant.conf and .ssh/authorized_keys!!" 55 | 56 | # sudo cp ~/electronic_badge_2018/bootup/virtual_sd_builder/skel/wpa_supplicant.conf.sample /etc/wpa_supplicant/wpa_supplicant.conf 57 | 58 | # sudo shutdown -h now 59 | -------------------------------------------------------------------------------- /show_img/README.md: -------------------------------------------------------------------------------- 1 | # `show_img` 2 | 3 | 単に画像をEpaperに表示します。他のプログラムなどからキックするなどで利用してください。 4 | 5 | Draw image file to Epaper display. 6 | 7 | ## usage 8 | 9 | ``` 10 | $ show_img.py your_image.png 11 | ``` 12 | 13 | ## info 14 | 15 | ハードウェア的にはディスプレイ解像度は400x300ですが、このコマンドは左に90度回転させますので300x400の画像を用意してください。 16 | 17 | 画像がディスプレイ解像度を超える場合は縮小されます。 18 | 19 | 電子ペーパーはモノクロ(2値)ですが、カラー画像を読み込むことができ、ライブラリによってディザリングによる擬似マルチカラー(グレイスケール)になります。 20 | 21 | 読み込めるファイルタイプはJpeg、PNG、BMPなどです。 22 | 23 | Dot by dot display size is 400x300, 24 | But `show_img` will be rotate an image to 90 degree counter clockwise (to 300x400). 25 | 26 | Loaded image will be resize when the image file is lager than the display. 27 | 28 | Epaper display is completely monochrome, 29 | But you can input color(RGB)file (jpeg, png, and other). 30 | Library will be do auto dithering (pseudo color). 31 | -------------------------------------------------------------------------------- /show_img/show_img.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # e-paper nafuda sample code. 4 | # just draw to e-paper program. 5 | # 6 | # usage: 7 | # $ python show_img.py your_image.png 8 | # 9 | # for development. 10 | # $ export PSEUDO_EPD_MODE=1 11 | # $ python show_img.py your_image.png 12 | # 13 | # Copyright (C) Aug 4 2018, Junichi Ishida 14 | # 15 | # LICENSE : MIT 16 | # 17 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 18 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation 19 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 20 | # to permit persons to whom the Software is furnished to do so, subject to the following conditions: 21 | # 22 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of 23 | # the Software. 24 | # 25 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 26 | # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 27 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 28 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | # 31 | 32 | import os 33 | import sys 34 | 35 | sys.path.append(os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + '/../lib')) 36 | 37 | from nafuda import Nafuda 38 | 39 | 40 | def main(): 41 | if len(sys.argv) != 2: 42 | show_help() 43 | sys.exit() 44 | 45 | if sys.argv[1] == '-h' or sys.argv[1] == '--help': 46 | show_help() 47 | sys.exit() 48 | 49 | file_path = sys.argv[1] 50 | 51 | if not os.path.isfile(file_path): 52 | print('file not found :' + file_path) 53 | sys.exit() 54 | 55 | nafuda = Nafuda() 56 | 57 | # Orientation 90 is vertical nafuda mode. 0 is horizontal 58 | nafuda.draw_image_file(file_path, orientation=90) 59 | 60 | 61 | def show_help(): 62 | print("usage: show_img.py filename.png\n") 63 | 64 | 65 | if __name__ == '__main__': 66 | main() 67 | -------------------------------------------------------------------------------- /show_txt/README.md: -------------------------------------------------------------------------------- 1 | # `show_txt` 2 | 3 | テキストををEpaperに表示します。他のプログラムなどからキックするなどで利用してください。 4 | 5 | Draw text to Epaper display. 6 | 7 | ## usage 8 | 9 | ``` 10 | $ echo 123 | show_txt.py - 11 | 12 | or 13 | 14 | $ show_txt.py something.txt 15 | ``` 16 | 17 | 18 | ## font 19 | 20 | 環境変数 `EPD_FONT_PATH` にTTFやOTFなどのフォントファイルパスを指定することでフォントが指定できます。 21 | 22 | ``` 23 | $ export EPD_FONT_PATH="/System/Library/Fonts/ヒラギノ角ゴシック W0.ttc" 24 | ``` 25 | -------------------------------------------------------------------------------- /show_txt/show_txt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # for development. 4 | # $ export PSEUDO_EPD_MODE=1 5 | # $ python show_txt.py your_image.png 6 | # 7 | # if you want to use your font 8 | # $ export EPD_FONT_PATH="/System/Library/Fonts/ヒラギノ角ゴシック W0.ttc" 9 | # or 10 | # $ export EPD_FONT_PATH="/System/Library/Fonts/Monaco.dfont" 11 | # $ python main.py 12 | # 13 | # Copyright (C) Aug 4 2018, Junichi Ishida 14 | # 15 | # LICENSE : MIT 16 | # 17 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 18 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation 19 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 20 | # to permit persons to whom the Software is furnished to do so, subject to the following conditions: 21 | # 22 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of 23 | # the Software. 24 | # 25 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 26 | # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 27 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 28 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | # 31 | import os 32 | import sys 33 | import textwrap 34 | 35 | sys.path.append(os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + '/../lib')) 36 | 37 | from nafuda import Nafuda 38 | 39 | 40 | def main(): 41 | if len(sys.argv) != 2: 42 | show_help() 43 | sys.exit() 44 | 45 | if sys.argv[1] == '-h' or sys.argv[1] == '--help': 46 | show_help() 47 | sys.exit() 48 | 49 | if sys.argv[1] == '-': 50 | # ここで一括で読むべきではないのでは? 51 | input_text = sys.stdin.read() 52 | 53 | else: 54 | file_path = sys.argv[1] 55 | 56 | if not os.path.isfile(file_path): 57 | print('file not found :' + file_path) 58 | sys.exit() 59 | 60 | with open(file_path) as f: 61 | # ここで一括で読むべきではないのでは? 62 | input_text = f.read() 63 | 64 | # print(input_text) 65 | 66 | if "EPD_FONT_PATH" in os.environ: 67 | font_path = os.environ['EPD_FONT_PATH'] 68 | else: 69 | font_path = '/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf' 70 | 71 | nafuda = Nafuda() 72 | 73 | # Orientation 90 is vertical nafuda mode. 0 is horizontal 74 | nafuda.draw_text(input_text, font_path=font_path, font_pt=16, orientation=90) 75 | 76 | 77 | def show_help(): 78 | print(textwrap.dedent('''\ 79 | usage: 80 | $ show_txt.py filename.txt 81 | $ echo abc | show_txt.py - 82 | ''')) 83 | 84 | 85 | if __name__ == '__main__': 86 | main() 87 | -------------------------------------------------------------------------------- /simple_nafuda/README.md: -------------------------------------------------------------------------------- 1 | # `simple_nafuda` 2 | 3 | `img` ディレクトリ内の画像ファイルをスライドショーします。 4 | 5 | Slide show with images in `img`. 6 | 7 | ## usage 8 | 9 | ``` 10 | $ python3 main.py 11 | ``` 12 | 13 | ## info 14 | 15 | `/boot/simple_nafuda`と同じものです 16 | 17 | The program is same as `/boot/simple_nafuda` 18 | 19 | ## クラウド同期機能 20 | 21 | `simple_nafuda_server`をどこかのサーバーに用意した後に、`settins.json`の`CLOUD_BASE_URL`にURLを入力する。 22 | 23 | > ※ builderscon tokyo 2018時は`https://eb2018.builderscon.io/`が該当しました。 24 | 25 | > ※ 空欄だと、クラウド同期はなされない 26 | -------------------------------------------------------------------------------- /simple_nafuda/exec_stop_post.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # the script will run when error exit on simple-nafuda.service 4 | # 5 | 6 | SCRIPT_DIR=$(cd $(dirname $0); pwd) 7 | VSD_BASE_DIR=/mnt/virtual_sd 8 | 9 | # 10 | #STOPPED 11 | #success 12 | #killed 13 | #TERM 14 | # 15 | #STOPPED 16 | #exit-code 17 | #exited 18 | #1 19 | 20 | if [ "${SERVICE_RESULT}" = "success" ] 21 | then 22 | exit 23 | fi 24 | 25 | sudo /home/pi/electronic_badge_2018/bootup/mount_vsd_rw.sh 26 | echo "exit with error" > ${VSD_BASE_DIR}/stop.log 27 | journalctl -b >> ${VSD_BASE_DIR}/stop.log 28 | sudo /home/pi/electronic_badge_2018/bootup/mount_vsd_ro.sh 29 | 30 | echo "== exit with error ==" > ${SCRIPT_DIR}/stop.log 31 | journalctl -u "simple-nafuda" >> ${SCRIPT_DIR}/stop.log 32 | 33 | cat ${SCRIPT_DIR}/stop.log | \ 34 | grep -v "\-\- Logs begin" | \ 35 | grep -v "pam_unix(sudo:session)" | \ 36 | grep -v "bootup/mount_vsd_" | \ 37 | sed "s/simple-nafuda.service: //" | \ 38 | sed "s/.*\]: //" | \ 39 | tail -10 | /home/pi/electronic_badge_2018/show_txt/show_txt.py - 40 | -------------------------------------------------------------------------------- /simple_nafuda/img/bcon-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/simple_nafuda/img/bcon-logo.png -------------------------------------------------------------------------------- /simple_nafuda/img/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/simple_nafuda/img/texture.png -------------------------------------------------------------------------------- /simple_nafuda/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # simple nafuda slide show 4 | # 5 | # for development. 6 | # $ export PSEUDO_EPD_MODE=1 7 | # $ python main.py 8 | # 9 | # Copyright (C) Aug 4 2018, Junichi Ishida 10 | # 11 | # LICENSE : MIT 12 | # 13 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 14 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation 15 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 16 | # to permit persons to whom the Software is furnished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of 19 | # the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 22 | # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | # 27 | 28 | import os 29 | import sys 30 | import time 31 | import re 32 | import qrcode 33 | import hashlib 34 | import requests 35 | import json 36 | import traceback 37 | from PIL import Image 38 | from PIL import ImageFont 39 | from PIL import ImageDraw 40 | 41 | sys.path.append(os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + '/../lib')) 42 | 43 | from nafuda import Nafuda 44 | 45 | if "IMG_DIR" in os.environ: 46 | IMG_DIR = os.environ["IMG_DIR"] 47 | else: 48 | if os.path.isdir('/mnt/virtual_sd/img'): 49 | IMG_DIR = '/mnt/virtual_sd/img' 50 | else: 51 | # virtual sdがない時(開発時など)用 52 | IMG_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + '/img') 53 | 54 | CLOUD_JSON_CACHE_PATH = IMG_DIR + "/cloud.json" 55 | CLOUD_QR_CODE_FILE_NAME = "__CONVERT_TO_QR__.png" 56 | CLOUD_QR_CODE_FILE_PATH = IMG_DIR + "/" + CLOUD_QR_CODE_FILE_NAME 57 | 58 | def main(): 59 | load_settings_from_cloud() 60 | 61 | # load image file list 62 | file_list = [] 63 | for file in os.listdir(IMG_DIR): 64 | # get (png|jpg|jpeg|gif) files. and skip dot files. 65 | if re.search('^[^\.].*\.(png|jpg|jpeg|gif)', file, re.IGNORECASE): 66 | file_list.append(file) 67 | 68 | if len(file_list) == 0: 69 | print('no image, exit.') 70 | sys.exit(0) 71 | 72 | file_list.sort() 73 | 74 | print(file_list) 75 | 76 | nafuda = Nafuda() 77 | 78 | data = local_settings() 79 | 80 | while True: 81 | for file in file_list: 82 | 83 | # QRコード置換用画像が来た 84 | if file == CLOUD_QR_CODE_FILE_NAME: 85 | # CLOUD_BASE_URLがなければ、QRコードを表示しない 86 | if "CLOUD_BASE_URL" in data and data['CLOUD_BASE_URL'] != "": 87 | # QRコードを合成して表示 88 | base_image = Image.open(CLOUD_QR_CODE_FILE_PATH) 89 | qr_img = get_control_url_qrcode_img() 90 | base_image.paste(qr_img, (10, 10)) 91 | nafuda.draw_image_buffer(base_image, orientation=90) 92 | if "PSEUDO_EPD_MODE" in os.environ: 93 | # guard for img bomb. 94 | time.sleep(3) 95 | 96 | continue 97 | 98 | try: 99 | nafuda.draw_image_file(IMG_DIR + '/' + file, 90) 100 | 101 | except OSError: 102 | # maybe, the file is not correct image file. 103 | print("load image fail: " + file) 104 | 105 | if "PSEUDO_EPD_MODE" in os.environ: 106 | # guard for img bomb. 107 | time.sleep(3) 108 | 109 | # 一枚しか画像がなければ、スライドショーする意味がないので終了 110 | if len(file_list) == 1: 111 | exit(0) 112 | 113 | 114 | def local_settings(): 115 | setting_json = "settings.json" 116 | 117 | if not os.path.exists(setting_json): 118 | raise LoadLocalSettingsError 119 | 120 | with open(setting_json, "r") as sj: 121 | data = json.loads(sj.read()) 122 | 123 | return data 124 | 125 | 126 | def load_settings_from_cloud(): 127 | data = local_settings() 128 | 129 | # 設定がなければ、実行しない 130 | if "CLOUD_BASE_URL" not in data or data['CLOUD_BASE_URL'] == "": 131 | print("no CLOUD_BASE_URL") 132 | return False 133 | 134 | # QRコードに置換する画像がなければ、クラウド機能は不要と判断して実行しない 135 | if not os.path.isfile(CLOUD_QR_CODE_FILE_PATH): 136 | return False 137 | 138 | # https://server/{SHA2}/json settings 139 | # https://server/{SHA2}/ UI 140 | # https://server/bc2018/{SHA2}/[0-9].(png|jpg|gif...) imgs 141 | print("try get data from cloud") 142 | try: 143 | # 設定JSONをサーバーから取得試行 144 | r = requests.get(get_control_url() + "json") 145 | if r.status_code != 200: 146 | return "json not found" 147 | 148 | json_str = r.text 149 | 150 | data = json.loads(json_str) 151 | img_list = data['list'] 152 | if not isinstance(img_list, list): 153 | return "maybe invalid json" 154 | 155 | if len(img_list) == 0: 156 | # 空の場合はなにもしない 157 | return "empty list" 158 | 159 | # 過去のJSONがあれば、更新があるか確認 160 | if os.path.isfile(CLOUD_JSON_CACHE_PATH): 161 | with open(CLOUD_JSON_CACHE_PATH, "r") as jc: 162 | cached_json = jc.read() 163 | if cached_json == json_str: 164 | return "json not updated" 165 | 166 | # rwで再マウント 167 | if os.path.isfile("/usr/bin/mount_vsd_rw"): 168 | if os.system('/usr/bin/mount_vsd_rw') != 0: 169 | return "mount_vsd_rw fail." 170 | 171 | # clean up img dir 172 | file_list_to_rm = os.listdir(IMG_DIR) 173 | for f in file_list_to_rm: 174 | p = IMG_DIR + "/" + f 175 | if os.path.isfile(p): 176 | if re.search('^[^\.].*\.(png|jpg|jpeg|gif)', p, re.IGNORECASE): 177 | if f != CLOUD_QR_CODE_FILE_NAME: 178 | os.remove(p) 179 | 180 | # 画像をDLして保存 181 | id = 1 182 | for img in img_list: 183 | root, ext = os.path.splitext(img) 184 | get_and_save_file(get_img_url_base() + "/" + img, IMG_DIR + "/" + str(id) + ext) 185 | id = id + 1 186 | 187 | # save json 188 | with open(CLOUD_JSON_CACHE_PATH, "w") as jc: 189 | jc.write(json_str) 190 | 191 | # roで再マウント 192 | if os.path.isfile("/usr/bin/mount_vsd_ro"): 193 | if os.system('/usr/bin/mount_vsd_ro') != 0: 194 | return "mount_vsd_ro fail." 195 | 196 | return True 197 | 198 | except: 199 | # 止まられると困る! 200 | traceback.print_exc() 201 | return False 202 | 203 | 204 | def get_nafuda_id(): 205 | h_path = '/mnt/virtual_sd/default_hostname.txt' 206 | p_path = '/mnt/virtual_sd/default_passwd.txt' 207 | 208 | if not os.path.isfile(h_path) or not os.path.isfile(p_path): 209 | raise CouldNotGenerateNafudaIdError 210 | 211 | try: 212 | hostname = open(h_path).read(128).encode('UTF-8') 213 | passwd = open(p_path).read(128).encode('UTF-8') 214 | 215 | hash = hashlib.sha256(hostname + passwd).hexdigest() 216 | # print(hash) 217 | 218 | except OSError: 219 | return False 220 | 221 | return hash 222 | 223 | 224 | def get_img_url_base(): 225 | data = local_settings() 226 | return data['CLOUD_BASE_URL'] + "/bc2018/" + get_nafuda_id() + "/" 227 | 228 | 229 | def get_control_url(): 230 | data = local_settings() 231 | return data['CLOUD_BASE_URL'] + get_nafuda_id() + "/" 232 | 233 | 234 | def get_control_url_qrcode_img(): 235 | qr = qrcode.QRCode( 236 | version=1, 237 | error_correction=qrcode.constants.ERROR_CORRECT_L, 238 | box_size=6, 239 | border=4, 240 | ) 241 | 242 | try: 243 | qr.add_data(get_control_url()) 244 | qr.make(fit=True) 245 | # print(qr.size) 246 | return qr.make_image(fill_color="black", back_color="white") 247 | 248 | except CouldNotGenerateNafudaIdError: 249 | # qrコード生成に失敗。エラーメッセージの画像を作って返す 250 | return generate_sorry_image("QRコードの生成に失敗:\n初期化してください") 251 | 252 | 253 | def generate_sorry_image(error_msg): 254 | if "EPD_FONT_PATH" in os.environ: 255 | font_path = os.environ['EPD_FONT_PATH'] 256 | else: 257 | font_path = '/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf' 258 | 259 | font_pt = 16 260 | font = ImageFont.truetype(font_path, font_pt) 261 | image = Image.new('1', (200, 200), 1) # 1: clear the frame 262 | draw = ImageDraw.Draw(image) 263 | 264 | draw.text( 265 | (0, 0), 266 | error_msg, 267 | font=font, 268 | fill=0) 269 | 270 | return image 271 | 272 | 273 | def get_and_save_file(url, file_path): 274 | r = requests.get(url, stream=True) 275 | with open(file_path, 'wb') as f: 276 | for chunk in r.iter_content(chunk_size=1024): 277 | if chunk: 278 | f.write(chunk) 279 | f.flush() 280 | 281 | 282 | class CouldNotGenerateNafudaIdError(Exception): 283 | """ファイル不足などで名札IDが生成できなかった場合のエラー""" 284 | 285 | 286 | class LoadLocalSettingsError(Exception): 287 | """settings.jsonをロードできなかった場合のエラー""" 288 | 289 | 290 | if __name__ == '__main__': 291 | main() 292 | -------------------------------------------------------------------------------- /simple_nafuda/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "CLOUD_BASE_URL": "" 3 | } -------------------------------------------------------------------------------- /simple_nafuda/simple-nafuda.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=simple nafuda 3 | 4 | [Service] 5 | WorkingDirectory=/home/pi/electronic_badge_2018/simple_nafuda/ 6 | ExecStart=/usr/bin/python3 main.py 7 | ExecStopPost=/home/pi/electronic_badge_2018/simple_nafuda/exec_stop_post.sh 8 | Restart=no 9 | Type=simple 10 | User=pi 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | 15 | ## how to install 16 | # sudo cp simple-nafuda.service /etc/systemd/system/ 17 | # sudo systemctl daemon-reload 18 | # sudo systemctl status simple-nafuda 19 | # (check output 20 | # sudo systemctl start simple-nafuda 21 | # -------------------------------------------------------------------------------- /simple_nafuda_server/.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | public/bc2018/* 3 | -------------------------------------------------------------------------------- /simple_nafuda_server/README.md: -------------------------------------------------------------------------------- 1 | simple nafuda ota update server 2 | =================== 3 | 4 | - 「`simple-nafuda`アプリをスマホとかでも更新できるようにしたい!」 5 | - 「えっ(時間ないんですけど…)…作ります…」 6 | 7 | TBD 8 | 9 | ## require 10 | 11 | - php 12 | - php-gd 13 | - etc. 14 | -------------------------------------------------------------------------------- /simple_nafuda_server/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "nikic/fast-route": "^1.3", 4 | "twig/twig": "^2.5", 5 | "intervention/image": "^2.4" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /simple_nafuda_server/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "e39604e1833283c20752cf4f49b2e4a4", 8 | "packages": [ 9 | { 10 | "name": "guzzlehttp/psr7", 11 | "version": "1.4.2", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/guzzle/psr7.git", 15 | "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", 20 | "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.4.0", 25 | "psr/http-message": "~1.0" 26 | }, 27 | "provide": { 28 | "psr/http-message-implementation": "1.0" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "~4.0" 32 | }, 33 | "type": "library", 34 | "extra": { 35 | "branch-alias": { 36 | "dev-master": "1.4-dev" 37 | } 38 | }, 39 | "autoload": { 40 | "psr-4": { 41 | "GuzzleHttp\\Psr7\\": "src/" 42 | }, 43 | "files": [ 44 | "src/functions_include.php" 45 | ] 46 | }, 47 | "notification-url": "https://packagist.org/downloads/", 48 | "license": [ 49 | "MIT" 50 | ], 51 | "authors": [ 52 | { 53 | "name": "Michael Dowling", 54 | "email": "mtdowling@gmail.com", 55 | "homepage": "https://github.com/mtdowling" 56 | }, 57 | { 58 | "name": "Tobias Schultze", 59 | "homepage": "https://github.com/Tobion" 60 | } 61 | ], 62 | "description": "PSR-7 message implementation that also provides common utility methods", 63 | "keywords": [ 64 | "http", 65 | "message", 66 | "request", 67 | "response", 68 | "stream", 69 | "uri", 70 | "url" 71 | ], 72 | "time": "2017-03-20T17:10:46+00:00" 73 | }, 74 | { 75 | "name": "intervention/image", 76 | "version": "2.4.2", 77 | "source": { 78 | "type": "git", 79 | "url": "https://github.com/Intervention/image.git", 80 | "reference": "e82d274f786e3d4b866a59b173f42e716f0783eb" 81 | }, 82 | "dist": { 83 | "type": "zip", 84 | "url": "https://api.github.com/repos/Intervention/image/zipball/e82d274f786e3d4b866a59b173f42e716f0783eb", 85 | "reference": "e82d274f786e3d4b866a59b173f42e716f0783eb", 86 | "shasum": "" 87 | }, 88 | "require": { 89 | "ext-fileinfo": "*", 90 | "guzzlehttp/psr7": "~1.1", 91 | "php": ">=5.4.0" 92 | }, 93 | "require-dev": { 94 | "mockery/mockery": "~0.9.2", 95 | "phpunit/phpunit": "^4.8 || ^5.7" 96 | }, 97 | "suggest": { 98 | "ext-gd": "to use GD library based image processing.", 99 | "ext-imagick": "to use Imagick based image processing.", 100 | "intervention/imagecache": "Caching extension for the Intervention Image library" 101 | }, 102 | "type": "library", 103 | "extra": { 104 | "branch-alias": { 105 | "dev-master": "2.4-dev" 106 | }, 107 | "laravel": { 108 | "providers": [ 109 | "Intervention\\Image\\ImageServiceProvider" 110 | ], 111 | "aliases": { 112 | "Image": "Intervention\\Image\\Facades\\Image" 113 | } 114 | } 115 | }, 116 | "autoload": { 117 | "psr-4": { 118 | "Intervention\\Image\\": "src/Intervention/Image" 119 | } 120 | }, 121 | "notification-url": "https://packagist.org/downloads/", 122 | "license": [ 123 | "MIT" 124 | ], 125 | "authors": [ 126 | { 127 | "name": "Oliver Vogel", 128 | "email": "oliver@olivervogel.com", 129 | "homepage": "http://olivervogel.com/" 130 | } 131 | ], 132 | "description": "Image handling and manipulation library with support for Laravel integration", 133 | "homepage": "http://image.intervention.io/", 134 | "keywords": [ 135 | "gd", 136 | "image", 137 | "imagick", 138 | "laravel", 139 | "thumbnail", 140 | "watermark" 141 | ], 142 | "time": "2018-05-29T14:19:03+00:00" 143 | }, 144 | { 145 | "name": "nikic/fast-route", 146 | "version": "v1.3.0", 147 | "source": { 148 | "type": "git", 149 | "url": "https://github.com/nikic/FastRoute.git", 150 | "reference": "181d480e08d9476e61381e04a71b34dc0432e812" 151 | }, 152 | "dist": { 153 | "type": "zip", 154 | "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", 155 | "reference": "181d480e08d9476e61381e04a71b34dc0432e812", 156 | "shasum": "" 157 | }, 158 | "require": { 159 | "php": ">=5.4.0" 160 | }, 161 | "require-dev": { 162 | "phpunit/phpunit": "^4.8.35|~5.7" 163 | }, 164 | "type": "library", 165 | "autoload": { 166 | "psr-4": { 167 | "FastRoute\\": "src/" 168 | }, 169 | "files": [ 170 | "src/functions.php" 171 | ] 172 | }, 173 | "notification-url": "https://packagist.org/downloads/", 174 | "license": [ 175 | "BSD-3-Clause" 176 | ], 177 | "authors": [ 178 | { 179 | "name": "Nikita Popov", 180 | "email": "nikic@php.net" 181 | } 182 | ], 183 | "description": "Fast request router for PHP", 184 | "keywords": [ 185 | "router", 186 | "routing" 187 | ], 188 | "time": "2018-02-13T20:26:39+00:00" 189 | }, 190 | { 191 | "name": "psr/http-message", 192 | "version": "1.0.1", 193 | "source": { 194 | "type": "git", 195 | "url": "https://github.com/php-fig/http-message.git", 196 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 197 | }, 198 | "dist": { 199 | "type": "zip", 200 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 201 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 202 | "shasum": "" 203 | }, 204 | "require": { 205 | "php": ">=5.3.0" 206 | }, 207 | "type": "library", 208 | "extra": { 209 | "branch-alias": { 210 | "dev-master": "1.0.x-dev" 211 | } 212 | }, 213 | "autoload": { 214 | "psr-4": { 215 | "Psr\\Http\\Message\\": "src/" 216 | } 217 | }, 218 | "notification-url": "https://packagist.org/downloads/", 219 | "license": [ 220 | "MIT" 221 | ], 222 | "authors": [ 223 | { 224 | "name": "PHP-FIG", 225 | "homepage": "http://www.php-fig.org/" 226 | } 227 | ], 228 | "description": "Common interface for HTTP messages", 229 | "homepage": "https://github.com/php-fig/http-message", 230 | "keywords": [ 231 | "http", 232 | "http-message", 233 | "psr", 234 | "psr-7", 235 | "request", 236 | "response" 237 | ], 238 | "time": "2016-08-06T14:39:51+00:00" 239 | }, 240 | { 241 | "name": "symfony/polyfill-ctype", 242 | "version": "v1.9.0", 243 | "source": { 244 | "type": "git", 245 | "url": "https://github.com/symfony/polyfill-ctype.git", 246 | "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" 247 | }, 248 | "dist": { 249 | "type": "zip", 250 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", 251 | "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", 252 | "shasum": "" 253 | }, 254 | "require": { 255 | "php": ">=5.3.3" 256 | }, 257 | "suggest": { 258 | "ext-ctype": "For best performance" 259 | }, 260 | "type": "library", 261 | "extra": { 262 | "branch-alias": { 263 | "dev-master": "1.9-dev" 264 | } 265 | }, 266 | "autoload": { 267 | "psr-4": { 268 | "Symfony\\Polyfill\\Ctype\\": "" 269 | }, 270 | "files": [ 271 | "bootstrap.php" 272 | ] 273 | }, 274 | "notification-url": "https://packagist.org/downloads/", 275 | "license": [ 276 | "MIT" 277 | ], 278 | "authors": [ 279 | { 280 | "name": "Symfony Community", 281 | "homepage": "https://symfony.com/contributors" 282 | }, 283 | { 284 | "name": "Gert de Pagter", 285 | "email": "BackEndTea@gmail.com" 286 | } 287 | ], 288 | "description": "Symfony polyfill for ctype functions", 289 | "homepage": "https://symfony.com", 290 | "keywords": [ 291 | "compatibility", 292 | "ctype", 293 | "polyfill", 294 | "portable" 295 | ], 296 | "time": "2018-08-06T14:22:27+00:00" 297 | }, 298 | { 299 | "name": "symfony/polyfill-mbstring", 300 | "version": "v1.9.0", 301 | "source": { 302 | "type": "git", 303 | "url": "https://github.com/symfony/polyfill-mbstring.git", 304 | "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" 305 | }, 306 | "dist": { 307 | "type": "zip", 308 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", 309 | "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", 310 | "shasum": "" 311 | }, 312 | "require": { 313 | "php": ">=5.3.3" 314 | }, 315 | "suggest": { 316 | "ext-mbstring": "For best performance" 317 | }, 318 | "type": "library", 319 | "extra": { 320 | "branch-alias": { 321 | "dev-master": "1.9-dev" 322 | } 323 | }, 324 | "autoload": { 325 | "psr-4": { 326 | "Symfony\\Polyfill\\Mbstring\\": "" 327 | }, 328 | "files": [ 329 | "bootstrap.php" 330 | ] 331 | }, 332 | "notification-url": "https://packagist.org/downloads/", 333 | "license": [ 334 | "MIT" 335 | ], 336 | "authors": [ 337 | { 338 | "name": "Nicolas Grekas", 339 | "email": "p@tchwork.com" 340 | }, 341 | { 342 | "name": "Symfony Community", 343 | "homepage": "https://symfony.com/contributors" 344 | } 345 | ], 346 | "description": "Symfony polyfill for the Mbstring extension", 347 | "homepage": "https://symfony.com", 348 | "keywords": [ 349 | "compatibility", 350 | "mbstring", 351 | "polyfill", 352 | "portable", 353 | "shim" 354 | ], 355 | "time": "2018-08-06T14:22:27+00:00" 356 | }, 357 | { 358 | "name": "twig/twig", 359 | "version": "v2.5.0", 360 | "source": { 361 | "type": "git", 362 | "url": "https://github.com/twigphp/Twig.git", 363 | "reference": "6a5f676b77a90823c2d4eaf76137b771adf31323" 364 | }, 365 | "dist": { 366 | "type": "zip", 367 | "url": "https://api.github.com/repos/twigphp/Twig/zipball/6a5f676b77a90823c2d4eaf76137b771adf31323", 368 | "reference": "6a5f676b77a90823c2d4eaf76137b771adf31323", 369 | "shasum": "" 370 | }, 371 | "require": { 372 | "php": "^7.0", 373 | "symfony/polyfill-ctype": "^1.8", 374 | "symfony/polyfill-mbstring": "~1.0" 375 | }, 376 | "require-dev": { 377 | "psr/container": "^1.0", 378 | "symfony/debug": "^2.7", 379 | "symfony/phpunit-bridge": "^3.3" 380 | }, 381 | "type": "library", 382 | "extra": { 383 | "branch-alias": { 384 | "dev-master": "2.5-dev" 385 | } 386 | }, 387 | "autoload": { 388 | "psr-0": { 389 | "Twig_": "lib/" 390 | }, 391 | "psr-4": { 392 | "Twig\\": "src/" 393 | } 394 | }, 395 | "notification-url": "https://packagist.org/downloads/", 396 | "license": [ 397 | "BSD-3-Clause" 398 | ], 399 | "authors": [ 400 | { 401 | "name": "Fabien Potencier", 402 | "email": "fabien@symfony.com", 403 | "homepage": "http://fabien.potencier.org", 404 | "role": "Lead Developer" 405 | }, 406 | { 407 | "name": "Armin Ronacher", 408 | "email": "armin.ronacher@active-4.com", 409 | "role": "Project Founder" 410 | }, 411 | { 412 | "name": "Twig Team", 413 | "homepage": "https://twig.symfony.com/contributors", 414 | "role": "Contributors" 415 | } 416 | ], 417 | "description": "Twig, the flexible, fast, and secure template language for PHP", 418 | "homepage": "https://twig.symfony.com", 419 | "keywords": [ 420 | "templating" 421 | ], 422 | "time": "2018-07-13T07:18:09+00:00" 423 | } 424 | ], 425 | "packages-dev": [], 426 | "aliases": [], 427 | "minimum-stability": "stable", 428 | "stability-flags": [], 429 | "prefer-stable": false, 430 | "prefer-lowest": false, 431 | "platform": [], 432 | "platform-dev": [] 433 | } 434 | -------------------------------------------------------------------------------- /simple_nafuda_server/public/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteCond %{REQUEST_FILENAME} !-f 3 | RewriteRule ^ index.php [QSA,L] 4 | 5 | # sudo a2enmod headers 6 | RequestHeader unset If-Modified-Since 7 | Header set Cache-Control no-store -------------------------------------------------------------------------------- /simple_nafuda_server/public/bc2018/image_store_dir: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/builderscon/electronic_badge_2018/0999c28f7f7c635e9f3a809f723c237b3856138d/simple_nafuda_server/public/bc2018/image_store_dir -------------------------------------------------------------------------------- /simple_nafuda_server/public/form.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | electronic badge - builderscon tokyo 2018 7 | 13 | 14 | 15 | 16 |

電子名札 画像設定ツール

17 | 18 |

こちらの画面で画像をアップロードした後、「ネットワークがある状態で」電子名札のrebootが必要です。
19 | (起動時の情報表示に、wlan0などでIPが振られえいることを確認してください)
20 | reboot後、nafuda slideshow前にDownloadが行われます。

21 | 22 |

23 | Max file size は 5MB です。アップロードに失敗する場合は、resizeしてください 24 |

25 | 26 |

27 | QRコードは他人にスキャンされないようにご注意ください(わざとであれば問題はありません) 28 |

29 | 30 |

一覧

31 | 32 | $exists) { $num = $i;?> 33 | 34 | 35 |
36 | . 37 | 38 |
39 | 40 |
41 | . 空 42 | 43 | 44 |
45 | 46 | 47 | 48 |
49 | 50 |

51 | 電子名札の情報やドキュメントなどが記載された、公式のGithubはこちらからどうぞ
52 | github/builderscon/electronic_badge_2018 53 |

54 | 55 |

56 | 初期に登録されていた注意書きや、メルカリ様ロゴなどは以下からDLできます
57 | IMG 58 |

59 | 60 | 61 | -------------------------------------------------------------------------------- /simple_nafuda_server/public/index.php: -------------------------------------------------------------------------------- 1 | addRoute('GET', '/{uid}/', 'form'); 9 | $r->addRoute('GET', '/{uid}/json', 'json'); 10 | $r->addRoute('POST', '/{uid}/upload/{num}', 'upload'); 11 | $r->addRoute('POST', '/{uid}/delete/{num}', 'delete'); 12 | }; 13 | $dispatcher = FastRoute\cachedDispatcher($handlers, [ 14 | 'cacheFile' => __DIR__ . '/../route.cache', 15 | 'cacheDisabled' => true 16 | ]); 17 | $uri = $_SERVER['REQUEST_URI']; 18 | $method = $_SERVER['REQUEST_METHOD']; 19 | $routeInfo = $dispatcher->dispatch($method, $uri); 20 | switch ($routeInfo[0]) { 21 | case FastRoute\Dispatcher::NOT_FOUND: 22 | echo "NotFound"; 23 | break; 24 | case FastRoute\Dispatcher::METHOD_NOT_ALLOWED: 25 | $allowedMethods = $routeInfo[1]; 26 | echo "Forbidden"; 27 | break; 28 | case FastRoute\Dispatcher::FOUND: 29 | $handler = $routeInfo[1]; 30 | $vars = $routeInfo[2]; 31 | echo $handler($vars); 32 | break; 33 | } 34 | 35 | function form($args) 36 | { 37 | $s = generate_something($args); 38 | 39 | $list = exists_img_list($s); 40 | include 'form.php'; 41 | } 42 | 43 | function exists_img_list($s) 44 | { 45 | if (!file_exists($s['base_img_dir'])) { 46 | mkdir($s['$base_img_dir']); 47 | } 48 | 49 | $list = []; 50 | for ($i = 1; $i < 10; $i++) { 51 | if (file_exists("{$s['base_img_dir']}/{$i}.png")) { 52 | $list[$i] = true; 53 | } else { 54 | $list[$i] = false; 55 | } 56 | } 57 | return $list; 58 | } 59 | 60 | function json($args) 61 | { 62 | $s = generate_something($args); 63 | $list = []; 64 | foreach (exists_img_list($s) as $k => $exists) { 65 | if ($exists) { 66 | $list[] = "{$k}.png"; 67 | } 68 | } 69 | 70 | header('content-type: application/json'); 71 | echo json_encode([ 72 | "last_update" => time(), # fix me!!! 73 | "list" => $list 74 | ], JSON_PRETTY_PRINT); 75 | 76 | } 77 | 78 | function upload($args) 79 | { 80 | if (!isset($_FILES['image'])) { 81 | redirect($s['your_form_url']); 82 | } 83 | 84 | $tmp = $_FILES['image']['tmp_name']; 85 | if (!is_uploaded_file($tmp)) { 86 | throw new \InvalidArgumentException("not upload file"); 87 | } 88 | 89 | if (filesize($tmp) > 1024 * 1024 * 5) { 90 | throw new \InvalidArgumentException("too large. 5MB is max."); 91 | } 92 | 93 | $img_size = getimagesize($tmp); 94 | if ($img_size[0] > 5000 || $img_size[1] > 5000) { 95 | throw new \InvalidArgumentException("too big. 5000px is max."); 96 | } 97 | 98 | $imgmanager = new \Intervention\Image\ImageManager(); 99 | 100 | $image = $imgmanager 101 | ->make($tmp) 102 | ->orientate()// なんか…色がおかしくなる… 103 | ->widen(300, function ($constraint) { 104 | $constraint->upsize(); 105 | }) 106 | ->heighten(400, function ($constraint) { 107 | $constraint->upsize(); 108 | }); 109 | 110 | $s = generate_something($args); 111 | 112 | if (!file_exists($s['base_img_dir'])) { 113 | mkdir($s['base_img_dir']); 114 | } 115 | 116 | $image->save("{$s['base_img_dir']}/{$s['num']}.png", 60); 117 | 118 | redirect($s['your_form_url']); 119 | } 120 | 121 | function delete($args) 122 | { 123 | $s = generate_something($args); 124 | $file_path = "{$s['base_img_dir']}/{$s['num']}.png"; 125 | if (file_exists($file_path)) { 126 | unlink($file_path); 127 | } 128 | redirect($s['your_form_url']); 129 | } 130 | 131 | # 132 | ### 133 | # 134 | 135 | function generate_something($args) 136 | { 137 | $rtn = []; 138 | 139 | $uid = $args['uid']; 140 | if (strlen($uid) != 64) { 141 | throw new \InvalidArgumentException('invalid nafuda id (len)' . strlen($uid)); 142 | } 143 | if (preg_match('/[^A-Za-z[0-9]]/u', $uid)) { 144 | throw new \InvalidArgumentException('invalid nafuda id (char)'); 145 | } 146 | $rtn['uid'] = $uid; 147 | 148 | $rtn['base_img_dir'] = IMG_DIR . '/' . $uid; 149 | $rtn['base_img_url'] = '/' . TG . '/' . $uid; 150 | 151 | $rtn['your_form_url'] = $uid . "/"; 152 | 153 | if (isset($args['num'])) { 154 | $num = (int)$args['num']; 155 | if ($num > 10 || 1 > $num) { 156 | throw new \InvalidArgumentException("invalid num"); 157 | } 158 | $rtn['num'] = $num; 159 | } 160 | 161 | return $rtn; 162 | } 163 | 164 | function redirect($url) 165 | { 166 | header("Location: /{$url}"); 167 | exit; 168 | } 169 | 170 | function e($str, $rtn = false) 171 | { 172 | if ($rtn) { 173 | return htmlspecialchars($str, ENT_QUOTES); 174 | } else { 175 | echo htmlspecialchars($str, ENT_QUOTES); 176 | return; 177 | } 178 | } 179 | 180 | -------------------------------------------------------------------------------- /simple_sample/README.md: -------------------------------------------------------------------------------- 1 | simple sample 2 | ============= 3 | 4 | 一番簡単なHello worldサンプル。 5 | 6 | 「ログインせずに」NAFUDAドライブの中身だけでハックする! 7 | 8 | # 試し方 9 | 10 | - このディレクトリの`startup.sh`をNAFUDAドライブのトップに設置する 11 | - 名札を再起動する 12 | - 「hello nafuda world!」 13 | 14 | 15 | # 概要説明 16 | 17 | systemdに登録された`nafuda-bootup.service`(`electronic_badge_2018/bootup/bootup.sh`)は、最後にNAFUDAドライブの直下にstartup.shがあればそれを起動します。 18 | 19 | > ※ その場合、simple-nafudaは起動されません。 20 | 21 | この`starup.sh`は無限ループで名札内に保存されている `show_txt`や`show_img`コマンドを用いて画像を表示しています。 22 | 23 | このシェルスクリプトを改変することで、簡単なものならばログインなしにプログラムが可能です。 24 | 25 | > ※ エラー原因を特定するなどを考えるとなかなか難しいですが… 26 | 27 | > ※ `show_txt`や`show_img`の使い方はGitHubのレポジトリのそれぞれをみてください。 28 | 29 | > ※ この`startup.sh`はroot権限で起動されます 30 | 31 | 32 | # たとえば他の例 33 | 34 | ``` 35 | # nictの現在時刻取得jsonをたたいて表示してみるとか 36 | curl http://ntp-a1.nict.go.jp/cgi-bin/json | show_txt - 37 | 38 | # 画像をDLして表示するとか 39 | # ※ /mnt/virtual_sdは初期ではread onlyでマウントしているため、 40 | #   書き込みが必要であれば/tmpを指定してください。 41 | curl 'https://builderscon.io/static/images/mrbeacon-001.png' > /tmp/bcon.png 42 | show_img /tmp/bcon.png 43 | #   (もしご自身でmount_vsd_rwを用いて再マウントすればその限りではありませんが、 44 | #    busyだと失敗するので、やはりログインできるようになってからが良いでしょう) 45 | ``` 46 | 47 | > ※ `show_img`の実体は `/home/pi/electronic_badge_2018/show_img/show_img.py` 48 | 49 | > ※ `show_txt`の実体は `/home/pi/electronic_badge_2018/show_txt/show_txt.py` 50 | 51 | # おまけ 52 | 53 | ## 各インタプリタへのパス 54 | 55 | ``` 56 | /usr/bin/python3 57 | ※ /usr/bin/python は python2なので推奨はしません 58 | /usr/bin/perl 59 | ``` 60 | 61 | ## 「とにかくなにか実行して結果が知りたい」 62 | 63 | `show_txt`をつかえば、epaperに表示することが可能です。 64 | 65 | ```bash 66 | #!/bin/bash 67 | ls -al ~/ 2>&1 | show_txt - 68 | # 2>&1 をいれないと、エラー出力がすてられます 69 | ``` 70 | 71 | > ※ 大変なので、ログインしたほうが良いとおもいます! 72 | 73 | 74 | ## git cloneやpullできるのでは? 75 | 76 | やってやれないことはありません。 77 | 78 | ```bash 79 | export GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" 80 | git -C /home/pi clone https://github.com/builderscon/electronic_badge_2018.git 2>&1 | show_txt - 81 | git -C /home/pi pull 2>&1 | show_txt - 82 | ``` 83 | 84 | > ※ コンフリクトしていたときに解消が難しいので、おすすめはできませんが… 85 | 86 | ## 「あれやこれやができるのでは?」 87 | 88 | root権限で、TTYが不要で、CLIで実行できることなら、大体できるとおもいます。 89 | 90 | ## 「危険では?!」 91 | 92 | microSDを抜いてマウントすればなんでもできますし、NAFUDAドライブの中にはデフォルトパスワードもみえるので、電子名札はそういう思想です。 93 | 94 | 電子名札には機密情報をいれないようにしましょう。入れる場合は自己責任。 95 | 96 | ## Do you want PHP? 97 | 98 | phpがお好き?そう、PHPもいれられますね。以下のstartup.shを作成して再起動すればインストールできるよ! 99 | 100 | ```bash 101 | #!/bin/bash 102 | echo "php installing now..." | show_txt - 103 | # とても時間かかります! 104 | sudo apt -y install php 2>&1 | show_txt - 105 | LOG=`which php` 106 | LOG="${LOG} `php -v`" 107 | echo "${LOG}" 108 | echo "finished. ${LOG}" | show_txt - 109 | ``` 110 | 111 | 112 | あとは、たとえば以下みたいなコードをかけば表示もできます! 113 | 114 | ```php 115 | ※ NAFUDAドライブは読み込み専用でマウントされているので、一時ファイルを作成するときは /tmp/ に作成してください。 123 | 124 | > ※ もしご自身の自己責任でmount_vsd_rw.shを用いて再マウントすればその限りではありません。 125 | -------------------------------------------------------------------------------- /simple_sample/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 無限ループ 4 | while : 5 | do 6 | 7 | # テキストを標準入力から受け取って、電子ペーパーに表示 8 | echo "hello NAFUDA world!" | show_txt - 9 | 10 | # テキストをファイルから読み込んで、電子ペーパーに表示 11 | show_txt /mnt/virtual_sd/startup.sh 12 | 13 | # 画像をファイルから読み込んで、電子ペーパーに表示 14 | show_img /mnt/virtual_sd/img/bcon-logo.png 15 | 16 | # コマンド結果を電子ペーパーに表示 17 | ls -al ~/ 2>&1 | show_txt - 18 | 19 | done 20 | 21 | -------------------------------------------------------------------------------- /weather/README.md: -------------------------------------------------------------------------------- 1 | # weather news sample 2 | 3 | 天気を表示するサンプルです。 4 | 5 | Show today's weather(in japanese). 6 | 7 | ## usage 8 | 9 | ``` 10 | $ python3 main.py 11 | ``` 12 | 13 | ## info 14 | 15 | Livedoorの天気 WebAPIを利用しています。 16 | -------------------------------------------------------------------------------- /weather/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # draw today weather 3 | 4 | # for development. 5 | # $ export PSEUDO_EPD_MODE=1 6 | # $ python show.py your_image.png 7 | # 8 | # if you want to use your fav font 9 | # $ export EPD_FONT_PATH="/System/Library/Fonts/ヒラギノ角ゴシック W0.ttc" 10 | # $ python main.py 11 | # 12 | # Copyright (C) Aug 4 2018, Junichi Ishida 13 | # 14 | # LICENSE : MIT 15 | # 16 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 17 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation 18 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 19 | # to permit persons to whom the Software is furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of 22 | # the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 25 | # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 26 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 27 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | # 30 | 31 | import os 32 | import sys 33 | from PIL import Image 34 | from PIL import ImageDraw 35 | from PIL import ImageFont 36 | import weather 37 | 38 | sys.path.append(os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + '/../lib')) 39 | 40 | # for PC development 41 | if "PSEUDO_EPD_MODE" in os.environ: 42 | import epd4in2_mock as epd4in2 43 | else: 44 | import epd4in2 45 | 46 | EPD_WIDTH = 400 47 | EPD_HEIGHT = 300 48 | 49 | 50 | def main(): 51 | epd = epd4in2.EPD() 52 | epd.init() 53 | 54 | weather_client = weather.Weather() 55 | 56 | # 130010 is tokyo 57 | weather_data = weather_client.get_usable_array(130010) 58 | 59 | # For simplicity, the arguments are explicit numerical coordinates 60 | image = Image.new('1', (EPD_WIDTH, EPD_HEIGHT), 1) # 1: clear the frame 61 | 62 | if "EPD_FONT_PATH" in os.environ: 63 | font_path = os.environ['EPD_FONT_PATH'] 64 | else: 65 | font_path = '/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf' 66 | 67 | draw_fit_text_to_image( 68 | image, 69 | font_path, 70 | weather_data['telop'], 71 | # 縦半分の高さで、y軸0始点 72 | (EPD_WIDTH, EPD_HEIGHT / 2), 73 | (0, 0)) 74 | 75 | temperture_text = "最高気温" + weather_data['max_temperature'] + "度" 76 | 77 | draw_fit_text_to_image( 78 | image, 79 | font_path, 80 | temperture_text, 81 | # 縦半分の高さで、y軸中央を始点 82 | (EPD_WIDTH, EPD_HEIGHT / 2), 83 | (0, EPD_HEIGHT / 2)) 84 | 85 | epd.display_frame(epd.get_frame_buffer(image)) 86 | 87 | 88 | def draw_fit_text_to_image(image, font_path, text, window_size, window_offset): 89 | font_pt = get_fit_font_pt(font_path, text, window_size[0], window_size[1]) 90 | 91 | font = ImageFont.truetype(font_path, font_pt) 92 | 93 | draw = ImageDraw.Draw(image) 94 | offset = get_offset_for_centering(window_size, font.getsize(text)) 95 | 96 | draw.text( 97 | (offset[0] + window_offset[0], offset[1] + window_offset[1]), 98 | text, 99 | font=font, 100 | fill=0) 101 | 102 | 103 | def get_fit_font_pt(font_path, text, canvas_w, canvas_h): 104 | pt = 0 105 | max_pt = 256 106 | 107 | while True: 108 | # too bulldoze method. but e-paper is more than slow. 109 | pt = pt + 1 110 | if max_pt < pt: 111 | break 112 | 113 | font = ImageFont.truetype(font_path, pt) 114 | font_w, font_h = font.getsize(text) 115 | 116 | if font_w > canvas_w: 117 | break 118 | if font_h > canvas_h: 119 | break 120 | 121 | return pt - 1 122 | 123 | 124 | def get_offset_for_centering(canvas_size, img_size): 125 | x = 0 126 | y = 0 127 | # print(canvas_size) 128 | # print(canvas_size[0]) 129 | # print(img_size) 130 | # print(img_size[0]) 131 | 132 | if canvas_size[0] > img_size[0]: 133 | x = int((canvas_size[0] - img_size[0]) / 2) 134 | if canvas_size[1] > img_size[1]: 135 | y = int((canvas_size[1] - img_size[1]) / 2) 136 | 137 | return x, y 138 | 139 | 140 | if __name__ == '__main__': 141 | main() 142 | -------------------------------------------------------------------------------- /weather/weather.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) Aug 4 2018, Junichi Ishida 3 | # 4 | # LICENSE : MIT 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 7 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation 8 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 9 | # to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of 12 | # the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 15 | # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 17 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | # 20 | 21 | import urllib.request 22 | import json 23 | from pprint import pprint 24 | 25 | 26 | class Weather: 27 | 28 | @staticmethod 29 | def get_data(city_id=130010): 30 | # Livedoor Weather Web Service 31 | # http://weather.livedoor.com/weather_hacks/webservice 32 | url = 'http://weather.livedoor.com/forecast/webservice/json/v1' 33 | params = { 34 | 'city': city_id, 35 | } 36 | 37 | req = urllib.request.Request('{}?{}'.format(url, urllib.parse.urlencode(params))) 38 | with urllib.request.urlopen(req) as res: 39 | body = res.read() 40 | 41 | return json.loads(body.decode('utf-8')) 42 | 43 | @staticmethod 44 | def get_usable_array(city_id): 45 | data = Weather.get_data(city_id) 46 | # pprint(data['forecasts']) 47 | 48 | telop = data['forecasts'][0]['telop'] 49 | 50 | if data['forecasts'][0]['temperature']['max'] is None: 51 | max_temperture = "データ無し" 52 | else: 53 | max_temperture = data['forecasts'][0]['temperature']['max']['celsius'] 54 | 55 | return { 56 | 'telop': telop, 57 | 'max_temperature': max_temperture 58 | } 59 | 60 | 61 | if __name__ == '__main__': 62 | pprint(Weather.get_usable_array(130010)) 63 | --------------------------------------------------------------------------------