de.dmarcini.submatix.android4.full.comm.BlueThoothComService.java Source code

Java tutorial

Introduction

Here is the source code for de.dmarcini.submatix.android4.full.comm.BlueThoothComService.java

Source

//@formatter:off
/*
    programm: SubmatixBTLoggerAndroid
    purpose:  configuration and read logs from SUBMATIX SPX42 divecomputer via Bluethooth    
    Copyright (C) 2012  Dirk Marciniak

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/
*/
//@formatter:on
package de.dmarcini.submatix.android4.full.comm;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.util.regex.Pattern;

import org.joda.time.DateTime;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import de.dmarcini.submatix.android4.full.ApplicationDEBUG;
import de.dmarcini.submatix.android4.full.R;
import de.dmarcini.submatix.android4.full.exceptions.FirmwareNotSupportetException;
import de.dmarcini.submatix.android4.full.gui.AreaListActivity;
import de.dmarcini.submatix.android4.full.utils.GasUpdateEntity;
import de.dmarcini.submatix.android4.full.utils.ProjectConst;
import de.dmarcini.submatix.android4.full.utils.SPX42Config;
import de.dmarcini.submatix.android4.full.utils.SPX42GasParms;

/**
 * 
 * Bluethooth COMM Service
 * 
 * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
 * 
 * @author Dirk Marciniak (dirk_marciniak@arcor.de)
 * 
 */
// @formatter:off
public class BlueThoothComService extends Service
{
  // @formatter:on
  /**
   * 
   * Der Thread zum Verbinden eines BT Devices
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.03.2013
   */
  private class ConnectThread extends Thread
  {
    private final String          TAGCON = ConnectThread.class.getSimpleName();
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;

    /**
     * 
     * Konstruktor des Threads, der die Verbindung aufbauen soll.
     * 
     * Project: SubmatixBluethoothLogger Package: de.dmarcini.submatix.logger.service
     * 
     * 
     * Stand: 24.02.2012
     * 
     * @param device
     */
    public ConnectThread( BluetoothDevice device )
    {
      mmDevice = device;
      BluetoothSocket tmp = null;
      if( ApplicationDEBUG.DEBUG )
      {
        Log.d( TAGCON, "createRfCommSocketToServiceRecord(" + ProjectConst.SERIAL_DEVICE_UUID + ")" );
      }
      //
      // Einen Socket fr das Gert erzeugen
      //
      try
      {
        tmp = device.createRfcommSocketToServiceRecord( ProjectConst.SERIAL_DEVICE_UUID );
      }
      catch( IOException e )
      {
        Log.e( TAGCON, "create() failed", e );
      }
      mmSocket = tmp;
    }

    /**
     * 
     * Den Verbindungsthread abbrechen.
     * 
     * Project: SubmatixBluethoothLogger Package: de.dmarcini.submatix.logger.service
     * 
     * 
     * Stand: 24.02.2012
     */
    public void cancel()
    {
      try
      {
        mmSocket.close();
      }
      catch( IOException e )
      {
        Log.e( TAGCON, "close() of connect socket failed", e );
      }
    }

    /**
     * Hier nimmt der Thread seinen Lauf.
     */
    @Override
    public void run()
    {
      Log.i( TAGCON, "BEGIN mConnectThread" );
      setName( "connect_thread" );
      // Teste mal ob ein Adapter da ist
      if( null == mAdapter )
      {
        Log.e( TAGCON, "not bt-adaper exist!" );
        return;
      }
      // immer discovering beenden, Verbindungsaufbau sonst schleppend
      mAdapter.cancelDiscovery();
      // Eine Verbindung zum BluetoothSocket
      try
      {
        //
        // Dies ist ein blockierender Call
        // er endet mit Verbindung oder Exception
        //
        Log.v( TAGCON, "Socket connecting (blocking)..." );
        mmSocket.connect();
        Log.v( TAGCON, "connected..." );
      }
      catch( IOException e )
      {
        connectionFailed();
        // Socket schlieen
        try
        {
          mmSocket.close();
        }
        catch( IOException ex )
        {
          Log.e( TAG, "unable to close() socket during connection failure", ex );
        }
        return;
      }
      // Ich lsch mich selber irgendwie...
      synchronized( BlueThoothComService.this )
      {
        mConnectThread = null;
      }
      // Start the connected thread
      if( ApplicationDEBUG.DEBUG )
      {
        Log.d( TAGCON, "run connected()" );
      }
      deviceConnected( mmSocket, mmDevice );
    }
  }

  /**
   * 
   * Der Binder sorgt spter fr die bergabe der referenz zum Service (Lokaler Service!)
   * 
   * Project: BtServiceVersuch Package: de.dmarcini.android.btservive
   * 
   * 
   * Stand: 21.02.2013
   */
  public class LocalBinder extends Binder
  {
    /**
     * 
     * Gib den Binder zurck
     * 
     * Project: SubmatixBTLoggerAndroid Package: de.dmarcini.submatix.android4.comm
     * 
     * Stand: 03.12.2013
     * 
     * @return Bindrerobj
     */
    public BlueThoothComService getService()
    {
      return BlueThoothComService.this;
    }

    /**
     * 
     * Registriere den Handler zum Versand von Nachrichten
     * 
     * Project: SubmatixBTLoggerAndroid Package: de.dmarcini.submatix.android4.comm
     * 
     * Stand: 03.12.2013
     * 
     * @param mHandler
     */
    public void registerServiceHandler( Handler mHandler )
    {
      Log.i( TAG, "Client register" );
      mClientHandler.add( mHandler );
      // isRunning = true;
      timeToStopService = 0L;
      // gibt der Activity gleich den Status
      setState( mConnectionState );
    }

    /**
     * 
     * Hebe die Registrierung eines Handlers auf
     * 
     * Project: SubmatixBTLoggerAndroid Package: de.dmarcini.submatix.android4.comm
     * 
     * Stand: 03.12.2013
     * 
     * @param mHandler
     */
    public void unregisterServiceHandler( Handler mHandler )
    {
      // if( mIsBusy ) return( null );
      Log.i( TAG, "Client unregister" );
      mClientHandler.remove( mHandler );
      if( mClientHandler.isEmpty() )
      {
        Log.i( TAG, "last Client ist removed..." );
        // isRunning = false;
        // zeit bis zum Ende des Service setzen
        timeToStopService = System.currentTimeMillis() + msToEndService;
      }
    }
  }

  /**
   * 
   * Der Thread, der whrend der Verbindung alle Datenstrme vom Gert bearbeitet
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.03.2013
   */
  private class ReaderThread extends Thread
  {
    private final String          TAGREADER    = ReaderThread.class.getSimpleName();
    private final BluetoothSocket mmSocket;
    private final InputStream     mmInStream;
    private Boolean               cancelThread = false;
    private final byte[]          buffer       = new byte[1024];
    private final StringBuffer    mInStrBuffer = new StringBuffer( 1024 );

    /**
     * 
     * Konstruktor des Threads
     * 
     * Project: SubmatixBluethoothLogger Package: de.dmarcini.submatix.logger.service
     * 
     * 
     * Stand: 24.02.2012
     * 
     * @param socket
     */
    public ReaderThread( BluetoothSocket socket )
    {
      if( ApplicationDEBUG.DEBUG )
      {
        Log.d( TAGREADER, "create ReaderThread" );
      }
      mmSocket = socket;
      InputStream tmpIn = null;
      cancelThread = false;
      // die BluetoothSocket input and output streams erstellen
      try
      {
        tmpIn = socket.getInputStream();
      }
      catch( IOException e )
      {
        Log.e( TAGREADER, "temp sockets not created", e );
      }
      mmInStream = tmpIn;
    }

    /**
     * 
     * Thread abbrechen
     * 
     * Project: SubmatixBluethoothLogger Package: de.dmarcini.submatix.logger.service
     * 
     * 
     * Stand: 24.02.2012
     */
    public void cancel()
    {
      try
      {
        cancelThread = true;
        if( mmSocket != null && mmSocket.isConnected() )
        {
          mmSocket.close();
        }
      }
      catch( IOException ex )
      {
        Log.e( TAGREADER, "close() of connect socket failed", ex );
      }
    }

    /**
     * 
     * Bearbeite einen Logeintrag (eine Zeile des Profils)
     * 
     * Project: SubmatixBluethoothLogger Package: de.dmarcini.submatix.logger.service
     * 
     * 
     * Stand: 24.02.2012
     * 
     * @param start
     * @param end
     * @param mInStrBuffer
     */
    private void execLogentryCmd( int start, int end, StringBuffer mInStrBuffer )
    {
      String readMessage;
      int lstart, lend;
      String[] fields;
      //
      if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "execLogentryCmd..." );
      lstart = mInStrBuffer.indexOf( ProjectConst.STX );
      lend = mInStrBuffer.indexOf( ProjectConst.ETX );
      if( lstart > -1 && lend > lstart )
      {
        // ups, hier ist ein "normales" Kommando verpackt
        if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "oops, normalCmd found.... change to execNormalCmd..." );
        isLogentryMode = false;
        execNormalCmd( lstart, lend, mInStrBuffer );
        return;
      }
      // muss der anfang weg?
      if( start > 0 )
      {
        // das davor kann dann weg...
        mInStrBuffer = mInStrBuffer.delete( 0, start );
        readMessage = mInStrBuffer.toString();
        // Indizies korrigieren
        end = mInStrBuffer.indexOf( ProjectConst.FILLER, start + ProjectConst.FILLER.length() );
        start = 0;
      }
      // lese das Ding ohne den Schmandzius der Fller
      readMessage = mInStrBuffer.substring( ProjectConst.FILLER.length(), end );
      // lsche das schon mal raus...
      mInStrBuffer = mInStrBuffer.delete( 0, end );
      readMessage = readMessage.replaceAll( ProjectConst.FILLERCHAR, "" );
      // Splitte das auf in Felder
      fields = fieldPatternTab.split( readMessage );
      // Sende an Activity
      if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "Logline Recived <" + readMessage.substring( 10 ).replaceAll( "\t", " " ) + "...>" );
      BtServiceMessage msg = new BtServiceMessage( ProjectConst.MESSAGE_LOGENTRY_LINE, fields );
      sendMessageToApp( msg );
    }

    /**
     * 
     * Bearbeite eine normale Zeile vom SPX kommend
     * 
     * Project: SubmatixBluethoothLogger Package: de.dmarcini.submatix.logger.service
     * 
     * 
     * Stand: 24.02.2012
     * 
     * @param start
     * @param end
     * @param mInStrBuffer
     */
    private void execNormalCmd( int start, int end, StringBuffer mInStrBuffer )
    {
      String readMessage;
      String[] fields;
      int command;
      BtServiceMessage msg;
      if( ApplicationDEBUG.DEBUG )
      {
        Log.d( TAGREADER, "execNormalCmd..." );
      }
      // muss der anfang weg?
      if( start > 0 )
      {
        // das davor kann dann weg...
        mInStrBuffer = mInStrBuffer.delete( 0, start );
        readMessage = mInStrBuffer.toString();
        // Indizies korrigieren
        end = mInStrBuffer.indexOf( ProjectConst.ETX );
        start = 0;
      }
      // jetz beginnt der String immer bei 0, lese das Ding
      readMessage = mInStrBuffer.substring( 1, end );
      // lsche das schon mal aus dem Puffer raus!
      mInStrBuffer = mInStrBuffer.delete( 0, end + 1 );
      if( ApplicationDEBUG.DEBUG )
      {
        Log.d( TAGREADER, "normal Message Recived <" + readMessage + ">" );
      }
      // Trenne die Parameter voneinander, fields[0] ist dann das Kommando
      fields = fieldPatternDp.split( readMessage );
      //
      //
      //
      if( 0 == readMessage.indexOf( ProjectConst.IS_END_LOGLISTENTRY ) )
      {
        // Logbucheintrge fertig gelesen
        msg = new BtServiceMessage( ProjectConst.MESSAGE_DIRENTRY_END );
        Log.v( TAGREADER, "SPX Logdir end readet!" );
        sendMessageToApp( msg );
        return;
      }
      //
      // Messages fr die Weiterverarbeitung prparieren
      //
      fields[0] = fields[0].replaceFirst( "~", "" );
      try
      {
        command = Integer.parseInt( fields[0], 16 );
      }
      catch( NumberFormatException ex )
      {
        Log.e( TAGREADER, "Convert String to Int (" + ex.getLocalizedMessage() + ")" );
        return;
      }
      //
      // bekomme heraus, welcher Art die ankommende Message ist
      //
      switch ( command )
      {
        case ProjectConst.SPX_MANUFACTURERS:
          // Sende Nachricht Gertename empfangen!
          connectedDeviceManufacturer = fields[1];
          msg = new BtServiceMessage( ProjectConst.MESSAGE_MANUFACTURER_READ, new String( fields[1] ) );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "SPX Devicename recived! <" + fields[1] + ">" );
          break;
        case ProjectConst.SPX_ALIVE:
          // Ackuspannung bertragen
          msg = new BtServiceMessage( ProjectConst.MESSAGE_SPXALIVE, new String( fields[1] ) );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "SPX is Alive, Acku value recived." );
          break;
        case ProjectConst.SPX_APPLICATION_ID:
          // Sende Nachricht Firmwareversion empfangen!
          msg = new BtServiceMessage( ProjectConst.MESSAGE_FWVERSION_READ, new String( fields[1] ) );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "Application ID (Firmware Version)  recived! <" + fields[1] + ">" );
          break;
        case ProjectConst.SPX_SERIAL_NUMBER:
          // Sende Nachricht Seriennummer empfangen!
          connectedDeviceSerialNumber = new String( fields[1] );
          msg = new BtServiceMessage( ProjectConst.MESSAGE_SERIAL_READ, new String( fields[1] ) );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "Serial Number recived! <" + fields[1] + ">" );
          break;
        case ProjectConst.SPX_SET_SETUP_DEKO:
          // Quittung fr Setze DECO
          msg = new BtServiceMessage( ProjectConst.MESSAGE_DECO_ACK );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "SPX_SET_SETUP_DEKO Acknoweledge recived" );
          break;
        case ProjectConst.SPX_SET_SETUP_SETPOINT:
          // Quittung fr Setzen der Auto-Setpointeinstelungen
          msg = new BtServiceMessage( ProjectConst.MESSAGE_SETPOINT_ACK );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "SPX_SET_SETUP_SETPOINT Acknoweledge recived " );
          break;
        case ProjectConst.SPX_SET_SETUP_DISPLAYSETTINGS:
          // Quittung fr Setzen der Displayeinstellungen
          msg = new BtServiceMessage( ProjectConst.MESSAGE_DISPLAY_ACK );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "MESSAGE_DISPLAY_ACK recived " );
          break;
        case ProjectConst.SPX_SET_SETUP_UNITS:
          // Quittung fr das Setzen der Masseinheiten
          msg = new BtServiceMessage( ProjectConst.MESSAGE_UNITS_ACK );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "MESSAGE_UNITS_ACK recived " );
          break;
        case ProjectConst.SPX_SET_SETUP_INDIVIDUAL:
          // Quittung fr Individualeinstellungen
          msg = new BtServiceMessage( ProjectConst.MESSAGE_INDIVID_ACK );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "MESSAGE_INDIVID_ACK recived " );
          break;
        case ProjectConst.SPX_GET_SETUP_DEKO:
          // Kommando DEC liefert zurck:
          // ~34:LL:HH:D:Y:C
          // LL=GF-Low, HH=GF-High,
          // D=Deepstops (0/1)
          // Y=Dynamische Gradienten (0/1)
          // C=Last Decostop (0=3 Meter/1=6 Meter)
          msg = new BtServiceMessage( ProjectConst.MESSAGE_DECO_READ, new String[]
          { fields[1], fields[2], fields[3], fields[4], fields[5] } );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "MESSAGE_DECO_READ recived " );
          break;
        case ProjectConst.SPX_GET_SETUP_SETPOINT:
          // Kommando GET_SETUP_SETPOINT liefert
          // ~35:A:P
          // A = Setpoint bei (0,1,2,3) = (0,5,15,20)
          // P = Partialdruck (0..4) 1.0 .. 1.4
          msg = new BtServiceMessage( ProjectConst.MESSAGE_SETPOINT_READ, new String[]
          { fields[1], fields[2] } );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "Setpoint recived!" );
          break;
        case ProjectConst.SPX_GET_SETUP_DISPLAYSETTINGS:
          // Kommando GET_SETUP_DISPLAYSETTINGS liefert
          // ~36:D:A
          // D= 0->10&, 1->50%, 2->100%
          // A= 0->Landscape 1->180Grad
          msg = new BtServiceMessage( ProjectConst.MESSAGE_DISPLAY_READ, new String[]
          { fields[1], fields[2] } );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "Display settings recived!" );
          break;
        case ProjectConst.SPX_GET_SETUP_UNITS:
          // Kommando GET_SETUP_UNITS
          // ~37:UD:UL:UW
          // UD= Fahrenheit/Celsius => immer 0 in der Firmware 2.6.7.7_U
          // UL= 0=metrisch 1=imperial
          // UW= 0->Salzwasser 1->Swasser
          msg = new BtServiceMessage( ProjectConst.MESSAGE_UNITS_READ, new String[]
          { fields[1], fields[2], fields[3] } );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "Device Units recived!" );
          break;
        case ProjectConst.SPX_GET_SETUP_INDIVIDUAL:
          // Kommando GET_SETUP_INDIVIDUAL liefert
          // ~38:SE:PS:SC:SN:LI
          // SE: Sensors 0->ON 1->OFF
          // PS: PSCRMODE 0->OFF 1->ON
          // SC: SensorCount
          // SN: Sound 0->OFF 1->ON
          // LI: Loginterval 0->10sec 1->30Sec 2->60 Sec
          // TS: TempStick (bei neuerer Firmware)
          if( fields.length == 6 )
          {
            msg = new BtServiceMessage( ProjectConst.MESSAGE_INDIVID_READ, new String[]
            { fields[1], fields[2], fields[3], fields[4], fields[5] } );
            sendMessageToApp( msg );
            if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "Device individual settings recived!" );
          }
          else if( fields.length == 7 )
          {
            msg = new BtServiceMessage( ProjectConst.MESSAGE_INDIVID_READ, new String[]
            { fields[1], fields[2], fields[3], fields[4], fields[5], fields[6] } );
            sendMessageToApp( msg );
            if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "Device individual settings (incl TempStick) recived!" );
          }
          else
          {
            if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "no individual license...!" );
          }
          break;
        case ProjectConst.SPX_GET_SETUP_GASLIST:
          // Kommando GET_SETUP_GASLIST
          // ~39:NR:ST:HE:BA:AA:CG
          // NR: Numer des Gases
          // ST Stickstoff in Prozent (hex)
          // HELIUM
          // Bailout
          // AA Diluent 1 oder 2 oder keins
          // CG curent Gas
          msg = new BtServiceMessage( ProjectConst.MESSAGE_GAS_READ, new String[]
          { fields[1], fields[2], fields[3], fields[4], fields[5], fields[6] } );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "Gas setup recived!" );
          break;
        case ProjectConst.SPX_SET_SETUP_GASLIST:
          // Besaetigung fuer Gas setzen bekommen
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "MESSAGE_GAS_ACK recived " );
          msg = new BtServiceMessage( ProjectConst.MESSAGE_GAS_ACK );
          sendMessageToApp( msg );
          break;
        case ProjectConst.SPX_GET_LOG_INDEX:
          // Ein Logbuch Verzeichniseintrag gefunden
          // ~41:21:9_4_10_20_44_55.txt:22
          // ~41:NR:FN:MAX
          // NR: Nummer des Eintrages
          // FN: Filename
          // MAX: letzter Eintrag
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "MESSAGE_GET_LOG_INDEX recived " );
          msg = new BtServiceMessage( ProjectConst.MESSAGE_DIRENTRY_READ, new String[]
          { fields[1], fields[2], fields[3] } );
          sendMessageToApp( msg );
          //
          // war das der lezte Eintrag?
          //
          if( fields[1].equals( fields[3] ) )
          {
            if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "END OF DIRINDEX recived " );
            msg = new BtServiceMessage( ProjectConst.MESSAGE_DIRENTRY_END );
            sendMessageToApp( msg );
          }
          break;
        case ProjectConst.SPX_GET_LOG_NUMBER_SE:
          if( 0 == fields[1].indexOf( "1" ) )
          {
            // bertragung Logfile gestartet
            if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "start of logfile recived " );
            msg = new BtServiceMessage( ProjectConst.MESSAGE_LOGENTRY_START, new String( fields[2] ) );
            sendMessageToApp( msg );
            isLogentryMode = true;
          }
          else if( 0 == fields[1].indexOf( "0" ) )
          {
            {
              // bertragung beendet
              if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "stop of logfile recived " );
              msg = new BtServiceMessage( ProjectConst.MESSAGE_LOGENTRY_STOP, new String( fields[2] ) );
              sendMessageToApp( msg );
              isLogentryMode = false;
            }
          }
          break;
        case ProjectConst.SPX_GET_DEVICE_OFF:
          // SPX meldet, er geht aus dem Sync-Mode
          msg = new BtServiceMessage( ProjectConst.MESSAGE_DISCONNECTED );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "SPX42 switch syncmode OFF! Connection will failure!" );
          break;
        case ProjectConst.SPX_LICENSE_STATE:
          // Kommando SPX_LICENSE_STATE
          // <~45:LS:CE>
          // LS : License State 0=Nitrox,1=Normoxic Trimix,2=Full Trimix
          // CE : Custom Enabled 0= disabled, 1=enabled
          connectedDeviceLicense = new String[]
          { fields[1], fields[2] };
          msg = new BtServiceMessage( ProjectConst.MESSAGE_LICENSE_STATE_READ, connectedDeviceLicense );
          sendMessageToApp( msg );
          if( ApplicationDEBUG.DEBUG ) Log.d( TAGREADER, "SPX42 license state recived!" );
          break;
        default:
          Log.w( TAGREADER, "unknown Messagetype recived <" + readMessage + ">" );
      }
    }

    /**
     * Hier nimmt der Thread seinen Lauf
     */
    @Override
    public void run()
    {
      Log.i( TAGREADER, "BEGIN ReaderThread" );
      String readMessage;
      int bytes, start, end, lstart, lend;
      boolean logCmd, normalCmd;
      // den Inputstream solange lesen, wie die Verbindung besteht
      cancelThread = false;
      while( !cancelThread )
      {
        // lese von InputStream, maximal buffer.length bytes lesen
        try
        {
          bytes = mmInStream.read( buffer );
          if( bytes == -1 )
          {
            // Verbindung beendet/verloren
            cancelThread = true;
            Log.e( TAGREADER, "reader connection lost..." );
            connectionLost();
            cancel();
            break;
          }
          readMessage = new String( buffer, 0, bytes );
        }
        catch( IOException e )
        {
          if( cancelThread )
          {
            Log.i( TAGREADER, "while cancel thread: disconnected " + e.getLocalizedMessage() );
          }
          else
          {
            Log.e( TAGREADER, "disconnected " + e.getLocalizedMessage() );
          }
          connectionLost();
          cancel();
          break;
        }
        readMessage = new String( buffer, 0, bytes );
        //
        // was mach ich jetzt mit dem empfangenen Zeuch?
        //
        // reicht der Platz noch?
        if( ( mInStrBuffer.capacity() + readMessage.length() ) > ProjectConst.MAXINBUFFER )
        {
          Log.e( TAGREADER, "INPUT BUFFER OVERFLOW!" );
          connectionLost();
          cancel();
          break;
        }
        // Die empfangene Nachricht an den Puffer anhngen
        mInStrBuffer.append( readMessage );
        // den Puffer in einen String berfhren
        readMessage = mInStrBuffer.toString();
        // die Nachricht abarbeitern, solange komplette MSG da sind
        start = mInStrBuffer.indexOf( ProjectConst.STX );
        end = mInStrBuffer.indexOf( ProjectConst.ETX );
        if( isLogentryMode )
        {
          lstart = mInStrBuffer.indexOf( ProjectConst.FILLER );
          lend = mInStrBuffer.indexOf( ProjectConst.FILLER, start + ProjectConst.FILLER.length() );
        }
        else
        {
          lstart = -1;
          lend = -1;
        }
        // solange etwas gefunden wird
        while( ( ( start > -1 ) && ( end > start ) ) || ( ( lstart > -1 ) && ( lend > lstart ) ) )
        {
          if( ( start > -1 ) && ( end > start ) )
          {
            normalCmd = true;
          }
          else
          {
            normalCmd = false;
          }
          if( ( lstart > -1 ) && ( lend > lstart ) )
          {
            logCmd = true;
          }
          else
          {
            logCmd = false;
          }
          // womit anfangen?
          // sind beide zu finden?
          if( normalCmd == true && logCmd == true )
          {
            // entscheidung, wer zuerst
            if( start < lstart )
            {
              execNormalCmd( start, end, mInStrBuffer );
            }
            else
            {
              execLogentryCmd( lstart, lend, mInStrBuffer );
            }
          }
          else
          {
            // nein, nur ein Typ. Welcher?
            if( normalCmd == true )
            {
              execNormalCmd( start, end, mInStrBuffer );
            }
            else if( logCmd == true )
            {
              execLogentryCmd( lstart, lend, mInStrBuffer );
            }
          }
          start = mInStrBuffer.indexOf( ProjectConst.STX );
          end = mInStrBuffer.indexOf( ProjectConst.ETX );
          if( isLogentryMode )
          {
            lstart = mInStrBuffer.indexOf( ProjectConst.FILLER );
            lend = mInStrBuffer.indexOf( ProjectConst.FILLER, start + ProjectConst.FILLER.length() );
          }
          else
          {
            lstart = -1;
            lend = -1;
          }
        }
      }
      Log.i( TAGREADER, "END ReaderThread" );
      connectionLost();
    }
  }

  /**
   * 
   * Thread zum schreiben von Kommandos und daten an den angeschlossenen SPX
   * 
   * Project: SubmatixBTLoggerAndroid Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 10.11.2013
   */
  private class WriterThread extends Thread
  {
    private final String            TAGWRITER    = WriterThread.class.getSimpleName();
    private final BluetoothSocket   mmSocket;
    private final OutputStream      mmOutStream;
    private Boolean                 cancelThread = false;
    private final ArrayList<String> writeList    = new ArrayList<String>();

    /**
     * 
     * Konstruktor des Writer Thread
     * 
     * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
     * 
     * 
     * Stand: 28.05.2013
     * 
     * @param socket
     */
    public WriterThread( BluetoothSocket socket )
    {
      if( ApplicationDEBUG.DEBUG )
      {
        Log.d( TAGWRITER, "create WriterThread" );
      }
      mmSocket = socket;
      OutputStream tmpOut = null;
      cancelThread = false;
      // die BluetoothSocket input and output streams erstellen
      try
      {
        tmpOut = socket.getOutputStream();
      }
      catch( IOException e )
      {
        Log.e( TAGWRITER, "temp sockets not created", e );
      }
      mmOutStream = tmpOut;
    }

    /**
     * 
     * Thread abbrechen
     * 
     * Project: SubmatixBluethoothLogger Package: de.dmarcini.submatix.logger.service
     * 
     * 
     * Stand: 28.05.2013
     */
    public void cancel()
    {
      try
      {
        cancelThread = true;
        if( mmSocket != null && mmSocket.isConnected() )
        {
          mmSocket.close();
        }
      }
      catch( IOException ex )
      {
        Log.e( TAGWRITER, "close() of connect socket failed", ex );
      }
    }

    /**
     * Hier luft der Thread
     */
    @Override
    public void run()
    {
      BtServiceMessage msg;
      //
      Log.i( TAGWRITER, "BEGIN WriterThread" );
      // den Inputstram solange schreiben, wie die Verbindung besteht
      cancelThread = false;
      while( !cancelThread )
      {
        // syncronisiete Methode aufrufen, damit wait und notify machbar sind
        synchronized( this )
        {
          if( writeList.isEmpty() )
          {
            try
            {
              wait( 15 );
            }
            catch( InterruptedException ex )
            {}
          }
          else
          {
            // ich gebe einen Eintrag aus...
            try
            {
              // Watchdog fr Schreiben aktivieren
              writeWatchDog = ProjectConst.WATCHDOG_FOR_WRITEOPS;
              // den String fr den Wachhund zwischenspeichern
              sendStr = writeList.remove( 0 );
              // also den String Eintrag in den Outstream...
              mmOutStream.write( sendStr.getBytes() );
              // kommt das an, den Watchog wieder AUS
              sendStr = null;
              writeWatchDog = -1;
              // zwischen den Kommandos etwas warten, der SPX braucht etwas bis er wieder zuhrt...
              Thread.sleep( 500 );
            }
            catch( IndexOutOfBoundsException ex )
            {
              Log.e( TAG, "WriterThread IndexOutOfBoundsException: <" + ex.getLocalizedMessage() + ">" );
              msg = new BtServiceMessage( ProjectConst.MESSAGE_DISCONNECTED );
              sendMessageToApp( msg );
              cancelThread = true;
              writeList.clear();
              return;
            }
            catch( IOException ex )
            {
              Log.e( TAG, "WriterThread Exception: <" + ex.getLocalizedMessage() + ">" );
              msg = new BtServiceMessage( ProjectConst.MESSAGE_DISCONNECTED );
              sendMessageToApp( msg );
              cancelThread = true;
              writeList.clear();
              return;
            }
            catch( InterruptedException ex )
            {}
          }
        }
      }
      Log.i( TAGWRITER, "END WriterThread" );
      connectionLost();
    }

    /**
     * 
     * Schreibe Daten zum SPX
     * 
     * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
     * 
     * 
     * Stand: 28.05.2013
     * 
     * @param msg
     */
    public synchronized void writeToDevice( String msg )
    {
      writeList.add( msg );
      notifyAll();
    }
  }

  private static final String  TAG                         = BlueThoothComService.class.getSimpleName();
  private static final long    msToEndService              = 35000L;                                    // Zeit, bis der Service ohne Verbindung Selbstmord begeht
  // private static final Pattern fieldPattern0x09 = Pattern.compile( ProjectConst.LOGSELECTOR );
  private static final Pattern fieldPatternDp              = Pattern.compile( ":" );
  private static final Pattern fieldPatternTab             = Pattern.compile( "\t" );
  private long                 tickToCounter               = 0L;
  private long                 timeToStopService           = 0L;
  private NotificationManager  nm;
  static int                   NOTIFICATION                = 815;
  private final Timer          timerThread                 = new Timer();
  private long                 timerCounter                = 0;
  private int                  writeWatchDog               = -1;
  private String               sendStr                     = null;                                      // Zwischenspeicher fr zu sendendes Kommando (fr watchdog)
  ArrayList<Handler>           mClientHandler              = new ArrayList<Handler>();                  // Messagehandler fr Clienten
  int                          mValue                      = 0;                                         // Holds last value set by a client.
  private final IBinder        mBinder                     = new LocalBinder();
  private BluetoothAdapter     mAdapter                    = null;
  private static ConnectThread mConnectThread              = null;
  private static ReaderThread  mReaderThread               = null;
  private static WriterThread  mWriterThread               = null;
  private static volatile int  mConnectionState;
  private volatile boolean     isLogentryMode              = false;
  private String               connectedDeviceMac          = null;
  private String               connectedDeviceSerialNumber = null;
  private String               connectedDeviceManufacturer = null;
  private String               connectedDeviceFWVersion    = null;
  private String[]             connectedDeviceLicense      = null;

  /**
   * 
   * Lese die Konfiguration vom SPX42
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.06.2013
   */
  public void askForConfigFromSPX42()
  {
    String kdoString;
    kdoString = String.format( "%s~%x~%x~%x~%x~%x~%x~%x%s", ProjectConst.STX, ProjectConst.SPX_GET_SETUP_DEKO, ProjectConst.SPX_GET_SETUP_SETPOINT,
            ProjectConst.SPX_GET_SETUP_DISPLAYSETTINGS, ProjectConst.SPX_GET_SETUP_UNITS, ProjectConst.SPX_GET_SETUP_INDIVIDUAL, ProjectConst.SPX_LICENSE_STATE,
            ProjectConst.SPX_ALIVE, ProjectConst.ETX );
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "askForConfigFromSPX42()...send <" + kdoString + ">" );
    }
    this.writeToDevice( kdoString );
  }

  /**
   * 
   * Frage nach den Masseinheiten
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 15.08.2013
   */
  public void aksForUnitsFromSPX42()
  {
    String kdoString;
    kdoString = String.format( "%s~%x%s", ProjectConst.STX, ProjectConst.SPX_GET_SETUP_UNITS, ProjectConst.ETX );
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "aksForUnitsFromSPX42()...send <" + kdoString + ">" );
    }
    this.writeToDevice( kdoString );
  }

  /**
   * 
   * Frage nach der Konfiguration der DECO-Parameter
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 13.07.2013
   */
  public void askForDecoConfig()
  {
    String kdoString;
    kdoString = String.format( "%s~%x~%x%s", ProjectConst.STX, ProjectConst.SPX_GET_SETUP_DEKO, ProjectConst.SPX_ALIVE, ProjectConst.ETX );
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "askForDecoConfig()...send <" + kdoString + ">" );
    }
    this.writeToDevice( kdoString );
  }

  /**
   * 
   * frag den SPX nach seiener Firmware
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.06.2013
   * 
   */
  public void askForFirmwareVersion()
  {
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "askForSerialNumber..." );
    }
    if( connectedDeviceFWVersion != null )
    {
      BtServiceMessage msg = new BtServiceMessage( ProjectConst.MESSAGE_FWVERSION_READ, new String( connectedDeviceFWVersion ) );
      sendMessageToApp( msg );
    }
    else
    {
      this.writeSPXMsgToDevice( String.format( "~%x", ProjectConst.SPX_APPLICATION_ID ) );
    }
  }

  /**
   * 
   * Erfrage beim SPX die Gaslisten
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 18.07.2013 TODO
   */
  public void askForGasFromSPX()
  {
    String kdoString;
    kdoString = String.format( "%s~%x~%x%s", ProjectConst.STX, ProjectConst.SPX_GET_SETUP_GASLIST, ProjectConst.SPX_ALIVE, ProjectConst.ETX );
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "askForGasFromSPX: sending <" + kdoString + ">" );
    this.writeSPXMsgToDevice( kdoString );
  }

  /**
   * 
   * Erfrage die Lizenz vom SPX
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 04.06.2013
   */
  public void askForLicenseFromSPX()
  {
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "askForLicenseFromSPX..." );
    }
    if( connectedDeviceLicense != null )
    {
      BtServiceMessage msg = new BtServiceMessage( ProjectConst.MESSAGE_LICENSE_STATE_READ, connectedDeviceLicense );
      sendMessageToApp( msg );
    }
    else
    {
      this.writeSPXMsgToDevice( String.format( "~%x", ProjectConst.SPX_LICENSE_STATE ) );
    }
  }

  /**
   * 
   * Erfrage den Herstellerstring
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 04.06.2013
   */
  public void askForManufacturer()
  {
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "askForLicenseFromSPX..." );
    }
    //
    // wenn der schon mal abgefragt wurde, kann ich das "cachen"
    //
    if( connectedDeviceManufacturer != null )
    {
      BtServiceMessage msg = new BtServiceMessage( ProjectConst.MESSAGE_MANUFACTURER_READ, new String( connectedDeviceManufacturer ) );
      sendMessageToApp( msg );
    }
    else
    {
      this.writeSPXMsgToDevice( String.format( "~%x", ProjectConst.SPX_MANUFACTURERS ) );
    }
  }

  /**
   * 
   * Frage den SPX nach der Seriennummer
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 28.05.2013
   */
  public void askForSerialNumber()
  {
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "askForSerialNumber..." );
    }
    if( connectedDeviceSerialNumber != null )
    {
      BtServiceMessage msg = new BtServiceMessage( ProjectConst.MESSAGE_SERIAL_READ, new String( connectedDeviceSerialNumber ) );
      sendMessageToApp( msg );
    }
    else
    {
      this.writeSPXMsgToDevice( String.format( "~%x", ProjectConst.SPX_SERIAL_NUMBER ) );
    }
  }

  /**
   * 
   * Frag den SPX, ob er noch da ist
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 28.05.2013
   */
  public void askForSPXAlive()
  {
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "askForSPXAlive..." );
    }
    this.writeSPXMsgToDevice( String.format( "~%x", ProjectConst.SPX_ALIVE ) );
  }

  /**
   * 
   * Rufe vom SPX das Logverzeichnis auf
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 06.08.2013
   */
  public void askForLogDirectoryFromSPX()
  {
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "askForLogDirectoryFromSPX..." );
    this.writeSPXMsgToDevice( String.format( "~%x", ProjectConst.SPX_GET_LOG_INDEX ) );
  }

  /**
   * 
   * Verbinde mit einem Gert
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.03.2013
   * 
   * @param addr
   *          (MAC)
   */
  public synchronized void connect( String addr )
  {
    BluetoothDevice device = null;
    Log.v( TAG, "connect to: " + addr );
    connectedDeviceMac = null;
    connectedDeviceSerialNumber = null;
    // connectedDeviceAlias = null;
    if( mAdapter == null )
    {
      Log.e( TAG, "None bt-adapter found!" );
      return;
    }
    device = mAdapter.getRemoteDevice( addr );
    // Thread stoppen, bevor eine Verbindung aufgebaut werden kann
    if( mConnectionState == ProjectConst.CONN_STATE_CONNECTING )
    {
      if( mConnectThread != null )
      {
        mConnectThread.cancel();
        mConnectThread = null;
      }
    }
    // Thread stoppen, bevor eine Verbindung aufgebaut werden kann
    if( mReaderThread != null )
    {
      mReaderThread.cancel();
      mReaderThread = null;
    }
    // Start the thread to connect with the given device
    mConnectThread = new ConnectThread( device );
    connectedDeviceMac = addr;
    mConnectThread.start();
    setState( ProjectConst.CONN_STATE_CONNECTING );
  }

  /**
   * 
   * Wenn die Verbindung nicht geklappt hat
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 04.03.2013
   */
  private void connectionFailed()
  {
    // connectedDeviceAlias = null;
    setState( ProjectConst.CONN_STATE_NONE );
    BtServiceMessage msg = new BtServiceMessage( ProjectConst.MESSAGE_DISCONNECTED );
    // Melde des Status an die Clienten
    sendMessageToApp( msg );
    BtServiceMessage msg1 = new BtServiceMessage( ProjectConst.MESSAGE_CONNECTERROR );
    // Melde des Status an die Clienten
    sendMessageToApp( msg1 );
  }

  /**
   * 
   * Wenn die Verbindung verloren geht
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 13.03.2013
   */
  private void connectionLost()
  {
    // connectedDeviceAlias = null;
    setState( ProjectConst.CONN_STATE_NONE );
    BtServiceMessage msg = new BtServiceMessage( ProjectConst.MESSAGE_DISCONNECTED );
    // Melde des Status an die Clienten
    sendMessageToApp( msg );
  }

  /**
   * 
   * Nachricht senden, dass ein Gert verbunden wurde
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.03.2013
   * 
   * @param mmSocket2
   * @param mmDevice2
   */
  private void deviceConnected( BluetoothSocket socket, BluetoothDevice device )
  {
    Log.v( TAG, "connected()..." );
    // den Verbindunsthread stoppen, seine Aufgabe ist erfllt
    if( mConnectThread != null )
    {
      if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "stop old mConnectThread..." );
      // mConnectThread.cancel();
      mConnectThread = null;
    }
    // Falls da noch verbundene Thread sind, stoppe diese
    if( mReaderThread != null )
    {
      if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "stop old mReaderThread..." );
      mReaderThread.cancel();
      mReaderThread = null;
    }
    if( mWriterThread != null )
    {
      if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "stop old mWriterThread..." );
      mWriterThread.cancel();
      mWriterThread = null;
    }
    // starte den Lesethread zur Bearbeitung der Daten vom SPX
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "create mReaderThread..." );
    mReaderThread = new ReaderThread( socket );
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "start mReaderThread..." );
    mReaderThread.start();
    // starte den Schreibhread zur Bearbeitung der Kommandos zum SPX
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "create mWriterThread..." );
    mWriterThread = new WriterThread( socket );
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "start mWriterThread..." );
    mWriterThread.start();
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "call setState" );
    setState( ProjectConst.CONN_STATE_CONNECTED );
    timerCounter = System.currentTimeMillis() + 100;
  }

  /**
   * 
   * Verbindung zum Gert trennen
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.03.2013
   */
  public void disconnect()
  {
    Log.v( TAG, "stopping bt-connection" );
    if( mConnectThread != null )
    {
      mConnectThread.cancel();
      mConnectThread = null;
    }
    if( mReaderThread != null )
    {
      mReaderThread.cancel();
      mReaderThread = null;
    }
    if( mWriterThread != null )
    {
      mWriterThread.cancel();
      mWriterThread = null;
    }
    setState( ProjectConst.CONN_STATE_NONE );
  }

  /**
   * 
   * Mit welchem Gert (Addrese) bin ich verbunden?
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 28.05.2013
   * 
   * @return status
   */
  public String getConnectedDevice()
  {
    if( mConnectionState == ProjectConst.CONN_STATE_CONNECTED )
    {
      if( connectedDeviceMac != null )
      {
        return( connectedDeviceMac );
      }
    }
    return( null );
  }

  /**
   * 
   * Gib die Seriennummer des Gertes zurck (wenn vorhanden)
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.06.2013
   * 
   * @return gerte Seriennummer
   */
  public synchronized String getConnectedDeviceSerialNumber()
  {
    return( connectedDeviceSerialNumber );
  }

  /**
   * 
   * Welcher Verbindunsstatus hat die BT Schnittstelle?
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 04.03.2013
   * 
   * @return verbindungsstatus
   */
  public int getConnectionState()
  {
    return( mConnectionState );
  }

  /**
   * Wird immer beim Binden eines Clienten aufgerufen
   */
  @Override
  public IBinder onBind( Intent intent )
  {
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "onBind..." );
    }
    showNotification( getText( R.string.notify_service ), getText( R.string.notify_service_connected ) );
    return mBinder;
  }

  @Override
  public void onCreate()
  {
    super.onCreate();
    timeToStopService = 0L;
    nm = ( NotificationManager )getSystemService( NOTIFICATION_SERVICE );
    Log.i( TAG, "Service Started." );
    // isRunning = true;
    // der berwachungsthread luft solange der Service aktiv ist aller 1 Sekunde
    timerThread.scheduleAtFixedRate( new TimerTask() {
      @Override
      public void run()
      {
        onTimerTick();
      }
    }, 100, 1000L );
    // Service ist Erzeugt!
    showNotification( getText( R.string.notify_service ), getText( R.string.notify_service_started ) );
    mAdapter = BluetoothAdapter.getDefaultAdapter();
  }

  @Override
  public void onDestroy()
  {
    super.onDestroy();
    if( timerThread != null )
    {
      timerThread.cancel();
    }
    nm.cancel( NOTIFICATION ); // Cancel the persistent notification.
    Log.i( TAG, "Service Stopped." );
    // isRunning = false;
  }

  @Override
  public int onStartCommand( Intent intent, int flags, int startId )
  {
    Log.i( TAG, "Received start id " + startId + ": " + intent );
    return START_STICKY;
  }

  /**
   * 
   * Wenn der Timer ein Ereignis hat (alle 1000 ms)
   * 
   * Project: BtServiceVersuch Package: de.dmarcini.android.btservive
   * 
   * 
   * Stand: 21.02.2013
   */
  private void onTimerTick()
  {
    BtServiceMessage msg;
    //
    // Timeout zum beenden des Service runterzhlen
    //
    if( timeToStopService != 0L )
    {
      if( System.currentTimeMillis() > timeToStopService )
      {
        Log.i( TAG, "Service stopping time to stop after last client..." );
        stopSelf();
      }
    }
    //
    // eine Tickernachricht in die gegend senden
    //
    if( System.currentTimeMillis() > tickToCounter )
    {
      tickToCounter = System.currentTimeMillis() + 2000L;
      try
      {
        msg = new BtServiceMessage( ProjectConst.MESSAGE_TICK );
        sendMessageToApp( msg );
      }
      catch( Throwable t )
      {
        // you should always ultimately catch all exceptions in timerThread tasks.
        Log.e( TAG, "Timer Tick Failed.", t );
      }
    }
    //
    // wenn ein Gert verbunden ist einige Sachen durchfhren
    //
    if( mConnectionState == ProjectConst.CONN_STATE_CONNECTED )
    {
      //
      // ist ein 90 Sekunden Intervall vergangen?
      //
      if( timerCounter <= System.currentTimeMillis() )
      {
        askForSPXAlive();
        timerCounter = System.currentTimeMillis() + 90000L;
      }
      //
      // soll der Soft-Watchdog aktiv sein?
      //
      if( writeWatchDog > -1 )
      {
        // ist der Timout aubgelaufen?
        if( writeWatchDog == 0 )
        {
          // ein Wachhund ist abgelaufen, benachrichtige den User!
          if( sendStr != null )
          {
            msg = new BtServiceMessage( ProjectConst.MESSAGE_COMMTIMEOUT, sendStr );
          }
          else
          {
            msg = new BtServiceMessage( ProjectConst.MESSAGE_COMMTIMEOUT );
          }
          sendMessageToApp( msg );
          writeWatchDog = -1;
        }
        // runterzhlen, bei -1 ist eh schluss
        writeWatchDog--;
      }
    }
  }

  @Override
  public boolean onUnbind( Intent intent )
  {
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "onUnbind..." );
    }
    showNotification( getText( R.string.notify_service ), getText( R.string.notify_service_disconnected ) );
    return( super.onUnbind( intent ) );
  }

  /**
   * 
   * Eine Nachricht (entkoppelt) zu den Empfngern schicken
   * 
   * Project: BtServiceVersuch Package: de.dmarcini.android.btservive
   * 
   * 
   * Stand: 21.02.2013
   * 
   * @param msg
   */
  private void sendMessageToApp( BtServiceMessage msg )
  {
    for( int i = mClientHandler.size() - 1; i >= 0; i-- )
    {
      try
      {
        mClientHandler.get( i ).obtainMessage( msg.getId(), msg ).sendToTarget();
      }
      catch( NullPointerException ex )
      {
        // das ging schief, den Clienten NICHT mehr benutzen
        mClientHandler.remove( i );
      }
      catch( Exception ex )
      {
        Log.e( TAG, "error while sendMessageToApp: " + ex.getLocalizedMessage() );
      }
    }
  }

  /**
   * 
   * Setze den Verbindungsstatus
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.03.2013
   * 
   * @param state
   */
  private void setState( int state )
  {
    BtServiceMessage msg;
    //
    mConnectionState = state;
    Log.v( TAG, "setState to <" + mConnectionState + ">  " + state );
    switch ( state )
    {
      default:
      case ProjectConst.CONN_STATE_NONE:
        // die Daten lschen
        connectedDeviceMac = null;
        connectedDeviceSerialNumber = null;
        connectedDeviceManufacturer = null;
        connectedDeviceFWVersion = null;
        connectedDeviceLicense = null;
        msg = new BtServiceMessage( ProjectConst.MESSAGE_DISCONNECTED );
        break;
      case ProjectConst.CONN_STATE_CONNECTING:
        msg = new BtServiceMessage( ProjectConst.MESSAGE_CONNECTING );
        break;
      case ProjectConst.CONN_STATE_CONNECTED:
        msg = new BtServiceMessage( ProjectConst.MESSAGE_CONNECTED );
        break;
    }
    // Melde des Status an die Clienten
    sendMessageToApp( msg );
  }

  /**
   * 
   * Zeigt Notification in der Statuszeile an!
   * 
   * Project: BtServiceVersuch Package: de.dmarcini.android.btservive
   * 
   * 
   * Stand: 21.02.2013
   */
  private void showNotification( CharSequence head, CharSequence msg )
  {
    // Icon Titel und Inhalt anzeigen, Intent beim Anckickcne setzen
    PendingIntent contentIntent = PendingIntent.getActivity( getApplicationContext(), 0, new Intent( getApplicationContext(), AreaListActivity.class ),
            PendingIntent.FLAG_UPDATE_CURRENT );
    //@formatter:off
    Notification notification = new Notification.Builder( getBaseContext() )
                                  .setContentTitle( head )
                                  .setContentText( msg )
                                  .setSmallIcon( R.drawable.bluetooth_icon_color )
                                  .setTicker( getText( R.string.notify_service_ticker ) )
                                  .setContentIntent(contentIntent)
                                  .getNotification();
    //@formatter:on
    // Send the notification.
    nm.notify( NOTIFICATION, notification );
  }

  /**
   * 
   * Schreibe in den SPX Autosetpoint und Setpoint als Indexwerte 0..3 oder 0..4
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.06.2013
   * 
   * @param cf
   * @param auto
   * @param pressure
   * @throws FirmwareNotSupportetException
   */
  public void writeAutoSetpoint( SPX42Config cf, int auto, int pressure ) throws FirmwareNotSupportetException
  {
    String kdoString;
    String debugString;
    //
    if( ApplicationDEBUG.DEBUG )
    {
      Log.d( TAG, "write setpoint propertys" );
    }
    if( !cf.isFirmwareSupported() )
    {
      Log.e( TAG, "firmware not supportet for write settings!" );
      throw new FirmwareNotSupportetException( "write Auto Setpoint" );
    }
    if( cf.isOldParamSorting() )
    {
      //
      // Kommando SPX_SET_SETUP_SETPOINT
      // ~30:P:A
      // P = Partialdruck (0..4) 1.0 .. 1.4
      // A = Setpoint bei (0,1,2,3,4) = (0,5,15,20,25)
      kdoString = String.format( "~%x:%x:%x", ProjectConst.SPX_SET_SETUP_SETPOINT, pressure, auto );
      if( ApplicationDEBUG.DEBUG )
      {
        debugString = String.format( "OLD-FIRMWARE <~%x:P%x:A%x>", ProjectConst.SPX_SET_SETUP_SETPOINT, pressure, auto );
        Log.d( TAG, "writeAutoSetpoint: sending <" + debugString + ">" );
      }
    }
    else
    {
      // ~30:A:P
      // A = Setpoint bei (0,1,2,3,4) = (0,5,15,20,25)
      // P = Partialdruck (0..4) 1.0 .. 1.4
      kdoString = String.format( "~%x:%x:%x", ProjectConst.SPX_SET_SETUP_SETPOINT, auto, pressure );
      if( ApplicationDEBUG.DEBUG )
      {
        debugString = String.format( "NEWER-FIRMWARE <~%x:A%x:P%x>", ProjectConst.SPX_SET_SETUP_SETPOINT, auto, pressure );
        Log.d( TAG, "writeAutoSetpoint: sending <" + debugString + ">" );
      }
    }
    this.writeSPXMsgToDevice( kdoString );
  }

  /**
   * 
   * schreibe DECO Parameter in den SPX
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 13.07.2013
   * 
   * @param cf
   * @param lowG
   *          Low gradient
   * @param highG
   *          High Gradient
   * @param deepSt
   *          Deppsstop Enable?
   * @param dynGr
   *          Dyn gradients enable?
   * @param lastStop
   *          last stop 3 oder 6 Meter?
   * @throws FirmwareNotSupportetException
   */
  public void writeDecoPrefs( SPX42Config cf, int lowG, int highG, int deepSt, int dynGr, int lastStop ) throws FirmwareNotSupportetException
  {
    String kdoString;
    //
    if( !cf.isFirmwareSupported() )
    {
      Log.e( TAG, "firmware not supportet for write settings!" );
      throw new FirmwareNotSupportetException( "write deco prefs" );
    }
    if( cf.isOldParamSorting() )
    {
      // ~29:GH:GL:LS:DY:DS
      // GH = Gradient HIGH
      // GL = Gradient LOW
      // LS = Last Stop 0=>3m 1=>6m
      // DY = Dynamische gradienten 0->off 1->on
      // DS = Deepstops 0=> enabled, 1=>disabled
      // kdoString = String.format( "~%x:%x:%x:%x:%x:%x", ProjectConst.SPX_SET_SETUP_DEKO, highG, lowG, ( ( lastStop == 1 ) ? 0 : 1 ), dynGr, deepSt );
      kdoString = String.format( "~%x:%x:%x:%x:%x:%x", ProjectConst.SPX_SET_SETUP_DEKO, highG, lowG, lastStop, dynGr, deepSt );
      if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeDecoPrefs: sending <OLDER-FIRMWARE <" + kdoString + ">>" );
    }
    else
    {
      // Kommando SPX_SET_SETUP_DEKO
      // ~29:GL:GH:DS:DY:LS
      // GL=GF-Low, GH=GF-High,
      // DS=Deepstops (0/1)
      // DY=Dynamische Gradienten (0/1)
      // LS=Last Decostop (0=3 Meter/1=6 Meter)
      kdoString = String.format( "~%x:%x:%x:%x:%x:%x", ProjectConst.SPX_SET_SETUP_DEKO, lowG, highG, deepSt, dynGr, lastStop );
      if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeDecoPrefs: sending <NEWER-FIRMWARE <" + kdoString + ">>" );
    }
    this.writeSPXMsgToDevice( kdoString );
  }

  /**
   * 
   * Schreibe Display Eigenschaften zum SPX42
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 14.07.2013
   * 
   * @param cf
   * @param lumin
   * @param orient
   * @throws FirmwareNotSupportetException
   */
  public void writeDisplayPrefs( SPX42Config cf, int lumin, int orient ) throws FirmwareNotSupportetException
  {
    String kdoString;
    if( !cf.isFirmwareSupported() )
    {
      Log.e( TAG, "firmware not supportet for write settings!" );
      throw new FirmwareNotSupportetException( "write display prefs" );
    }
    kdoString = String.format( "~%x:%x:%x", ProjectConst.SPX_SET_SETUP_DISPLAYSETTINGS, lumin, orient );
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeDisplayPrefs: sending <" + kdoString + ">" );
    this.writeSPXMsgToDevice( kdoString );
  }

  /**
   * 
   * Schreibe ein Gas in den SPX
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 18.07.2013
   * 
   * @param cf
   * @param gasNr
   * @param gasParms
   * @throws FirmwareNotSupportetException
   */
  public void writeGasSetup( SPX42Config cf, int gasNr, SPX42GasParms gasParms ) throws FirmwareNotSupportetException
  {
    String kdoString;
    int diluent;
    //
    if( gasParms.d1 )
    {
      diluent = 1;
    }
    else if( gasParms.d2 )
    {
      diluent = 2;
    }
    else
    {
      diluent = 0;
    }
    if( !cf.isFirmwareSupported() )
    {
      Log.e( TAG, "firmware not supportet for write settings!" );
      throw new FirmwareNotSupportetException( "write gas setup" );
    }
    if( cf.isOldParamSorting() )
    {
      // Kommando SPX_SET_SETUP_GASLIST
      // ~40:NR:HE:N2:BO:DI:CU
      // NR -> Gas Nummer
      // HE -> Heliumanteil
      // N2 -> Stickstoffanteil
      // BO -> Bailoutgas? (3?)
      // DI -> Diluent ( 0, 1 oder 2 )
      // CU Current Gas (0 oder 1)
      kdoString = String.format( "~%x:%x:%x:%x:%x:%x:%x", ProjectConst.SPX_SET_SETUP_GASLIST, gasNr, gasParms.he, gasParms.n2, ( gasParms.bo ? 3 : 0 ), diluent,
              ( gasParms.isCurr ? 1 : 0 ) );
      if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeDecoPrefs: sending <OLDER-FIRMWARE <" + kdoString + ">>" );
    }
    else
    {
      // Kommando SPX_SET_SETUP_GASLIST
      // ~40:NR:N2:HE:BO:DI:CU
      // NR: Nummer des Gases 0..7
      // N2: Sticksoff in %
      // HE: Heluim in %
      // BO: Bailout (Werte 0,1 und 3 gefunden, 0 kein BO, 3 BO Wert 1 unbekannt?)
      // DI: Diluent 1 oder 2
      // CU: Current Gas
      kdoString = String.format( "~%x:%x:%x:%x:%x:%x:%x", ProjectConst.SPX_SET_SETUP_GASLIST, gasNr, gasParms.n2, gasParms.he, ( gasParms.bo ? 1 : 0 ), diluent,
              ( gasParms.isCurr ? 1 : 0 ) );
      if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeDecoPrefs: sending <NEWER-FIRMWARE <" + kdoString + ">>" );
    }
    //
    this.writeSPXMsgToDevice( kdoString );
  }

  /**
   * 
   * Schreibe eine Anzahl von Gasen in den SPX
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 23.07.2013
   * 
   * @param cf
   * @param gasUpdates
   * @throws FirmwareNotSupportetException
   */
  public void writeGasSetup( SPX42Config cf, Vector<GasUpdateEntity> gasUpdates ) throws FirmwareNotSupportetException
  {
    String kdoString = "";
    SPX42GasParms gasParms;
    int diluent, gasNr;
    //
    if( gasUpdates.isEmpty() ) return;
    //
    if( !cf.isFirmwareSupported() )
    {
      Log.e( TAG, "firmware not supportet for write settings!" );
      throw new FirmwareNotSupportetException( "write gas setup" );
    }
    //
    // einen iterator fr die Durchforstung des Vectors erzeugen
    //
    Iterator<GasUpdateEntity> it = gasUpdates.iterator();
    // und nun durchforste mal bitte
    while( it.hasNext() )
    {
      GasUpdateEntity ent = it.next();
      gasParms = ent.getGasParms();
      gasNr = ent.getGasNr();
      //
      if( gasParms.d1 )
      {
        diluent = 1;
      }
      else if( gasParms.d2 )
      {
        diluent = 2;
      }
      else
      {
        diluent = 0;
      }
      if( cf.isOldParamSorting() )
      {
        // Kommando SPX_SET_SETUP_GASLIST
        // ~40:NR:HE:N2:BO:DI:CU
        // NR -> Gas Nummer
        // HE -> Heliumanteil
        // N2 -> Stickstoffanteil
        // BO -> Bailoutgas? (3?)
        // DI -> Diluent ( 0, 1 oder 2 )
        // CU Current Gas (0 oder 1)
        kdoString = String.format( "~%x:%x:%x:%x:%x:%x:%x", ProjectConst.SPX_SET_SETUP_GASLIST, gasNr, gasParms.he, gasParms.n2, ( gasParms.bo ? 1 : 0 ), diluent,
                ( gasParms.isCurr ? 1 : 0 ) );
        if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeDecoPrefs: sending <OLDER-FIRMWARE <" + kdoString + ">>" );
        this.writeSPXMsgToDevice( kdoString );
      }
      else
      {
        // Kommando SPX_SET_SETUP_GASLIST
        // ~40:NR:N2:HE:BO:DI:CU
        // NR: Nummer des Gases 0..7
        // N2: Sticksoff in %
        // HE: Heluim in %
        // BO: Bailout (Werte 0,1 und 3 gefunden, 0 kein BO, 3 BO Wert 1 unbekannt?)
        // DI: Diluent 1 oder 2
        // CU: Current Gas
        kdoString += String.format( "~%x:%x:%x:%x:%x:%x:%x", ProjectConst.SPX_SET_SETUP_GASLIST, gasNr, gasParms.n2, gasParms.he, ( gasParms.bo ? 1 : 0 ), diluent,
                ( gasParms.isCurr ? 1 : 0 ) );
        if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeDecoPrefs: sending <NEWER-FIRMWARE <" + kdoString + ">>" );
        this.writeSPXMsgToDevice( kdoString );
      }
    }
  }

  /**
   * 
   * Schreibe individual Einstellungen in den SPX42
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 14.07.2013
   * 
   * @param cf
   * @param sensorsOff
   * @param pscrOff
   * @param sensorsCount
   * @param soundOn
   * @param logInterval
   * @param tempStick
   *          bei neuerer Firmware Typ des Tempstick
   * @throws FirmwareNotSupportetException
   */
  public void writeIndividualPrefs( SPX42Config cf, int sensorsOff, int pscrOff, int sensorsCount, int soundOn, int logInterval, int tempStick )
          throws FirmwareNotSupportetException
  {
    String kdoString;
    //
    if( !cf.isFirmwareSupported() )
    {
      Log.e( TAG, "firmware not supportet for write settings!" );
      throw new FirmwareNotSupportetException( "write individual prefs" );
    }
    if( cf.hasSixValuesIndividual() )
    {
      kdoString = String.format( "~%x:%x:%x:%x:%x:%x:%x", ProjectConst.SPX_SET_SETUP_INDIVIDUAL, sensorsOff, pscrOff, sensorsCount, soundOn, logInterval, tempStick );
    }
    else
    {
      kdoString = String.format( "~%x:%x:%x:%x:%x:%x", ProjectConst.SPX_SET_SETUP_INDIVIDUAL, sensorsOff, pscrOff, sensorsCount, soundOn, logInterval );
    }
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeIndividualPrefs: sending <" + kdoString + ">" );
    this.writeSPXMsgToDevice( kdoString );
  }

  /**
   * 
   * Screibe Kommando zum SPX, fge protokoll Start/Ende an
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 02.06.2013
   * 
   * @param msg
   */
  public synchronized void writeSPXMsgToDevice( String msg )
  {
    this.writeToDevice( ProjectConst.STX + msg + ProjectConst.ETX );
  }

  /**
   * 
   * Schreibe Daten zum SPX
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 28.05.2013
   * 
   * @param msg
   */
  public synchronized void writeToDevice( String msg )
  {
    if( mConnectionState == ProjectConst.CONN_STATE_CONNECTED && mWriterThread != null )
    {
      mWriterThread.writeToDevice( msg );
    }
  }

  /**
   * 
   * Schreibe die Masseinheiten in den SPX42
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 14.07.2013
   * 
   * @param cf
   * @param isTempMetric
   * @param isDepthMetric
   * @param isFreshwater
   * @throws FirmwareNotSupportetException
   */
  public void writeUnitPrefs( SPX42Config cf, int isTempMetric, int isDepthMetric, int isFreshwater ) throws FirmwareNotSupportetException
  {
    String kdoString;
    //
    if( !cf.isFirmwareSupported() )
    {
      Log.e( TAG, "firmware not supportet for write settings!" );
      throw new FirmwareNotSupportetException( "write unit prefs" );
    }
    kdoString = String.format( "~%x:%x:%x:%x", ProjectConst.SPX_SET_SETUP_UNITS, isTempMetric, isDepthMetric, isFreshwater );
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeUnitPrefs: sending <" + kdoString + ">" );
    this.writeSPXMsgToDevice( kdoString );
  }

  /**
   * 
   * schreibe Datum und Zeit in das Gert
   * 
   * Project: SubmatixBTLoggerAndroid Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 27.10.2013
   * 
   * @param cf
   * @param dTime
   * @throws FirmwareNotSupportetException
   * 
   */
  public void writeDateTimeToDevice( SPX42Config cf, DateTime dTime ) throws FirmwareNotSupportetException
  {
    String kdoString;
    //
    //
    if( !cf.isFirmwareSupported() )
    {
      Log.e( TAG, "firmware not supportet for write settings!" );
      throw new FirmwareNotSupportetException( "write datetime to device" );
    }
    if( cf.canSetDateTime() )
    {
      //
      // Setze das Zeit und Datum als Kommandostring zusammen
      //
      kdoString = String.format( "%s~%x:%02x:%02x:%02x:%02x:%02x%s", ProjectConst.STX, ProjectConst.SPX_DATETIME, dTime.getHourOfDay(), dTime.getMinuteOfHour(),
              dTime.getDayOfMonth(), dTime.getMonthOfYear(), dTime.getYearOfCentury(), ProjectConst.ETX );
      {
        if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeDateTimeToDevice()...send <" + kdoString + "> (DATETIME)" );
      }
      this.writeToDevice( kdoString );
    }
    else
    {
      if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "writeDateTimeToDevice()...Firmware not support <set datetime> yet" );
    }
  }

  /**
   * 
   * Frage nach den schmutzigen Details des Logs
   * 
   * Project: SubmatixBTLoggerAndroid_4 Package: de.dmarcini.submatix.android4.comm
   * 
   * 
   * Stand: 07.08.2013
   * 
   * @param numberOnSPX
   */
  public void askForLogDetail( int numberOnSPX )
  {
    String kdoString;
    kdoString = String.format( "~%x:%x", ProjectConst.SPX_GET_LOG_NUMBER, numberOnSPX );
    if( ApplicationDEBUG.DEBUG ) Log.d( TAG, "askForLogDetail: sending <" + kdoString + ">" );
    this.writeSPXMsgToDevice( kdoString );
  }

  /**
   * 
   * Den Service zerstren!!!!
   * 
   * Project: SubmatixBTLoggerAndroid Package: de.dmarcini.submatix.android4.comm
   * 
   * Stand: 30.11.2013
   */
  public void destroyService()
  {
    disconnect();
    stopSelf();
  }
}