Habr E-bookのディスプレイとしての記事を思い出しました。古い半死状態のSony PRS-505リーダーを棚から取り出して、自動化コントローラーの画面の役割で第二の人生を送ることにしました。 ただし、フラッシュメモリ経由で写真を送信することはお勧めできません。 電子書籍のRAMを直接操作する方法を学ぶ必要がありました。 これにより、表示速度と信頼性が向上します。 Goでの描画の経験をバーコードジェネレーターの例と共有し、Wirenboar 5コントローラーを介して電子書籍に表示させてください。
タスク
- 壊れることはありません。 新しい機能を追加するだけです。
- 電子ブックはポートをリッスンし、 1秒以内に画像を表示できる必要があります
- RAMのバッファのみを使用し、フラッシュメモリは使用しない
- great ddコマンドで出力をテストする
- Goで目的のサイズのバーコードを描画し、キャンバスの中央に配置します
- 画像を電子書籍のクリップボードに転送する
- 結果をお楽しみください
USB経由のRAMドライブ
Sony PRS-505にはwi-fiがなく、USBガジェットカーネルモジュールのg_file_storageのみがあるため、これが画像をすばやく転送する唯一の方法です。 幸いなことに、 PRSPlusのファームウェアは、電子書籍の電源を入れたときに任意のスクリプトを実行できます。 必要なのは、必要なファイルをディレクトリ/データベース/システム/ PRSPlusフォルダーに入れるだけで、prsp.shスクリプトがブート時に起動されます。
フラッシュメモリをバッファとして使用することはできません。そのため、USB経由でアクセスできるRAMに小さなtmpfsディスクが必要です。g_file_storageカーネルモジュールをアンロードし、USB経由で作成されたRAMディスクを公開するために必要なパラメーターをロードする必要があります。 次に、特定の領域の変更を追跡し、電子インクディスプレイに画像を表示する必要があります。
prsp.sh
#!/ bin / sh
echo $ '\ n ================ \ nSTART SCRYPT \ n' >> / dev / console
#TODO "代わりにカーネルイベントが必要です。そして、最下部にスリープします"
関数waitnewdata
{
echo $ '\ n ================= \ n新しいデータを待つ\ n' >> / dev / console
#画像ファイルの変更時間のみ表示
MODIFYTIMEOLD = `ls -l --full-time /tmp/raw.img | awk '{print $ 9}' '
MODIFYTIMENEW = $ MODIFYTIMEOLD
while ["$ MODIFYTIMEOLD" == "$ MODIFYTIMENEW"]
する
MODIFYTIMENEW = `ls -l --full-time /tmp/raw.img | awk '{print $ 9}' '
寝る0.2
やった
if ["$ MODIFYTIMEOLD"!= "$ MODIFYTIMENEW"]
それから
派手な
fi
}
関数showpic
{
echo $ '\ n ================ \ n新しいデータを受信\ n' >> / dev / console
#最適なクリアe-inkのためのバック画面の生成(オプション)
dd if = / dev / zero of = / tmp / img.raw bs = 1k count = 480
/ tmp / showpic /tmp/img.raw
dd if = / tmp / raw.img of = / tmp / img.raw bs = 1k count = 480
/ tmp / showpic /tmp/img.raw
waitnewdata
}
#ldconfig
PATH = "/ usr / local / bin:/ usr / bin:/ bin:/ usr / bin / X11:/ usr / games:/ usr / local / sony / bin:/ usr / sbin:/ sbin"
LD_LIBRARY_PATH = "/ Data / opt / sony / ebook / application:/ lib:/ usr / lib:/ usr / local / sony / lib:/ opt / sony / ebook / lib"
エクスポートパスLD_LIBRARY_PATH
#初期日付を設定
/ビン/日付0101000007
#カーネルモジュールのアンロード
rmmod g_file_storage
#生ファイル1Mbを作成
dd if = / dev / zero of = / tmp / raw.img bs = 1k count = 1k
grep Data / proc / mtd> / dev / null
[$? == 0]; それから
NUM = `grep Data / proc / mtd | awk -F: '{print $ 1}' | awk -Fd '{print $ 2}' '
insmod /lib/modules/2.4.17_n12/kernel/drivers/usb/g_file_storage.o file = / dev / mtdblock $ NUM、/ dev / sdmscard / r5c807b、/ dev / sdmscard / r5c807a、/ tmp / raw.img ProductID = $ MODEL VendorSpecific = $ VENDOR sn_select = 0 iSerialNumber = $ ID
他に
insmod /lib/modules/2.4.17_n12/kernel/drivers/usb/g_file_storage.o file = / dev / sdmscard / r5c807b、/ dev / sdmscard / r5c807a、/ tmp / raw.img ProductID = $ MODEL VendorSpecific = $ VENDOR sn_lect = 0 iSerialNumber = $ ID
fi
#kbookアプリケーションの起動
nohup / opt / sony / ebook / application / tinyhttp> / dev / null&
cp /データ/データベース/システム/ PRSPlus / showpic / tmp /
waitnewdata
echo $ '\ n ================ \ nSTART SCRYPT \ n' >> / dev / console
#TODO "代わりにカーネルイベントが必要です。そして、最下部にスリープします"
関数waitnewdata
{
echo $ '\ n ================= \ n新しいデータを待つ\ n' >> / dev / console
#画像ファイルの変更時間のみ表示
MODIFYTIMEOLD = `ls -l --full-time /tmp/raw.img | awk '{print $ 9}' '
MODIFYTIMENEW = $ MODIFYTIMEOLD
while ["$ MODIFYTIMEOLD" == "$ MODIFYTIMENEW"]
する
MODIFYTIMENEW = `ls -l --full-time /tmp/raw.img | awk '{print $ 9}' '
寝る0.2
やった
if ["$ MODIFYTIMEOLD"!= "$ MODIFYTIMENEW"]
それから
派手な
fi
}
関数showpic
{
echo $ '\ n ================ \ n新しいデータを受信\ n' >> / dev / console
#最適なクリアe-inkのためのバック画面の生成(オプション)
dd if = / dev / zero of = / tmp / img.raw bs = 1k count = 480
/ tmp / showpic /tmp/img.raw
dd if = / tmp / raw.img of = / tmp / img.raw bs = 1k count = 480
/ tmp / showpic /tmp/img.raw
waitnewdata
}
#ldconfig
PATH = "/ usr / local / bin:/ usr / bin:/ bin:/ usr / bin / X11:/ usr / games:/ usr / local / sony / bin:/ usr / sbin:/ sbin"
LD_LIBRARY_PATH = "/ Data / opt / sony / ebook / application:/ lib:/ usr / lib:/ usr / local / sony / lib:/ opt / sony / ebook / lib"
エクスポートパスLD_LIBRARY_PATH
#初期日付を設定
/ビン/日付0101000007
#カーネルモジュールのアンロード
rmmod g_file_storage
#生ファイル1Mbを作成
dd if = / dev / zero of = / tmp / raw.img bs = 1k count = 1k
grep Data / proc / mtd> / dev / null
[$? == 0]; それから
NUM = `grep Data / proc / mtd | awk -F: '{print $ 1}' | awk -Fd '{print $ 2}' '
insmod /lib/modules/2.4.17_n12/kernel/drivers/usb/g_file_storage.o file = / dev / mtdblock $ NUM、/ dev / sdmscard / r5c807b、/ dev / sdmscard / r5c807a、/ tmp / raw.img ProductID = $ MODEL VendorSpecific = $ VENDOR sn_select = 0 iSerialNumber = $ ID
他に
insmod /lib/modules/2.4.17_n12/kernel/drivers/usb/g_file_storage.o file = / dev / sdmscard / r5c807b、/ dev / sdmscard / r5c807a、/ tmp / raw.img ProductID = $ MODEL VendorSpecific = $ VENDOR sn_lect = 0 iSerialNumber = $ ID
fi
#kbookアプリケーションの起動
nohup / opt / sony / ebook / application / tinyhttp> / dev / null&
cp /データ/データベース/システム/ PRSPlus / showpic / tmp /
waitnewdata
psrp.shスクリプトのハイライト
- まず、モジュールをアンロードします。
rmmod g_file_storage
- / tmpにサイズ1 MBの空のファイルを作成します。 以前にリーダーのUARTコンソールに接続していたので、/ tmpが特にtmpfsにマウントされていることを確認する機会がありました。 これが必要なものです。
dd if=/dev/zero of=/tmp/raw.img bs=1k count=1k
- /tmp/raw.imgを追加してg_file_storageモジュールをロードします
insmod /lib/modules/2.4.17_n12/kernel/drivers/usb/g_file_storage.o file=/dev/mtdblock$NUM,/dev/sdmscard/r5c807b,/dev/sdmscard/r5c807a,/tmp/raw.img
- tinyhttpネイティブシェルを起動します。残念ながら、このドライブがないとUSB経由で共有されません。
nohup /opt/sony/ebook/application/tinyhttp > /dev/null &
- waitnewdata関数を開始します。これは、200msの一時停止のサイクルで、仮想ディスクのイメージファイルの変更を監視します。 時間の変更により
while [ "$MODIFYTIMEOLD" == "$MODIFYTIMENEW" ] do MODIFYTIMENEW=`ls -l --full-time /tmp/raw.img | awk ' {print $9} '` sleep 0.2 done'
はい、私は気高い自転車に同意しますが、残念ながらファームウェアにはinotifyがなく、カーネルイベントに対処するのが面倒でした。 さらに、1秒でfpsが適しています。
- 変更が見つかった場合は、armv4lアーキテクチャ用の特別なツールチェーンを使用したクロスコンパイルによって取得されたバイナリでイメージを表示します(上記の記事の詳細については、完成したバイナリとソースもここにあります)。
これで、私たちの電子書籍は聞くことができ、同時に、以前と同じように本を読むことができます。 スクリプトを開始し、画像を転送するために留意する必要がある主なことは、USB e-bookがオンになっている場合、ケーブルを接続しないことです。 それ以外の場合、本はprsp.shスクリプトなしでロードされます。 つまり、最初に本の電源を入れ、シェルがロードされるのを待ってから、USBケーブルを接続します。 (この機能はデフォルトでPRSPlusファームウェアに登録されていますが、必要に応じて変更して独自のイメージを作成することもできます)
確認する
電子書籍で[リセット]をクリックし、ダウンロードが完了するのを待って、USBケーブルを接続します。 確認のために、テスト画像を送信できます。 たとえば、Ubuntuの場合、これは次のように実行できます。
リーダーが読み込まれたときにスクリプトが正常に起動した場合、USB経由で接続すると、サイズが1 MBのデバイスが表示されます。
fdisk -l
次の行を見つけます。
Disk /dev/sdx: 1 MB, 1048576 bytes
これで、電子書籍
/dev/sdx
のRAMが
/dev/sdx
。
jpegから変換するには、djpegが必要です。必要なパッケージをインストールします。
apt-get install libjpeg-turbo-progs
次に、お気に入りのエディターでJPEGファイルを作成し、サイズを600x800にして、電子書籍に送信します。
djpeg -pnm -grayscale test.jpg | dd bs=1 skip=15 | dd of=/dev/sdx bs=480k
このパイプラインでは、jpegをモノクロのpgmに変換し、ヘッダーをスキップして、単一ブロック内の480Kbを/ dev / sdxデバイスに転送します。 そして、結果が表示されます。
バーコードジェネレーターとデバイスへの送信
Golangでバーコードを描画するには、追加のライブラリが必要です。
go get github.com/boombuler/barcode go get golang.org/x/image/bmp
main.go
package main import ( "bytes" "fmt" "image" "log" "os" "image/color" "image/draw" "golang.org/x/image/bmp" "syscall" "github.com/boombuler/barcode" "github.com/boombuler/barcode/ean" "github.com/boombuler/barcode/qr" ) func main() { switch string(os.Args[2]) { case "qr": base64 := os.Args[3] log.Println("Original data:", base64) code1pixel, err := qr.Encode(base64, qr.L, qr.Unicode) if err != nil { log.Fatal(err) } log.Println("Encoded data: ", code1pixel.Content()) if base64 != code1pixel.Content() { log.Fatal("data differs") } log.Println("Encoded data: ", code1pixel.Content()) if base64 != code1pixel.Content() { log.Fatal("data differs") } codeScalled, err := barcode.Scale(code1pixel, 300, 200) if err != nil { log.Fatal(err) } drtest(codeScalled) case "ean": // code, err := ean.Encode("123456789012") code1pixel, err := ean.Encode(os.Args[3]) if err != nil { log.Fatal(err) } log.Println("Encoded data: ", code1pixel.Content()) codeScalled, err := barcode.Scale(code1pixel, 300, 300) if err != nil { log.Fatal(err) } drtest(codeScalled) } } func drtest(imgSrc image.Image) { // create a new Image with the same dimension of image newImg := image.NewGray(image.Rect(0, 0, 600, 800)) // we will use white background to replace background // you can change it to whichever color you want with // a new color.RGBA{} and use image.NewUniform(color.RGBA{<fill in color>}) function draw.Draw(newImg, newImg.Bounds(), &image.Uniform{color.White}, image.Point{}, draw.Src) // paste image OVER to newImage draw.Draw(newImg, newImg.Bounds().Add(image.Point{150, 300}), imgSrc, imgSrc.Bounds().Min, draw.Over) buf := new(bytes.Buffer) err := bmp.Encode(buf, newImg) if err != nil { fmt.Println(err) os.Exit(1) } send_s3 := buf.Bytes() fmt.Println("OK") if os.Args[1] != "false" { devout(send_s3[1078:]) } } func devout(buffer []byte) { // disk := "/dev/sde" disk := os.Args[1] var fd, numread int var err error fd, err = syscall.Open(disk, syscall.O_RDWR, 0777) if err != nil { fmt.Print(err.Error(), "\n") return } //WRITE numread, err = syscall.Write(fd, buffer) if err != nil { fmt.Print(err.Error(), "\n") } fmt.Printf("Numbytes write: %d\n", numread) // fmt.Printf("Buffer: %x\n", buffer[:1000]) err = syscall.Close(fd) if err != nil { fmt.Print(err.Error(), "\n") } }
コードのハイライト:
- EANの例を使用して、最初に1ピクセルの厚さのバーコードを描画します。
code1pixel, err := ean.Encode(os.Args[3])
- 希望のサイズに伸ばします:
codeScalled, err := barcode.Scale(code1pixel, 300, 300)
- 画面サイズに合わせて600x800のキャンバスを作成します。
newImg := image.NewGray(image.Rect(0, 0, 600, 800))
- 目的の色で塗りつぶします。
draw.Draw(newImg, newImg.Bounds(), &image.Uniform{color.White}, image.Point{}, draw.Src)
- キャンバスにバーコード画像を追加します。
draw.Draw(newImg, newImg.Bounds().Add(image.Point{150, 300}), imgSrc, imgSrc.Bounds().Min, draw.Over)
- 次に、記録のためにデバイスを開き、BMPヘッダーを除くデータをそこに送信します。
devout(send_s3[1078:])
Wirenboard 5でのクロスコンパイル
Wirenboard開発者は、Dockerコンテナに基づく非常に便利なクロスコンパイルツールを提供します。 しかし、この記事の枠組みでは、それを考慮しません。 ARMv5では、シンプルなアプリケーションを1つのチームで組み立てることができます。
GOOS=linux GOARCH=arm GOARM=5 go build main.go
すべてをWirenboard 5に転送します。
scp main root@192.168.xx:/tmp
Wirenboardに移動して、サイズが1 MBのデバイスの名前を見てください。この例では/ dev / sddです。
以下を開始します。
/tmp/main /dev/sdd qr "Privet Habr"
結論
電子書籍を画面として使用するのは現実的です。 その可能性により、電子インク技術はインテリアデザインでの使用を促進します。 E-inkスクリーンは、特に明るい壁でよく見えます。 ホームコントローラーから有用な情報を表示できます。
ご清聴ありがとうございました!
PSソース、 こちらとこちらをご覧ください。 電子書籍用ファームウェアPRSPlusはこちら 。