AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Wie geht das: TTY IO für Android

Ein Thema von QuickAndDirty · begonnen am 12. Jun 2025 · letzter Beitrag vom 20. Jun 2025
Antwort Antwort
QuickAndDirty

Registriert seit: 13. Jan 2004
Ort: Hamm(Westf)
2.056 Beiträge
 
Delphi 12 Athens
 
#1

Wie geht das: TTY IO für Android

  Alt 12. Jun 2025, 08:50
Hallo
Ich möchte gerne in input und output streams für Android von und nach TTYS9 bekommen.
Geht das mit assignfile und writeLN und Readln ?
oder mit TStream klassen?
oder sind das tatsächlich filestreams unter Android/Linux die sich wie TFilestream öffnen lassen?

Ich brauche diesen Terminal Zugriff um die Beleuchtung eines Android Tablets zu steuern.
Andreas
Nobody goes there anymore. It's too crowded!
  Mit Zitat antworten Zitat
QuickAndDirty

Registriert seit: 13. Jan 2004
Ort: Hamm(Westf)
2.056 Beiträge
 
Delphi 12 Athens
 
#2

AW: Wie geht das: TTY IO für Android

  Alt 17. Jun 2025, 13:02
Ich nehme an keiner weiß wie sowas geht?
Andreas
Nobody goes there anymore. It's too crowded!
  Mit Zitat antworten Zitat
fisipjm

Registriert seit: 28. Okt 2013
345 Beiträge
 
Delphi 12 Athens
 
#3

AW: Wie geht das: TTY IO für Android

  Alt 17. Jun 2025, 13:40
Ich nehme an keiner weiß wie sowas geht?
Absolut keine Erfahrung damit.

Aber das hört sich für mich, nach kurzer Recherche, so an als bräuchtest du auf dem Gerät Root zugriff um auf diesen Bereich zugreifen zu können. Um was geht es denn genau? Hängt an dem Tablet eine externe Leuchte dran die über Seriell angesprochen wird?
  Mit Zitat antworten Zitat
HolgerX

Registriert seit: 10. Apr 2006
Ort: Leverkusen
989 Beiträge
 
Delphi 6 Professional
 
#4

AW: Wie geht das: TTY IO für Android

  Alt 17. Jun 2025, 16:53
Hmm..

Mal nach RS232 Adroid API gegoogelt und dann das gefunden

https://code.google.com/archive/p/an...guideline.wiki

Generell ist es gleich, ob TTY / RS232 / RS485....
Es sind alles serielle Protokolle, welche einen Adapter brauchen.

Optional kannst Du aber auch ComServer nehmen
( so was wie https://www.wut.de/e-58www-10-inde-000.php ),
diese gab es früher auch für TTY...

Dann kannst du direkt per TCP/IP auf die Serielle Schnittstelle zugreifen.
Hatte bei W&T mal ein Handbuch mit den Protokolldetails und Beispielen gesehen...

Solche ComServer gibt es auch von anderen Herstellern (z.B. Moxa).

Holger
(Ja ich Verwende Delphi 6 Pro und will NICHT wechseln!)
  Mit Zitat antworten Zitat
QuickAndDirty

Registriert seit: 13. Jan 2004
Ort: Hamm(Westf)
2.056 Beiträge
 
Delphi 12 Athens
 
#5

AW: Wie geht das: TTY IO für Android

  Alt 18. Jun 2025, 09:11
Ich nehme an keiner weiß wie sowas geht?
Absolut keine Erfahrung damit.

Aber das hört sich für mich, nach kurzer Recherche, so an als bräuchtest du auf dem Gerät Root zugriff um auf diesen Bereich zugreifen zu können. Um was geht es denn genau? Hängt an dem Tablet eine externe Leuchte dran die über Seriell angesprochen wird?
Das Tablet hat einen bunt leichtenden rahmen.
Die standard einstellung ist blinkend in allen fareben des regenbogens.
EIn Tablet mit selbem Android and selbem chip lässt sich per HTTP url parametern und ein HTTP Get steuern...
Aber das tablet um das es geht will leider TTYS9 um diesen LED Rahmen zu steuern...
Andreas
Nobody goes there anymore. It's too crowded!
  Mit Zitat antworten Zitat
QuickAndDirty

Registriert seit: 13. Jan 2004
Ort: Hamm(Westf)
2.056 Beiträge
 
Delphi 12 Athens
 
#6

AW: Wie geht das: TTY IO für Android

  Alt 18. Jun 2025, 09:15
Hmm..

Mal nach RS232 Adroid API gegoogelt und dann das gefunden

https://code.google.com/archive/p/an...guideline.wiki

Generell ist es gleich, ob TTY / RS232 / RS485....
Es sind alles serielle Protokolle, welche einen Adapter brauchen.

Optional kannst Du aber auch ComServer nehmen
( so was wie https://www.wut.de/e-58www-10-inde-000.php ),
diese gab es früher auch für TTY...

Dann kannst du direkt per TCP/IP auf die Serielle Schnittstelle zugreifen.
Hatte bei W&T mal ein Handbuch mit den Protokolldetails und Beispielen gesehen...

Solche ComServer gibt es auch von anderen Herstellern (z.B. Moxa).

Holger
Ich wollte auf TTYS9 mit einer App zugreifen die auch auf dem gerät läuft. Es handelt sich nicht um einen fern zugriff...wobei man theoretisch sowas wie ne Baud rate usw. angeben muss wenn man TTY auf Linux öffnet. Ich habe das einfach noch nie gemacht.

Und die Kurze recherche im internet (Gemini pro 2.5) sagt ich bräuchte eine java library die TTY kann , die ich in delphi einbinde per Marhsalling... habe aber nichts davon verstanden.

EDIT:
Gemini pro 2.5
hat doch noch was ausgespukt was vielleicht geht. werde es nächste woche testen. Es hängt alles an der funktion fcntl . Delphi liefert anscheinend wrapper für lowlevel posix funktionen. Yay.

Delphi-Quellcode:
unit Android.Serial;
{
//usage
// In your FMX Form unit...
uses
  System.IOUtils,
  Android.Serial; // <-- Add the new unit

...

procedure TMyForm.Button1Click(Sender: TObject);
var
  SerialPort: TAndroidSerialPort;
  DataToSend: string;
  Buffer: TBytes;
  BytesRead: Integer;
begin
  // Create an instance for /dev/ttyS9
  SerialPort := TAndroidSerialPort.Create('ttyS9');
  MemoLog.Lines.Add('Attempting to communicate with ' + SerialPort.DeviceName);

  try
    try
      // 1. Open the port
      SerialPort.Open;
      MemoLog.Lines.Add('Port opened successfully.');

      // 2. Configure the port settings (e.g., 9600 baud, 8 data bits, No parity, 1 stop bit)
      SerialPort.Configure(9600, 8, 'N', 1);
      MemoLog.Lines.Add('Port configured: 9600, 8N1');

      // 3. Write data to the peripheral
      DataToSend := 'Hello from Delphi App!';
      SerialPort.WriteString(DataToSend + #13#10); // Send string with CR+LF
      MemoLog.Lines.Add('Sent: ' + DataToSend);

      // 4. Wait a bit for the device to respond
      // IMPORTANT: In a real app, do not sleep the main thread!
      // Use a TThread or TTask for reading. This is just for a simple example.
      Sleep(500);

      // 5. Read data back
      // The Read method will return immediately or after a short timeout (set in Configure)
      BytesRead := SerialPort.Read(Buffer, 256); // Attempt to read up to 256 bytes
      if BytesRead > 0 then
      begin
        MemoLog.Lines.Add(Format('Received %d bytes: %s', [BytesRead, TEncoding.UTF8.GetString(Buffer)]));
      end
      else
      begin
        MemoLog.Lines.Add('No data received.');
      end;

    except
      on E: ESerialPortError do
      begin
        MemoLog.Lines.Add('ERROR: ' + E.Message);
      end;
    end;
  finally
    // 6. Ensure the port is closed and the object is freed
    if Assigned(SerialPort) then
    begin
      SerialPort.Close;
      SerialPort.Free;
      MemoLog.Lines.Add('Port closed and resources freed.');
    end;
  end;
end;
   
}


interface

uses
  System.SysUtils, System.Classes, System.SyncObjs;

type
  // Exception class for serial port errors
  ESerialPortError = class(Exception);

  // A class to encapsulate low-level TTY communication on Android
  TAndroidSerialPort = class
  private
    FDeviceName: string;
    FFileDescriptor: Integer;
    FIsOpened: Boolean;
    procedure SetBaudRate(const Value: Integer);
    procedure SetDataBits(const Value: Integer);
    procedure SetParity(const Value: Char); // 'N', 'E', 'O'
    procedure SetStopBits(const Value: Integer);
    function CheckOpened: Boolean;
  public
    constructor Create(const ADeviceName: string);
    destructor Destroy; override;

    // --- Core Methods ---
    procedure Open;
    procedure Close;
    procedure Configure(BaudRate: Integer; DataBits: Integer; Parity: Char; StopBits: Integer);

    // --- Read/Write Methods ---
    function Read(var Buffer: TBytes; Count: Integer): Integer;
    function Write(const Buffer: TBytes; Count: Integer): Integer;
    procedure WriteString(const s: string);

    // --- Properties ---
    property IsOpened: Boolean read FIsOpened;
    property DeviceName: string read FDeviceName;
    property FileDescriptor: Integer read FFileDescriptor;
  end;

//------------------------------------------------------------------------------
// Implementation
//------------------------------------------------------------------------------

implementation

// We need to import the low-level POSIX functions for Android.
// These units contain the definitions for open, read, write, close, tcgetattr, etc.
{$IFDEF ANDROID}
uses
  Posix.Fcntl, Posix.SysStat, Posix.Termios, Posix.Unistd, Androidapi.Log;
{$ENDIF}

const
  TAG = 'AndroidSerial'; // For logging in logcat

{ TAndroidSerialPort }

constructor TAndroidSerialPort.Create(const ADeviceName: string);
begin
  inherited Create;
  if not ADeviceName.StartsWith('/dev/') then
    FDeviceName := '/dev/' + ADeviceName
  else
    FDeviceName := ADeviceName;

  FFileDescriptor := -1;
  FIsOpened := False;
end;

destructor TAndroidSerialPort.Destroy;
begin
  if FIsOpened then
    Close;
  inherited Destroy;
end;

procedure TAndroidSerialPort.Open;
{$IFDEF ANDROID}
begin
  if FIsOpened then
    Exit;

  // O_RDWR: Open for reading and writing.
  // O_NOCTTY: The port will not become the controlling terminal of the process.
  // O_NDELAY: Don't wait for DCD line (data carrier detect).
  FFileDescriptor := Posix.Fcntl.open(PAnsiChar(AnsiString(FDeviceName)), O_RDWR or O_NOCTTY or O_NDELAY);

  if FFileDescriptor = -1 then
  begin
    FIsOpened := False;
    // Log the specific error for debugging via logcat
    Androidapi.Log.d(TAG, 'Failed to open serial port %s. Error: %s', [FDeviceName, strerror(errno)]);
    raise ESerialPortError.CreateFmt('Cannot open serial port %s. Check permissions or if the device exists. Error: %s', [FDeviceName, strerror(errno)]);
  end
  else
  begin
    FIsOpened := True;
    // Set file status flags
    fcntl(FFileDescriptor, F_SETFL, 0);
    Androidapi.Log.d(TAG, 'Successfully opened port %s with FD: %d', [FDeviceName, FFileDescriptor]);
  end;
end;
{$ELSE}
begin
  // This is a placeholder for non-Android platforms
  raise ESerialPortError.Create('This class is only for the Android platform.');
end;
{$ENDIF}

procedure TAndroidSerialPort.Close;
{$IFDEF ANDROID}
begin
  if not FIsOpened then
    Exit;

  if FFileDescriptor <> -1 then
  begin
    Posix.Unistd.close(FFileDescriptor);
    FFileDescriptor := -1;
    FIsOpened := False;
    Androidapi.Log.d(TAG, 'Closed port %s', [FDeviceName]);
  end;
end;
{$ELSE}
begin
  // Placeholder
end;
{$ENDIF}

procedure TAndroidSerialPort.Configure(BaudRate: Integer; DataBits: Integer; Parity: Char; StopBits: Integer);
{$IFDEF ANDROID}
var
  options: termios;
  baudConstant: Integer;
begin
  if not CheckOpened then Exit;

  // 1. Get the current options for the port
  if tcgetattr(FFileDescriptor, options) <> 0 then
  begin
    Androidapi.Log.d(TAG, 'Failed to get termios attributes. Error: %s', [strerror(errno)]);
    raise ESerialPortError.Create('Failed to get terminal attributes.');
  end;

  // 2. Set the baud rate (input and output)
  case BaudRate of
    9600: baudConstant := B9600;
    19200: baudConstant := B19200;
    38400: baudConstant := B38400;
    57600: baudConstant := B57600;
    115200: baudConstant := B115200;
    else baudConstant := B9600; // Default
  end;
  cfsetispeed(options, baudConstant);
  cfsetospeed(options, baudConstant);

  // 3. Set control flags (c_cflag)
  options.c_cflag := options.c_cflag or CLOCAL or CREAD; // Enable receiver, ignore modem control lines

  // 4. Set data bits
  options.c_cflag := options.c_cflag and not CSIZE; // Clear the mask
  case DataBits of
    7: options.c_cflag := options.c_cflag or CS7;
    8: options.c_cflag := options.c_cflag or CS8;
  else
    options.c_cflag := options.c_cflag or CS8; // Default to 8
  end;

  // 5. Set parity
  case UpCase(Parity) of
    'N': begin // No parity
      options.c_cflag := options.c_cflag and not PARENB;
      options.c_iflag := options.c_iflag and not INPCK;
    end;
    'E': begin // Even parity
      options.c_cflag := options.c_cflag or PARENB;
      options.c_cflag := options.c_cflag and not PARODD;
      options.c_iflag := options.c_iflag or INPCK;
    end;
    'O': begin // Odd parity
      options.c_cflag := options.c_cflag or PARENB or PARODD;
      options.c_iflag := options.c_iflag or INPCK;
    end;
  end;

  // 6. Set stop bits
  if StopBits = 2 then
    options.c_cflag := options.c_cflag or CSTOPB // 2 stop bits
  else
    options.c_cflag := options.c_cflag and not CSTOPB; // 1 stop bit (default)

  // 7. Set local flags (c_lflag) -> RAW MODE
  // This makes the terminal input and output raw.
  options.c_lflag := options.c_lflag and not (ICANON or ECHO or ECHOE or ISIG);

  // 8. Set output flags (c_oflag) -> RAW MODE
  options.c_oflag := options.c_oflag and not OPOST;

  // 9. Set read timeout settings
  options.c_cc[VMIN] := 0; // Minimum number of characters for non-canonical read
  options.c_cc[VTIME] := 10; // Timeout in deciseconds (e.g., 10 = 1 second) for non-canonical read

  // 10. Apply the new options
  // TCSANOW: the change occurs immediately.
  if tcsetattr(FFileDescriptor, TCSANOW, options) <> 0 then
  begin
     Androidapi.Log.d(TAG, 'Failed to set termios attributes. Error: %s', [strerror(errno)]);
     raise ESerialPortError.Create('Failed to set terminal attributes.');
  end;

  // Flush the port
  tcflush(FFileDescriptor, TCIFLUSH);

  Androidapi.Log.d(TAG, 'Successfully configured port %s', [FDeviceName]);

end;
{$ELSE}
begin
  // Placeholder
end;
{$ENDIF}

function TAndroidSerialPort.CheckOpened: Boolean;
begin
  Result := FIsOpened;
  if not Result then
    raise ESerialPortError.Create('Serial port is not open.');
end;

function TAndroidSerialPort.Read(var Buffer: TBytes; Count: Integer): Integer;
{$IFDEF ANDROID}
begin
  if not CheckOpened then Exit(0);
  if Count <= 0 then Exit(0);

  SetLength(Buffer, Count);
  Result := Posix.Unistd.read(FFileDescriptor, Buffer, Count);

  if Result < 0 then
  begin
    // An error occurred
    Androidapi.Log.d(TAG, 'Error reading from serial port. Error: %s', [strerror(errno)]);
    raise ESerialPortError.CreateFmt('Error reading from serial port: %s', [strerror(errno)]);
  end;

  // Resize buffer to actual number of bytes read
  SetLength(Buffer, Result);
end;
{$ELSE}
begin
  Result := 0; // Placeholder
end;
{$ENDIF}

function TAndroidSerialPort.Write(const Buffer: TBytes; Count: Integer): Integer;
{$IFDEF ANDROID}
begin
  if not CheckOpened then Exit(0);
  if Count <= 0 then Exit(0);

  Result := Posix.Unistd.write(FFileDescriptor, Buffer, Count);
  if Result < 0 then
  begin
     Androidapi.Log.d(TAG, 'Error writing to serial port. Error: %s', [strerror(errno)]);
     raise ESerialPortError.CreateFmt('Error writing to serial port: %s', [strerror(errno)]);
  end;
end;
{$ELSE}
begin
  Result := 0; // Placeholder
end;
{$ENDIF}

procedure TAndroidSerialPort.WriteString(const s: string);
var
  bytesToWrite: TBytes;
begin
  // Convert Delphi string to TBytes for writing
  bytesToWrite := TEncoding.UTF8.GetBytes(s);
  Write(bytesToWrite, Length(bytesToWrite));
end;


// --- These are just placeholders for the property setters for now ---
// A real implementation might re-call Configure.
procedure TAndroidSerialPort.SetBaudRate(const Value: Integer);
begin
  // Re-configure would be needed here
end;

procedure TAndroidSerialPort.SetDataBits(const Value: Integer);
begin
  // Re-configure would be needed here
end;

procedure TAndroidSerialPort.SetParity(const Value: Char);
begin
  // Re-configure would be needed here
end;

procedure TAndroidSerialPort.SetStopBits(const Value: Integer);
begin
  // Re-configure would be needed here
end;

end.
Andreas
Nobody goes there anymore. It's too crowded!

Geändert von QuickAndDirty (18. Jun 2025 um 10:43 Uhr)
  Mit Zitat antworten Zitat
fisipjm

Registriert seit: 28. Okt 2013
345 Beiträge
 
Delphi 12 Athens
 
#7

AW: Wie geht das: TTY IO für Android

  Alt 18. Jun 2025, 13:23
Ich hoffe du hast damit Erfolg damit, aber wenn ich den Code nur mal kurz überfliege sieht mir das doch stark nach Halluzinationen aus. Zumindest "Androidapi.Log.d" wäre mir neu. Das ist normal in den FMX.Types drin und wird einfach nur mit log.d angesprochen.
Aber vielleicht ist ja ein Ansatz drin, der dich weiter bringt
  Mit Zitat antworten Zitat
QuickAndDirty

Registriert seit: 13. Jan 2004
Ort: Hamm(Westf)
2.056 Beiträge
 
Delphi 12 Athens
 
#8

AW: Wie geht das: TTY IO für Android

  Alt 20. Jun 2025, 10:55
Ich hoffe du hast damit Erfolg damit, aber wenn ich den Code nur mal kurz überfliege sieht mir das doch stark nach Halluzinationen aus. Zumindest "Androidapi.Log.d" wäre mir neu. Das ist normal in den FMX.Types drin und wird einfach nur mit log.d angesprochen.
Aber vielleicht ist ja ein Ansatz drin, der dich weiter bringt
ja fcntl ist im prinzip das ding was ich bräuchte oder?
und das zumindest existiert in Studio\23.0\source\rtl\posix\Posix.Fcntl.pas

Delphi-Quellcode:
{*******************************************************}
{                                                       }
{           CodeGear Delphi Runtime Library             }
{                                                       }
{ Copyright(c) 1995-2023 Embarcadero Technologies, Inc. }
{              All rights reserved                      }
{                                                       }
{   Copyright and license exceptions noted in source    }
{                                                       }
{*******************************************************}

unit Posix.Fcntl;

{$WEAKPACKAGEUNIT}
{$HPPEMIT NOUSINGNAMESPACE}

interface

uses Posix.Base, Posix.SysTypes;

(*$HPPEMIT '#include <fcntl.h>' *)
{$DEFINE IN_POSIXFCNTL}

{$IFDEF MACOS}
{$I osx/FcntlTypes.inc}
{$ELSEIF defined(LINUX)}
{$I linux/FcntlTypes.inc}
{$ELSEIF defined(ANDROID)}
{$I android/FcntlTypes.inc}
{$ENDIF}

{$I FcntlAPI.inc}


implementation

end.
und in FcntlAPI.inc
Delphi-Quellcode:
{*******************************************************}
{                                                       }
{           CodeGear Delphi Runtime Library             }
{                                                       }
{ Copyright(c) 1995-2023 Embarcadero Technologies, Inc. }
{              All rights reserved                      }
{                                                       }
{*******************************************************}

{$IFNDEF MACOS}
function openat(Handle: Integer; Path: MarshaledAString; Flags: Integer): Integer; cdecl; varargs;
  external libc name _PU + 'openat';
{$EXTERNALSYM openat}
{$ENDIF}

function __open(PathName: MarshaledAString; Flags: Integer): Integer; cdecl; varargs;
  external libc name _PU + 'open';
{$EXTERNALSYM __open}

function open(PathName: MarshaledAString; Flags: Integer): Integer; cdecl; varargs;
  external libc name _PU + 'open';
{$EXTERNALSYM open}
{ Do the file control operation described by CMD on FD.
   The remaining arguments are interpreted depending on CMD.  }


function fcntl(Handle: Integer; Command: Integer): Integer; cdecl; varargs;
  external libc name _PU + 'fcntl';
{$EXTERNALSYM fcntl}

function creat(Path: MarshaledAString; Mode: mode_t): Integer; cdecl
  external libc name _PU + 'creat';
{$EXTERNALSYM creat}
Von hier aus kann ich wahrscheinlich mit denen die mehr Ahnung von C und "Baudraten" haben zu einer Lösung kommen.

Also zumindest der Kern "öffnen einer Datei mit Baudrate" scheint verfügbar zu sein.
Andreas
Nobody goes there anymore. It's too crowded!

Geändert von QuickAndDirty (20. Jun 2025 um 10:59 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:15 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz