Tuesday, February 17, 2015

Bluetooth (via UARTSB-2.2) now works (sort of)

I'm fiddling with my remaining hardware drawer. It was about 2009-2010 when I bought an UART_SB 2.2 on SeeedStudio; don't know why I got it. A little later I bought a solderable Bluetooth Bee and practiced 2mm-pitched hand soldering with a 60 W soldering station!! Arrrgh.

Well, I now have this beast working either as a "USBserial to Bluetooth" adapter for the PC or "TTL serial to Bluetooth" adapter for the Arduino. I spent three entire evenings to figure out a working sequence, and finally was able to print on the LCD of the Arduino Mega a string sent via Bluetooth from Linux PC and getting an answer.


First, it requires a long initialization (ouch!) up to fourteen seconds at boot time before it can accept a connection (while everything other completes in less than 60 milliseconds).

I think this is due to an old firmware release of the soldered BluetoothBee unit. It does not answer "OK" to commands, but a rather clumsy "WORK:SLAVER". So I checked for a capitalized "K" instead of "OK" - it gets both "OK" and "ORK...".

So in my .ino source I wrote a function bluebee(char*) that:
  • waits 120msec
  • sends the command string with Serial3.print
  • reads any incoming characters
  • if a 'K' is received, then flush incoming characters in 30msec and return "OK"
  • if the function is running for more than 3.3 seconds, then call for "FAIL".
And then initialized it on the Arduino Mega using:
  delay(1000);                      // at boot: wait 1sec before sending commands
  bluebee("\r\n+STWMOD=0\r\n");     // 0: client (slave) mode
  bluebee("\r\n+STNA=MyArdu\r\n");  // set bluetooth hostname
  bluebee("\r\n+STAUTO=0\r\n");     // 0: disable "autoconnect last paired device"
  bluebee("\r\n+STOAUT=1\r\n");     // 1: enable paired device to connect
  bluebee("\r\n+STPIN=0000\r\n");   // set a pin to accept incoming connections
  delay(2000);                      // required pause after setting up the pin
  Serial3.print("\r\n+INQ=1\r\n");  // 1: enable inquiring
  delay(2000);                      // required pause after sending the INQ=1

Yes, the +INQ=1 command does not answer anything. Yes, those delays are quite tight, do not reduce them. Yes, I did not use the +STBD to setup baudrate because I was happy with the default 38400; you don't need ultra-turbo speeds to send a few characters at a time.

Connections: the TX3/RX3 of the ArduinoMega are connected to the RX/TX pins of the UARTSB (note the mini-dip-switches on "5V" and "Bluetooth"), while the +5V and GND are required on the USB plug side. I cut a miniUSB connector and soldered its red/black (+/-) cables to the 5V/GND pins of the Arduino Mega.

For debugging reasons, I added to the .ino source a loop to print on the LCD of the ArduinoMega the incoming characters of the Serial3 port.


Operation:
  • apply power to turn on the ArduinoMega
  • wait for its boot and bluetooth initialization
  • on the Linux PC bluetooth section, select "Setup (pair) a new device"
  • wait the name to appear in the device list (the above example uses "MyArdu")
  • choose a pin number to pair it (the above example uses 0000)
Yes, the UARTSB+BluetoothBee do not have "memory": on my PC I have to delete the pairing ad redo it again every time the ArduinoMega is rebooted.
If I forget to delete/redo the pairing, the cat /dev/rfcomm0 shown below will silently terminate in about 3.7 seconds because a rebooted BluetoothBee does not "remember" having paired with my PC.
Then release (if already assigned) and create the radiofreq communications channel /dev/rfcomm0 as a serial port (type 1). If you don't remember the fixed address of the BluetoothBee you can use the hcitool scan --flush command to find out after the initialization is completed (in the example below it is 00:18:E4:E5:E6:E7).

  hcitool scan --flush
  sudo rfcomm release 0; sleep 1 ; sudo rfcomm bind 0 00:18:E4:E5:E6:E7 1 

Now if I write something on /dev/rfcomm0 I get a bizarre "Transport endpoint is not connected" error. This is due to the BluetoothBee requiring some time to get a connection up and running.
Note that if you did neither pairing nor rfcomm bind but the /dev/rfcomm0 is still there, you may even get a "no error" on a write... but it will not reach the Arduino.
So I open a terminal window and start cat /dev/rfcomm0 and then in another terminal window I can write using echo ' #OMG it works! ' >/dev/rfcomm0
Trick explained: the cat /dev/rfcomm0 opens the port and stays there waiting for a line of characters. After some time (some five seconds), the BluetoothBee is ready to accept characters.
Note that the Arduino HardwareSerial library has a limited serial buffer - on the Mega it is only 64 characters. This means that if I try to write 70 characters at a time, the entire Bluetooth packet is received by the Bee, then sent to the HardwareSerial library of the Arduino Mega which fills immediately the 64 bytes buffer, losing the extra six characters. The Arduino should continuously consume incoming characters (while(Serial3.available()) { processthisbyte(Serial3.read()); ...) because the 64 bytes buffer will fill in some 16 milliseconds (at 38400 baud speed).

Note: Ubuntu 14.04 Bluetooth Settings has a bug in the "Bluetooth New Device Setup": it forgets the entered pin. So I need first to wait some 10 seconds that the BluetoothBee device appears in its list, then click on it, then click on Pin Options; then select "0000 [most headsets/GPS/etc]" and "Close" the dialog, then click again on the BluetoothBee device in the list, then select again "0000" (most of the times it was "Auto" again) and "Close", and then "Continue". It should not require to enter a six-digit pin...!

Note: when actually "connected", the BluetoothBee name is listed in bold characters in the Settings / Bluetooth window of the Unity Control Center.

Note: one I got you may try to use /dev/rfcomm1 if you think that rfcomm0 is stuck

Yes, this old BluetoothBee has its drawbacks but it works:
  • ArduinoMega has the UART_SB + BluetoothBee ready to accept a "secure" wireless serial channel ("secure" means that you use a non-trivial pin like 0000... but only setup it after you are sure that everything works with the default 0000), that I placed on the TX3/RX3 (because I don't want to fiddle with pins 0/1 TX/RX unless absolutely required); you may consider the SoftwareSerial if you don't have a Mega;
  • once established a connection, ArduinoMega can regularly read incoming characters using Serial.available and Serial3.read and then answer with Serial.write functions
  • on the PC side, it is required to pair again, then release/bind the port, and then open the connection and wait a little while before sending characters.
The above examples use cat and echo but once the serial wireless channel is established, I can work out multiple interrogations. I used a thread for a fake "open channel to read" (to emulate the cat) that gets killed as soon as the main task is "go": here is the rubytalkswitharduino.rb script:

#!/usr/bin/env ruby

fake = Thread.new do
  fpfake = File.open '/dev/rfcomm0'
  while true
    fpfake.gets
  end
end

ok = Thread.new do
  sleep 5
  fp = File.open '/dev/rfcomm0', 'w+'
  Thread.kill fake

  fp.puts " [Ruby: #{$$} OK] "
  puts "PID #{$$} talked; response:"
  puts fp.gets

  fp.puts " <#OMG: #{Time.now}> "
  while true
    print fp.read(1)
  end
end

ok.join

Note that the gets requires a newline (then Serial3.println on the Arduino side). Note also that the gets in the fake thread does not have to get actual data (it just has to stay there waiting for the rfchannel to warm-up). My ArduinoMega just answered to "OK" and "OMG" commands sending (Serial3.println) some text.

Below, the complete sequence script (note that the "sleep 30" was to be sure that some time passed after last "rfcomm release 0"):

sleep 30
sudo rfcomm bind 0 00:18:E4:E5:E6:E7 1
sleep 1
ruby ./rubytalkswitharduino.rb

No comments:

Post a Comment