aldostrof
Posts: 2
Joined: Mon Apr 01, 2019 10:13 am

Raspberry acting as Bluetooth gateway

Mon Apr 01, 2019 10:33 am

Hi guys,

I need to build a bluetooth gateway using a raspberry pi 3 model b.
Specifically, I have some BLE sensors and I need to read data from them, assemble and structure that data in a proper format, and then send that data to an Android application (over Bluetooth, again).
In that situation, the raspberry would have a double role:
- act as a "server" (from the Android Application perspective), receiving commands from the app
- act as a "client" (from the sensors perspective), by receiving sensor readings, structure them and then forward those to the Smartphone app

As far as I know, the BLE specification allows a mixed topology of this kind.
In my case, from an hardware perspective the Raspberry is equipped with Bluetooth 4.1.
I would like to know if the provided Bluetooth software stack (BlueZ) support this kind of simultaneous Client/Server BLE topology.

Thanks!

psulli10
Posts: 2
Joined: Tue Mar 10, 2020 5:00 pm

Re: Raspberry acting as Bluetooth gateway

Tue Mar 31, 2020 10:16 am

Hi aldostrof,

I'm looking at doing something very similar for a project. Did you manage to get the answers you were looking for?

Most importantly I suppose did you manage to get this to work and could share some details if so?

Thanks!

petzval
Posts: 36
Joined: Sat Aug 10, 2013 12:15 pm

Re: Raspberry acting as Bluetooth gateway

Thu Apr 02, 2020 11:52 am

I'm working on something similar. I have Android and Pi code to set up a Bluetooth link, with either side as the client. They can exchange data and swap roles (client/server) under program control. So the Pi can control the Android app, or vice-versa. It should be in a state to post something workable within the next week - I'll put it up under a heading such as: Android/Pi Bluetooth code.
In the meantime, here is the LE side of the equation. This code can connect to multiple LE devices simultaneously, and read/write characteristics.

BEFORE COMPILE
1. Put LE board addresses in struct devicedata device[]
2. Put characteristic data in struct cticdata motor[]

compile instructions
gcc btle.c -o btle

Code: Select all

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>

// Bluetooth development files option
// It is not necessary to:  apt-get install libbluetooth-dev
// uncomment #define BTDEV if libbluetooth-dev is installed
// and you want to add any of its features to the code

// #define BTDEV

// BEFORE COMPILE
// 1. Put LE board addresses in struct devicedata device[] 
// 2. Put characteristic data in struct cticdata motor[]

// compile instructions
// BTDEV not defined     gcc btle.c -o btle
// BTDEV defined         gcc btle.c -lbluetooth -o btle


#ifdef BTDEV
   // libbluetooth-dev installed
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#else
   // these are the definitions supplied by 
   // the above libbluetooth-dev include files
#define BTPROTO_HCI 1
#define SOL_HCI 0
#define HCI_FILTER 2
#define HCIDEVUP _IOW('H',201,int)
#define HCIDEVDOWN _IOW('H',202,int)

struct sockaddr_hci
  {
  unsigned short hci_family;
  unsigned short hci_dev;      
  unsigned short hci_channel;
  };

struct hci_filter
  {
  unsigned int type_mask;
  unsigned int event_mask[2];
  unsigned short opcode;
  };
#endif

void devinfo(void);
int devconnect(int ndevice);
int devdisconnect(int ndevice);
int setdhandle(int ndevice);
int readreply(int replyflag,int numrep,unsigned char *data,int readflag);
int sendcmd(unsigned char *cmd,int len);
int devsock(void);
int writectic(int ndevice,int cticn,unsigned char *data);
int readctic(int ndevice,int cticn,unsigned char *data);
void hexdump(unsigned char *buf, int len);

     // simple user interface functions
void printhelp(void);
int inputdev(void);
int inputctic(int ndevice);
int inputval(void);
int inputint(void);

// LE characteristics

struct cticdata 
  {
  int size;     // number of bytes  (0=no entry)
  int ack;      // 0=no acknowledge  1=acknowledge
  char *name;   // name of characteristic - your choice
  int chandle;  // characteristic handle 
  };

#define NUMCTICS 8

struct cticdata motor[NUMCTICS] = {
{1,0,"Alert" ,0x000B},   // 1 byte, no acknowledge, handle = 000B
{1,0,"Control",0x000E},  // 1 byte, no acknowledge, handle = 000E
{1,0,"PWM hi",0x0010},   // 1 byte, no ack, 0010
{1,0,"PWM lo",0x0012},   // 1 byte, no ack, 0012 
{2,1,"Test",0x0014},     // 2 byte, acknowledge, handle = 0014
{0,0,NULL,0},            // no entry
{0,0,NULL,0},
{0,0,NULL,0},
};

// LE device info

struct devicedata
  {
  int conflag;                // 0=disconnected 1=connected
                 // index of characteristic info in ctic[] 
  unsigned char dhandle[2];   // LE device handle returned by connect  [0]=lo [1]=hi
  char name[16];              // name of LE device - your choice
  unsigned char baddr[6];     // board address hi byte first
  struct cticdata *ctic;      // characteristic info array
  };

#define NUMDEVICES 4

struct devicedata device[NUMDEVICES] = {
{ 0,{0,0},"LE Device", {0x00,0x1E,0xC0,0x2D,0x17,0x7C},motor }, // board address = 00:1E:C0:2D:17:7C 
{ 0,{0,0},"Device B",{0x22,0x22,0x22,0x22,0x22,0x22},motor },
{ 0,{0,0},"Device C",{0x33,0x33,0x33,0x33,0x33,0x33},motor },
{ 0,{0,0},"Device D",{0x44,0x44,0x44,0x44,0x44,0x44},motor },
};

// GLOBAL CONSTANTS

struct globpar 
  {
  int printflag;       // print BT sends and replies
  int hci;             // PI's BT handle
  int handev;          // command strings set with this device's handle  -1=none
  int replyflag;       // 0=wait for expected number of replies  1=wait for time out                      
  int conrep;          // connect expected replies   
  int timout;          // time out for replies seconds
  };
  
struct globpar gpar;

               // command strings
unsigned char hciopen[32] = {1,0x0D,0x20,0x19,0x60,0,0x60,0,0,0,0x7C,0x17,0x2D,0xC0,0x1E,0,0,0x18,0,0x28,0,0,0,0x2A,0,0,0,0,0}; // len 29
unsigned char hciclose[8] = {1,6,4,3,0x40,0,0x13};  // len 7
unsigned char hciwrite[32] = {2,0x40,0,8,0,4,0,4,0,0x52,0x0B,0,0};  // len 13 if 1 byte
              //  [1][2]=device handle  [9]=opcode  [10][11]=characteristic handle  [12]=data - up to 20 bytes
unsigned char hciread[16] = {2,0x40,0,7,0,3,0,4,0,0x0A,0x12,0};  // len 12   [9]=0A read req [10][11]=handle of characteristic

int main()
  {
  int speed,ndevice,dirn,cticn,cticval;
  unsigned char cmd,cmds[8],dat[8],rdat[32];
  
     // global parameters you choose
  gpar.printflag = 1;   // 0=no traffic prints  1=print bluetooth traffic
  gpar.replyflag = 0;   // 0=wait for specific number of replies  1=time out
  gpar.conrep = 2;     // expected number of replies for connect
  gpar.timout = 1;      // reply wait time out = 1 second

    // global parameters set by code
  gpar.hci = -1;         // hci socket
  gpar.handev = -1;      // handles set for this device number 

  
  if(devsock() == 0)    // open HCI socket gpar.hci 
    {
    printf("Socket open ERROR\n");  
    return(0);
    }
  
  ndevice = 0;  // device index
  cticn = 0;    // characteristic index
  cticval = 0;  // characteristic value

  printhelp();
  
  do
    {
    printf("Input command: ");
    scanf("%s",cmds);
    cmd = cmds[0];
 
       // further inputs
          
    if(cmd == 'c' || cmd == 'd' || cmd == 'w' || cmd == 'r')
      {
      ndevice = inputdev();
      if(cmd == 'w' || cmd == 'r')
        {
        cticn = inputctic(ndevice);
        if(cmd == 'w')
          cticval = inputval();
        }  
      }
      
      // execute command
      
    if(cmd == 'h')            // help
      printhelp();
    else if(cmd == 'c')            // connect device
      devconnect(ndevice);
    else if(cmd == 'd')       // disconnect device
      devdisconnect(ndevice);
    else if(cmd == 'w')       // write characteristic
      {
      if(setdhandle(ndevice) != 0)  // set device handles in command strings
        {
        dat[0] = (unsigned char)(cticval & 0xFF);         // convert to unsigned char array
        dat[1] = (unsigned char)((cticval >> 8) & 0xFF);  // in case 2 byte
        writectic(ndevice,cticn,dat);  
        }   
      }
    else if(cmd == 'r')
      {
      if(setdhandle(ndevice) != 0)
        readctic(ndevice,cticn,rdat);
      }
    else if(cmd == 'i')      // print all device info
      devinfo();
    else if(cmd != 'q')
      printf("Invalid command\n");
    }
  while(cmd != 'q');
  
  close(gpar.hci);  // close HCI socket
  
  return(0);
  }

/*************** PRINT DEVICE INFO ********************/

void devinfo()
  {
  int n,k;
  struct devicedata *tdp;
  struct cticdata *cp;
  
  for(n = 0 ; n < NUMDEVICES ; ++n)
    {
    tdp = &device[n];
    printf("DEVICE %d  %s  Connect=%d\n",n,tdp->name,tdp->conflag);
    printf("  ADDR = ");
    for(k = 0 ; k < 6 ; ++k)
      printf("%02X ",tdp->baddr[k]);
    if(tdp->conflag != 0)
      printf("   Handle=%02X%02X",tdp->dhandle[1],tdp->dhandle[0]);
    printf("\n");
    for(k = 0 ; k < NUMCTICS ; ++k)
      {
      cp = &tdp->ctic[k];
      if(cp->size > 0)
        printf("  %s - Handle %04X Size %d Ack %d\n",cp->name,cp->chandle,cp->size,cp->ack);
      }
    }
  }
  
/***********  CONNECT DEVICE index ndevice *******************/

int devconnect(int ndevice)
  {
  int n,retval;
  struct devicedata *dp;
  
  if(ndevice < 0 || ndevice >= NUMDEVICES)
    {
    printf("Invalid device\n");
    return(0);
    }
    
  dp = &device[ndevice];
  if(dp->conflag != 0)
    {
    printf("Already connected\n");
    return(0);
    }

     // copy board address to command string  low byte first
  for(n = 0 ; n < 6 ; ++n)
    hciopen[n+10] = dp->baddr[5-n];
  
  if(sendcmd(hciopen,29) == 0)
    return(0);
   
   /***** CONNECT REPLIES *****************
   This is set up to exit readreply when it times out:
      readreply(1,  ....second parameter number of expected replies ignored... 
    
   If you see a consistent number of replies (say 2)
   modify replyflag and gpar.conrep to exit on that number:
   
      readreply(0,2,dp->dhandle,1)
        
   This will eliminate the time out wait and will be faster.
  
   When experimenting with commands, call readreply(1,..
   to see all the replies when the number is uncertain  
  **************************************/
    
  retval = readreply(1,gpar.conrep,dp->dhandle,1);  // will read device handle
  if(retval == 0)
    {
    printf("Time out - failed\n",retval);
    return(0);
    }
  if(retval == 1)
    {
    printf("Fail - no handle\n");
    return(0);
    }
  printf("Connect OK\n");
    
  dp->conflag = 1;
  
       
  return(1);
  }

/***********  DISCONNECT *******************/

int devdisconnect(int ndevice)
  {
  int n;
  struct devicedata *dp;
  
  dp = &device[ndevice];
  if(dp->conflag == 0)
    {
    printf("Already disconnected\n");
    return(0);
    }
    
  if(setdhandle(ndevice) == 0)   
    return(0);                  
    
  if(sendcmd(hciclose,7) == 0)
    return(0);
    
  if(readreply(gpar.replyflag,2,NULL,0) == 0)  
    return(0);

  dp->conflag = 0;
  gpar.handev = -1;    

  printf("Disconnected\n");       
  return(1);
  }


/************* SET DEVICE HANDLE in command strings *********/

int setdhandle(int ndevice)
  {
  struct devicedata *dp;
 
  if(gpar.handev == ndevice)
    return(1);   // device handles already set
  
  if(ndevice < 0 || ndevice >= NUMDEVICES)
    {
    printf("Invalid device\n");
    return(0);
    }
    
  dp = &device[ndevice];
  if(dp->conflag == 0)
    {
    printf("Not connected\n");
    return(0);
    }
     
  hciwrite[1] = dp->dhandle[0];
  hciwrite[2] = dp->dhandle[1];

  hciread[1] = dp->dhandle[0];
  hciread[2] = dp->dhandle[1];
  
  hciclose[4] = dp->dhandle[0];
  hciclose[5] = dp->dhandle[1];

  gpar.handev = ndevice;   // device handles set for this device number
  return(1);
  }
  
/***********  WRITE CHARACTERISTIC *****************
must set up hciwrite first with device handle via setdhandle(ndevice)
ndevice = device index in device[ndevice]
cticn = characteristic index in device[ndevice].ctic[cticn]
data = array of bytes to write - low byte first 
*****************/

int writectic(int ndevice,int cticn,unsigned char *data)
  {
  struct cticdata *cp;  // characteristic info structure
  int n,chandle,size,ack;
    
  cp = &device[ndevice].ctic[cticn];
  chandle = cp->chandle;  // characteristic handle
  size = cp->size;        // number of bytes
  ack = cp->ack;          // acknowledge        
           
                          // set characteristic handle
  hciwrite[10] = (unsigned char)(chandle & 0xFF);
  hciwrite[11] = (unsigned char)((chandle >> 8) & 0xFF);
  for(n = 0 ; n < size ; ++n)     // set data
    hciwrite[12+n] = data[n];     // low byte first

  hciwrite[3] = size+7;
  hciwrite[5] = size+3;
  
  if(ack == 0)           // no acknowledge 
    hciwrite[9] = 0x52;  // write command opcode
  else                   // acknowledge
    hciwrite[9] = 0x12;  // write request opcode

  printf("Write %s %s =",device[ndevice].name,cp->name);    
  for(n = 0 ; n < size ; ++n)
    printf(" %02X",hciwrite[n+12]);
  printf("\n");
       
  if(sendcmd(hciwrite,12+size) == 0)   // send 13 for 1 byte
    return(0);
    
  if(readreply(gpar.replyflag,1+ack,NULL,0) == 0)    // 2 replies if acknowledge  
    return(0);   
 
  return(1);
  }


/***********  READ CHARACTERISTIC *****************
ndevice = device index in device[ndevice]
cticn = characteristic index in device[ndevice].ctic[cticn]
data = array of bytes to receive read - low byte first 
*****************/

int readctic(int ndevice,int cticn,unsigned char *data)
  {
  struct cticdata *cp;  // characteristic info structure
  int n,chandle,size,ack;
  unsigned char dat[32];
 
  if(setdhandle(ndevice) == 0)
    return(0);
    
  cp = &device[ndevice].ctic[cticn];
  chandle = cp->chandle;  // characteristic handle
  size = cp->size;        // number of bytes      
           
                          // set characteristic handle
  hciread[10] = (unsigned char)(chandle & 0xFF);
  hciread[11] = (unsigned char)((chandle >> 8) & 0xFF);
  
  for(n = 0 ; n < size ; ++n)
    data[n] = 0;   // in case read fails
         
  if(sendcmd(hciread,12) == 0)   
    return(0);
    
  if(readreply(gpar.replyflag,2,data,2) != 3)   // 2 replies    
    {
    printf("Failed to read %s %s\n",device[ndevice].name,cp->name);   
    return(0);   
    }

  printf("Read %s %s =",device[ndevice].name,cp->name);    
  for(n = size-1 ; n >= 0 ; --n)  // print hi byte first
    printf(" %02X",data[n]);
  printf("\n");
 
  return(1);
  }




  
/*************** SEND COMMAND *********
cmd = string to send
len = number of bytes to send
**************************************/

int sendcmd(unsigned char *cmd,int len)
  {
  int nwrit,ntogo;
  unsigned char *s;
  time_t tim0;
   
     
  s = cmd;  
  
  if(gpar.printflag != 0)
    {
    printf("< CMD");
    if(cmd[0] == 2)
      printf(": Opcode %02X\n",cmd[9]);
    else if(cmd[0] == 1)
      printf(": OGF=%02X OCF=%02X\n",(cmd[2] >> 2) & 0x3F,((cmd[2] << 8) & 0x300) + cmd[1]);
    hexdump(s,len); 
    }
    
  ntogo = len;

  tim0 = time(NULL);
  do
    {
    nwrit = write(gpar.hci,s,ntogo);
    if(nwrit > 0)
      {
      ntogo -= nwrit;
      s += nwrit;
      }  
    if(time(NULL) - tim0 > 3)
      {
      printf("Send CMD timeout\n");
      return(0);
      }
    }
  while(ntogo != 0);

  return(1);
  }


/************** READ REPLY ***********************
replyflag = 0  wait for expected number of replies
            1  wait for time out to see all replies when number unknown 
numrep = expected number of replies for replyflag=0 

data = destination for 2 byte device handle from connect reply (readflag=1)
       destination for characteristic value if read (readflag=2)
       NULL if readflag=0     
       
readflag = 0   nothing to read from replies
           1   read device handle from connect reply
           2   read characteristic from read reply 
           3   read scan LE advertising report reply
           4   info request reply                    
return 1=OK   2=connect OK, got device handle in data 
       3=got characteristic value in data
       0=time out when waiting for reply count
****************************/

int readreply(int replyflag,int numrep,unsigned char *data,int readflag)
  {
  unsigned char b0,buf[512];   
  int len,blen,wantlen,retval;
  int n,k,n0;
  time_t tim0;

        
  tim0 = time(NULL);
  n = 0;   // number of reply
   
  blen = 0;         // existing buffer length
  wantlen = 8192;   // expected messaage length - new message flag 
  retval = 1;       // non-connect OK return   
   
  do   
    {
    // next message may loop with data in buf
    do       // wait for complete message
      { 
      if(blen != 0 && wantlen == 8192)   // find expected messaage length
        {
        b0 = buf[0];
        if(!(b0==1 || b0==2 || b0==4))
          {   
          printf("Unknown packet type\n");
          // clear reads and exit
          do
            {
            len = read(gpar.hci,buf,sizeof(buf));
            }
          while(time(NULL) - tim0 <= 3);
          return(0);
          }
        if(b0 == 1 && blen > 3)
          wantlen = buf[3] + 4;
        else if(b0 == 2 && blen > 4)
          wantlen = buf[3] + (buf[4] << 8) + 5;
        else if(b0 == 4 && blen > 2)
          wantlen = buf[2] + 3;
        }
       
      if(blen < wantlen)   // need more        
        {   
        do       // read block of data - may be less than or more than one line
          {
          len = read(gpar.hci,&buf[blen],sizeof(buf)-blen);
        
          if(time(NULL) - tim0 > gpar.timout+1)
            {                                   // timed out
            if(replyflag == 0 && retval != 2)   // counting replies and not connecting 
              {
              printf("Timed out waiting for %d replies\n",numrep);
              return(0);       // time out is error
              }
            if(retval == 2)
              printf("Seen %d connect replies - see notes in devconnect()\n",n);
            return(retval);   //  time out exit - OK return
            }
          }
        while(len < 0);
        blen += len;  // new length of buffer
        }                 
      }    
    while(blen < wantlen);
       
               // got a complete message length = wantlen 
               // buffer length blen may be larger
               
    if(readflag == 1 && data != NULL)   // reply from connect - looking for device handle 
      {
      if(buf[0] == 4 && buf[1] == 0x3E && buf[3] == 1 && buf[4] == 0)   // event 3E subevent 1  status 0
        {   // Vol 2 Part E 7.7.65
            // connect OK
        data[0] = buf[5];  // read returned device handle
        data[1] = buf[6];
        retval = 2;       // connect OK return even if missed expected reply count
        }
      }  // end connect


    if(readflag == 3 && data != NULL)   // reply from scan - looking for board address 
      {
      if(buf[0] == 4 && buf[1] == 0x3E && buf[3] == 2)   // event 3E subevent 2 
        {   // Vol 2 Part E 7.7.65.2
        n0 = data[0]*6 + 1;
        for(k = 0 ; k < 6 ; ++k)
          data[n0+k] = buf[k+7];  // read returned board address
        ++data[0];  // count of addresses
        }
      }  // end connect


    if(readflag == 2 && data != NULL)   // reply from read - looking for characteristic value 
      {
      if(buf[0] == 2 && buf[9] == 0x0B)   // read reply 
        {
        if(buf[5] == 1)
          printf("No data returned - characteristic not set in BLE device\n");
        else
          {         
          for(k = 0 ; k < buf[5] - 1 && k < 20 ; ++k)   // number of bytes=buf[5]-1  max 20
            data[k] = buf[10+k];  // read returned bytes
          retval = 3;             // characteristic value in data
          }
        }
      }  // end connect


    if(readflag == 4 && data != NULL)   // reply from info req - looking for characteristics 
      {
      if(buf[0] == 2 && buf[9] == 0x05)   // req info reply 
        {
        data[1] = buf[10];   // 1=2byte 2=16 byte    UUID
        if(buf[10] == 2)
          k = 18;   // 16 byte UUID
        else
          k = 4;    // 2 byte UUID
        n0 = buf[5] + (buf[6] << 8) - 2;  // length of data
        data[0] = n0/k;  // number of handle/uuid pairs         
        for(k = 0 ; k < n0 && k < 126 ; ++k)   // number of bytes=buf[5]-1  max 20
          data[k+2] = buf[11+k];  // read returned bytes
        }
      }  // end connect

      
    ++n;  // reply count  
         
    if(gpar.printflag != 0)
      { 
      b0 = buf[0];
      if(b0 == 4)
        {
        printf("%d > Event: %02X\n",n,buf[1]);
        if(buf[1] == 0x0E && buf[2] == 4 && buf[3] == 1 && buf[6] != 0)
          printf("**** ERROR return **** CMD %02X %02X\n",buf[4],buf[5]);
        }
      else if(b0 == 2)
        printf("%d > ACL Opcode %02X\n",n,buf[9]);
      else
        printf("%d > Unknown\n",n);
      hexdump(buf,wantlen);
      }
    
    if(blen == wantlen)
      {    // have got exact message length
      blen = 0;
      }
    else
      {   // have got part of next message as well
          // wipe last message length wantlen - copy next down
          // starts at buf[wantlen] ends at buf[blen-1]
          // copy to buf[0]             
      for(k = wantlen ; k < blen ; ++k)
        buf[k-wantlen] = buf[k];       
      blen -= wantlen;
      }

    wantlen = 8192;  // new message flag 

    }  // next message
  while(n < numrep || replyflag != 0 || blen != 0);
   
  return(retval);
  } 


/************** OPEN HCI SOCKET ******
channel  0=channel 0 - HCI sends extra commands
         1=user channel - no extra commands
          
return 0=fail
       1= OK and sets gpar.hci= socket handle
*************************************/       

int devsock()
  {
  int dd;
  struct sockaddr_hci sa;
  struct hci_filter flt;

  
         // AF_BLUETOOTH=31
  dd = socket(31, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI);

  if(dd < 0)
    {
    printf("Socket open error\n");
    return(0);
    }

  flt.type_mask = 0x14;  
  flt.event_mask[0] = 0xFFFFFFFF;
  flt.event_mask[1] = 0xFFFFFFFF;
  flt.opcode = 0;
  /** same as ****
  hci_filter_clear(&flt);
  hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
  hci_filter_set_ptype(HCI_ACLDATA_PKT, &flt);
  hci_filter_all_events(&flt);
  ***************/
        
  if(setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0)
    {
    printf("HCI filter setup failed\n");
    close(dd);
    return(0);
    }   


        // same as hciconfig hci0 down
        // must be down to set channel=HCI_CHANNEL_USER   
  if(ioctl(dd,HCIDEVDOWN,0) < 0)   // 0=hci0
    {
    if(errno != EALREADY)
      {
      printf("hci0 not down\n");
      close(dd);
      return(0);
      }
    }
   
  sa.hci_family = 31;   // AF_BLUETOOTH;
  sa.hci_dev = 0;       // hci0
  sa.hci_channel = 1;   // HCI_CHANNEL_USER    
  if(bind(dd,(struct sockaddr *)&sa,sizeof(sa)) < 0)
    {
    printf("Bind failed\n");
    close(dd);
    return(0);
    }
      
  gpar.hci = dd;       
          
  printf("HCI Socket OK\n");
   
  return(1);
  }  


/************** OPEN HCI SOCKET ******
return 0=fail
       1= OK and sets gpar.hci= socket handle
*************************************/       

int devsockx()
  {
  int dd;
  struct sockaddr_hci sa;
  struct hci_filter flt;

         // AF_BLUETOOTH=31
  dd = socket(31, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI);

  if(dd < 0)
    {
    printf("Socket open error\n");
    return(0);
    }
     
          //  same as hciconfig hci0 up  
  if(ioctl(dd,HCIDEVUP,0) < 0)   // 0=hci0
    {
    if(errno != EALREADY)
      {
      printf("hci0 not up\n");
      close(dd);
      return(0);
      }
    }
   
  sa.hci_family = 31;   // AF_BLUETOOTH;
  sa.hci_dev = 0;
  sa.hci_channel = 0;    
  if(bind(dd,(struct sockaddr *)&sa,sizeof(sa)) < 0)
    {
    printf("Bind failed\n");
    close(dd);
    return(0);
    }
      
  flt.type_mask = 0x14;  
  flt.event_mask[0] = 0xFFFFFFFF;
  flt.event_mask[1] = 0xFFFFFFFF;
  flt.opcode = 0;
  /** same as ****
  hci_filter_clear(&flt);
  hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
  hci_filter_set_ptype(HCI_ACLDATA_PKT, &flt);
  hci_filter_all_events(&flt);
  ***************/
        
  if(setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0)
    {
    printf("HCI filter setup failed\n");
    close(dd);
    return(0);
    }   
 
  gpar.hci = dd;    
       
  printf("HCI Socket OK\n");
   
  return(1);
  }  

/******** HEX DUMP - print buf in hex format, length = len ********/

void hexdump(unsigned char *buf, int len)
  {
  int i,i0,n;

  if(len <= 0)
    {
    printf("No data\n");
    return;
    }
     
  i = 0;
  do
    {
    i0 = i;
    n = 0;
    printf("  %04X  ",i0);
    do
      {
      if(n == 8)
        printf("- ");
      printf("%02X ",buf[i]);
      ++n;
      ++i;
      }
    while(n < 16 && i < len);
    printf("\n");
    }
  while(i < len);  
  }

/*************** USER INTERFACE *******************/

void printhelp()
  {
  printf("  h = print this help\n");
  printf("  i = print all device info\n");
  printf("  c = connect a device\n");
  printf("  w = write characteristic\n");
  printf("  r = read characteristic\n");
  printf("  d = disconnect a device\n");
  printf("  q = quit\n");
  } 

int inputdev()
  {
  int n;
     
  for(n = 0 ; n < NUMDEVICES ; ++n)
    printf("  DEVICE %d = %s\n",n,device[n].name);
  do
    {
    printf("Enter device number 0-%d: ",NUMDEVICES-1);
    n = inputint();   
    }
  while(n < 0 || n >= NUMDEVICES);

  return(n);
  }
  
int inputctic(int ndevice)
  {
  int n,nx;
    
  for(n = 0 ; n < NUMCTICS && device[ndevice].ctic[n].size > 0 ; ++n)
    {
    nx = n;
    printf("  CHARACTERISTIC %d = %s\n",n,device[ndevice].ctic[n].name);
    }
  do
    {
    printf("Enter characteristic number 0-%d :",nx);
    n = inputint();
    }
  while(n < 0 || n > nx);
  return(n);
  } 
  
int inputval()
  {
  int n;
  
  do
    {
    printf("Enter value 0-255: ");
    n = inputint();
    }
  while(n < 0 || n > 255);
  return(n);
  }  
  
  
int inputint()
  {
  int n,flag;
  char s[64];
 
  do
    { 
    scanf("%s",s);
    flag = 0;
    for(n = 0 ; s[n] != 0 ; ++n)
      {
      if(s[n] < '0' || s[n] > '9')
        flag = 1;
      }
    if(flag == 0)
      n = atoi(s);
    }
  while(flag != 0);
  return(n);
  } 


petzval
Posts: 36
Joined: Sat Aug 10, 2013 12:15 pm

Re: Android to Pi Bluetooth code

Sat Apr 11, 2020 12:57 pm

Here's matched Pi and Android code to connect devices via Bluetooth.

I have tried connections over a variety of device pairs. Pis will always connect to each other. Androids will usually connect to Pis, but you may have to pair the Pi from the Android first. Sometimes Scan will not work on the Android, and you must use Paired to find active devices instead. Pis only connect to Androids occasionally. In this case it is usually possible connect from the Android and swap roles.
From Pi to Android the statements that fail are:
connect() from the Pi
mmServerSocket.accept() in the Android
Can anyone see why this fails so often?

PI CODE
C code complie with gcc. Instructions at top of listing.

Code: Select all


/*************** BLUETOOTH CLIENT/SERVER **********
Two Pis running this code will connect to each other.
Or a Pi and an Android device running the macthing code.

SET UP REQUIREMENTS

1. Needs the bluetooth development files 
   apt-get install libbluetooth-dev

2. Edit the file  /lib/systemd/dbus-org.bluez.service
   add -C to the ExecStart line:
   ExecStart=/usr/....bluetoothd -C     

BEFORE COMPILE
Set the board address of the remote device that
this code will connect to on the line:
unsigned char badd[] = ......your server address

COMPILE
gcc -lbluetooth btlink.c -o btlink

EVERY LOG ON - must do these two things
1. chmod 777 /var/run/sdp
2. sdptool add sp
       expect Service added message


To connect two devices
1. Start one device as a server with listen
2. In other device, connect to the server as a client
   In this code the board address of the server must be set in badd[]
3. The client can then send strings to the server and receive replies
4. The client/server can swap roles. So if a connection is refused in
   one direction, try in the other direction and swap roles from the client.
   Don't laugh - this often works.
   
***********************************************/

  
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <termios.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/hci.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/ioctl.h>


void printhelp(void);
int sethci(int flag);
int clientsetup(void);
int clientconnect(void);
int clientread(char *s);
int serversetup(void);
int sendstring(char *comd,int termflag,int abtflag);
int receivechars(char *c,int count,int abtflag);
int sendchars(unsigned char *c,int count,int abtflag);
int serverhandle(void);
void closeblue(void);
int getkey(void);
int set_input_mode(void);
void reset_input_mode(void);


int bluesock = 0;  // socket for server setup
int filed = 0;    // file descriptor for client/server
int connectflag = 0;  // 0=not connected 1=client 2=server 

// hci info by sethci
int upflag,pflag,iflag,aflag;

char termchar = '!';   // terminate char for string sent from client
char repchar = '!';    // terminate char for reply sent from server

int blockflag = 1;   // 0=non blocking connect  1=blocking 

   //  will connect to this board address
unsigned char badd[6] = {0xDC,0xA6,0x32,0x04,0xDB,0x56};  // DC:A6:32:04:DB:56 

   // keyboard mode
struct termios saved_attributes;

int main()
  {
  int n,csflag,cancelflag,bn,retval,swapflag;
  char c,fun,funs[8],coms[128],s[128];
  
  funs[0] = 'h';
  funs[1] = 0;
  coms[0] = 0;
  s[0] = 0;

  if(sethci(1) == 0)  
    {
    printf("hciconfig hci0 up/piscan ERROR\n");   // same as command hciconfig hci0 piscan
    return(0);
    }

  printf("\nHave you done these?\n");
  printf("  sdptool add sp\n");
  printf("  chmod 777 /var/run/sdp\n\n");
  
  printhelp();
  
  funs[0] = 0;
  do
    {
    // wait for function char
    printf("> ");
    scanf("%s",funs);
    fun = funs[0];
  
    swapflag = 0;  // client/server swap
    
    if(fun == 'B')
      {
      blockflag = 1-blockflag;
      printf("Block %d\n",blockflag);
      }
  
    if(fun == 'h')
      printhelp();
      

    if(fun == 'c')  
      {  // connect as client 
      if(connectflag != 0)
        printf("Already connected\n");
      else
        {
        retval = clientsetup();
        if(retval == 0)
          printf("Connection refused\n");
        else if(retval == 1)
          printf("Connection uncertain\n");
        else
          printf("Connection OK\n");
        }    
      }
        
    if(fun == 't' || fun == 'r' || fun == 'd' || fun == 'e')
      {  // client functions - send string to server
      if(connectflag != 1)
        printf("Not connected as client\n");
      else
        {
        coms[0] = 0;
        coms[1] = 0;
        if(fun == 't')
          {
          printf("Send ! for OK reply\n");
          printf("Input string to send: ");
          scanf("%s",s);
          n = 0;
          while(s[n] != 0 && n < 127)
            {
            coms[n] = s[n];
            ++n;
            }
          coms[n] = 0;
          } 
        if(fun == 'd')
          {
          coms[0] = 'x';
          printf("Disconnecting client and server\n");
          }
        if(fun == 'e')
          {
          coms[0] = 'X';
          printf("Disconnecting client and server re-listens\n");
          }         
       if(fun == 'r')
          {  
          coms[0] = 'C';       // tell server to become a client - no reply
          printf("Swapping client/server\n");
                               // will become a server - still connected
          swapflag = 1;        // go to serverhandle after C send
          }         
             
        // clear input buffer before send string
        while(read(filed,&c,1) == 1);   
  
        if(sendstring(coms,1,1) != 0 && fun != 'r')  // 1=add termchar 1=time out
          clientread(NULL);
          
        if(fun == 'd' || fun == 'e')
          closeblue();   // disconnect
        }
      }
         
    if(fun == 's' || swapflag == 1)  
      {  // become a server or is swapping from client to server
      if(connectflag != 0 && swapflag == 0)
        printf("Already connected\n");
      else
        {
        do
          {
          set_input_mode();
          if(swapflag == 0)  // wait for connect
            retval = serversetup(); 
          else
            {  // already connected
            printf("Now a server\n");
            swapflag = 0;
            connectflag = 2;  // server
            retval = 1;       // swapping - already connected
            }
          if(retval != 0)
            retval = serverhandle();
          reset_input_mode();
          }
        while(retval == 2);  // re-listen after disconnect
        // if retval = 3 now a client still connected
        // else now disconnected
        }
      }

    }  
  while(fun != 'q');    
  
  closeblue();
  //  sethci(0);  // hciconfig hci0 down
  return(0);
  }
  
void printhelp()
  {
  printf("  c Connect as client to %02X:%02X:%02X:%02X:%02X:%02X\n",badd[0],badd[1],badd[2],badd[3],badd[4],badd[5]);
  printf("  s Become a server - listen for client to connect\n");
  printf("  t Send a string to server\n");
  printf("  r Swap roles to become server\n");
  printf("  d Disconnect and tell server to close\n");
  printf("  e Disconnect and tell server to re-listen\n");
  printf("  h Print this help\n");
  printf("  q Quit\n");
  }

 
/***************** SERVER HANDLE *************************
filed = socket for read/write to client
*********************************************************/

int serverhandle()
  {
  int n,key,k;
  char c,firstc;
  char buf[128];

  printf("Waiting for client to send string. Key x to stop\n");
  
  do
    {  // receive string from client termchar or x key press terminates
    n = 0;
    buf[0] = '\0';
    do
      {
      c = 0;
      key = receivechars(&c,1,0);  // key press exit
      if(key == 0)  // got c from client
        {
        buf[n] = (char)c;
        if(n < 126)
          ++n;
        }     
      }
    while(c != termchar && key != 'x');
    buf[n] = '\0';
    
    if(key == 'x')  // x key press forces disconnect - same as x from client
      {
      printf("Key x abort\n"); 
      buf[0] = 'x';      
      buf[1] = termchar;
      buf[2] = 0;
      }
     
    if(buf[0] == termchar || (buf[0] != 0 && buf[1] == termchar))
      firstc = buf[0];  // is a single char - may be a command from client
    else
      firstc = 0;       // more than one char - not a command  
    
    for(k = 0 ; buf[k] != 0 ; ++k)
      {   // strip non-ascii chars for print
      if(buf[k] < 32 || buf[k] > 126)
        buf[k] = '.';
      }

    if(key != 'x')  
      printf("Received: %s\n",buf);

    // check if received string is a known command 
    // and send reply to client
          
    if(firstc == termchar)
      {
      printf("OK request\n");
      sendstring("OK",2,1);  // REPLY 2=add repchar 1=time out  
      }
    else if(firstc == 'x')
      {
      printf("Disconnect\n");
      sendstring("Server disconnecting",2,1);
      closeblue();
      }
    else if(firstc == 'X')
      {
      printf("Disconnect and re-listen\n");
      sendstring("Server disconnecting and re-listening",2,1);
      closeblue();
      }
    else if(firstc == 'C')
      {
      printf("Now a client\n");
      // no reply
      connectflag = 1;
      }
    else
      {
      printf("No action\n");
      sendstring("Unknown command - no action",2,1);    
      }
    }  // loop to wait for another string
  while(firstc != 'x' && firstc != 'X' && firstc != 'C');
  
  if(firstc == 'C')
    return(3);   // swap to client
  if(firstc == 'x')
    return(1);   // no re-listen
  return(2);     // re-listen
  }
 

/********************************** SERVER **********
client = file descriptor for read/write to client
****************************************************/ 

int serversetup()
  {
  struct sockaddr_rc loc_addr,rem_addr;
  socklen_t opt = sizeof(rem_addr);
  int key;
  int retval,sockflags,cliflags;

    // allocate a socket
  bluesock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    // create a socket     SOCK_STREAM | SOCK_NONBLOCK to unblock here
  if(bluesock < 0)
    {
    printf("Bluetooth socket allocate fail\n");
    return(0);
    }

  // bluesock openeed - close on return
  // bind socket to port 1 of the first available 
  // local bluetooth adapter
  
  loc_addr.rc_family = AF_BLUETOOTH; 
  loc_addr.rc_bdaddr = *BDADDR_ANY;
  loc_addr.rc_channel = (uint8_t) 1;
  retval =  bind(bluesock, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
  if(retval != 0)
    {
    printf("Bind error\n");
    closeblue();
    return(0);
    }
     
  printf("Listening for client to connect. Key x to stop\n");

    // put socket into listening mode
  retval = listen(bluesock,50);
  if(retval != 0)
    {
    printf("Listen error\n");
    perror("Listen ");
    closeblue();
    return(0);
    }
     
  sockflags = fcntl(bluesock,F_GETFL);
  if(sockflags < 0)
    perror("Read blue flags ");
  else if(fcntl(bluesock,F_SETFL,sockflags | O_NONBLOCK) < 0)
    perror("Set blue flags ");  // read(client) non blocking

       // accept one connection from client
  do
    {
    filed = accept(bluesock, (struct sockaddr *)&rem_addr, &opt);
    key = getkey();
    }
  while(key != 'x' && filed <= 0);
  if(key == 'x')
    {
    printf("Key press x stop\n");
    closeblue();
    return(0);    
    }

  printf("Accepted connection\n");

      // unblock socket 
  cliflags = fcntl(filed,F_GETFL);
  if(cliflags < 0)
    perror("Read client flags ");
  else if(fcntl(filed,F_SETFL,cliflags | O_NONBLOCK) < 0)
    perror("Set client flags ");  // read(client) non blocking

  connectflag = 2;

        // check connection by sending OK
  sendstring("OK",2,1);  // REPLY 2=add repchar 1=time out  
  
  return(1);  // OK
  }

/************ CLOSE **********/


void closeblue()
  {
  if(bluesock > 0)
    close(bluesock);
  bluesock = 0;
  if(filed > 0)
    close(filed);
  filed = 0;
  connectflag = 0;
  printf("Disconnected\n");
  }  

    
/************* SETUP BLUETOOTH ************
flag = 0  close
       1  open
*********************************************/
         
int sethci(int flag)
  {
  int n,hcisock,retval;
  struct hci_dev_req dr;
  struct hci_dev_info di;
             
  upflag = 0;
  pflag = 0;
  iflag = 0;       
         
  hcisock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
  if(hcisock < 0)
    {
    perror("Socket ");
    return(0);
    }
    
  retval = 1;
  if(flag == 0)
    {
    if(ioctl(hcisock,HCIDEVDOWN,0) < 0)   // 0=hci0
      {
      perror("Down ");
      retval = 0;
      }
    }
  else
    {
    if(ioctl(hcisock,HCIDEVUP,0) < 0)   // 0=hci0
      {
      if(errno != EALREADY)
        {
        perror("Up ");
        retval = 0;
        }
      }
             
    if(retval != 0)
      {
      dr.dev_id = 0;  // hci0
      dr.dev_opt = SCAN_PAGE | SCAN_INQUIRY;
      if(ioctl(hcisock,HCISETSCAN,(unsigned long)&dr) < 0)
        {
        perror("Set scan ");
        retval = 0;
        }
      else
        {
        di.dev_id = 0;
        if(ioctl(hcisock,HCIGETDEVINFO,(void*)&di) < 0)
          {
          perror("Info ");
          retval = 0;
          }
        else
          {
          upflag = (di.flags >> HCI_UP) & 1;
          pflag =  (di.flags >> HCI_PSCAN) & 1;
          iflag = (di.flags >> HCI_ISCAN) & 1;
          aflag = (di.flags >> HCI_AUTH) & 1;
          // di.name = hci0
          }
        }
      }
    }
  
     
  close(hcisock);
  return(retval);
  }


/***************************** CLIENT SETUP ****************/

int clientsetup()
  {
  int sockflags,retval;
  char *c; 
      
  printf("Connecting...\n");  

    // allocate a socket
  
  if(blockflag == 0)
    filed = socket(AF_BLUETOOTH, SOCK_STREAM | SOCK_NONBLOCK, BTPROTO_RFCOMM);
  else
    filed = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
 
  if(filed < 0)
    {
    printf("Bluetooth socket allocate fail\n");
    filed = 0;
    return(0);
    }  

  if(clientconnect() == 0)
     {
     closeblue();
     return(0);
     }

  if(blockflag != 0)
    {      
    // unblock socket  
    sockflags = fcntl(filed,F_GETFL);
    if(sockflags < 0)
      {
      perror("Read blue flags ");
      return(0);
      }
    if(fcntl(filed,F_SETFL,sockflags | O_NONBLOCK) < 0)
      {
      perror("Set blue flags ");  
      return(0);
      }
    }

     // expect server to send OK on connection  
  if(clientread(NULL) == 1)
    return(2);   // OK seen reply
  printf("Timed out waiting for server OK\n");
  return(1);  // uncertain
  }
 
/********************** CLIENT CONECT **********************
connect to badd[]
filed must be vaiid socket
return  1 = OK
        0 = fail
***************************************************/ 
  
int clientconnect()
  {
  int n,k,status,retval,serr,len;
  fd_set wrset;
  struct timeval tv;
  char *c;
  struct sockaddr_rc address;  // for client connect to selected device

     // zero address
  c = (char*)&address;
  for(n= 0 ; n < sizeof(struct sockaddr_rc) ; ++n)
    c[n] = 0;  
  
  address.rc_family = AF_BLUETOOTH;
  address.rc_channel = (uint8_t)1;  
  c = (char *)&address.rc_bdaddr;  // board address size = 6 bytes
  for(n = 0 ; n < 6 ; ++n)
    c[n] = badd[5-n];              // in reverse order - lo first
  
   // connect to server, through socket filed
  
  status = connect(filed, (struct sockaddr *)&address, sizeof(struct sockaddr_rc));
  // if status != 0  failed and errno=111 connection refused
 
  if(blockflag == 0 && status != 0 && errno == EINPROGRESS)
    {     // non blocking, connect still in progress - wait 10s for connect
    printf("Waiting...\n");  
    FD_ZERO(&wrset);
    FD_SET(filed,&wrset);
    tv.tv_sec = 10;
    tv.tv_usec = 0;
    retval = select(filed+1,0,&wrset,0,&tv);  // time out in 10s
    serr = 1;     // assume error
    if(retval != 0)  // connect has finished
      {   // check if connected by reading SO_ERROR to serr
      len = sizeof(int);
      if(getsockopt(filed,SOL_SOCKET,SO_ERROR,&serr,&len) != 0)
        serr = 1;  // getsockopt error
      // serr = 0    OK     
      // serr = 111  connection refused
      }
      
    if(serr == 0)
      status = 0;
    }
  
           
  if(status == 0)
    {
    connectflag = 1;
    return(1);
    }
    
  return(0);
  }


/*********** CLIENT READ ******************
if s = NULL - print
else no print - return in s
read from server until receive terminate repchar
or times out
********************************/

int clientread(char *s)
  {
  char c,cp;
  int n,key,sockflags;
  
  n = 0;
  if(s == NULL)  
    printf("Reply: ");
  do
    {
    c = 0;
    key = receivechars(&c,1,1);  // time out exit -1
    if(key == 0)
      {
      cp = c;      // char to print
      if(cp < 32 || cp > 126)
        cp = '.';  // non-ascii
      if(s == NULL)
        printf("%c",cp);
      else if(n < 63)
        {
        s[n] = c;
        ++n;
        }
      }
    }
  while(key != -1 && c != repchar);
   
  if(s == NULL)    
    printf("\n");
  else
    s[n] = '\0';
    
  if(c == repchar)  
    return(1);
    
  printf("Timed out waiting for reply\n");  
  return(0);
  }


/************** SEND STRING **************************
termflag  0 = send comd as is
          1 = force termchar terminate
          2 = force repchar terminate
abtflag   0 = abort on x key press
          1 = abort on time out 
************************************************/

int sendstring(char *comd,int termflag,int abtflag)
  {
  int n,clen,key,addflag;
  unsigned char addc,addcp;
  
  if(termflag == 1)
    addc = termchar;
  else if(termflag == 2)
    addc = repchar;
  else
    addc = ' ';
       
  clen = strlen(comd);  // assumes comd is 0 terminated ascii string - not binary data
  addflag = 0;
  addcp = addc;        // char to print
  if(termflag != 0)
    {
    if(clen == 0)
      addflag = 1;
    else if(comd[clen-1] != addc)
      addflag = 1;
    else if(comd[clen-1] == addc)
      addcp = ' ';
    }
  
  if(addcp < 32 || addcp > 126)
    addcp = '.';  // terminate char not ascii
    
  if(clen == 0 && addflag == 0)
    {
    printf("Empty string\n");
    return(0);
    }
    
  printf("Sending: %s%c\n",comd,addcp);
  
  key = 0;  
  if(clen > 0)
     key = sendchars(comd,clen,abtflag);   // 0=sent OK   -1 time out  key press
  if(key == 0 && addflag != 0)
     key = sendchars(&addc,1,abtflag);   // add terminate char if necessary     
     
  if(key == 0)
    return(1);  // OK
    
  if(abtflag == 0)
    printf("Key press abort\n");
  else
    printf("Timed out trying to send\n");
  
  return(0); 
  }


/*************** SEND CHARS ************
send count bytes from c[]
return 0 = have sent OK
      -1 = time out
      or key code 
abtflag  0 = key press and return key 
         1 = time out return -1
can send binary data 
************************************/

int sendchars(unsigned char *c,int count,int abtflag)
  {
  int key,ntogo,nwrit;
  unsigned char *cx;
  time_t tim0;
  
  cx = c;
  ntogo = count;
  key = 0;
  
  tim0 = time(NULL);
  do
    {    
    nwrit = write(filed,cx,ntogo);
    if(nwrit > 0)
      {
      ntogo -= nwrit;
      cx += nwrit;
      }  
    if(abtflag == 0)
      key = getkey();
    }
  while(!(ntogo == 0 || (abtflag == 1 && time(NULL)-tim0 > 5) || (abtflag == 0 && key != 0) ) );
  
  if(ntogo == 0)
    return(0); // OK
 
  if(abtflag == 0)
    return(key);  // key press
 
  return(-1);  // time out  
  }  



/*************** RECEIVE CHAR ************
abtflag 0=abort on key press - MUST set_input_mode 
        1=abort on time out 5s
return 0=have read chars to c
      -1=time out
       else key press returns key code 
***********************************/

int receivechars(char *c,int count,int abtflag)
  {
  int key,ntogo,nread;
  unsigned char *cx;
  time_t tim0;
  
  cx = c;
  ntogo = count;
  key = 0;
  
  tim0 = time(NULL);  
  do
    {    
    nread = read(filed,cx,ntogo);     // waits for input unless O_NONBLOCK
    if(nread > 0)
      {
      ntogo -= nread;
      cx += nread;
      }
    if(abtflag == 0)
      key = getkey();
    }
  while(!(ntogo == 0 || (abtflag == 0 && key != 0) || (abtflag == 1 && time(NULL)-tim0 > 5) ) );
  
  if(ntogo == 0)
    return(0);
   
  if(abtflag == 0)
    return(key);
 
  return(-1);    
  }  

int getkey()
  {
  char c;
  if(read(STDIN_FILENO,&c,1) == 1)
    return((int)c);
  return(0);
  }
      
  
/************ SET INPUT MODE *************
so getkey() returns key immediately on a single key press 
without requiring enter press 
***************************************/
  
int set_input_mode()
  {
  struct termios tattr;
  int c,n,getout;
   
  /* Save the terminal attributes so we can restore them later. */
  tcgetattr(STDIN_FILENO, &saved_attributes);
  /* Set the terminal modes. */
  tcgetattr(STDIN_FILENO,&tattr);
  tattr.c_lflag &= ~(ICANON|ECHO|ISIG|ECHONL|IEXTEN);                   
  tattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
  tattr.c_oflag = OPOST|ONLCR;   
  tattr.c_cflag &= ~(CSIZE | PARENB);
  tattr.c_cflag |= CS8;
  tattr.c_cc[VMIN] = 0;
  tattr.c_cc[VTIME] = 0;
  tcsetattr(STDIN_FILENO,TCSAFLUSH,&tattr);
 
  return(0);
  }
  
void reset_input_mode()
  {
  tcsetattr(STDIN_FILENO,TCSANOW,&saved_attributes); 
  }


ANDROID CODE
In Android Studio: New Project/Empty Activity
This Empty Activity creates three files: AndoridManifest.xml/activity_main.xml/MainActivity.java
Edit them with the following three listings.

ANDROID FILE 1
app/manifests/AndoridManifest.xml
Leave the top lines alone:
<?xml...
<manifest...
package=....>

Delete the rest and replace with the following:
AndroidManifest.xml

Code: Select all



  <uses-permission android:name="android.permission.BLUETOOTH" />
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.WAKE_LOCK" />

  <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <activity android:name=".MainActivity"
      android:configChanges="orientation|screenSize|screenLayout">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>

</manifest>

ANDROID FILE 2
app/res/layout/activity_main.xml

Leave the top lines alone:
<?xml...
<android...
....
tools:context.....MainActivity.>

Leave the bottom line alone:
</android:support......ConstraintLayout>

Delete everything between:
<TextView "Hello World...
.......
......./>

Replace with the following:
activity_main.xml

Code: Select all


  <EditText
    android:id="@+id/editText"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:inputType="text"
    android:text="Petzval                                                        "
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

  <Button
    android:id="@+id/b0"
    android:visibility="invisible"
    android:onClick="butClick0"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginTop="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"


    android:text="But0"
    app:layout_constraintTop_toBottomOf="@+id/editText"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent" />

  <Spinner
    android:id="@+id/bs0"
    android:visibility="invisible"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textSize="12sp"
    android:background="#FFD0D0"
    app:layout_constraintTop_toBottomOf="@+id/editText"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent" />



  <Button
    android:id="@+id/b1"
    android:visibility="invisible"
    android:onClick="butClick1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginTop="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:text="But1"
    app:layout_constraintTop_toBottomOf="@+id/editText"
    app:layout_constraintBaseline_toBaselineOf="@+id/b0"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toEndOf="@+id/b0" />

  <Spinner
    android:id="@+id/bs1"
    android:visibility="invisible"


    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textSize="12sp"
    android:background="#FFD0D0"
    app:layout_constraintTop_toBottomOf="@+id/editText"
    app:layout_constraintBaseline_toBaselineOf="@+id/b0"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toEndOf="@+id/b0" />




  <Button
    android:id="@+id/b2"
    android:visibility="invisible"
    android:onClick="butClick2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginTop="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"


    android:text="But2"
    app:layout_constraintTop_toBottomOf="@+id/editText"
    app:layout_constraintBaseline_toBaselineOf="@+id/b0"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toEndOf="@+id/b1" />

  <Spinner
    android:id="@+id/bs2"
    android:visibility="invisible"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"


    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textSize="12sp"
    android:background="#FFD0D0"
    app:layout_constraintTop_toBottomOf="@+id/editText"
    app:layout_constraintBaseline_toBaselineOf="@+id/b0"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toEndOf="@+id/bs1" />


  <Button
    android:id="@+id/b3"
    android:visibility="invisible"
    android:onClick="butClick3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginTop="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:text="But3"
    app:layout_constraintTop_toBottomOf="@+id/editText"
    app:layout_constraintBaseline_toBaselineOf="@+id/b1"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toEndOf="@+id/b2" />

  <Spinner
    android:id="@+id/bs3"
    android:visibility="invisible"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"


    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textSize="12sp"
    android:background="#FFD0D0"
    app:layout_constraintTop_toBottomOf="@+id/editText"
    app:layout_constraintBaseline_toBaselineOf="@+id/b1"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toEndOf="@+id/bs2" />










  <ScrollView
    android:id="@+id/scrollView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginBottom="0dp"
    android:layout_marginTop="0dp"
    android:layout_marginEnd="0dp"
    android:layout_marginStart="0dp"
    app:layout_constraintTop_toBottomOf="@+id/b1"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent">


    <TextView
      android:id="@+id/textView"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"

      android:layout_marginBottom="8dp"
      android:layout_marginTop="8dp"
      android:layout_marginEnd="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:paddingLeft="8dp"
      android:paddingStart="8dp"
      android:textColor="#000000"


      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent" />

  </ScrollView>


  <TextView
    android:id="@+id/pr0"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textColor="#000000"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

  <EditText
    android:id="@+id/tx0"
    android:visibility="invisible"
    android:text="Petzval                                                        "
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:inputType="text"
    app:layout_constraintBaseline_toBaselineOf="@id/pr0"
    app:layout_constraintStart_toEndOf="@id/pr0"
    app:layout_constraintEnd_toEndOf="parent"  />


  <Spinner
    android:id="@+id/st0"
    android:visibility="invisible"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"
    android:textSize="12sp"
    android:background="#DDDDDD"
    app:layout_constraintBaseline_toBaselineOf="@id/pr0"
    app:layout_constraintStart_toEndOf="@id/pr0"
    />





  <TextView
    android:id="@+id/pr1"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"


    android:textColor="#000000"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/pr0" />

  <EditText
    android:id="@+id/tx1"
    android:visibility="invisible"
    android:text="Petzval                                                        "
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:inputType="number"
    app:layout_constraintBaseline_toBaselineOf="@id/pr1"
    app:layout_constraintStart_toEndOf="@id/pr1"
    app:layout_constraintEnd_toEndOf="parent"  />

  <Spinner
    android:id="@+id/st1"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"
    android:textSize="12sp"
    android:background="#DDDDDD"
    app:layout_constraintBaseline_toBaselineOf="@id/pr1"
    app:layout_constraintStart_toEndOf="@id/pr1"
    />

  <TextView
    android:id="@+id/pr2"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textColor="#000000"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/pr1" />

  <EditText
    android:id="@+id/tx2"
    android:visibility="invisible"
    android:text="Petzval                                                        "
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:inputType="text"
    app:layout_constraintBaseline_toBaselineOf="@id/pr2"
    app:layout_constraintStart_toEndOf="@id/pr2"
    app:layout_constraintEnd_toEndOf="parent"  />


  <Spinner
    android:id="@+id/st2"
    android:visibility="invisible"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textSize="12sp"
    android:background="#DDDDDD"
    app:layout_constraintBaseline_toBaselineOf="@id/pr2"
    app:layout_constraintStart_toEndOf="@id/pr2"
    />

  <TextView
    android:id="@+id/pr3"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textColor="#000000"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/pr2" />


  <EditText
    android:id="@+id/tx3"
    android:visibility="invisible"
    android:text="Petzval                                                        "
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:inputType="text"
    app:layout_constraintBaseline_toBaselineOf="@id/pr3"
    app:layout_constraintStart_toEndOf="@id/pr3"
    app:layout_constraintEnd_toEndOf="parent"  />


  <Spinner
    android:id="@+id/st3"
    android:visibility="invisible"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"
    android:textSize="12sp"
    android:background="#DDDDDD"
    app:layout_constraintBaseline_toBaselineOf="@id/pr3"
    app:layout_constraintStart_toEndOf="@id/pr3"
    />



  <TextView
    android:id="@+id/pr4"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"


    android:textColor="#000000"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/pr3" />

  <EditText
    android:id="@+id/tx4"
    android:visibility="invisible"
    android:text="Petzval                                                        "
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:inputType="text"
    app:layout_constraintBaseline_toBaselineOf="@id/pr4"
    app:layout_constraintStart_toEndOf="@id/pr4"
    app:layout_constraintEnd_toEndOf="parent"  />


  <Spinner
    android:id="@+id/st4"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"
    android:textSize="12sp"
    android:background="#DDDDDD"
    app:layout_constraintBaseline_toBaselineOf="@id/pr4"
    app:layout_constraintStart_toEndOf="@id/pr4"
    />

  <TextView
    android:id="@+id/pr5"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textColor="#000000"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/pr4" />

  <EditText
    android:id="@+id/tx5"
    android:visibility="invisible"
    android:text="Petzval                                                        "
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:inputType="text"
    app:layout_constraintBaseline_toBaselineOf="@id/pr5"
    app:layout_constraintStart_toEndOf="@id/pr5"
    app:layout_constraintEnd_toEndOf="parent"  />


  <Spinner
    android:id="@+id/st5"
    android:visibility="invisible"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textSize="12sp"
    android:background="#DDDDDD"
    app:layout_constraintBaseline_toBaselineOf="@id/pr5"
    app:layout_constraintStart_toEndOf="@id/pr5"
    />

  <TextView
    android:id="@+id/pr6"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textColor="#000000"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/pr5" />


  <EditText
    android:id="@+id/tx6"
    android:visibility="invisible"
    android:text="Petzval                                                        "
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:inputType="text"
    app:layout_constraintBaseline_toBaselineOf="@id/pr6"
    app:layout_constraintStart_toEndOf="@id/pr6"
    app:layout_constraintEnd_toEndOf="parent"  />


  <Spinner
    android:id="@+id/st6"
    android:visibility="invisible"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textSize="12sp"
    android:background="#DDDDDD"
    app:layout_constraintBaseline_toBaselineOf="@id/pr6"
    app:layout_constraintStart_toEndOf="@id/pr6"
    />

  <TextView
    android:id="@+id/pr7"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textColor="#000000"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/pr6" />



  <EditText
    android:id="@+id/tx7"
    android:visibility="invisible"
    android:text="Petzval                                                        "
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:inputType="text"
    app:layout_constraintBaseline_toBaselineOf="@id/pr7"
    app:layout_constraintStart_toEndOf="@id/pr7"
    app:layout_constraintEnd_toEndOf="parent"  />


  <Spinner
    android:id="@+id/st7"
    android:visibility="invisible"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textSize="12sp"
    android:background="#DDDDDD"
    app:layout_constraintBaseline_toBaselineOf="@id/pr7"
    app:layout_constraintStart_toEndOf="@id/pr7"
    />

  <TextView
    android:id="@+id/pr8"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textColor="#000000"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/pr7" />


  <EditText
    android:id="@+id/tx8"
    android:visibility="invisible"
    android:text="Petzval                                                        "
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    android:inputType="text"
    app:layout_constraintBaseline_toBaselineOf="@id/pr8"
    app:layout_constraintStart_toEndOf="@id/pr8"
    app:layout_constraintEnd_toEndOf="parent"  />


  <Spinner
    android:id="@+id/st8"
    android:visibility="invisible"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textSize="12sp"
    android:background="#DDDDDD"
    app:layout_constraintBaseline_toBaselineOf="@id/pr8"
    app:layout_constraintStart_toEndOf="@id/pr8"
    />

  <TextView
    android:id="@+id/pr9"
    android:visibility="invisible"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"

    android:paddingTop="8dp"
    android:paddingLeft="8dp"
    android:paddingStart="8dp"
    android:paddingRight="8dp"
    android:paddingEnd="8dp"
    android:paddingBottom="8dp"

    android:textColor="#000000"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/pr8" />






  <Button
    android:id="@+id/ok"
    android:visibility="invisible"
    android:onClick="clickOK"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginTop="8dp"
    android:layout_marginBottom="8dp"
    android:text="OK"
    android:textSize="24sp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent" />

  <Button
    android:id="@+id/cancel"
    android:visibility="invisible"
    android:onClick="clickCancel"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginEnd="8dp"
    android:layout_marginTop="8dp"
    android:layout_marginBottom="8dp"
    android:text="Cancel"
    android:textSize="24sp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toEndOf="@+id/ok" />


ANDROID FILE 3
app/java/com../MainActivity.java
Leave the top line alone:
package com.example.....

Delete everything else
Replace with the following:
MainActivity.java

I've had to put this code in a separate posting (Androd to Pi Bluetooth code - Continued) because of size limits.

petzval
Posts: 36
Joined: Sat Aug 10, 2013 12:15 pm

Re: Android to Pi Bluetooth code - Continued

Sat Apr 11, 2020 12:57 pm

Here is the Android FILE 3 from the previous post.

MainActivity.java

Code: Select all



    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothServerSocket;
    import android.bluetooth.BluetoothSocket;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    
    import android.content.IntentFilter;
    import android.os.Handler;
    import android.os.Message;
    import android.os.ParcelUuid;
    import android.support.v4.app.DialogFragment;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.Button;
    import android.widget.ScrollView;
    import android.widget.TextView;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.WindowManager.LayoutParams;
    import android.widget.AdapterView;
    import android.widget.ArrayAdapter;
    import android.widget.Spinner;

    import android.app.AlertDialog;
    import android.app.Dialog;
    import android.content.DialogInterface;
    
    import android.util.Log;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.Set;
    import java.util.UUID;
    

public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener,
    View.OnFocusChangeListener
  {
  
  /************ PROGRAMMED strings via String button when a client **************/
  
  static final String alsel1 = "String,One,Two,Three";       // command names
  static final String[] alcoms1 = {"","one","two","three"};
           // puts these strings in text box at top of screen
  
  
  /************* INTERFACE VARIABLES **************/
  public TextView output;
  // int lineheight;  // for uiprint scroll
  public ScrollView scrview;
  public TextView input;
  
    // text size control
  static float textsz;    // of buttons, to fit across screen width
  static float iotextsz;  // of text input at top of screen and screen prints
  static int textszn;
  static int textsznx;

    // set up blank entries for dialog screens
    // numid = number of entries in textid/selectid/selectcount
    //         promptid must have one more
    // must have entries in activity_main.xml
  static final int numid = 8;
  
  static final int[] promptid = {R.id.pr0,R.id.pr1,R.id.pr2,R.id.pr3,R.id.pr4,
      R.id.pr5,R.id.pr6,R.id.pr7,R.id.pr8};
  static final int[] textid = {R.id.tx0,R.id.tx1,R.id.tx2,R.id.tx3,R.id.tx4,
      R.id.tx5,R.id.tx6,R.id.tx7};
  static final int[] selectid = {R.id.st0,R.id.st1,R.id.st2,R.id.st3,R.id.st4,
      R.id.st5,R.id.st6,R.id.st7};
  static final int[] selectcount = {0,0,0,0,0,0,0,0};
  
  // define buttons at top of screen
  // see INTERFACE GUIDE just below for details
  // numbut = number of buttons
  //        = number of entries in butid/butselid/butflag/buts/butval
  //          must have entries in activity_main.xml in whichh
  //          must set constraintEndtoEndOf of active buttons/spinners
  static final int numbut = 4;
  
  static final int[] butid = {R.id.b0,R.id.b1,R.id.b2,R.id.b3};
  static final int[] butselid =  {R.id.bs0,R.id.bs1,R.id.bs2,R.id.bs3};
  static int[] butflag = {1,2,2,2};  // button,spinner,spinner,spinner
  static int[] butval = {0,0,0,0};
      // button and spinner/drop down text
  static final String[] buts = {"Send",
     "Server,Listen,Stop listen,Force client,Force disct,Settings",
     "Client,Scan,Paired,Connect,Swap server,Abort,Disconnect,Dis/Serv lis",
     alsel1  };
    
  static int dialogline,dialogflag,fromline,dialogindex;

  static Handler hand;


/****  INTERFACE GUIDE *************************
 
 How to define the buttons at the top of the screen and what they do.
 How to design and call dialog screens.
 
 An example:
 
 button 0 = button   calls despatch with butval[0]=1 when clicked
 button 1 = spinner  calls despatch with butval[1]=1 when One clicked
                                         butval[1]=2 when Two clicked
                                         butval[1]=3 when Three clicked
 button 2 = button   calls despatch with butval[2]=1 when clicked
 button 3 = invisible
 
 static int[] butflag = {1,2,1,0};  // button,spinner,button,invisible
 static final String[] buts = {"But0","Select,One,Two,Three","But2","None"};
 
 in activity_main.xml set button/spinner constraintStart spacing controls
 
       button 1 spinner (bs1) to right of button 0 (b0)
 <Spinner
 android:id="@+id/bs1"
 app:layout_constraintStart_toEndOf="@+id/b0"
 
       button 2 button (b2) to right of button 1 spinner (bs1)
 <Button
 android:id="@+id/b2"
 app:layout_constraintStart_toEndOf="@+id/bs1"
 
 
 public void despatch(int dialogret)
 {
 if(butval[0] != 0)
 {  // press button 0
 }
 else if(butval[1] == 2)
 {  // select button 1 = Two
 dialogset(0);  // calls dialog() with dialogindex=0 and returns from this function
 // on OK input screen calls despatch with dialogret=1 dialogindex=0
 }
 else if(dialogret != 0)
 {
 if(dialogindex == 0)
 {
 // clicked OK on dialogindex=0 input screen
 }
 }
 }
 
 public void dialog()
 {
 if(dialogindex == 0)
 {
 myprint("First line");
 nrat = inputn("Second line", nrat);
 if(nrat > 100)
 inerror("Out of range");
 nsel = selectmem("Third line", "One,Two,Three,Four", nsel);   // Strings
 nstr = inputs("Enter string");  // int nstr = index for getdialogs(nstr,0);
 }
 }
 
 **************/
  
  
  
  /************** END INTERFACE VARIABLES ***********/
  
  /********** BLUETOOTH VARIABLES *************/
  BluetoothAdapter mBluetoothAdapter;
  BluetoothDevice bluedev;
  BluetoothDevice pairdev[];
  static int pdsel,pdselx;  // pairdev index and selected pdn
  BluetoothSocket mmSocket;
  InputStream instream = null;
  OutputStream outstream = null;
  
  AcceptThread serv;
  static int acceptactive = 0;  // serv active
  ConnectThread client;
  static int connectactive = 0;  // client active
  ReadThread inread;
  static int readactive = 0;  // inread active
  WriteThread outwrite;
  static int writeactive = 0;
  
  static byte inbuff[];
  static int ibn;  // inbuff index
  static byte outbuff[];
  
  static int closeflag = 0;    // 1=closing
  static int closereply = 1;   // 1=send/receive reply on disconnect x command
  static int togtoserver = 0;
  static int cliserflag = 0;  // 0=not connected 1=client 2=server
  static int spuuidflag = 1;    // 0=search external device UUIDs
                         // 1=assume standard serial port UUID
  static int secureflag = 0;   // 1=secure RFCOMM connection
  static int sertimeout = 0;   // time out for server connect 0=none
  static int pdn = 0;  // number of discovered devices in pairdev[]
  static int pairok = 0;
  static int connectok = 0;
  static int openok = 0;
     // terminate char for reply string sent from server
  static int repchar = (int)'!';
     // terminate char for string sent from client
  static int termchar = (int)'!';
 
  /*********** END BLUETOOTH VARIABLES *************/

  // dialogindex=0 sample code variables
  static int nrat = 2;
  static int nsel = 1;
  static int nstr;
  
  @Override
  protected void onCreate(Bundle savedInstanceState)
    {
    int scrwid,scrht;
    float den;
    
    super.onCreate(savedInstanceState);
    hand = new Handler()
      {
      @Override
      public void handleMessage(Message msg)
        {
        int fun,abort;
        
        if(msg.what == 1)
          {
          scrview.fullScroll(View.FOCUS_DOWN);
          //scrview.scrollBy(0,1000);
          return;
          }
        fun = (msg.what & 14);
        abort = (msg.what & 1);
        
        if(fun == 2)
          readblue();      // fresh listen
        else if(fun == 4)
          endread(abort);   // end ReadThread
        else if(fun == 6)
          endwrite(abort);    // end WriteThread
        else if(fun == 8)
          endconnect(abort);   // end ConnectThread
        else if(fun == 10)
          endaccept(abort);    // end AcceptThread
          
        }
      };


    acceptactive = 0;  // serv active
    connectactive = 0;  // client active
    readactive = 0;  // inread active
    writeactive = 0;
    closeflag = 0;    // 1=closing
    closereply = 1;   // 1=send/receive reply on disconnect x command
    togtoserver = 0;
    cliserflag = 0;  // 0=not connected 1=client 2=server
    spuuidflag = 1;    // 0=search external device UUIDs
    secureflag = 0;   // 1=secure RFCOMM connection
    sertimeout = 0;   // time out for server connect 0=none
    pdn = 0;  // number of discovered devices in pairdev[]
    pairok = 0;
    connectok = 0;
    openok = 0;
    
    setContentView(R.layout.activity_main);
    
       // stop screen saver because it calls onStop/closefinal
    getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);

    scrview = (ScrollView) findViewById(R.id.scrollView);
    
    output = (TextView) findViewById(R.id.textView);
    input = (TextView) findViewById(R.id.editText);
    
    //lineheight = output.getLineHeight();
    scrwid = getResources().getDisplayMetrics().widthPixels;
    scrht = getResources().getDisplayMetrics().heightPixels;
    den = getResources().getDisplayMetrics().density;
    if(scrht < scrwid)
      scrwid = scrht;
      // text size from screen width
    textszn = scrwid/(int)(40*den);
    if(textszn < 8)
      textszn = 8;
    if(textszn > 20)
      textszn = 20;
    textsz = textszn;
    iotextsz = textszn;
    scalelayout();  // set all text size
    
    input.setText("!");
    visMain(View.VISIBLE, 1);   // init=1 set button text
    
    inbuff = new byte[1024];
    outbuff = new byte[1024];
    clearinbuff();
    outbuff[0] = 0;
    
    pairdev = new BluetoothDevice[20];
    pdn = 0;     // number of discovered devices
    pdsel = -1;  // selected device pairdev[] index
    uiprint("RESTARTED\n");
    
    /*** print info to screen like this **/
    // String s = "Text size " + textszn + "\n";
    // uiprint(s);
    
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if(mBluetoothAdapter == null)
      uiprint("Bluetooth not supported\n");// Device does not support Bluetooth
    else
      uiprint("Bluetooth OK\n");
    
    if(!mBluetoothAdapter.isEnabled())
      {
      Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
      startActivity(enableBtIntent);
      }
    
    
    // Register for broadcasts when a device is discovered by findbluesearch.
    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
    registerReceiver(mReceiver, filter);
    
    }   // end  onCreate
  
  @Override
  protected void onStop()
    {
    super.onStop();
    closefinal();
    }
  
  
  @Override
  protected void onDestroy()
    {
    int n;
    
    super.onDestroy();
    //  unregister the ACTION_FOUND receiver.
    unregisterReceiver(mReceiver);
    for(n = 1 ; n <= 11 ; ++n)
      hand.removeMessages(n);  // remove messages with what=n
    // to prevent memory leaks
    // add remove runnables if posted
    }
  

  
  
  /******************* DESPATCH *********************
   *  dialogret = 0 use non-zero butval[] - button/select click
   *  button pressed butval[] = 1
   *  all selects butval[0] = do nothing  others=select index
   *  dialogret = 1 use dialogindex - return from dialogset(dialogindex)
   */
  public void despatch(int dialogret)
    {
    int n;
    
    if(butval[0] != 0)  // SEND button
      {  // Client send command currently in edit box at top of screen
      if(checkcs(1) == 0)
        return;
      uiprint("Sending\n");
      senddata(input.getText().toString(),1);
      //bluelisten();   // listen for reply done by WriteThread terminate
      }
    
    
    else if(butval[3] != 0)  // STRING button selection
      {  // Client  put command string into edit box at top of screen - do not send
      // selection butval[3] has been pressed
      // get corresponding command and put in edit box
      if(checkcs(1) == 0)
        return;
      input.setText(alcoms1[butval[3]]);
      String[] membersx = alsel1.split(",");
      String s = "Press SEND for: " + membersx[butval[3]] + "\n";
      uiprint(s);
      }
    
      
    else if(butval[2] == 1)   // CLIENT Scan
      {
      if(checkcs(0) == 0)
        return;
      uiprint("Scan for active devices for 10s\n");
      pairok = findbluesearch();
      // leaves discovery going - must cancel
      }
    else if(butval[2] == 2)   // CLIENT Paired
      {
      if(checkcs(0) == 0)
        return;
      uiprint("List paired devices\n");
      pairok = findbluepair("fish",0);
      }
    else if(butval[2] == 3)   // CLIENT Connect
      {
      if(checkcs(0) == 0)
        return;
      closeflag = 0;
      mBluetoothAdapter.cancelDiscovery();
      pdsel = -1;
      if(pdn > 0)
        {
        pdselx = 0;
        dialogset(1);  // input pdselx but only set pdsel if OK return
        }
      else
        uiprint("No devices found. Run Scan or Paired\n");
      }
    else if(butval[2] == 4)  // CLIENT Swap to server
      {
      if(checkcs(1) == 1)
        senddata("C",1);  // command to server - swap to client
      // togtoserver = 1;  done in senddata
      }
    else if(butval[2] == 5)  // CLIENT Abort threads
      {
      abortthreads();
      }
    else if(butval[2] == 6 || butval[2] == 7)   // close - send x/X command to server
      {
      if(checkcs(1) == 0)
        closefinal();
      else
        {
        closeflag = 1;
        if(butval[2] == 6)
          senddata("x", 1);  // disconnect both
        else
          senddata("X", 1);   // server re-listen
        }
      }
    
    
    
    else if(butval[1] == 1)   // SERVER listen
      {
      if(checkcs(0) == 0)
        return;
      if(acceptactive != 0)
        {
        uiprint("Already listening\n");
        return;
        }
      
      closeflag = 0;
      serv = new AcceptThread();
      serv.start();
      }
    else if(butval[1] == 2)    // SERVER Stop listen
      {
      abortthreads();
      }
    else if(butval[1] == 3)   // SERVER Force to client
      {
      if(checkcs(2) == 1)  // is a server
        {
        if(readactive == 1)
          inread.cancel();  // stop read thread
        cliserflag = 1;     // to OS wait for user to initiate command
        butcolours();
        }
      }
    else if(butval[1] == 4)    // SERVER Force disconnect
      {
      closeflag = 1;
      // abort read thread to trigger close
      if(readactive != 0)   // should be
        inread.cancel();    // triggers close because closeflag=1
      else                  // in case
        closefinal();
      }
    else if(butval[1] == 5)    // SERVER Settings
      {
      textsznx = textszn;  // temp for input
      dialogset(2);
      }
    
    
    else if(dialogret != 0)  // return from a dialog screen
      {
      if(dialogindex == 0)
        {   // sample code - not implemented
        uiprint("Dialog 0 return\n");
        uiprint("n = " + nrat + " S = " + nsel + "\n");
        uiprint(getdialogs(nstr,1));
        }
      else if(dialogindex == 1)
        {  // select connect device
        pdsel = pdselx;
        uiprint("Selected device = " + pairdev[pdsel].getName() + "\n");
        connectblue(0);  // 0=use bluedev = pairdev[pdsel]
        }
      else if(dialogindex == 2)
        {  // settings
        if(textsznx < 8)
          textsznx = 8;
        if(textsznx > 32)
          textsznx = 32;
        if(textsznx != textszn)
          {  // has changed
          textszn = textsznx;
          iotextsz = textszn;
          output.setTextSize(iotextsz);
          input.setTextSize((float)(iotextsz*1.25));
          }
          
        }
      }
    else
      uiprint("Despatch error\n");
    
    zerobuts();
    }
  
  
  public void dialog()
    {
    if(dialogindex == 0)
      {
      /******  sample dialog screen
       myprint("First line");
       nrat = inputn("Second line", nrat);
       if(nrat > 100)
       inerror("Out of range");
       nsel = selectmem("Third line", "One,Two,Three,Four", nsel);
       nstr = inputs("Enter string");  // int nstr = index for getdialogs(nstr,0);
       ***************/
      }
    else if(dialogindex == 1)
      {  // select device to connect to
      myprint("Select device\n");
      String s = new String(inbuff);
      pdselx = selectmem("Device",s,pdselx);
      }
    else if(dialogindex == 2)
      {   // settings
      myprint("Settings\n");
      textsznx = inputn("Text size",textsznx);
      if(textsznx < 8 || textsznx > 32)
        inerror("Out of range");
      
      spuuidflag = selectmem("UUID","Search,Serial Port",spuuidflag);
      secureflag = selectmem("Secure RFCOMM","No,Yes",secureflag);
      closereply = selectmem("Disconnect reply","No,Yes",closereply);
      sertimeout = inputn("Server time out s",sertimeout);
 

      }
    }
  
  
  public int checkcs(int wantflag)
    {
    if(cliserflag == wantflag)
      return(1);
    
    if(wantflag == 0)
      uiprint("Client/server active");
    else if(wantflag == 1)
      uiprint("Not a client\n");
    else if(wantflag == 2)
      uiprint("Not a server\n");
    return(0);
    }
  
  public void butcolours()
    {  // change button colours  pink=inactive/off  green=active/on
    if(cliserflag == 0)  // not connected
      {
      setbutcolour(0,0xFFD0D0);  // send off
      setbutcolour(3,0xFFD0D0);  // string off
      setbutcolour(2,0xFFD0D0);  // client off
      setbutcolour(1,0xFFD0D0);  // server off
      }
    else if(cliserflag == 1)  // client
      {
      setbutcolour(0,0xD0FFD0);  // send on
      setbutcolour(3,0xD0FFD0);  // string on
      setbutcolour(2,0xD0FFD0);  // client on
      setbutcolour(1,0xFFD0D0);  // server off
      }
    else if(cliserflag == 2)  // server
      {
      setbutcolour(0,0xFFD0D0);  // send off
      setbutcolour(3,0xFFD0D0);  // string off
      setbutcolour(2,0xFFD0D0);  // client off
      setbutcolour(1,0xD0FFD0);  // server on
      }
      
    }
  
  
  public void abortthreads()
    {
    if(acceptactive != 0)
      serv.cancel();
    if(connectactive != 0)
      client.cancel();
    if(readactive != 0)
      inread.cancel();
    if(writeactive != 0)
      outwrite.cancel();
    }
  
  
  
  public void clearinbuff()
    {
    for(ibn = 0; ibn < 1024; ++ibn)
      inbuff[ibn] = 0;
    ibn = 0;
    }
  
  public void addinbuff(String s)
    {
    int n,len,c;
    
    len = s.length();
    if(len == 0)
      return;
    for(n = 0 ; n < len && ibn < 1022 ; ++n)
      {
      c = s.charAt(n);
      inbuff[ibn] = (byte) c;
      ++ibn;
      }
    inbuff[ibn] = 0;
    }
  
  public void addinbuff(char c)
    {
    if(ibn < 1022)
      {
      inbuff[ibn] = (byte)c;
      ++ibn;
      inbuff[ibn] = 0;
      }
    }
  
  /*********** END READ ************
   * ReadThread terminated by endchar
   ************************************/
  
  public void endread(int abort)
    {
    
    if(cliserflag == 1)  // client got reply
      {
      if(closeflag == 1)
        closefinal();       // closing - then to OS
      else if(abort == 0)  // got good reply
        {
        uiprint("Reply: ");
        String s = new String(inbuff);
        uiprint(s);
        }
      clearinbuff();
      // to OS wait for user to initiate next command
      }
    else if(cliserflag == 2)  // server got command
      {
      if(abort != 0)
        {   // ABORT
        clearinbuff();
        if(closeflag == 0)
          hand.sendEmptyMessage(2);  // calls readblue via post to message queue
        else
          closefinal();  // then to OS
        return;
        }
      
      // got good command
      if(inbuff[0] == termchar)
        {
        uiprint("OK command\n");
        senddata("OK", 1);
        }
      else if(inbuff[0] == 'C')
        {  // toggle to client - no reply
        uiprint("Swap to client\n");
        cliserflag = 1;
        butcolours();
        // to OS for user initiate command
        }
      else if(inbuff[0] == 'x' || inbuff[0] == 'X')
        {   // no re-listen - dialog pops up when open server so no point
        if(inbuff[0] == 'X')
          uiprint("Server re-listen not implemented\n");
        uiprint("Disconnecting\n");
        closeflag = 1;   // for threads
        if(closereply == 0)
          {   // no reply - close now
          clearinbuff();
          closefinal();
          return;   // to OS
          }
        else
          senddata("Server disconnecting",1);  // closefinal on write OK
        }
      else
        {
        uiprint("Unknown Command\n");
        senddata("You what?", 1);
        }
      clearinbuff();
      }
    }
  
  
  /****** SEND DATA *************
   *
   *  String or byte array overloaded
   *  termflag=1 auto add endchar at end
   **************************************/
  
  public void senddata(String s,int termflag)
    {
    int len,n;
    char c;
    
 
    len = s.length();
    if(len == 0)
      {
      uiprint("No data to send\n");
      return;
      }
    if(len > 1022)
      {
      uiprint("Data string too long\n");
      return;
      }
    
    for(n = 0 ; n < len ; ++n)
      {
      c = s.charAt(n);  // char is 2 byte unicode
      if(c > 255)
        c = '.';
      outbuff[n] = (byte)c;
      }
    outbuff[len] = 0;
    
    
    senddata(outbuff,termflag);
    }
  
  public void senddata(byte dat[],int termflag)
    {
    int n,endchar;
    int c;
    
 
    
    if(dat[0] == 0)
      {
      uiprint("No data to send\n");
      return;
      }
    
    n = 1;
    while(dat[n] != 0 && n < 1022)
      ++n;
    
    // add endchar  dat[n] is term 0
    if(cliserflag == 1)
      {  // client sending string
      // empty input buffer of spurious replies from server
      try
        {
        while(instream.available() > 0)
          c = instream.read();
        }
      catch(IOException e)
        {
        }
      clearinbuff();
      
      if(dat[0] == 'x')
        closeflag = 1;
      else if(dat[0] == 'C')
        togtoserver = 1;
      }
    if(termflag != 0 && !(cliserflag == 1 && dat[n-1] == termchar))
      {    // no bother if client command already has termchar at end
      if(cliserflag == 1)   // client sending command
        endchar = termchar;
      else
        endchar = repchar;   // server sending reply
      dat[n] = (byte)endchar;
      dat[n+1] = 0;
      }
    
    
    outwrite = new WriteThread(dat);
    outwrite.start();
    return;
    }
  
  
  
  private class WriteThread extends Thread
    {
    byte outdat[];
    int abort;
    
    public WriteThread(byte dat[])
      {
      outdat = dat;
      writeactive = 1;
      abort = 0;
      }
    
    public void cancel()
      {
      thprint("Abort write\n");
      writeactive = 0;
      abort = 1;
      togtoserver = 0;
      }
    
    public void run()
      {
      int n;
      //thprint("Read run\n");
      n = 0;
      while(outdat[n] != 0 && writeactive != 0)
        {
        try
          {
          outstream.write((int) outdat[n]);
          }
        catch(IOException e)
          {
          --n;
          //output.append("Send error\n");
          //return;
          }
        ++n;
        }  // end while n loop
      
      writeactive = 0;
      hand.sendEmptyMessage(6 + abort);  // handler reads msg.what=96
      }  // end run
      
    }    // end WriteThread
  
  
  public void endwrite(int abort)
    {
    if(cliserflag == 1)   // client just sent string
      {
      if(abort == 1 || (closeflag == 1 && closereply == 0))
        {
        togtoserver = 0;
        if(closeflag != 0)
          closefinal();
        return;   // OS for user to initiate next command
        }
      else
        {
        if(togtoserver == 1)  // just sent C to server
          {
          uiprint("Swap to server\n");
          cliserflag = 2;     // toggle to server - listen for command
          butcolours();
          }
        hand.sendEmptyMessage(2);    // readblue for reply
        }
      }
    else if(cliserflag == 2)   // server just sent reply
      {
      if(closeflag == 0)   // abort or OK
        hand.sendEmptyMessage(2);    // readblue for next command
      else
        closefinal();  // abort or OK
      }
    togtoserver = 0;
    }
  
   
  
  public int readblue()
    {
    
    if(openok == 0)
      {
      uiprint("Streams not open\n");
      return (0);
      }
    
    if(readactive != 0)
      {
      uiprint("Already reading\n");
      return(0);
      }
    
    //output.append("Start listener thread\n");
    clearinbuff();
    inread = new ReadThread();
    inread.start();
    return(1);
    }
  
  
  
  private class ReadThread extends Thread
    {
    int endchar,abort;
    
    public ReadThread()
      {
      if(cliserflag == 1)   // client reading reply
        endchar = repchar;
      else
        endchar = termchar;   // server reading command
      readactive = 1;
      abort = 0;
      }
    
    public void cancel()
      {
      thprint("Abort read thread: ");
      inbuff[ibn] = '\n';
      inbuff[ibn+1] = '\0';
      String s = new String(inbuff);
      thprint(s);
      readactive = 0;
      abort = 1;
      }
    
    public void run()
      {
      int c;
      //thprint("Read run\n");
      while(readactive != 0)
        {
        try
          {
          if(instream.available() > 0)
            c = instream.read();
          else
            c = -1;
          }
        catch(IOException e)
          {
          c = -1;
          }
        
        if(c >= 0)
          {
          inbuff[ibn] = (byte) c;
          if(ibn < 1022)
            ++ibn;
          inbuff[ibn] = 0;
          
          if(c == endchar)
            {
            // inbuff [ibn-1]=endchar  [ibn]=0
            // client reading reply - replace endchar with cr for print
            if(cliserflag == 1)
              inbuff[ibn-1] = '\n';
            
            // server reading command - remove endchar unless endchar alone
            if(cliserflag == 2 && ibn > 1)
              inbuff[ibn-1] = '\0';
            
            // execute on UI thread on terminate thread
            // puts message in queue, looper sends to handler hand
            readactive = 0;
            }   // end got endchar
          }     // end got good char
        }       // end read loop
      readactive=0;
      hand.sendEmptyMessage(4+ abort);  // handler reads msg.what=99
      }   // end run
      
    }    // end ReadThread
  
  
  
  
  
  // Create a BroadcastReceiver for ACTION_FOUND.
  private final BroadcastReceiver mReceiver = new BroadcastReceiver()
    {
    public void onReceive(Context context, Intent intent)
      {
      int n,flag;
      
      String action = intent.getAction();
      if (BluetoothDevice.ACTION_FOUND.equals(action))
        {
        // Discovery has found a device. Get the BluetoothDevice
        // object and its info from the Intent.
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        String deviceName = device.getName();
        String deviceHardwareAddress = device.getAddress(); // MAC address
        
        flag = 0;
        for(n = 0 ; n < pdn && flag == 0 ; ++n)
          {
          if(deviceHardwareAddress.equals(pairdev[n].getAddress()))
            flag = 1;  // already found
          }
        if(pdn < 19 && flag == 0)
          {
          pairdev[pdn] = device;
          if(pdn > 0)
            addinbuff(',');
          addinbuff(deviceName);
          ++pdn;
          uiprint(pdn + " " + deviceName + " " + deviceHardwareAddress + "\n");
          }

        }
      }
    };
  
  
  
  
  /************* FIND BLUETOOTH *****************
   *  find bluetooth device
   *  name = name of other device
   *  flag = 0   assemble list of devices for user select
   *         1   look for name only and set bluedev
   **************************************/
  
  public int findbluesearch()
    {
    if(connectok != 0)
      {
      uiprint("Blue already connected\n");
      return(1);
      }
    
    new Handler().postDelayed(new Runnable()
      {
      public void run()
        {
        thprint("Stop scan\n");
        mBluetoothAdapter.cancelDiscovery();
        }
      },10000);
    
    clearinbuff();
    pdn = 0;
    mBluetoothAdapter.startDiscovery();
    return (1);
    }
  
  public int findbluepair(String name,int flag)
    {
    
    if(connectok != 0)
      {
      uiprint("Blue already connected\n");
      return(1);
      }
    
    

    
    Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
    
    // Cancel discovery because it otherwise slows down the connection.
    mBluetoothAdapter.cancelDiscovery();
    
    if(pairedDevices.size() == 0)
      {
      uiprint("No devices found\n");
      return (0);
      }
    
    // pairedDevices.toArray(pairdev);
    clearinbuff();
    pdn = 0;
    // There are paired devices. Get the name and address of each paired device.
    for(BluetoothDevice device : pairedDevices)
      {
      if(pdn < 19)
        {
        String deviceName = device.getName();
        pairdev[pdn] = device;
        if(pdn > 0)
          addinbuff(',');  // assemble select list for dialog input
        addinbuff(deviceName);
        ++pdn;
        String deviceHardwareAddress = device.getAddress(); // MAC address
        uiprint(pdn + " " + deviceName + " " + deviceHardwareAddress + "\n");
        if(flag != 0 && deviceName.equals(name))
          {
          bluedev = device;
          uiprint("Found bluetooth " + name + "\n");
          return (1);
          }
        }
      }
    
    if(flag == 0)
      return(1);  // got pairdev[] list
    uiprint(name + "not found\n");
    return(0);    // not found name - bluedev not set
    }
  
  /**********
   *
   *  flag = 0 use pdsel index into pairdev[]
   *         1 use bluedev already set
   * @return
   */
  public int connectblue(int flag)
    {
    int n;
    ParcelUuid[] pud;
    
    mBluetoothAdapter.cancelDiscovery();
    uiprint("Opening a socket\n");
    
    if(pairok == 0)
      {
      uiprint("Find not completed\n");
      return (0);
      }
    if(connectok != 0)
      {
      uiprint("Already connected\n");
      return (1);
      }
    
    if(flag == 0)
      {
      if(pdsel < 0 || pdsel >= pdn)
        {
        uiprint("Invalid device index\n");
        return (0);
        }
      bluedev = pairdev[pdsel];
      }
    
    client = new ConnectThread();
    client.start();
    return(0);
    }
  
  
  
  
  private class ConnectThread extends Thread
    {
    int n,conflag,abort,sockok;
    ParcelUuid[] pud;
    
    public ConnectThread()
      {
      connectactive = 1;
      abort = 0;
      }
    
    
    
    public void run()
      {
      while(connectactive != 0)
        {
        conflag = 0;
        sockok = thsocket();
        if(sockok == 1)   // got socket
          {
          conflag = thconnect();
          if(conflag == 1)  // connected ok
            conflag = thopenstreams();
          }
        
        if(conflag == 1)  // got socket and connected
          connectactive = 0;  // done OK or open fail - exit loop
        else         // wait 3 seconds and retry
          {
          if(sockok == 1)
            thclose();
          
          try
            {
            Thread.sleep(3000);
            }
          catch(InterruptedException e)
            {
            }
          }
        }  // end while connectactive
      
      connectactive = 0;
      hand.sendEmptyMessage(8 + abort);  // calls endconnect
      return;   // end thread
      }         // end run
    
    
    private void thclose()
      {
      try
        {
        mmSocket.close();
        }
      catch(IOException closeException)
        {
        uiprint("Could not close the client socket");
        }
      sockok = 0;
      }
    
    public void cancel()
      {
      thprint("Abort connect thread\n");
      if(sockok == 1)
        {
        thclose();
        connectok = 0;
        }
      
      connectactive = 0;
      abort = 1;
      }
    
    private int thsocket()
      {
      String s;
      int pudn;
      
      thprint("Getting socket\n");
      pudn = -1;
      if(spuuidflag == 0)   // search for 1101 UUID
        {
        bluedev.fetchUuidsWithSdp();
        pud = bluedev.getUuids();
        if(pud.length == 0)
          {
          thprint("No UUIDs\n");
          return (0);  // to conflag
          }
        
        //blueuuid = pud[0].getUuid();
        
        for(n = 0; n < pud.length; ++n)
          {
          s = pud[n].toString();
          thprint("UUID " + s + "\n");
          if(s.charAt(4) == '1' && s.charAt(5) == '1' &&
              s.charAt(6) == '0' && s.charAt(7) == '1')
            {
            pudn = n;
            }
          }
        
        if(pudn < 0)
          {
          thprint("No 1101 UUID\n");
          return (0);
          }
        }  // end look for external device UUIDs
      
      try
        {

        if(spuuidflag == 0 && pudn >= 0)  // have read external UUIDs
          {
          if(secureflag == 0)
            mmSocket = bluedev.createInsecureRfcommSocketToServiceRecord(pud[pudn].getUuid());
          else
            mmSocket = bluedev.createRfcommSocketToServiceRecord(pud[pudn].getUuid());
          }
        else
          {  // assume serial port UUID
          if(secureflag == 0)
            mmSocket = bluedev.createInsecureRfcommSocketToServiceRecord(
                UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
          else
            mmSocket = bluedev.createRfcommSocketToServiceRecord(
                UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
          }
        }
      catch(IOException e)
        {
        thprint("Socket's create() method failed\n");
        return(0);
        }
      
      
      thprint("Got socket\n");
      // 2s delay
      try
        {
        Thread.sleep(2000);
        }
      catch(InterruptedException e)
        {
        }
      
      return(1);
      }  // end thsocket()
    
    
    private int thconnect()
      {
      // got socket
      thprint("Trying to connect. Abort to stop\n");
      try
        {
        // Connect to the remote device through the socket. This call blocks
        // until it succeeds or throw an exception. 12s time out?
        // thprint("Trying to connect\n");
        mmSocket.connect();
        }
      catch(IOException conEx)
        {
        thprint("Unable to connect because: " + conEx.getMessage() + "\n");
        return(0);  // got socket but not connected
        }
      
      // discovery slows down connection done in findblue
      // mBluetoothAdapter.cancelDiscovery();
      // connectok = 1; done in thscoket
      thprint("Connect OK\n");
      connectok = 1;
      return(1);  // connected
      }   // end thconnect()
    
    private int thopenstreams()
      {
      try
        {
        instream = mmSocket.getInputStream();
        }
      catch(IOException e)
        {
        thprint("Open error input stream\n");
        return(0);  // fail
        }
      try
        {
        outstream = mmSocket.getOutputStream();
        }
      catch(IOException e)
        {
        thprint("Error open output stream\n");
        return(0);
        }
      
      thprint("Streams open OK\n");
      openok = 1;
      return(1);
      }  // end thopenstreams
      
    }  // end ConnectThread class
  
  
  public void endconnect(int abort)
    {
    if(abort != 0)
      return;
         // just connected to server
    cliserflag = 1;   // this device is now a client
    butcolours();
    readblue();   // expect server to send OK on connect
    // to OS for user initiate command
    }
  
  
  public void closefinal()
    {
    uiprint("Disconnected\n");
    
    abortthreads();  // stop all threads - should not be necessary
    closestreams();  // close instream/outstream
    disconnect();  // close mmSocket
    cliserflag = 0;
    butcolours();
    }
  
  
  
  public void disconnect()
    {
    if(connectok == 0)
      {
      uiprint("Not connected\n");
      return;
      }
    try
      {
      mmSocket.close();
      }
    catch(IOException closeException)
      {
      uiprint("Could not close the client socket");
      return;
      }
    connectok = 0;
    uiprint("Disconnected\n");
    return;
    }
  
  
  
  public void closestreams()
    {
    
    if(openok == 0)
      {
      uiprint("Streams already closed\n");
      return;
      }
    
    openok = 0;
    
    try
      {
      instream.close();
      }
    catch(IOException e)
      {
      uiprint("Close instream error\n");
      }
    try
      {
      outstream.close();
      }
    catch(IOException e)
      {
      uiprint("Close outstream error\n");
      }
    uiprint("Streams closed\n");
    
    }
  
  /****************** SERVER *******************/
  
  
  
  /************* PRINT ************
   *  uiprint   from UI thread
   *  thprint   from a thread
   *********************/
  
  public void uiprint(String s)
    {
    // is the UI thread
    output.append(s);
    hand.sendEmptyMessage(1);   // scroll via post message
    
    // scrview.fullScroll(View.FOCUS_DOWN);
    // output.scrollBy(0,lineheight);
    }
  
  public void thprint(String s)
    {
    final String ss;
    // not UI thread
    ss = s;
    runOnUiThread(new Runnable()
      {
      @Override
      public void run()
        {
        output.append(ss);
        }
      });
    }
  
  
  private class AcceptThread extends Thread
    {
    private BluetoothServerSocket mmServerSocket;
    int abort;
    
    public AcceptThread()
      {
      acceptactive = 1;
      abort = 0;
      thprint("Server setup\n");
      // Log.e("Rat","Server setup");
      // make discoverable for 300 seconds
      Intent discoverableIntent =
          new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
      discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
      startActivity(discoverableIntent);
      
      try
        {
        if(secureflag == 0)
          mmServerSocket = mBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(
              "Android", UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
        else
          mmServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(
              "Android", UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") );
        }
      catch (IOException e)
        {
        thprint("Listen fail\n");
        //Log.e("Rat", "Socket's listen() method failed", e);
        mmServerSocket = null;
        acceptactive = 0;  // cause immediate exit when run()
        abort = 1;         // fail flag to handler
        }
      //Log.e("Rat","Server OK");
      }  // end
    
    public void run()
      {
      mmSocket = null;
      
      thprint("Listening for client to connect\n");
      
      if(mmServerSocket == null)
        {
        //Log.e("Rat","Thread run Ss null");
        thprint("No socket\n");
        acceptactive = 0;  // thread ending
        abort = 1;
        }
      //Log.e("Rat","Start thread run");
      // Keep listening until exception occurs or a socket is returned.
      while(acceptactive != 0)
        {
        try
          {
          if(sertimeout == 0)
            mmSocket = mmServerSocket.accept();
          else
            mmSocket = mmServerSocket.accept(sertimeout*1000);
          }
        catch (IOException e)
          {
          thprint("Accept fail because: " + e.getMessage() + "\n");
          // thprint("Server socket fail\n");
          //Log.e("Rat", "Socket's accept() method failed", e);
          acceptactive = 0;
          abort = 1;
          }
        
        if (mmSocket != null && acceptactive != 0)
          {
          // A connection was accepted.
          connectok = 1;
          thprint("Server OK\n");
          
          //Log.e("Rat","Server connected");
          // close server socket
          // socket stays open
          try
            {
            mmServerSocket.close();
            }
          catch (IOException e)
            {
            thprint("Server socket close fail\n");
            // Log.e("Rat", "Could not close the connect socket", e);
            }
          //Log.e("Rat","Server closed");
          
          if(thopenstreams() == 0)
            abort = 1;
            
          acceptactive = 0;
          }
        }    // end acceptactive loop
      thprint("Server wait ending\n");
      hand.sendEmptyMessage(10 + abort);  // calls endaccept()
      }     // end run() and AcceptThread
    
    // Closes the connect socket and causes the thread to finish.
    public void cancel()
      {
      acceptactive = 0;
      abort = 1;
      thprint("Abort accept\n");
      try
        {
        mmServerSocket.close();
        }
      catch (IOException e)
        {
        thprint("Server socket close failed\n");
        // Log.e(TAG, "Could not close the connect socket", e);
        }
      }  // end cancel()
    
    
    private int thopenstreams()
      {
      try
        {
        instream = mmSocket.getInputStream();
        }
      catch(IOException e)
        {
        thprint("Open error input stream\n");
        return(0);
        }
      try
        {
        outstream = mmSocket.getOutputStream();
        }
      catch(IOException e)
        {
        thprint("Error open output stream\n");
        return(0);
        }
      
      thprint("Streams open OK\n");
      openok = 1;
      return(1);
      }  // end thopenstreams
      
      
    }    // end AcceptThread class
  
  public void endaccept(int abort)
    {
    //Log.e("Rat","End accept");
    if(abort != 0)
      return;  // to OS
    
    cliserflag = 2;   // this device is now server
    senddata("OK", 1);  // send just connected OK to client
    uiprint("Now a server\n");

    butcolours();
    hand.sendEmptyMessage(2);  // calls readblue via post to message queue
    }
  
  
  
  /************** END BLUETOOTH ****************/
  
  
  
  
  /****************** INTERFACE ************************************/
  public void butClick0(View view)
    {
    setbutval(0);
    despatch(0);
    }
  public void butClick1(View view)
    {
    setbutval(1);
    despatch(0);
    }
  public void butClick2(View view)
    {
    setbutval(2);
    despatch(0);
    }
  public void butClick3(View view)
    {
    setbutval(3);
    despatch(0);
    }
  
  /************* SCALE LAYOUT **********
   padfac = new percent
   ********************************/
  
  public void scalelayout()
    {
    int n;
    Button but;
    Spinner spin;
    TextView tx;
    
   input.setTextSize((float)(iotextsz*1.25));
   output.setTextSize(iotextsz);
    
   for(n = 0 ; n < numbut ; ++n)
     {
     but = (Button) findViewById(butid[n]);
     //but.setPadding(fn,fn,fn,fn);
     but.setTextSize(textsz);
     // spin = (Spinner) findViewById(butselid[n]);
     //spin.setPadding(fn,fn,fn,fn);
     }
   
    
    for(n = 0 ; n < numid ; ++n)
      {
      // input
      tx = (TextView) findViewById(textid[n]);
      // tx.setPadding(fn,fn,fn,fn);
      tx.setTextSize(textsz);
      
      // prompts
      tx = (TextView) findViewById(promptid[n]);
      // tx.setPadding(fn,fn,fn,fn);
      tx.setTextSize(textsz);
      
      // selects
      // spin = (Spinner) findViewById(selectid[n]);
      // spin.setPadding(fn,fn,fn,fn);
      }
      
      
    }
  
  
  
  /******* SELECT BUTTON COLOUR ***********
   * top row buttons
   * @param butn   of butid and butselid
   * @param col    0xRRGGBB   0xFF000000 added FF=transparency opaque
   ********************************/
  
  public void setbutcolour(int butn,int col)
    {
    Spinner spinner = (Spinner) findViewById(butselid[butn]);
    spinner.setBackgroundColor(0xFF000000 + col);
    
    Button but = (Button) findViewById(butid[butn]);
    but.setBackgroundColor(0xFF000000 + col);
    }
  
  /******* SET BUT VAL ********
   * set = 1 if a button
   * if select - has been set to select already - do nothing
   *
   ***********************/
  
  public void setbutval(int butn)
    {
    if(butflag[butn] != 2)
      {
      zerobuts();
      butval[butn] = 1;
      }
    }
  
  public void zerobuts()
    {
    int n;
    
    for(n = 0 ; n < numbut ; ++n)
      butval[n]=0;
    }
  
  
  public void dialogset(int index)
    {
    dialogindex = index;
    setDialog();
    clearDialog(0);
    dialogflag = 0;
    dialogline = 0;
    fromline = 0;
    dialog();
    }
  
  public void clickOK(View view)
    {
    exitDialog(1);
    despatch(1);  // 1=use dialogindex
    }
  public void clickCancel(View view)
    {
    exitDialog(0);
    }
  
  
  public void setDialog()
    {
    visMain(View.INVISIBLE,0);  // 0=no init button text
    visDialog(View.VISIBLE);         // show OK/Cancel
    }
  /***** exit dialog ************
   * hide dialog / show main
   *************************/
  
  public void exitDialog(int errflag)
    {
    dialogflag = 2;  // read all values
    dialogline = 0;
    dialog();
    if(errflag != 0 && dialogflag > 2)
      return;   // error in screen for OK press
    visDialog(View.INVISIBLE);  // hide OK/Cancel
    clearDialog(0);
    visMain(View.VISIBLE,0);  // 0=no init button text
    
    }
  
  @Override
  public void onFocusChange(View view,boolean hasfocus)
    {
    int n;
    
    // view.getId() == selectid[n] if select loses focus
    if(hasfocus == false)
      {
      for(n = 0; n < numid; ++n)
        {
        if(view.getId() == textid[n])
          {
          Log.v("Focus lost", "by " + n);
          readvalue(n);
          }
        }
      }
      
      
    }
  
  /********** Adapter View OnItemSelectedListener interface ********/
  public void onItemSelected(AdapterView<?> parent, View view,
                             int pos, long id)
    {
    int n;
    
    // An item was selected. You can retrieve the selected item using
    // parent.getItemAtPosition(pos)
    // id is same as pos
    for(n = 0 ; n < numbut ; ++n)
      {
      if(butflag[n] == 2)
        {
        if(parent.getId() == butselid[n])
          {
          zerobuts();
          if(pos == 0)  // first select is do nothing
            return;
          else
            {
            butval[n] = pos;
            Spinner spinner = (Spinner) findViewById(butselid[n]);
            spinner.setSelection(0);
            despatch(0);
            }
          }
        }
      }
    
    for(n = 0 ; n < numid ; ++n)
      {
      if(parent.getId() == selectid[n])
        {  // stop first call when create select dialogflag=0
        if(selectcount[n] != 0)    // stop first call when create select
          {
          Log.v("Select", "ID " + n + " Member " + pos);
          readvalue(n);
          }
        selectcount[n] = 1;
        }
      }
      
    }
  
  
  public void readvalue(int vline)
    {
    Log.v("Read","Value " + vline);
    
    fromline = vline;
    dialogflag = 1;
    dialogline = 0;
    //clearDialog(vline+1);
    dialog();
    }
  
  public void onNothingSelected(AdapterView<?> parent)
    {
    // Another interface callback
    }
  /******** End interface implementation *******/
  
  
  /***************** visMain **********
   * hide/show main screen
   * setMain(View.INVISIBLE)
   * setMain(View.VISIBLE)
   * init=1 initialise button text - do only once
   **************************/
  public void visMain(int hideshow,int init)
    {
    int n,k,maxlen,maxk;
    Button but;
    
    for(n = 0 ; n < numbut ; ++n)
      {
      if(butflag[n] == 1)
        {
        but = (Button) findViewById(butid[n]);
        but.setVisibility(hideshow);  // invisible=leave space gone=not
        if(init != 0)
          but.setText(buts[n]);
        }
      else if(butflag[n] == 2)
        {
        Spinner spinner = (Spinner) findViewById(butselid[n]);
        spinner.setVisibility(hideshow);
        
        if(init != 0)
          {
          String[] membersx = buts[n].split(",");
              // set button text to set spinner button width
              // which is done by button size not spinner size
          but = (Button) findViewById(butid[n]);
          
          
          // set invisible button text to longest member for correct spacing
          // which is done by button size not by spinner size
          maxlen = 0;
          maxk = 0;
          for(k = 0 ; k < membersx.length ; ++k)
            {
            if(membersx[k].length() > maxlen)
              {
              maxlen = membersx[k].length();
              maxk = k;
              }
            }
          but = (Button) findViewById(butid[n]);
          but.setText(membersx[maxk]);
          // end set underlying button text
          
          
          
         // ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
         //     android.R.layout.simple_spinner_item, membersx);

          ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
              android.R.layout.simple_spinner_item, membersx) {
          @Override
          public View getView(int position, View convertView, ViewGroup parent) {
          TextView textView = (TextView) super.getView(position, convertView, parent);
          textView.setTextSize(textsz);
          return textView;
          }
          };


          adapter.getView(0,null,null);
          adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

          spinner.setAdapter(adapter);
          spinner.setSelection(0);
          spinner.setOnFocusChangeListener(this);
          spinner.setFocusable(true);
          spinner.setFocusableInTouchMode(true);
          spinner.setOnItemSelectedListener(this);
          }
        }
        
      }
    
    TextView ebox = (TextView) findViewById(R.id.editText);
    ebox.setVisibility(hideshow);
    output.setVisibility(hideshow);
    
    }
  
  public void clearDialog(int startline)
    {
    int n;
    for(n = startline ; n <= numid ; ++n)
      {
      TextView promptbox = (TextView) findViewById(promptid[n]);
      promptbox.setVisibility(View.INVISIBLE);
      if(n < numid)
        {
        TextView editbox = (TextView) findViewById(textid[n]);
        editbox.setOnFocusChangeListener(null);
        editbox.setVisibility(View.INVISIBLE);
        Spinner spinner = (Spinner) findViewById(selectid[n]);
        spinner.setOnItemSelectedListener(null);
        spinner.setVisibility(View.INVISIBLE);
        }
      }
    }
  
  public void visDialog(int hideshow)
    {
    Button but;
    
    but = (Button) findViewById(R.id.ok);
    but.setVisibility(hideshow);
    but = (Button) findViewById(R.id.cancel);
    but.setVisibility(hideshow);
    }
  
  public void myprint(String s)
    {
    if(dialogline >= numid)
      return;  // too many inputs
    
    if(dialogflag == 0)
      {
      TextView textbox = findViewById(promptid[dialogline]);
      textbox.setText(s);
      textbox.setVisibility(View.VISIBLE);
      }
    
    ++dialogline;
    }
  
  public int inputn(String prompt,int n)
    {
    if(dialogline >= numid)
      return(n);  // too many inputs
    
    if(dialogflag == 0)
      {
      TextView promptbox = (TextView) findViewById(promptid[dialogline]);
      promptbox.setText(prompt);
      promptbox.setVisibility(View.VISIBLE);
      
      TextView editbox = (TextView) findViewById(textid[dialogline]);
      editbox.setVisibility(View.VISIBLE);
      editbox.setText(Integer.toString(n));
      editbox.setOnFocusChangeListener(this);
      
      }
    else if(dialogflag == 2 || (dialogflag == 1 && dialogline == fromline))
      {
      int nx;
      
      TextView editbox = (TextView) findViewById(textid[dialogline]);

      
      try
        {
        nx = Integer.parseInt(editbox.getText().toString());
        }
      catch(NumberFormatException e)
        {
        OKbox("Alert","Int format error",0);
        nx = n;
        editbox.setText(Integer.toString(n));
        }
      n = nx;
      // Log.v("Edit box "," n " + n);
      //dialogflag = 0;  // rewrite rest
      }
    ++dialogline;
    return(n);
    }
  
  public String getdialogs(int line,int crflag)
    {
    TextView inbox = (TextView) findViewById(textid[line]);
    if(crflag == 0)
      return(inbox.getText().toString());
    else
      return(inbox.getText().toString() + "\n");
    }
  
  
  public int inputs(String prompt)
    {
    if(dialogline >= numid)
      return(0);  // too many inputs
    
    if(dialogflag == 0)
      {
      TextView promptbox = (TextView) findViewById(promptid[dialogline]);
      promptbox.setText(prompt);
      promptbox.setVisibility(View.VISIBLE);
      
      TextView editbox = (TextView) findViewById(textid[dialogline]);
      editbox.setVisibility(View.VISIBLE);
      editbox.setText("?");
      // editbox.setOnFocusChangeListener(this);
      
      }
    
    ++dialogline;
    return(dialogline-1);
    }
  
  
  public int selectmem(String prompt,String members,int n)
    {
    int k;
    View v;
    
    if(dialogline >= numid)
      return(n);  // too many inputs
    
    if(dialogflag == 0)
      {
      // set corresponding button text for correct spacing
      
      
      
      selectcount[dialogline] = 0;  // so can ignore first listener call triggered here
      TextView promptbox = (TextView) findViewById(promptid[dialogline]);
      promptbox.setText(prompt);
      promptbox.setVisibility(View.VISIBLE);
      
      Spinner spinner = (Spinner) findViewById(selectid[dialogline]);
      spinner.setVisibility(View.VISIBLE);
      
      String[] membersx = members.split(",");
      if(n < 0 || n >= membersx.length)
        n = 0;
      
      
 //     ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
 //         android.R.layout.simple_spinner_item, membersx);
      ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
          android.R.layout.simple_spinner_item, membersx) {
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
      TextView textView = (TextView) super.getView(position, convertView, parent);
      textView.setTextSize(textsz);
      return textView;
      }
      };

      adapter.getView(0,null,null);
      adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

      spinner.setAdapter(adapter);
      
      spinner.setOnFocusChangeListener(this);
      spinner.setFocusable(true);
      spinner.setFocusableInTouchMode(true);
      spinner.setOnItemSelectedListener(this);
      spinner.setSelection(n);
      
      }
    else if(dialogflag == 2 || (dialogflag == 1 && dialogline == fromline))
      {
      Spinner spinner = (Spinner) findViewById(selectid[dialogline]);
      n = spinner.getSelectedItemPosition();
      // Log.v("Vole","Selected " + n);
      //  dialogflag = 0; // rewrite rest
      }
    ++dialogline;
    return(n);
    }
  
  
  public void inerror(String s)
    {
    OKbox("Line " + dialogline,s,0);
    if(dialogflag == 2)
      dialogflag = 3;  // stop OK exit and further reads
    }
  
  public void OKbox(String ma,String mb,int cancelflag)
    {
    Bundle b = new Bundle();
    b.putInt("CAN",cancelflag);  // 0=OK only  1=OK and Cancel
    b.putString("Ma",ma);
    b.putString("Mb",mb);
    DialogFragment dialog = new OKdialx();
    dialog.setArguments(b);
    dialog.show(getSupportFragmentManager(), "OKTag");
    }
  
  
  public static class OKdialx extends DialogFragment
    {
    
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
      {
      // Use the Builder class for convenient dialog construction
      AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
      Bundle b = getArguments();
      builder.setTitle(b.getString("Ma"));
      builder.setMessage(b.getString("Mb"));
      
      builder.setPositiveButton(
          "OK",new DialogInterface.OnClickListener()
            {
            public void onClick(DialogInterface dialog, int id)
              {
              // You don't have to do anything here if you just
              // want it dismissed when clicked
              
              }
            }
      );
      
      if(b.getInt("CAN") != 0)
        {
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
          {
          public void onClick(DialogInterface dialog, int id)
            {
            // You don't have to do anything here if you just
            // want it dismissed when clicked
            }
          }
        );
        }
      // Create the AlertDialog object and return it
      return builder.create();
      }
    }
/*********************** END INTERFACE ***************/

  }


tshivam
Posts: 11
Joined: Wed Mar 18, 2020 4:39 pm

Re: Raspberry acting as Bluetooth gateway

Fri Apr 17, 2020 4:21 pm

Hi i have a requirement as below:

I am receiving data from arduino into raspberry pi zero w and logging the data into a .txt file. This txt file needs to be stored in the pi zero w SD card and the contents of this .txt file have to be transmitted into the android application of the smartphone. This can be done via sequential or complete file in one go. Till now i am able to store the data in a txt file in the pi board but unable to transmit the file into android. Please let me know the process ahead.

petzval
Posts: 36
Joined: Sat Aug 10, 2013 12:15 pm

Re: Raspberry acting as Bluetooth gateway

Sat Apr 18, 2020 3:38 pm

The Apr 11 post has bare-bones Bluetooth Pi/Android communication code that will need Pi/C and Android/java re-programming to do something useful. PI CODE is a Pi C program, and MainActivity.java is a custom Android app. In the PI CODE when connected as a client, sendstring() will send ASCII text to ReadThread in the server MainActivity. The transfer stops when sendstring() sends a termination character (termchar) that is currently set as ! - so just make sure that the text does not contain the termination character. The text will be delivered to inbuff[] in MainActivity.
Note my comments that it is difficult to connect a Pi as a client to an Android server, in which case connect the Android as a client and swap roles.

tshivam
Posts: 11
Joined: Wed Mar 18, 2020 4:39 pm

Re: Raspberry acting as Bluetooth gateway

Tue Apr 21, 2020 8:44 am

Thanks, that is helpful. Further do u know of any source where i can get more detailed knowledge of connecting raspberry pi to android and streaming data to android from raspberry. I am completely new to it.


tshivam
Posts: 11
Joined: Wed Mar 18, 2020 4:39 pm

Re: Raspberry acting as Bluetooth gateway

Wed Apr 22, 2020 6:58 am

Thanks again friend!!
One more query is file transfer possible. I want to txt file in one go. I don't want to stream data as it is time taking. I want to do file transfer.

petzval
Posts: 36
Joined: Sat Aug 10, 2013 12:15 pm

Re: Raspberry acting as Bluetooth gateway

Wed Apr 22, 2020 2:48 pm

File transfer is exactly the kind of thing that can be implemented with this code as a base. (But if the aim is to transfer a file from storage on one device to storage on another, there are plenty of ready-to-go apps to do that - no point in re-inventing the wheel).
The point of the code is to put two devices in contact to exchange any kind of information to be used internally within the code at each end. At the moment it will exchange strings, but only three have any meaning by which the client triggers an action in the server:

C! Swap roles client/server
x! Disconnect client and server
X! Disconnect client and server, but tell server to re-listen for the next connection

All other strings will trigger an "unknown command" response from the server, and the server will do nothing.
Here's a possible sequence that can be implemented by adding to the code.

1. Android connects as a client to the Pi as a server
2. Android sends a string that the Pi interprets as "send the file"
3. Pi sends the file text as a reply string
4. Android sends an X! command to disconnect and tell the Pi to re-listen for the next connection.
5. Android processes/prints received info.

In this case, no user interaction with the Pi is needed. It is a passive slave to the Android, and can be headless with no keyboard or monitor. If the data to be transferred is binary rather than ASCII, sendchars() should be used rather than sendstring() and some method of terminating the transfer will need to be implemented, such as an initial exchange of information about how many binary bytes will be sent.

Return to “Advanced users”