Wrong data transfered via USB control transfer in Linux/Mono - c#

We have a USB device here which has a EZ-USB FX chip on it and we "talk" to it via libusb-1.0 using LibUsbDotNet. Using Windows everything works fine, but using Mono 3.12.1 under Ubuntu 14.04 it does not work. I did some investigation and testing and found out it can't initialize the device, where we use the following logic (this is from my test program, but the "real logic" is equivalent):
protected bool ResetDevice(UsbDevice Device)
{
Console.WriteLine("First ensure that the EZ-USB 8051 is RESET (CPUCS=1)");
if (!ControlTransfer(Device, new byte[] { 1 }))
return false;
Console.WriteLine ("Then allow the EZ-USB CPU to 're-enumerate' (CPUCS=0)");
if (!ControlTransfer(Device, new byte[] { 0 }))
return false;
Console.WriteLine("Reset done...");
return true;
}
public bool ControlTransfer(UsbDevice Device, byte[] data, short? value = null)
{
int written;
var setupPacket = SetupPacket(value);
return Device.ControlTransfer(ref setupPacket, data, data.Length, out written);
}
internal UsbSetupPacket SetupPacket(short? value = null)
{
var controlPacket = new UsbSetupPacket();
controlPacket.Request = 0xA0;
controlPacket.RequestType = 64;
controlPacket.Value = value ?? 0x07F92;
controlPacket.Index = 0;
return controlPacket;
}
I then used an USB analyzer to profile the commands sent via the USB-Port.
Using Windows I get the following:
which is correct and as you can see the device resets an can be used, but when using Linux I get the following:
Now instead of 01 and 00 the command sends 68 and A8 and so the device does nothing.
Does anyone have an idea what could be the reason of that? Why does it send the wrong data packet under Linux?

It seems this is a Bug of LibUsbDotNet (very likely THIS one), as a build of the current trunk (v2.2.9) did solve this problem.

Related

How to use Raspberry Pi as NRF24L01+ receiver, using dotnet?

The transmitter I am using is an Arduino nano every, and I have it working just fine with an Arduino Uno as the receiver, however using the same nrf module with a raspberry pi does not work.
I am using a Pi Model 3B V1.2, with the nrf24l01+ and the 5v power adapter. The nrf also has a 100uf capacitor attached to the vcc/grnd.
I am using the GettingStarted example for the arduino nrf, with different pin configs for either the nano every or the uno, and the addresses changed to hardcoded hex:
/*
* See documentation at https://nRF24.github.io/RF24
* See License information at root directory of this library
* Author: Brendan Doherty (2bndy5)
*/
/**
* A simple example of sending data from 1 nRF24L01 transceiver to another.
*
* This example was written to be used on 2 devices acting as "nodes".
* Use the Serial Monitor to change each node's behavior.
*/
#include <SPI.h>
#include "printf.h"
#include "RF24.h"
// instantiate an object for the nRF24L01 transceiver
RF24 radio(7, 8); // using pin 7 for the CE pin, and pin 8 for the CSN pin, for the uno
//RF24 radio(10, A0); // this pin setup for the nano every
// Let these addresses be used for the pair
//uint8_t address[][6] = {"1Node", "2Node"};
uint8_t address[][5] = {{0xE0, 0xE0, 0xE0, 0xE0, 0xE0}, {0xF0, 0xF0, 0xF0, 0xF0, 0xF0}};
// It is very helpful to think of an address as a path instead of as
// an identifying device destination
// to use different addresses on a pair of radios, we need a variable to
// uniquely identify which address this radio will use to transmit
bool radioNumber = 0; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
// Used to control whether this node is sending or receiving
bool role = false; // true = TX role, false = RX role
// For this example, we'll be using a payload containing
// a single float number that will be incremented
// on every successful transmission
float payload = 0.0;
void setup() {
Serial.begin(115200);
while (!Serial) {
// some boards need to wait to ensure access to serial over USB
}
// initialize the transceiver on the SPI bus
if (!radio.begin()) {
Serial.println(F("radio hardware is not responding!!"));
while (1) {} // hold in infinite loop
}
// print example's introductory prompt
Serial.println(F("RF24/examples/GettingStarted"));
// To set the radioNumber via the Serial monitor on startup
Serial.println(F("Which radio is this? Enter '0' or '1'. Defaults to '0'"));
while (!Serial.available()) {
// wait for user input
}
char input = Serial.parseInt();
radioNumber = input == 1;
Serial.print(F("radioNumber = "));
Serial.println((int)radioNumber);
// role variable is hardcoded to RX behavior, inform the user of this
Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
// Set the PA Level low to try preventing power supply related problems
// because these examples are likely run with nodes in close proximity to
// each other.
radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
// save on transmission time by setting the radio to only transmit the
// number of bytes we need to transmit a float
radio.setPayloadSize(sizeof(payload)); // float datatype occupies 4 bytes
//radio.setAutoAck(false);
// set the TX address of the RX node into the TX pipe
radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
// set the RX address of the TX node into a RX pipe
radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
//radio.setAutoAck(false);
// additional setup specific to the node's role
if (role) {
radio.stopListening(); // put radio in TX mode
} else {
radio.startListening(); // put radio in RX mode
}
// For debugging info
//These dont work for the nano every:
//printf_begin(); // needed only once for printing details
//radio.printDetails(); // (smaller) function that prints raw register values
//radio.printPrettyDetails(); // (larger) function that prints human readable data
} // setup
void loop() {
if (role) {
// This device is a TX node
unsigned long start_timer = micros(); // start the timer
bool report = radio.write(&payload, sizeof(float)); // transmit & save the report
unsigned long end_timer = micros(); // end the timer
if (report) {
Serial.print(F("Transmission successful! ")); // payload was delivered
Serial.print(F("Time to transmit = "));
Serial.print(end_timer - start_timer); // print the timer result
Serial.print(F(" us. Sent: "));
Serial.println(payload); // print payload sent
payload += 0.01; // increment float payload
} else {
Serial.println(F("Transmission failed or timed out")); // payload was not delivered
}
// to make this example readable in the serial monitor
delay(1000); // slow transmissions down by 1 second
} else {
// This device is a RX node
uint8_t pipe;
if (radio.available(&pipe)) { // is there a payload? get the pipe number that recieved it
uint8_t bytes = radio.getPayloadSize(); // get the size of the payload
radio.read(&payload, bytes); // fetch payload from FIFO
Serial.print(F("Received "));
Serial.print(bytes); // print the size of the payload
Serial.print(F(" bytes on pipe "));
Serial.print(pipe); // print the pipe number
Serial.print(F(": "));
Serial.println(payload); // print the payload's value
}
} // role
if (Serial.available()) {
// change the role via the serial monitor
char c = toupper(Serial.read());
if (c == 'T' && !role) {
// Become the TX node
role = true;
Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK"));
radio.stopListening();
} else if (c == 'R' && role) {
// Become the RX node
role = false;
Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"));
radio.startListening();
}
}
} // loop
For the Raspberry pi I am using dotnet 6 with the Iot.Device.Bindings package, building on my windows machine, scp'ing the dotnet publish output to the pi and running through the dotnet cli with 'dotnet MyProgram.dll':
using System.Text;
using System.Linq.Expressions;
using System.Device.Spi;
using System.Device.Gpio;
using System.Device.Gpio.Drivers;
using Iot.Device.Nrf24l01;
byte[] address1 = new byte[] { 0xE0, 0xE0, 0xE0, 0xE0, 0xE0 };
byte[] address2 = new byte[] { 0xF0, 0xF0, 0xF0, 0xF0, 0xF0 };
int spiBusId = 1;
int spiChipSelect = 2;
int nrfCE = 5;
int nrfIRQ = 6;
byte nrfPacketSize = 4;
byte nrfChannel = 76;
OutputPower nrfPower = OutputPower.N12dBm;
DataRate nrfRate = DataRate.Rate1Mbps;
using RaspberryPi3Driver rpiDriver = new();
using GpioController gpio = new(PinNumberingScheme.Logical, rpiDriver);
SpiConnectionSettings spiSettings = new(spiBusId, spiChipSelect)
{
Mode = Nrf24l01.SpiMode,
ClockFrequency = Nrf24l01.SpiClockFrequency
};
using SpiDevice spi = SpiDevice.Create(spiSettings);
using Nrf24l01 nrf = new(spi, nrfCE, nrfIRQ, nrfPacketSize, gpioController: gpio, channel: nrfChannel, dataRate: nrfRate, outputPower: nrfPower);
// nrf.Address = address2;
nrf.Pipe0.Address = address1;
nrf.Pipe0.AutoAck = true;
nrf.DataReceived += DataReceived;
Console.WriteLine(nrf.NRFDetails());
while (true)
{
Thread.Sleep(200);
}
static void DataReceived(object sender, DataReceivedEventArgs e)
{
var data = e.Data.ToHexString();
Console.WriteLine($"Data received: {data}");
}
static class DataExtensions
{
public static string ToHexString(this byte[] data)
{
return string.Join(", ", data?.Select(x => $"{x:X}") ?? Array.Empty<string>());
}
public static string ToDecString(this byte[] data)
{
return string.Join(", ", data?.Select(x => $"{x}") ?? Array.Empty<string>());
}
public static string NRFDetails(this Nrf24l01 nrf, Expression<Func<Nrf24l01, Nrf24l01Pipe>>? pipeSel = null)
{
StringBuilder builder = new();
builder.AppendLine($"Address: {nrf.Address.ToHexString()}");
builder.AppendLine($"Channel: {nrf.Channel}");
builder.AppendLine($"PacketSize: {nrf.PacketSize}");
builder.AppendLine($"DataRate: {nrf.DataRate}");
builder.AppendLine($"OutputPower: {nrf.OutputPower}");
builder.AppendLine($"PowerMode: {nrf.PowerMode}");
builder.AppendLine($"WorkingMode: {nrf.WorkingMode}");
pipeSel ??= ((x) => x.Pipe0);
string mName = "Pipe";
if (pipeSel.Body is MemberExpression me)
mName = me.Member.Name;
Nrf24l01Pipe pipe = pipeSel.Compile().Invoke(nrf);
builder.AppendLine($"{mName}.Address: {pipe.Address.ToHexString()}");
builder.AppendLine($"{mName}.AutoAck: {pipe.AutoAck}");
builder.AppendLine($"{mName}.Enable: {pipe.Enable}");
builder.AppendLine($"{mName}.Payload: {pipe.Payload}");
return builder.ToString();
}
}
The radio seems to initialize fine, as I can write/read values from the nrf registers without issue, but I am unable to transmit or receive anything from the raspberry pi.
I have tried using the GettingStarted example with an arduino nano every and an arduino uno, using the same nrf modules+adapters and that works fine.
I've tried receiving/transmitting to/from the pi using both the nano every and the uno with no success.
Is this a power issue with the pi? Is this an issue with the dotnet iot library? I was unable to get the nrf24 library by tmrh working on my pi as I get g++ compilation errors from the install.sh provided, so I haven't been able to test a different library yet.
Has anyone run into similar issues, or has anyone made this setup work?

Windows IoT Raspberry Pi 3 C# RTC DS3231

this post may seems duplicate as Windows IoT and DS3231 RTC clock
but it doesn't seem to work for me. i don't have experience using i2c in this environment.
My intended functionality is,
at startup check if there is any network connectivity to update system clock
if offline the system will read datetime from DS3231 and update
the system datetime
any changes of datetime from datepicker or
timepicker it will update DS3231
My problem is
How to check for network availibility to update system clock.
to read from DS3231
using i1cdevice.writeread requires to write read address separately?
does the i2c automatically reads all the data until stop bit is received or i have to setup a counter ?
private const byte DS3231_I2C_WADDR = 0xD0;
private const byte DS3231_I2C_RADDR = 0xD1;
private I2cDevice DS3231_RTC;
byte[] i2CWriteBuffer;
byte[] i2CReadBuffer;
private async void Read_DS3231_RTC()
{
var settings = new I2cConnectionSettings(DS3231_I2C_RADDR);
settings.BusSpeed = I2cBusSpeed.StandardMode;
var controller = await I2cController.GetDefaultAsync();
DS3231_RTC = controller.GetDevice(settings);
try
{
DS3231_RTC.WriteRead(new byte[] { DS3231_I2C_RADDR }, i2CReadBuffer);
}
catch (Exception e)
{
StatusMessage.Text = "Fail to Init I2C:" + e.Message;
return;
}
}
to write to DS3231
there are dateicker & timepicker for time setting
upon setting the datetime how could I update to DS3231
private void DatePicker_DateChanged(object sender, DatePickerValueChangedEventArgs e)
{
DateTimeSettings.SetSystemDateTime(e.NewDate.UtcDateTime);
SetTime_DS3231();
}
private void TimePicker_TimeChanged(object sender, TimePickerValueChangedEventArgs e)
{
var currentDate = DateTime.Now.ToUniversalTime();
var newDateTime = new DateTime(currentDate.Year,
currentDate.Month,
currentDate.Day,
e.NewTime.Hours,
e.NewTime.Minutes,
e.NewTime.Seconds);
DateTimeSettings.SetSystemDateTime(newDateTime);
}
Thank you.
1.How to check for network availibility to update system clock.
You can use GetIsNetworkAvailable method of NetworkInterface class to check whether internet connected or not. You can get more knowleage about how to check internet connectivity type in Universal Windows Platform from this topic
.
bool isInternetConnected = NetworkInterface.GetIsNetworkAvailable();
2.to read from DS3231 using i1cdevice.writeread requires to write read address separately? does the i2c automatically reads all the data
until stop bit is received or i have to setup a counter ?
It is not necessary to separate write read. WriteRead method Performs an atomic operation to write data to and then read data from the inter-integrated circuit (I2 C) bus on which the device is connected, and sends a restart condition between the write and read operations. The second parameter is the buffer to which you want to read the data from the I2 C bus. The length of the buffer determines how much data to request from the device.It does not stop automatically until stop bit is received.If the I2 C device negatively acknowledged the data transfer before the entire buffer was read, there will be error(Error Code 0x8007045D).
3.to write to DS3231 there are dateicker & timepicker for time setting upon setting the datetime how could I update to DS3231
As TRS replied in the topic Windows IoT and DS3231 RTC clock
, he/she provided the method to set clock.
New Update:
private async void SetTime_DS3231(DateTime dt)
{
int SlaveAddress = 0x68;
try
{
// Initialize I2C
var Settings = new I2cConnectionSettings(SlaveAddress);
Settings.BusSpeed = I2cBusSpeed.StandardMode;
if (AQS == null || DIS == null)
{
AQS = I2cDevice.GetDeviceSelector("I2C1");
DIS = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(AQS);
}
using (I2cDevice Device = await I2cDevice.FromIdAsync(DIS[0].Id, Settings))
{
byte write_seconds = decToBcd((byte)dt.Second);
byte write_minutes = decToBcd((byte)dt.Minute);
byte write_hours = decToBcd((byte)dt.Hour);
byte write_dayofweek = decToBcd((byte)dt.DayOfWeek);
byte write_day = decToBcd((byte)dt.Day);
byte write_month = decToBcd((byte)dt.Month);
byte write_year = decToBcd((byte)(dt.Year%100));
byte[] write_time = { 0x00, write_seconds, write_minutes, write_hours, write_dayofweek, write_day, write_month, write_year };
Device.Write(write_time);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.Write(ex.Message);
}
}
private static byte decToBcd(byte val)
{
return (byte)(((int)val / 10 * 16) + ((int)val % 10));
}

.NET C# reading from serial after NModBus call

I'm issuing a NModBus call (setting a register to 1) to a sensor that then triggers a transmission of binary data from the sensor via the serial port.
As long as I only read/write registers via NModBus, everything works just fine but when I want to read this returning byte flow, it just doesn't work.
I've tried a few different approaches:
Using the same SerialPort
Using two different SerialPort objects (on the same port of course) in case there were trailing buffers
Reading "synchronously" immediately after the NModBus call
Reading using SerialPortDataReceived
Adding timeouts here and there (including adding waiting times on the sensor itself before starting transmission)
Etc.
Depending on the approach, I may read 4 (four) bytes but I think they are the ModBus answer to the write register call. No way to retrieve the rest of the flow (a few hundred bytes). And yes, in case you were wondering, I have verified via a separate tool that such flow is indeed transmitted. :-P
I have exchanged information via serial port in other programs with no particular problems so I was wondering if the fact that I'm using NModBus then standard serial port operations on the same port is generating concerns?
The code - that is supposed to be pretty basic - looks like:
using (SerialPort serialForModBus = new SerialPort(Port))
{
// Basic serial port settings
Open(serialForModBus);
// Create ModBus RTU master
IModbusSerialMaster master = CreateMaster(serialForModBus);
// Write register to trigger reading...
master.WriteMultipleRegisters(SLAVE_ID, SensorModBusRegisters.COMMAND_SEND_LOGS, new ushort[] { (ushort)1 });
}
// Now read...
using (SerialPort serialToReadLogs = new SerialPort(Port))
{
Open(serialToReadLogs);
serialToReadLogs.ReadTimeout = 10000;
// Externalize reading to separate class (that will also do the parsing..)
SensorLogSerialReader logReader = new SensorLogSerialReader();
serialToReadLogs.DataReceived += logReader.SerialPortDataReceived;
}
// In SensorLogSerialReader
public void SerialPortDataReceived(object senderSerialPort, SerialDataReceivedEventArgs e)
{
SerialPort sender = (SerialPort)senderSerialPort;
List<byte> bytes = new List<byte>();
try
{
bool moreToRead = (sender.BytesToRead != 0);
int cur;
while (moreToRead)
{
cur = sender.ReadByte();
bytes.Add((byte)cur);
moreToRead = (sender.BytesToRead != 0);
}
Done = true;
}
catch (...)
}
Any idea? Am I missing something "evident"?
Thank you!
Note: I've seen an post that may be similar but remained unanswered

Communication with HID device hangs on read/write (AS3992 RFID reader)

I'm trying to communicate with UHF RFID reader based on AS3992 chip.
This device is detected by Windows as standard HID and it works with 3rd party app (I found some UHF RFID Reader GUI by LinkSprite which works, but it seems like some older C++ application).
So I'm trying to integrate this device support into my .NET application. After some research I tried HidLibrary, but when I'm trying to write something to this device (initial sequence in this sample), it hangs on "write".
Does anybody know what I'm doing wrong?
Thank you!
My OS is Win 8.1 x64.
Here's the sample application:
using HidLibrary;
namespace HidTest2
{
class Program
{
static void Main(string[] args)
{
var devices = HidDevices.Enumerate(0x1325);
var rfid = devices.First();
rfid.OpenDevice();
rfid.Write(new byte[] { 0x31, 0x03, 0x01 }); // Application hangs here
while (true) // I can't get here
{
Thread.Sleep(50);
var result = rfid.Read();
Console.Write(result.Data);
}
}
}
}
PS: I also tried HidSharp, but I got same result. HID device detected, but I can't write into it.
PSS: This is the device: Link to ebay
I can't find a datasheet for the AS3229 chip that you mentioned, so I'm guessing here...
The device is probably presenting as a USB keyboard, so you would typically only be able to write LED status bits to it (Caps lock, Num lock, Shift). Is that what you are trying to write to it?
Try removing the write and just wait for the scanned RFID string to come in.
Edit: It looks like this device is presenting as a serial device over USB...I found a description closely matching it here:
https://s3.amazonaws.com/linksprite/cuttonwood/datasheet.pdf
If it's the same device you are testing then I would try communicating to it over a COM port API rather than using the relatively lower level HID APIs you have been using.
Because time by time I get an email how and if I solved this issue, here's an answer:
I had to replace original firmware for HID communication by firmware for serial communication (search for "as399x uart 115200 hex" or "as399x uart 9600 hex" on the internet) and then it worked like a sharm. Of course you need proper programmer for C8051Fxxx (about 20$ from China), USB-Serial converter and be familiar with some soldering (You'll have to solder pins on board for JTAG and Serial port).
As mentioned above, the device may not actually be a Hid device. Have you tried enumerating through USB devices instead of Hid devices? Here is some code to enumerate USB or Hid devices. The code is here.
For Hid devices use a ClassGuid of : 4D1E55B2-F16F-11CF-88CB-001111000030
and for Win USB devices use: dee824ef-729b-4a0e-9c14-b7117d33a817
https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Device.Net/Windows/WindowsDeviceConstants.cs
public async Task<IEnumerable<DeviceDefinition>> GetConnectedDeviceDefinitions(uint? vendorId, uint? productId)
{
return await Task.Run<IEnumerable<DeviceDefinition>>(() =>
{
var deviceDefinitions = new Collection<DeviceDefinition>();
var spDeviceInterfaceData = new SpDeviceInterfaceData();
var spDeviceInfoData = new SpDeviceInfoData();
var spDeviceInterfaceDetailData = new SpDeviceInterfaceDetailData();
spDeviceInterfaceData.CbSize = (uint)Marshal.SizeOf(spDeviceInterfaceData);
spDeviceInfoData.CbSize = (uint)Marshal.SizeOf(spDeviceInfoData);
var guidString = ClassGuid.ToString();
var copyOfClassGuid = new Guid(guidString);
var i = APICalls.SetupDiGetClassDevs(ref copyOfClassGuid, IntPtr.Zero, IntPtr.Zero, APICalls.DigcfDeviceinterface | APICalls.DigcfPresent);
if (IntPtr.Size == 8)
{
spDeviceInterfaceDetailData.CbSize = 8;
}
else
{
spDeviceInterfaceDetailData.CbSize = 4 + Marshal.SystemDefaultCharSize;
}
var x = -1;
var productIdHex = GetHex(productId);
var vendorHex = GetHex(vendorId);
while (true)
{
x++;
var isSuccess = APICalls.SetupDiEnumDeviceInterfaces(i, IntPtr.Zero, ref copyOfClassGuid, (uint)x, ref spDeviceInterfaceData);
if (!isSuccess)
{
var errorCode = Marshal.GetLastWin32Error();
if (errorCode == APICalls.ERROR_NO_MORE_ITEMS)
{
break;
}
throw new Exception($"Could not enumerate devices. Error code: {errorCode}");
}
isSuccess = APICalls.SetupDiGetDeviceInterfaceDetail(i, ref spDeviceInterfaceData, ref spDeviceInterfaceDetailData, 256, out _, ref spDeviceInfoData);
WindowsDeviceBase.HandleError(isSuccess, "Could not get device interface detail");
//Note this is a bit nasty but we can filter Vid and Pid this way I think...
if (vendorId.HasValue && !spDeviceInterfaceDetailData.DevicePath.ToLower().Contains(vendorHex)) continue;
if (productId.HasValue && !spDeviceInterfaceDetailData.DevicePath.ToLower().Contains(productIdHex)) continue;
deviceDefinitions.Add(GetDeviceDefinition(spDeviceInterfaceDetailData.DevicePath));
}
APICalls.SetupDiDestroyDeviceInfoList(i);
return deviceDefinitions;
});
}

Pair bluetooth devices to a computer with 32feet .NET Bluetooth library

If you want to know how to use 32feet.NET library to communicate with bluetooth devices, read the solution
I am currently trying to communicate via bluetooth between a computer and a self-built .NET Gadgeteer prototype.
The Gadgeteer prototype consists of the mainboard, a power supply and a bluetooth module. The module is in discoverable mode.
On the computer a custom bluetooth program based on 32feet .NET Bluetooth is running. The program detects all bluetooth devices in range and tries to pair with them. However, this is not done automatically at the moment, I have to enter a pairing code for the device.
How can I pair devices without entering the pairing code?
Devices are found, the problem is the pairing part. I experimented a lot, but didn't find a solution...
foreach (BluetoothDeviceInfo device in this.deviceList)
{
try
{
//BluetoothClient client = new BluetoothClient(this.CreateNewEndpoint(localAddress));
//BluetoothEndPoint ep = this.CreateNewEndpoint(device.DeviceAddress);
EventHandler<BluetoothWin32AuthenticationEventArgs> handler = new EventHandler<BluetoothWin32AuthenticationEventArgs>(HandleRequests);
BluetoothWin32Authentication auth = new BluetoothWin32Authentication(handler);
BluetoothSecurity.PairRequest(device.DeviceAddress, null);
}
}
This code block initiates the pairing and it works, but Windows is asking me to enter the pairing code for the device. I read about the BluetoothWin32Authentication to prevent this case but I don't get it right.
private void HandleRequests(object that, BluetoothWin32AuthenticationEventArgs e)
{
e.Confirm = true;
}
This is the code of the event handler (http://32feet.codeplex.com/wikipage?title=BluetoothWin32Authentication)
If you simply want to allow the pairing to go ahead when to SSP devices are connecting then handling the callback and setting e.Confirm=True will be enough -- but that is a little insecure...
I am confused -.- The goal is that the application and the gadgeteer module can send data in both directions without any user interference.
Is it true that I can't pair devices automatically without user interaction?
Is it true that if two device were already paired they can exchange data without user interaction?
I figured out how to solve my problems and my knowledge about Bluetooth connections is a bit bigger now. If someone else has problems with that, I provide my solution. The code examples represent the C# implementation of a bluetooth controller with the 32feet Bluetooth library.
Scanning
This means that devices in range are detected. My code:
// mac is mac address of local bluetooth device
BluetoothEndPoint localEndpoint = new BluetoothEndPoint(mac, BluetoothService.SerialPort);
// client is used to manage connections
BluetoothClient localClient = new BluetoothClient(localEndpoint);
// component is used to manage device discovery
BluetoothComponent localComponent = new BluetoothComponent(localClient);
// async methods, can be done synchronously too
localComponent.DiscoverDevicesAsync(255, true, true, true, true, null);
localComponent.DiscoverDevicesProgress += new EventHandler<DiscoverDevicesEventArgs>(component_DiscoverDevicesProgress);
localComponent.DiscoverDevicesComplete += new EventHandler<DiscoverDevicesEventArgs>(component_DiscoverDevicesComplete);
private void component_DiscoverDevicesProgress(object sender, DiscoverDevicesEventArgs e)
{
// log and save all found devices
for (int i = 0; i < e.Devices.Length; i++)
{
if (e.Devices[i].Remembered)
{
Print(e.Devices[i].DeviceName + " (" + e.Devices[i].DeviceAddress + "): Device is known");
}
else
{
Print(e.Devices[i].DeviceName + " (" + e.Devices[i].DeviceAddress + "): Device is unknown");
}
this.deviceList.Add(e.Devices[i]);
}
}
private void component_DiscoverDevicesComplete(object sender, DiscoverDevicesEventArgs e)
{
// log some stuff
}
Pairing
This means that devices get coupled with the local bluetooth device. This needs to be done once by entering a code of both sides. Can be done via code so that the user doesn't even notice that a device was added. My code for this purpose:
// get a list of all paired devices
BluetoothDeviceInfo[] paired = localClient.DiscoverDevices(255, false, true, false, false);
// check every discovered device if it is already paired
foreach (BluetoothDeviceInfo device in this.deviceList)
{
bool isPaired = false;
for (int i = 0; i < paired.Length; i++)
{
if (device.Equals(paired[i]))
{
isPaired = true;
break;
}
}
// if the device is not paired, pair it!
if (!isPaired)
{
// replace DEVICE_PIN here, synchronous method, but fast
isPaired = BluetoothSecurity.PairRequest(device.DeviceAddress, DEVICE_PIN);
if (isPaired)
{
// now it is paired
}
else
{
// pairing failed
}
}
}
Connecting
This means establishing a connection and exchanging of data. Again some code:
// check if device is paired
if (device.Authenticated)
{
// set pin of device to connect with
localClient.SetPin(DEVICE_PIN);
// async connection method
localClient.BeginConnect(device.DeviceAddress, BluetoothService.SerialPort, new AsyncCallback(Connect), device);
}
// callback
private void Connect(IAsyncResult result)
{
if (result.IsCompleted)
{
// client is connected now :)
}
}
If you keep the order scan, pair, connect, everything should work fine. To send or receive data, use the GetStream() method of the BluetoothClient. It provides a network stream that can be manipulated.
Receiving a connection
If you want another device to connect with your device you need to listen to incoming connection requests. This only works if the device have already been paired before. My code:
BluetoothListener l = new BluetoothListener(LOCAL_MAC, BluetoothService.SerialPort);
l.Start(10);
l.BeginAcceptBluetoothClient(new AsyncCallback(AcceptConnection), l);
void AcceptConnection(IAsyncResult result){
if (result.IsCompleted){
BluetoothClient remoteDevice = ((BluetoothListener)result.AsyncState).EndAcceptBluetoothClient(result);
}
}
Replace LOCAL_MAC with a valid BluetoothAddress (e.g. by using BluetoothAddress.Parse();). After the devices are connected they can exchange messages via the underlying stream. If the connection does not work there might be authentication issues, so try setting the local device pin in the listener (l.SetPin(LOCAL_MAC, MY_PASSWORD);

Categories