Skip to content

Commit

Permalink
Merge pull request #65 from owulveryck/fw3.6
Browse files Browse the repository at this point in the history
Add support for firmware 3.6
  • Loading branch information
owulveryck authored Aug 29, 2023
2 parents b052d38 + 2ed6c17 commit f730288
Show file tree
Hide file tree
Showing 17 changed files with 341 additions and 144 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ server/htdocs/assets/toEpub.wasm
client/htdocs/assets/toEpub.wasm
goMarkableStream
testServer.arm
testdata/36.raw
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ Its primary goal is to enable users to stream their reMarkable tablet screen to
## Version support

- version < 0.8.6 are supported on FW < 3.4
- version >= 0.8.6 are supported on FW >= 3.4
- version >= 0.8.6 are supported on 3.6 > FW >= 3.4
- version >= 0.11.0 are supported on FW >= 3.6

## Features

Expand Down
66 changes: 39 additions & 27 deletions assets/stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ async function initiateStream() {


var offset = 0;
var count = 0;
var value = 0;


// Define a function to process the chunks of data as they arrive
Expand All @@ -169,51 +171,61 @@ async function initiateStream() {
// Process the received data chunk
// Assuming each pixel is represented by 4 bytes (RGBA)
var uint8Array = new Uint8Array(value);

for (let i = 0; i < uint8Array.length; i++) {
const [count, value] = unpackValues(uint8Array[i]);
// if no count, then it is a count
if (count === 0) {
count = uint8Array[i];
continue;
}
// if we have a count, it is a value...
const value = uint8Array[i];
for (let c=0;c<count;c++) {
offset += 4;
switch (value) {
case 5:
imageData.data[offset+c*4] = 255;
imageData.data[offset+c*4+1] = 0;
imageData.data[offset+c*4+2] = 0;
imageData.data[offset+c*4+3] = 255;
case 10: // red
imageData.data[offset] = 255;
imageData.data[offset+1] = 0;
imageData.data[offset+2] = 0;
imageData.data[offset+3] = 255;
break;
case 9:
imageData.data[offset+c*4] = 0;
imageData.data[offset+c*4+1] = 0;
imageData.data[offset+c*4+2] = 255;
imageData.data[offset+c*4+3] = 255;
case 18: // blue
imageData.data[offset] = 0;
imageData.data[offset+1] = 0;
imageData.data[offset+2] = 255;
imageData.data[offset+3] = 255;
break;
case 11:
imageData.data[offset+c*4] = 125;
imageData.data[offset+c*4+1] = 184;
imageData.data[offset+c*4+2] = 86;
imageData.data[offset+c*4+3] = 255;
case 20: // green
imageData.data[offset] = 125;
imageData.data[offset+1] = 184;
imageData.data[offset+2] = 86;
imageData.data[offset+3] = 255;
break;
case 13:
imageData.data[offset+c*4] = 255;
imageData.data[offset+c*4+1] = 253;
imageData.data[offset+c*4+2] = 84;
imageData.data[offset+c*4+3] = 255;
case 24: // yellow
imageData.data[offset] = 255;
imageData.data[offset+1] = 253;
imageData.data[offset+2] = 84;
imageData.data[offset+3] = 255;
break;
default:
imageData.data[offset+c*4] = value * 17;
imageData.data[offset+c*4+1] = value * 17;
imageData.data[offset+c*4+2] = value * 17;
imageData.data[offset+c*4+3] = 255;
imageData.data[offset] = value * 17;
imageData.data[offset+1] = value * 17;
imageData.data[offset+2] = value * 17;
imageData.data[offset+3] = 255;
break;
}
}
offset += (count*4);

// value is treated, wait for a count
count = 0;
if (offset >= fixedCanvas.height*fixedCanvas.width*4) {

offset = 0;
// Display the ImageData on the canvas
fixedContext.putImageData(imageData, 0, 0);

copyCanvasContent();
}

}

// Read the next chunk
Expand Down
64 changes: 64 additions & 0 deletions exploration/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import (
"image"
"image/png"
"log"
"os"

"github.com/owulveryck/goMarkableStream/internal/remarkable"
)

func main() {
palette := make(map[uint8]int64)
spectre := make(map[uint8]int64)
//testdata := "../testdata/full_memory_region.raw"
testdata := "../testdata/multi.raw"
stats, _ := os.Stat(testdata)
f, err := os.Open(testdata)
if err != nil {
log.Fatal(err)
}
defer f.Close()
backend := make([]uint8, stats.Size())
log.Printf("backend is %v bytes", len(backend))
n, err := f.ReadAt(backend, 0)
if err != nil {
log.Fatal(err)
}
log.Printf("read %v bytes from the file", n)
picture := backend[8 : 1872*1404*2+8]
boundaries := image.Rectangle{
Min: image.Point{
X: 0,
Y: 0,
},
Max: image.Point{
X: remarkable.ScreenWidth,
Y: remarkable.ScreenHeight,
},
}
img := image.NewGray(boundaries)
w := remarkable.ScreenWidth
h := remarkable.ScreenHeight
unflipAndExtract(picture, img.Pix, w, h)
for i := 0; i < len(picture); i += 2 {
spectre[picture[i]]++
}
for _, v := range img.Pix {
palette[v]++
}
log.Println(spectre)
log.Println(palette)

png.Encode(os.Stdout, img)
}
func unflipAndExtract(src, dst []uint8, w, h int) {
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
srcIndex := (y*w + x) * 2 // every second byte is useful
dstIndex := (h-y-1)*w + x // unflip position
dst[dstIndex] = src[srcIndex] * 17
}
}
}
Binary file added exploration/test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ func setMux() *http.ServeMux {
})
streanHandler := stream.NewStreamHandler(file, pointerAddr)
mux.Handle("/stream", streanHandler)
if c.DevMode {
rawHandler := stream.NewRawHandler(file, pointerAddr)
mux.Handle("/raw", rawHandler)
}
return mux
}

Expand Down
19 changes: 6 additions & 13 deletions internal/remarkable/fb.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package remarkable

import (
"io"
"math/rand"
"os"
)

// GetFileAndPointer finds the filedescriptor of the xochitl process and the pointer address of the virtual framebuffer
Expand All @@ -16,17 +16,10 @@ func GetFileAndPointer() (io.ReaderAt, int64, error) {
type dummyPicture struct{}

func (dummypicture *dummyPicture) ReadAt(p []byte, off int64) (n int, err error) {
for i := 0; i < len(p); i++ {
// Generate a random number between 1 and 10
num := rand.Intn(10) + 1

// Set the byte to zero with a probability of 80%
if num <= 8 {
p[i] = 255
} else {
// Otherwise, generate a random byte between 1 and 15
p[i] = uint8(rand.Intn(15))
}
f, err := os.Open("./testdata/full_memory_region.raw")
if err != nil {
return 0, err
}
return len(p), nil
defer f.Close()
return f.ReadAt(p, off)
}
18 changes: 8 additions & 10 deletions internal/rle/rle.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ var encodedPool = sync.Pool{
},
}

func pack(value1, value2 uint8) uint8 {
// Shift the first value by 4 bits and OR it with the second value
encodedValue := (value1 << 4) | value2
return encodedValue
}

// NewRLE creates a default RLE
func NewRLE(w io.Writer) *RLE {
return &RLE{
Expand Down Expand Up @@ -50,15 +44,19 @@ func (rlewriter *RLE) Write(data []byte) (n int, err error) {
count := -1

for _, datum := range data {
if count < 15 && datum == current {
if count < 255 && datum == current {
count++
} else {
encoded = append(encoded, pack(uint8(count), current))
encoded = append(encoded, uint8(count))
encoded = append(encoded, uint8(current))
current = datum
count = 0
}
}

encoded = append(encoded, pack(uint8(count), current))
return rlewriter.sub.Write(encoded)
encoded = append(encoded, uint8(count))
encoded = append(encoded, uint8(current))

n, err = rlewriter.sub.Write(encoded)
return
}
86 changes: 0 additions & 86 deletions internal/rle/rle_test.go

This file was deleted.

15 changes: 9 additions & 6 deletions internal/stream/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ const (
rate = 200
)

var imagePool = sync.Pool{
var rawFrameBuffer = sync.Pool{
New: func() any {
return make([]uint8, remarkable.ScreenWidth*remarkable.ScreenHeight) // Adjust the initial capacity as needed
return make([]uint8, remarkable.ScreenWidth*remarkable.ScreenHeight*2) // Adjust the initial capacity as needed
},
}

Expand Down Expand Up @@ -61,14 +61,17 @@ func (h *StreamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.ticker.Reset(rate * time.Millisecond)
defer h.ticker.Stop()

imageData := imagePool.Get().([]uint8)
defer imagePool.Put(imageData) // Return the slice to the pool when done
rawData := rawFrameBuffer.Get().([]uint8)
defer rawFrameBuffer.Put(rawData) // Return the slice to the pool when done
// the informations are int4, therefore store it in a uint8array to reduce data transfer
rleWriter := rle.NewRLE(w)
extractor := &oneOutOfTwo{rleWriter}
writing := true
stopWriting := time.NewTicker(2 * time.Second)
defer stopWriting.Stop()

w.Header().Set("Content-Type", "application/octet-stream")

for {
select {
case <-ctx.Done():
Expand All @@ -80,11 +83,11 @@ func (h *StreamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
writing = false
case <-h.ticker.C:
if writing {
_, err := h.file.ReadAt(imageData, h.pointerAddr)
_, err := h.file.ReadAt(rawData, h.pointerAddr)
if err != nil {
log.Fatal(err)
}
rleWriter.Write(imageData)
extractor.Write(rawData)
}
}
}
Expand Down
Loading

0 comments on commit f730288

Please sign in to comment.