Hi, Habr! In the development process, sometimes you have to reinvent the
wheel with tools for testing applications and it happens that some of them can be useful for example in home automation. Here is a case from my personal experience. When developing an application for mobile data collection terminals, there is a need to test laser scanners, without printing bar codes on paper. Unfortunately, these scanners do not know how to read from conventional screens. Give them the paper, but they also really liked the electronic paper.

I remembered an article on Habré
E-book as a display , it was decided to get an old half-dead reader Sony PRS-505 from the shelf and give it a second life as a screen for an automation controller. But sending pictures through flash memory is a bad idea. It was necessary to learn how to work directly with the RAM of the e-book. This increases display speed and reliability. Let me share the experience of drawing in Go using the example of a barcode generator and displaying on an e-book through the Wirenboar 5 controller.
Tasks
- Nothing to break. We are just adding a new feature.
- The e-book should be able to listen to the port and display the image within 1 second.
- Work only through the buffer in RAM, no flash memory
- Test output with the great dd command
- Drawing a barcode of the desired size in Go and placing it in the center of the canvas
- Image transfer to e-book buffer
- Enjoy the result
RAM Drive via USB
There is no wi-fi in the Sony PRS-505 reader, and besides, there is only g_file_storage from the USB Gadget kernel modules, so this is the only way to quickly transfer an image. Fortunately, the
PRSPlus firmware can launch any script when the e-book is turned on. All we need is to simply put the necessary files into the directory / database / system / PRSPlus folder and the prsp.sh script will be launched when loading.
')
Flash memory cannot be used as a buffer, so we need a small tmpfs disk in RAM that will be accessible via USB, unload the g_file_storage kernel module and load it with the necessary parameters to publish our newly created RAM disk via USB. Next, we must track the changes in the specified area and display the image on the e-ink display.
prsp.sh#! / bin / sh
echo $ '\ n ================ \ nSTART SCRYPT \ n' >> / dev / console
#TODO "here need Kernel Event instead
function waitnewdata
{
echo $ '\ n ================ \ nWait new data \ n' >> / dev / console
#Show only modify time the image file
MODIFYTIMEOLD = `ls -l --full-time /tmp/raw.img | awk '{print $ 9}' `
MODIFYTIMENEW = $ MODIFYTIMEOLD
while ["$ MODIFYTIMEOLD" == "$ MODIFYTIMENEW"]
do
MODIFYTIMENEW = `ls -l --full-time /tmp/raw.img | awk '{print $ 9}' `
sleep 0.2
done
if ["$ MODIFYTIMEOLD"! = "$ MODIFYTIMENEW"]
then
showpic
fi
}
function showpic
{
echo $ '\ n ================ \ nNew data received \ n' >> / dev / console
#Generating Back screen for best clear e-ink (optional)
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"
export PATH LD_LIBRARY_PATH
# set initial date
/ bin / date 0101000007
#Unload kernel module
rmmod g_file_storage
#Create raw file 1Mb
dd if = / dev / zero of = / tmp / raw.img bs = 1k count = 1k
grep Data / proc / mtd> / dev / null
if [$? == 0]; then
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
else
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_select = 0 iSerialNumber = $ ID
fi
#start kbook application
nohup / opt / sony / ebook / application / tinyhttp> / dev / null &
cp / Data / database / system / PRSPlus / showpic / tmp /
waitnewdata
Highlights of what the psrp.sh script does
- First we unload the module:
rmmod g_file_storage
- Create an empty file in / tmp size of 1Mb. Before connecting to the UART console of the reader, I had the opportunity to make sure that / tmp is mounted in tmpfs. That's what we need.
dd if=/dev/zero of=/tmp/raw.img bs=1k count=1k
- Load the g_file_storage module by adding /tmp/raw.img
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
- We start the tinyhttp native shell, unfortunately without this, the disks will not be shared via USB
nohup /opt/sony/ebook/application/tinyhttp > /dev/null &
- We start the waitnewdata function ; it keeps track of changes in the image file of our virtual disk in a cycle with a pause of 200ms. By the time of change
while [ "$MODIFYTIMEOLD" == "$MODIFYTIMENEW" ] do MODIFYTIMENEW=`ls -l --full-time /tmp/raw.img | awk ' {print $9} '` sleep 0.2 done'
Yes, I agree, a noble bike, but unfortunately there is no inotify firmware, and I was lazy with the Kernel Event. Especially since fps in 1 second will suit me.
- If changes are detected, then display the image with a binary, obtained by cross-compiling with a special toolchain for the armv4l architecture (for more information, see the article above, also you can find the finished binary and source code here .)
Now our e-book is able to listen and at the same time, you can still read books on it. The main thing to keep in mind is that in order for the script to start and we can transfer images, when the e-book is turned on, the USB cable should
not be connected. Otherwise, the book will load without our script prsp.sh. That is, first we turn on the booklet, wait for the shell to load, then connect the USB cable. (this feature is by default written in the PRSPlus firmware, if desired, this can also be changed by making your own image)
Check
Press reset on the e-book, waiting for the end of the download, connect the USB cable. For verification, we can send a test image. For example from Ubuntu it can be done like this:
If the script is successfully launched when the reader is loaded, then when connected via USB, we will see a device with a size of 1Mb.
fdisk -l
Find the following string:
Disk /dev/sdx: 1 MB, 1048576 bytes
Now we know, here it is our piece of RAM of the e-book
/dev/sdx
.
To convert from jpeg, we need djpeg, install the necessary packages:
apt-get install libjpeg-turbo-progs
Next, create a JPEG file in your favorite editor, size
600x800 and send it to the e-book.
djpeg -pnm -grayscale test.jpg | dd bs=1 skip=15 | dd of=/dev/sdx bs=480k
In this pipeline, we convert jpeg to monochrome pgm, skip the header, and transfer 480Kb as a single block to the / dev / sdx device. And here we see the result.
Barcode generator and sending it to the device
To draw a barcode in Golang, we need additional libraries:
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":
Key points in the code:
- Using the example of EAN, we first draw a 1 pixel-thick bar code:
code1pixel, err := ean.Encode(os.Args[3])
- Stretch it to the desired size:
codeScalled, err := barcode.Scale(code1pixel, 300, 300)
- Create a 600x800 canvas to fit the screen:
newImg := image.NewGray(image.Rect(0, 0, 600, 800))
- Fill it with the desired color:
draw.Draw(newImg, newImg.Bounds(), &image.Uniform{color.White}, image.Point{}, draw.Src)
- Overlay on our canvas barcode image:
draw.Draw(newImg, newImg.Bounds().Add(image.Point{150, 300}), imgSrc, imgSrc.Bounds().Min, draw.Over)
- Next, we open the device for recording and send the data there by deleting the BMP header:
devout(send_s3[1078:])
Cross compilation under Wirenboard 5
Wirenboard developers provide us with a very convenient cross-compilation tool based on the Docker container. But in this article we will not consider it. Under ARMv5, our simple application can be assembled as a team.
GOOS=linux GOARCH=arm GOARM=5 go build main.go
We transfer everything to Wirenboard 5:
scp main root@192.168.xx:/tmp
Go to Wirenboard, look at the device name of 1 MB in size, in my example / dev / sdd.
Run:
/tmp/main /dev/sdd qr "Privet Habr"

findings
Using an e-book as a screen is quite real. With its potential, electronic ink technology prompts the use of interior design. E-ink screen will look good especially on a light wall. You can display useful information from the home controller.
Thanks for attention!
PS Sources, you can see
here and
here . Firmware PRSPlus for e-books
here .