Hi Duke Nukem,
I`m working on this issue for some days and I have a working solution. It is not ready, some work and testing has still to be done but the modified bluetooth class sets the baudrate to the wanted value, independent of what the baudrate was before.
Edit: June 02, 2013 Changed the code to the latest Version
Edit: June 27 Info: Works only on spider. On Cerbuino the Messages form the BT- module come in chunks, which seems to be the reason that it does not work on this Mainboard.
// This is a Gadgeteer driver for the GHI Bluetooth Module NET. Microframework Vers. 4.2
// the original driver is from Eduardo Velloso -https://gadgeteerbluetooth.codeplex.com/
// Modification to set the baudrate of the Bluetooth module were made by
// Brett Pound -https://gadgeteerbluetooth.codeplex.com/discussions/357827
// This modification by Roland Schmidt is intended to improve the setting of the baudrate of the Bluetooth Module
using System;
using Microsoft.SPOT;
using System.Threading;
using System.Text;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.Interfaces;
namespace Gadgeteer.Modules.Velloso
{
/// <summary>
/// A Bluetooth module for Microsoft .NET Gadgeteer
/// </summary>
public class Bluetooth : GTM.Module
{
/// <summary>
/// Direct access to Serial Port.
/// </summary>
// Note by RoSchmi: Reference Gadgeteer.Serial must be added
public GTI.Serial serialPort;
private GTI.DigitalOutput reset;
private GTI.InterruptInput statusInt;
private Thread readerThread;
//Added by RoSchmi
private bool readerThread_isRunning = false;
static public AutoResetEvent Wait_For_Baudrates_Match;
// We take the possible baudrates two times in the array to try each baudrate two times to test for matching
long[] possibleBaudrates = new long[16] { 0, 9600, 19200, 38400, 57600, 115200, 230400, 460800,
0, 9600, 19200, 38400, 57600, 115200, 230400, 460800};
//End Added by RoSchmi
/// <summary>
/// Possible states of the Bluetooth module
/// </summary>
public enum BluetoothState
{
/// <summary>
/// Module is initializing
/// </summary>
Initializing = 0,
/// <summary>
/// Module is ready
/// </summary>
Ready = 1,
/// <summary>
/// Module is in pairing mode
/// </summary>
Inquiring = 2,
/// <summary>
/// Module is making a connection attempt
/// </summary>
Connecting = 3,
/// <summary>
/// Module is connected
/// </summary>
Connected = 4,
/// <summary>
/// Module is diconnected
/// </summary>
Disconnected = 5
}
//Constructors changed by RoSchmi
// Note: A constructor summary is auto-generated by the doc builder.
/// <summary></summary>
/// <param name="socketNumber">The socket that this module is plugged in to.</param>
public Bluetooth(int socketNumber)
{
Complete_Common_Constructor(socketNumber, 38400);
}
// Note: A constructor summary is auto-generated by the doc builder.
/// <summary></summary>
/// <param name="socketNumber">The socket that this module is plugged in to.</param>
/// <param name="baud">The baud rate to communicate with the module with.</param>
public Bluetooth(int socketNumber, long baud)
{
// Throw an exception if a not allowed baudrate is passed as argument
int pos = Array.IndexOf(possibleBaudrates, baud);
if (!(pos >- 1)|(baud == 0))
{
throw new System.ArgumentException("Argument of parameter baud (baudrate) not allowed. "
+ "Use 9600, 19200, 38400, 57600, 115200, 230400 or 460800");
}
Complete_Common_Constructor(socketNumber, baud);
}
private void Complete_Common_Constructor(int socketNumber, long baud) // Common part of constructor
{
Thread.Sleep(1000);
// This finds the Socket instance from the user-specified socket number.
// This will generate user-friendly error messages if the socket is invalid.
// If there is more than one socket on this module, then instead of "null" for the last parameter,
// put text that identifies the socket to the user (e.g. "S" if there is a socket type S)
Socket socket = Socket.GetSocket(socketNumber, true, this, null);
this.reset = new GTI.DigitalOutput(socket, Socket.Pin.Six, false, this);
this.statusInt = new GTI.InterruptInput(socket, Socket.Pin.Three, GTI.GlitchFilterMode.Off, GTI.ResistorMode.Disabled, GTI.InterruptMode.RisingAndFallingEdge, this);
this.serialPort = new GTI.Serial(socket, (int)baud, GTI.Serial.SerialParity.None, GTI.Serial.SerialStopBits.One, 8, GTI.Serial.HardwareFlowControl.NotRequired, this);
//this.statusInt.Interrupt += new GTI.InterruptInput.InterruptEventHandler(statusInt_Interrupt);
this.serialPort.ReadTimeout = Timeout.Infinite;
this.serialPort.Open();
Debug.Print("Message from Class Bluetooth: Set up serialPort with baudrate: " + baud.ToString());
Thread.Sleep(5);
this.reset.Write(true);
// Tests revealed that at least 470 msec are needed
// Thread.Sleep(600);
Thread.Sleep(600);
possibleBaudrates[0] = baud; // try first to connect with baudrate passed to the constructor as argument
// if not successful with this baudrate, we try all possible baudrates two times
possibleBaudrates[8] = baud; // preset for the second run
Wait_For_Baudrates_Match = new AutoResetEvent(false);
for (int i = 0; i < possibleBaudrates.Length; i++)
{
if (possibleBaudrates[i] != baud) // if baudrate of serialPort is not already
{ // set to the new value, set baudrate of serialPort
this.serialPort.Flush(); // to new value
this.serialPort.Close();
Debug.Print("Message from Class Bluetooth: Set serialPort baudrate to: " + possibleBaudrates[i].ToString());
this.serialPort.BaudRate = (int)possibleBaudrates[i];
this.serialPort.Open();
}
Debug.Print("Message from Class Bluetooth: Try to set BT-Module baudrate to: " + possibleBaudrates[i].ToString());
this.SetDeviceBaud(possibleBaudrates[i]); // try to set baudrate of BT-Module on this baudrate
// if we have the baudrate to which the
// BT-Module is actually set we will get
// "OK" or "ERROR" and the bautrate is set to the new
// value, otherwise we get not readable signs and the
// new bautrate will not be set
if (!readerThread_isRunning) // if readerThread is not running, start new readerThread
{
readerThread = new Thread(new ThreadStart(runReaderThread));
readerThread.Start();
readerThread_isRunning = true;
Thread.Sleep(500);
}
if (Wait_For_Baudrates_Match.WaitOne(50,false)) // Wait for matching bautrates
{
Debug.Print("Message from Class Bluetooth: Could connect to BT-Module with baudrate " + possibleBaudrates[i].ToString());
if (possibleBaudrates[i] != baud)
{
Debug.Print("Message from Class Bluetooth: Set BT-Device_Baudrate to baudrate passed as argument: " + baud.ToString());
this.SetDeviceBaud(baud); // Set BT-Module to baudrate passed as argument
this.serialPort.Flush();
this.serialPort.Close();
Debug.Print("Message from Class Bluetooth: Set serialPort to baudrate passed as argument: " + baud.ToString());
this.serialPort.BaudRate = (int)baud;
this.serialPort.Open();
}
break;
}
else
{
Debug.Print("Message from Class Bluetooth: Could not connect to BT-Module with baudrate " + possibleBaudrates[i].ToString());
}
}
}
/// <summary>
/// Hard Reset Bluetooth module
/// </summary>
public void Reset()
{
this.reset.Write(false);
Thread.Sleep(5);
this.reset.Write(true);
}
/// <summary>
/// Gets a value that indicates whether the bluetooth connection is connected.
/// </summary>
public bool IsConnected
{
get
{
return this.statusInt.Read();
}
}
// Reader Thread changed by RoSchmi
/// <summary>
/// Thread that continuously reads incoming messages from the module,
/// parses them and triggers the corresponding events.
/// </summary>
private void runReaderThread()
{
Debug.Print("Message from Class Bluetooth, Reader Thread: Started");
while (true)
{
String response = "";
while (serialPort.BytesToRead > 0)
{
response = response + (char)serialPort.ReadByte();
}
if (response.Length > 0)
{
//Debug.Print("In reader thread arrived: " + response);
//Check Bluetooth State Changed
if (response.IndexOf("+BTSTATE:") > -1)
{
string atCommand = "+BTSTATE:";
//String parsing
// Return format: +COPS:<mode>[,<format>,<oper>]
int first = response.IndexOf(atCommand) + atCommand.Length;
int last = response.IndexOf("\n", first);
// Changed by RoSchmi
int state = 0;
try
{
state = int.Parse(((response.Substring(first, last - first)).Trim()));
}
catch
{
Debug.Print("Error message: "+ response);
}
// End Changed by RoSchmi
OnBluetoothStateChanged(this, (BluetoothState)state);
}
//Check Pin Requested
if (response.IndexOf("+INPIN") > -1)
{
// EDUARDO : Needs testing
OnPinRequested(this);
}
if (response.IndexOf("+RTINQ") > -1)
{
//EDUARDO: Needs testing
string atCommand = "+RTINQ=";
//String parsing
int first = response.IndexOf(atCommand) + atCommand.Length;
int mid = response.IndexOf(";", first);
int last = response.IndexOf("\r", first);
// Keep reading until the end of the message
while (last < 0)
{
while (serialPort.BytesToRead > 0)
{
response = response + (char)serialPort.ReadByte();
}
last = response.IndexOf("\r", first);
}
string address = ((response.Substring(first, mid - first)).Trim());
string name = (response.Substring(mid+1, last-mid));
OnDeviceInquired(this, address, name);
//Debug.Print("Add: " + address + ", Name: " + name );
}
// Added by RoSchim
//Debug.Print("Message from Class Bluetooth, Reader Thread: Arrived at OK or ERROR parsing: " + response);
if ((response.IndexOf("OK") > -1) | (response.IndexOf("ERROR") > -1))
{
//Debug.Print("Message from Class Bluetooth, Reader Thread: Wait_For_Baudrates_Match Set (Released)");
Wait_For_Baudrates_Match.Set();
OnDataReceived(this, response);
}
else
{
OnDataReceived(this, response);
}
//End Added by RoSchmi
}
Thread.Sleep(1); //poundy changed from thread.sleep(10)
}
}
#region DELEGATES AND EVENTS
#region Bluetooth State Changed
/// <summary>
/// Represents the delegate used for the <see cref="BluetoothStateChanged"/> event.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
/// <param name="btState">Current state of the Bluetooth module</param>
public delegate void BluetoothStateChangedHandler(Bluetooth sender, BluetoothState btState);
/// <summary>
/// Event raised when the bluetooth module changes its state.
/// </summary>
public event BluetoothStateChangedHandler BluetoothStateChanged;
private BluetoothStateChangedHandler onBluetoothStateChanged;
/// <summary>
/// Raises the <see cref="BluetoothStateChanged"/> event.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
/// <param name="btState">Current state of the Bluetooth module</param>
protected virtual void OnBluetoothStateChanged(Bluetooth sender, BluetoothState btState)
{
if (onBluetoothStateChanged == null) onBluetoothStateChanged = new BluetoothStateChangedHandler(OnBluetoothStateChanged);
if (Program.CheckAndInvoke(BluetoothStateChanged, onBluetoothStateChanged, sender, btState))
{
BluetoothStateChanged(sender, btState);
}
}
#endregion
#region DataReceived
/// <summary>
/// Represents the delegate used for the <see cref="DataReceived"/> event.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
/// <param name="data">Data received from the Bluetooth module</param>
public delegate void DataReceivedHandler(Bluetooth sender, string data);
/// <summary>
/// Event raised when the bluetooth module changes its state.
/// </summary>
public event DataReceivedHandler DataReceived;
private DataReceivedHandler onDataReceived;
/// <summary>
/// Raises the <see cref="DataReceived"/> event.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
/// <param name="data">Data string received by the Bluetooth module</param>
protected virtual void OnDataReceived(Bluetooth sender, string data)
{
if (onDataReceived == null) onDataReceived = new DataReceivedHandler(OnDataReceived);
if (Program.CheckAndInvoke(DataReceived, onDataReceived, sender, data))
{
DataReceived(sender, data);
}
}
#endregion
#region PinRequested
/// <summary>
/// Represents the delegate used for the <see cref="PinRequested"/> event.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
public delegate void PinRequestedHandler(Bluetooth sender);
/// <summary>
/// Event raised when the bluetooth module changes its state.
/// </summary>
public event PinRequestedHandler PinRequested;
private PinRequestedHandler onPinRequested;
/// <summary>
/// Raises the <see cref="PinRequested"/> event.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
protected virtual void OnPinRequested(Bluetooth sender)
{
if (onPinRequested == null) onPinRequested = new PinRequestedHandler(OnPinRequested);
if (Program.CheckAndInvoke(PinRequested, onPinRequested, sender))
{
PinRequested(sender);
}
}
#endregion
#endregion
#region DeviceInquired
/// <summary>
/// Represents the delegate used for the <see cref="DeviceInquired"/> event.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
/// <param name="macAddress">MAC Address of the inquired device</param>
/// <param name="name">Name of the inquired device</param>
public delegate void DeviceInquiredHandler(Bluetooth sender, string macAddress, string name);
/// <summary>
/// Event raised when the bluetooth module changes its state.
/// </summary>
public event DeviceInquiredHandler DeviceInquired;
private DeviceInquiredHandler onDeviceInquired;
/// <summary>
/// Raises the <see cref="PinRequested"/> event.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
/// <param name="macAddress">MAC Address of the inquired device</param>
/// <param name="name">Name of the inquired device</param>
protected virtual void OnDeviceInquired(Bluetooth sender, string macAddress, string name)
{
if (onDeviceInquired == null) onDeviceInquired = new DeviceInquiredHandler(OnDeviceInquired);
if (Program.CheckAndInvoke(DeviceInquired, onDeviceInquired, sender, macAddress, name))
{
DeviceInquired(sender, macAddress, name);
}
}
#endregion
private object _lock = new Object();
private Client _client;
/// <summary>
/// Sets Bluetooth module to work in Client mode.
/// </summary>
public Client ClientMode
{
get
{
lock (_lock)
{
if (_host != null) throw new InvalidOperationException("Cannot use both Client and Host modes for Bluetooth module");
if (_client == null) _client = new Client(this);
return _client;
}
}
}
private Host _host;
/// <summary>
/// Sets Bluetooth module to work in Host mode.
/// </summary>
public Host HostMode
{
get
{
lock (_lock)
{
if (_client != null) throw new InvalidOperationException("Cannot use both Client and Host modes for Bluetooth module");
if (_host == null) _host = new Host(this);
return _host;
}
}
}
/// <summary>
/// Sets the device name as seen by other devices
/// </summary>
/// <param name="name">Name of the device</param>
public void SetDeviceName(string name)
{
this.serialPort.Write("\r\n+STNA=" + name + "\r\n");
}
/// <summary>
/// Switch the device to the directed speed
/// </summary>
/// <param baud="number">Name of the device</param>
public void SetDeviceBaud(long baud)
{
string cmd = "";
switch (baud)
{
case 9600:
cmd = "9600";
break;
case 19200:
cmd = "19200";
break;
case 38400:
cmd = "38400";
break;
case 57600:
cmd = "57600";
break;
case 115200:
cmd = "115200";
break;
case 230400:
cmd="230400";
break;
case 460800:
cmd="460800";
break;
default:
cmd="";
break;
}
if (cmd != "")
this.serialPort.Write("\r\n+STBD=" + cmd + "\r\n");
//Thread.Sleep(1000);
//todo: check it is working?! Probably should check the return code and do something about it. in the meantime,
Thread.Sleep(500);
}
/// <summary>
/// Sets the PIN code for the Bluetooth module
/// </summary>
/// <param name="pinCode"></param>
public void SetPinCode(string pinCode)
{
this.serialPort.Write("\r\n+STPIN=" + pinCode +"\r\n");
}
// Added by RoSchmi
/// <summary>
/// Permit Auto-connect
/// </summary>
public void PermitAutoConnect()
{
Debug.Print("Message from Class Buetooth: Permit auto-connect");
this.serialPort.Write("\r\n+STAUTO=1\r\n");
}
/// <summary>
/// Auto-connect forbidden
/// </summary>
public void DisableAutoConnect()
{
Debug.Print("Message from Class Buetooth: Disable auto-connect");
this.serialPort.Write("\r\n+STAUTO=0\r\n");
}
/// <summary>
/// Permit paired device to connect me
/// </summary>
public void PermitBeConnected()
{
Debug.Print("Message from Class Bluetooth: Permit be connected");
this.serialPort.Write("\r\n+STOAUT=1\r\n");
}
/// <summary>
/// Do not permit paired device to connect me
/// </summary>
public void DisableBeConnected()
{
Debug.Print("Message from Class Bluetooth: Disable be connected");
this.serialPort.Write("\r\n+STOAUT=0\r\n");
}
/// <summary>
/// Permit Auto-reconnect after getting out of range
/// </summary>
public void PermitAutoReconnect_LostConnection()
{
// Needs to be tested
Debug.Print("Message from Class Bluetooth: Permit AutoReconnect after getting out of range");
this.serialPort.Write("\r\n+LOSSRECONN=1\r\n");
}
/// <summary>
/// Do not permit Auto-reconnect after getting out of range
/// </summary>
public void DisableAutoReconnect_LostConnection()
{
// Needs to be tested
Debug.Print("Message from Class Bluetooth: Disable AutoReconnect after getting out of range");
this.serialPort.Write("\r\n+LOSSRECONN=0\r\n");
}
/// <summary>
/// Disable ECHO
/// </summary>
public void DisableECHO()
{
Debug.Print("Disable ECHO");
this.serialPort.Write("\r\n+STECHO=0\r\n");
}
/// <summary>
/// Enable ECHO
/// </summary>
public void EnableECHO()
{
Debug.Print("Enable ECHO");
this.serialPort.Write("\r\n+STECHO=1\r\n");
}
// End added by RoSchmi
/// <summary>
/// Client functionality for the Bluetooth module
/// </summary>
public class Client
{
private Bluetooth bluetooth;
internal Client(Bluetooth bluetooth)
{
Debug.Print("Client Mode");
this.bluetooth = bluetooth;
bluetooth.serialPort.Write("\r\n+STWMOD=0\r\n");
}
/// <summary>
/// Enters pairing mode
/// </summary>
public void EnterPairingMode()
{
Debug.Print("Enter Pairing Mode");
bluetooth.serialPort.Write("\r\n+INQ=1\r\n");
}
/// <summary>
/// Inputs pin code
/// </summary>
/// <param name="pinCode">Module's pin code. Default: 0000</param>
public void InputPinCode(string pinCode)
{
Debug.Print("Inputting pin: " + pinCode);
bluetooth.serialPort.Write("\r\n+RTPIN=" + pinCode + "\r\n");
}
/// <summary>
/// Closes current connection. Doesn't work yet.
/// </summary>
public void Disconnect()
{
Debug.Print("Disconnection is not working...");
//NOT WORKING
// Documentation states that in order to disconnect, we pull PIO0 HIGH,
// but this pin is not available in the socket... (see schematics)
}
/// <summary>
/// Sends data through the connection.
/// </summary>
/// <param name="message">String containing the data to be sent</param>
public void Send(string message)
{
//Debug.Print("Sending string via bluetooth: .........");
bluetooth.serialPort.Write(message);
}
//Added by RoSchmi
/// <summary>
/// Sends data through the connection.
/// </summary>
/// <param name="byteArray">Byte Array containing the data to be sent</param>
///
public void Send(byte[] byteArray)
{
//Debug.Print("Sending byteArray via bluetooth: .........");
bluetooth.serialPort.Write(byteArray);
}
// End Added by RoSchmi
/// <summary>
/// Sends data through the connection.
/// </summary>
/// <param name="message">String containing the data to be sent</param>
///
public void SendLine(string message)
{
Debug.Print("Sending: " + message);
bluetooth.serialPort.WriteLine(message);
}
}
/// <summary>
/// Implements the host functionality for the Bluetooth module
/// </summary>
public class Host
{
private Bluetooth bluetooth;
internal Host(Bluetooth bluetooth)
{
Debug.Print("Host mode");
this.bluetooth = bluetooth;
bluetooth.serialPort.Write("\r\n+STWMOD=1\r\n");
}
/// <summary>
/// Starts inquiring for devices
/// </summary>
public void InquireDevice()
{
Debug.Print("Inquiring device");
bluetooth.serialPort.Write("\r\n+INQ=1\r\n");
}
/// <summary>
/// Makes a connection with a device using its MAC address.
/// </summary>
/// <param name="macAddress">MAC address of the device</param>
public void Connect(string macAddress)
{
Debug.Print("Connecting to: " + macAddress);
bluetooth.serialPort.Write("\r\n+CONN="+macAddress+"\r\n");
}
/// <summary>
/// Inputs the PIN code.
/// </summary>
/// <param name="pinCode">PIN code. Default 0000</param>
public void InputPinCode(string pinCode)
{
Debug.Print("Inputting pin: " + pinCode);
bluetooth.serialPort.Write("\r\n+RTPIN=" + pinCode + "\r\n");
}
/// <summary>
/// Closes the current connection. Doesn't work yet.
/// </summary>
public void Disconnect()
{
Debug.Print("Disconnection is not working...");
//NOT WORKING
// Documentation states that in order to disconnect, we pull PIO0 HIGH,
// but this pin is not available in the socket... (see schematics)
}
}
}
}