Ich bastel gerade an einem kleinen Home Automation Projekt und habe mir das C-Berry Touch für eine Steuerungszentrale angeschafft. Es gibt vom Hersteller einige Beispiele in C wie das Display anzusteuern ist, und hier im Forum findet man auch einen Kernel-Treiber um es als Framebuffer Device zu nutzen. Ich bin zwar ein Anwendungsentwickler, bin aber eher in den objektorientierten Hochsprachen bewandert, daher setze ich das ganze auch mit Java um. Leider gibt es bisher keine brauchbaren Beispiele wie dieses Display in Java anzusprechen ist (wenn es überhaupt ohne Nutzung von C Libraries geht). Daher habe ich eine kleine VHDDBIA Methode entwickelt um das Display zu nutzen, die ich gerne teilen möchte, falls jemand anderes dies gebrauchen kann.
Was ist denn bitte eine VHDDBIA Mehtode denkt ihr euch ?
Die "Von hinten durch die Brust ins Auge" Methode!
Direktes Ansteuern des Displays habe ich nicht geschafft. Ich habe auch versucht die Quellen vom Hersteller zu einer Library zu kompilieren und dann mit JNI oder mit JNA daraus eine Bindung zu Java herzustellen... alles ohne erfolg. Daher hab ich ein wenig in C experimentiert, und aus den Quellen des Herstellers ein simples Kontrollprogramm gemacht, das ich zwischen Java und dem Display einsetzen kann.
Die Quellen vom Hersteller gibt es auf dessen Seite, hier das Steuerprogramm dazu:
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <bcm2835.h>
#include <stdint.h>
#include <time.h>
#include <errno.h> // for errno
#include <limits.h> // for INT_MAX
#include "tft.h"
#include "RAIO8870.h"
#include "bmp.h"
#include "examples.h"
/*
*
*/
int main(int argc, char** argv) {
char *p;
int delayTime = 10;
errno = 0;
long conv = strtol(argv[1], &p, 10);
// Check for errors: e.g., the string does not represent an integer
// or the integer is larger than int
if (errno != 0 || *p != '\0' || conv > INT_MAX) {
} else {
// No error
delayTime = conv;
}
if (!bcm2835_init()) return EXIT_FAILURE;
TFT_init_board();
TFT_hard_reset();
RAIO_init();
RAIO_SetBacklightPWMValue(255);
char brightFile[] = "./brightness";
char displayFile[] = "./display.bmp";
char touchFile[] = "./touch";
char killFile[] = "./killtft";
int i = 0, rt;
while (1) {
if (file_exist(&brightFile[0])) {
FILE* file = fopen(&brightFile[0], "r");
int i = 0;
fscanf(file, "%d", &i);
fclose(file);
RAIO_SetBacklightPWMValue(i);
remove(&brightFile[0]);
}
if (file_exist(&displayFile[0])) {
uint16_t picture[1][ PICTURE_PIXELS ];
RAIO_set_cursor(0, 0);
Read_bmp2memory(&displayFile[0], &picture[0][ PICTURE_PIXELS - 1 ]);
RAIO_Write_Picture(&picture[0][0], PICTURE_PIXELS);
remove(&displayFile[0]);
}
rt = RAIO_gettouch();
if ((my_touch.state == pressed)) {
FILE *f = fopen(&touchFile[0], "w");
fprintf(f, "%d, %d\n", my_touch.touch_x, my_touch.touch_y);
fclose(f);
}
if (file_exist(&killFile[0])) {
remove(&killFile[0]);
break;
}
delay(delayTime);
}
bcm2835_close();
return (EXIT_SUCCESS);
}
int file_exist(char *filename) {
struct stat buffer;
return (stat(filename, &buffer) == 0);
}
Alles anzeigen
Wie man sieht einfach ein Thread der auf bestimmte Dateien reagiert. Eine Datei zum steuern der Helligkeit des Display, eine Datei zum anzeigen eines Bildes auf dem Display, eine Datei um das Programm zu beenden. Wenn das Display berührt wird, dann werden die Koordinaten in eine Ausgabedatei geschrieben.
Jetzt muss ich in Java eigentlich nur noch diese Dateien liefern bzw die Ausgabe überwachen, was sich auch mit einem Thread prima lösen lässt.
Hier ein erstes "Framework" für das GUI welches ich auf diese Weise anzeigen lassen möchte:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
public class HCTFTController extends Thread {
/* The current software can display BMP files on the C-Berry. */
/* The BMP file must have a dimension of 320 x 240 pixel and */
/* a color depth of 24Bit. */
private final int SCREEN_WIDTH = 320;
private final int SCREEN_HEIGHT = 240;
//
private final File TEMP_DISPLAY_FILE;
private final File DISPLAY_FILE;
private final File BRIGHTNESS_FILE;
private final File TEMP_BRIGHTNESS_FILE;
private final File TOUCH_FILE;
private final File KILL_FILE;
//
private boolean RefreshNeeded = true;
private boolean DisplayLight = true;
public HCTFTController() {
this.TEMP_DISPLAY_FILE = new File("/home/pi/libtft2/displaytmp.bmp");
this.DISPLAY_FILE = new File("/home/pi/libtft2/display.bmp");
this.BRIGHTNESS_FILE = new File("/home/pi/libtft2/brightness");
this.TEMP_BRIGHTNESS_FILE = new File("/home/pi/libtft2/brightness.tmp");
this.TOUCH_FILE = new File("/home/pi/libtft2/touch");
this.KILL_FILE = new File("/home/pi/libtft2/killtft");
}
@Override
public void run() {
BufferedImage img = new BufferedImage(SCREEN_WIDTH, SCREEN_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics g = img.createGraphics();
switchDisplay(DisplayLight);
boolean running = true;
while (running) {
checkInput();
if (RefreshNeeded) {
drawGUI(g);
sendScreenToDevice(img);
}
}
g.dispose();
}
private void checkInput() {
if (TOUCH_FILE.exists()) {
try {
List<String> filecontend = Files.readAllLines(TOUCH_FILE.toPath());
TOUCH_FILE.delete();
if (filecontend == null || filecontend.isEmpty()) {
return;
}
String[] touchValues = filecontend.get(0).split(",");
if (touchValues.length == 2) {
processInput(Integer.parseInt(touchValues[0]), Integer.parseInt(touchValues[1]));
}
} catch (IOException ex) {
Logger.getLogger(HCTFTController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private void processInput(int xCoord, int yCoord) {
// if light is off any touch will just turn display light on
if (!DisplayLight) {
switchDisplay(!DisplayLight);
return;
}
// if square in corner is pressed switch light off
if (xCoord >= SCREEN_WIDTH-20 && xCoord <= SCREEN_WIDTH && yCoord >= 0 && yCoord <= 20) {
switchDisplay(!DisplayLight);
}
}
private void drawGUI(Graphics g) {
// draw background
g.setColor(Color.red);
g.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
// draw GUI, for now a square in top left corner to turn screen on/off
g.setColor(Color.GREEN);
g.fillRect(SCREEN_WIDTH - 20, 0, 20, 20);
}
private void sendScreenToDevice(BufferedImage img) {
try {
ImageIO.write(img, "bmp", TEMP_DISPLAY_FILE);
TEMP_DISPLAY_FILE.renameTo(DISPLAY_FILE);
} catch (IOException ex) {
Logger.getLogger(HCTFTController.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void switchDisplay(boolean lightOn) {
try {
Files.write(TEMP_BRIGHTNESS_FILE.toPath(), lightOn ? "255".getBytes() : "0".getBytes());
TEMP_BRIGHTNESS_FILE.renameTo(BRIGHTNESS_FILE);
DisplayLight = lightOn;
} catch (IOException ex) {
Logger.getLogger(HCTFTController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Alles anzeigen
Nach und nach werde ich dies nun erweitern. Mit simplen Bildmanipulations-Funktionen von Java lässt sich auf das BufferedImage Objekt ein GUI selbst zeichnen, und je nachdem was angezeigt wird kann dann auch die Eingabe abgefangen werden, und entsprechend darauf reagiert werden.
Ja, es ist eine sehr seltsame Lösung, aber es Funktioniert. Es lassen sich ca 2 Bilder pro Sekunde übertragen, also vollkommen ausreichend für eine Steuerungskonsole. Es sollen ja keine großartigen Animationen ablaufen. Auf diese Art kriegt man zwar auch keinen "Swipe" wie von Smartphones bekannt hin, aber auch das ist ja nich zwingend notwendig. Einfaches Tasten-drücken reicht mir aus.
Falls jemand konstruktive Vorschläge hat oder einen Weg kennt das Display direkter mit Java anzusprechen immer her damit
Das spannende ist nur, das die Koordinaten, die bei Berührung registriert werden irgendwie nicht mir den Pixelkoordinaten überein stimmen, und ich bisher nirgendwo Informationen gefunden habe wie diese Koordinaten zusammengesetzt sind.
X und Y fangen bei ca 15 an und gehen beide bis ca 160. Seltsam, es muss also noch irgend eine Konvertierung eingebaut werden um auf Bild-Koordinaten zu kommen.