Serielle Kommunikation über Bluetooth

Für ein kleines Projekt (Mobiltelefon steuert Arduino) habe ich mir ein HC-06 Bluetooth Modul für wenig Geld in China besorgt. Bluetooth habe ich nie gemocht, weil ich nie viel Freude daran hatte diese Gerätschaften unter Linux zu konfigurieren, aber der Preisunterschied zu Arduino WLAN Modulen hatte dann doch den Ausschlag gegeben:).
 Über GUI ließ sich das Gerät leider nicht konfigurieren, aber  mit den bluez-utils auf der Kommandozeile ließ sich das Problem rasch beheben.

Zuerst muß die MAC Adresse des Moduls in Erfahrung gebracht werden.

ms@debian:~/$ hcitool scan
Scanning ...
	0C:DF:A4:DE:01:54	S3350
	20:14:04:11:15:16	HC-06

Das File rfcomm.conf anpassen

root@debian:/# cat /etc/bluetooth/rfcomm.conf 
#
# RFCOMM configuration file.
#
 
rfcomm0 {
#	# Automatically bind the device at startup
	bind yes;
#
#	# Bluetooth address of the device
	device 20:14:04:11:15:16;
#	# RFCOMM channel for the connection
	channel	1;
#
#	# Description of the connection
	comment "HC-06";
}

Nun die Pin des Geräts (Default ist 1234) setzen – Mac Adresse am Besten per Tabulator vervollständigen
root@debian:/# sudo echo „20:14:04:11:15:16 1234“ >> /var/lib/bluetooth/08\:3E\:8E\:E5\:1C\:5E/pincodes

Und den Bluetooth Daemon neu starten.

root@debian:/# sudo /etc/init.d/bluetooth restart

Befehle an den Bluetooth Client senden

Strings kann man nun einfach an das Gerät redirecten
ms@debian:~$ echo „beep“ >/dev/rfcomm0

Ausgabe des Bluetooth Clients lesen

Einfach mit cat die Gerätedatei ausgeben

ms@debian:~$ cat /dev/rfcomm0
==Commands==
led on
led off
beep
beep_d [duration]
==Commands==
led on
led off
beep
beep_d [duration]

fin.

Click’N’Load per Tunnelbau an die Pyload Maschine weiterleiten

Wer ab und an bei Sharehostern Dateien lädt kennt sicherlich Click’n’Load. Nach dem Klick auf einen Button im Browser füllt sich die Queue des Downloadmanagers mit neuen Files. Dabei werden Informationen zu Dateien per Http POST vom lokal laufenden Download Programm wie jDownloader oder PyLoad entegengenommen. Dies geschieht durch einen kleinen Webserver der auf dem Port 9666 lauscht. Wenn der Downloadmanager nicht lokal läuft funktioniert der Mechanismus leider nicht.

Mit einem SSH-Tunnel kann man den POST Request an einen anderen Rechner weitleiten. Dort muß neben dem Downloadmanager auch ein SSH-Server laufen.

In meinem kleinen Beispiel läuft Pyload auf einem Pi mit der Ip 192.168.0.110. Um den Tunnel nachzubauen mußt Du also nur die Ip anpassen:

ms@debian:/home/ms# ssh -L 127.0.0.1:9666:192.168.0.110:9666  pi@192.168.0.110 -N

Den Tunnel kannst du mit diesem Link testen.

Nachtrag 21.06.2014
Die Arbeit mit ssh kann man sich generell einfacher machen wenn man ein Configfile für das Tool benutzt. Dort kann man Aliase für Server definieren, deklarieren auf welchem Port sshd läuft oder welcher Key zur Authentifizierung genutzt werden soll. Nähere Informationen dazu gibt es bei den Ubuntuusers oder Nerderati.

Über Shared Preferences mit einem Service kommunizieren

Für ein Live Wallpaper mit Settings-Dialog brauche ich einen Callback im Service um Änderungen in den Einstellungen so schnell wie möglich  zu verarbeiten. Periodisches Pollen zur Laufzeit ist nicht nur unelegant sondern braucht auch CPU Zeit. Der einfachste Weg für micht geht über SharedPreferences und einem OnSharedPreferenceChanceListener Interface.

 
private class MyWallpaperEngine extends Engine implements OnHttpRequestResultListener, OnSharedPreferenceChangeListener {
 
        public MyWallpaperEngine() {
		    //get the image
		    SharedPreferences sp = getApplicationContext().getSharedPreferences(
					Constants.NAMESPACE_SETTINGS, Activity.MODE_PRIVATE);
			sp.registerOnSharedPreferenceChangeListener(this);
	}
 
        /* .... */
	@Override
	public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
		draw();
 
	}
}

In der implementierten Methode onSharedPreferenceChanged wird einmal neu gezeichnet. Fertig.

Mysql INSERT if not exists

Für ein kleines Projekt speichere Auflösungen von Geräten in einer Tabelle ab um Bildbearbeitungen im vorraus zu erledigen und nicht erst wenn das Bild angefragt wird.
Um die Datenbank sauber zu halten möchte ich natürlich jeden Datensatz genau einmal haben und das mit so wenig Sql Queries wie möglich erledigen.
Dies ist die einfachste Lösung auf die ich gestoßen bin:

INSERT INTO resolutions (w, h) 
 
SELECT  100, 100 FROM resolutions
WHERE NOT EXISTS(
        SELECT w, h FROM resolutions WHERE w=100 AND h=100
 
) LIMIT 1;

Prüfen ob Service aktiv ist

Ich schreibe momentan ein Live Wallpaper für Android, mit einer Activity um Infos zum aktuellen Bild darzustellen. Um komfortabel das Live Wallpaper zu aktivieren brauche ich eine Prüfung, ob das auch läuft. WallpaperService ist von Service abgeleitet und damit ist dies kein Problem:

    /* ..... */
	private boolean isMyServiceRunning(String className) {
	    ActivityManager manager = (ActivityManager) getActivity().getSystemService(Context.ACTIVITY_SERVICE);
	    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
	        if (className.equals(service.service.getClassName())) {
	            return true;
	        }
	    }
	    return false;
	}
    /* ..... */

Nun kann bei Bedarf der Wallpaper Picker angezeigt werden:

	if(isMyServiceRunning(MyWallpaperService.class.getName())){
			Log.d(TAG, "active" );
	} else {
			Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER);
			startActivity(Intent.createChooser(intent, getActivity().getString(R.string.title_for_wallpaper_chooser)));
	}

Fin.

Links in TextView clickbar machen

Html Markup bringt man am einfachsten auf diesem Weg in TextViews:

/***** .... *****/
TextView mContent;
mContent.setText(Html.fromHtml("<a href=\"http://www.giftimessen.de\">Link</a>"));
/***** .... *****/

Beim Draufdrücken passiert leider noch nicht soviel. Um die Links gangbar zu machen einfach die MovementMethod setzen:

 
mContent.setMovementMethod(LinkMovementMethod.getInstance());

fertig :).

Howto – Daydream / Screensaver App erstellen

Ab Android 4.2 – Api Level 17 bietet Android die Möglichkeit mit Daydream einen Screensaver einzurichten. Daydream startet wenn das Gerät
am Netz hängt oder in einer Dockingstation steckt. Daydream kann man in den Systemeinstellungen unter Display -> Daydream aktivieren.
Ich beschreibe an dieser Stelle wie man einen einfachen DayDream Service implementiert der bei Aktivierung ein Video abspielt, damit alles schön übersichtlich bleibt.

Daydream wird als Service implementiert der von android.service.dreams.DreamService erbt, man hat den vollen Zugriff auf die UI, kann also Layouts, Canvas, 3d und Animationen nutzen.

Manifest

Im Manifest muß nur der Service deklariert werden. Falls ein Settingsmenü benötigt wird muß die Klasse im Meta gesetzt werden gesetzt werden.

<service
    android:name=".VideoDreamService"
    android:exported="true"
    android:label="@string/my_daydream_name" >
    <intent-filter>
        <action android:name="android.service.dreams.DreamService" />
 
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
 
    <meta-data
        android:name="android.service.dream"
        android:resource="@xml/dream_info" />
</service>

Layout

Zum einfacheren Testen benutze ich ein ganz normales Layout mit einer VideoView .

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".MainActivity" >
 
    <VideoView
        android:id="@+id/videoView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true" />
 
</RelativeLayout>

Service

In meinem Beispiel wird ein Video ohne Ton in einer Endlosschleife abgespielt.

package de.schmitt.michael.daydream;
 
/******* imports ***********/
/** ...**/
 
public class VideoDreamService extends DreamService {
 
 
	public void onDreamingStarted() {
		super.onDreamingStarted();
 
		setContentView(R.layout.activity_main);
 
 
		VideoView vv = (VideoView)findViewById(R.id.videoView1);
		vv.setVideoURI(Uri.parse("android.resource://" + getPackageName() +"/"+R.raw.video));
 
		Log.i(TAG,  Environment.getExternalStorageDirectory().toString());
		Log.i(TAG,  getApplicationContext().getPackageName() );
		vv.setOnPreparedListener(new OnPreparedListener() {
		    @Override
		    public void onPrepared(MediaPlayer mp) {
		        mp.setLooping(true);
 
		    }
		});
 
		vv.setSoundEffectsEnabled(false);
 
		vv.start();
	}
	public void onDreamingStopped() {
		// Wird aufgerufen wenn der Service beendet wird. 
 
        }
 
}

Links

http://android-developers.blogspot.de/2012/12/daydream-interactive-screen-savers.html

Node.js Mysql

Ich muß einen recht großen Haufen Daten von einer Mysqldatenbank nach Mongodb schaufeln. Das ganze spielt in der Kommandozeile.

app.js

var mysql = require('mysql');
 
config=require('./config').production;
 
var connection = mysql.createConnection({
    host : config.mysqlHost,
    port : config.mysqlPort,
    database: config.mysqlDatabase,
    user : config.mysqlUser,
    password : config.mysqlPassword
});
 
 
connection.connect(function(err){
	if(err != null) {
		console.log(err);
	}
	connection.query("SELECT * from  feeds", function(err, rows){
	if(err != null) {
		console.log("Problem with Query "  +err);    
	} else {
	    // Shows the result on console window
	    console.log(rows[0]);
 
 
	    connection.end();
 
	}
 
	});
 
});

package.json

{
  "name": "node-mysqltest",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app"
  },
  "dependencies": {
 
    "mysql" : "*"
  }
}

config.js

var config ={};
 
config.development = {
 
};
 
config.production = {
 
 
  mysqlPort:3306,
  mysqlHost:'localhost',
  mysqlDatabase:'<DB>',
  mysqlUser: '<USR>',
  mysqlPassword:  '<PWD>',
 
  env : global.process.env.NODE_ENV || 'production'
};
 
 
module.exports  = config;

Node.js Notizen & Mongodb – Konfiguration + Tools

Im Moment schaue ich mir Node.js und Mongodb. Hier sammle ich für mich nützliche Informationen bzgl. der Einrichtung und Konfiguration.
Der Webspace kommt von Uberspace, entsprechend sind ein Paar Links in das ordentlich geschriebene Wiki von denen drin.

1. Einrichtung Uberspace

Anweisungen im Wiki folgen

Subdomain

Node Applikation auf Port 80 weiterleiten:

[schmimi@ara node.radikalblogger.de]$ cat .htaccess
RewriteEngine On
RewriteRule (.*) http://localhost:61667/$1 [P]

2. NPM konfigurieren

cat >  ~/.npmrc >> __EOF__
prefix = $HOME
umask = 077
__EOF__

 

3. Watch for Filechanges

Sehr praktisch zum entwickeln ist Supervisor

npm install supervisor -g
#Nun das Script mit dem Befehl supervisor starten.
[schmimi@ara ~]$ supervisor node/example.js

4. Node.js Tutorials

5. Mongo DB konfigurieren

Anleitung im Uberspace Wiki
Mongodb Handbuch

User und Datenbank anlegen

[schmimi4@octans nodejs]$ mongo admin --port 20522 -u schmimi4_mongoadmin -p
MongoDB shell version: 2.4.8
Enter password: 
connecting to: 127.0.0.1:20522/admin
> use node-mongo-blog
switched to db node-mongo-blog
use products
db.addUser( { user: "Alice",
              pwd: "Moon1234",
              roles: [ "readWrite", "dbAdmin" ]
            } )
> quit

Verbindung testen

mongo node-mongo-blog --port 20522 -u mongoblog -p

Datenbank löschen

> use mydb; 
> db.dropDatabase();

Ein (aus Anfängersicht) brauchbares Webinterface um mit der Datenbank rumzumachen scheint RockMongo zu sein. Konfiguration problemlos.

6. Auf Produktivbetriebumstellen

Interessanter Blogartikel

[schm@bla blog]export NODE_ENV=production