Map
Grand Prize Winner
(Dec 2012)
Second Prize Winner
(July 2012)

Quiz System DIY Arduino Project for a Budget Price

This time our task was to build a Quiz System (also known as ‘Quiz Buzzer’ or ‘Quiz Show’). The real challenge was in meeting two major requirements: being really inexpensive and looking attractive and ‘factory-made’ at the same time as the project was intended for use in elementary school. To our surprise, it became almost next to impossible: when it comes to a pushbutton combined with a bulb or a LED, large enough to be pushed with a palm the price climbs unjustifiably high even for a single button. Given that our project was envisaged to be used by up to six participants the overall cost per unit looked really bleak. And visa versa, cheap (or at least affordable) buttons available at online electronic shops looked too simple or even ugly. That is why we turned our attention to doorbells hoping to find big and reliable buttons. Again, without any success as there is a tendency to shift to wireless doorbells because apparently selling just plain buttons is not a profitable business anymore. We began wandering around visiting numerous local shops looking for something that could work as a pushbutton. And all of a sudden a battery powered wall light in a Chinese shop caught our attention – it looked like a button but at the same time it had a round dome which was illuminated by a bulb just like we needed! But most importantly it had a price tag of just $2! The only problem was that it acted as ‘push and hold’ instead of just ‘push’. In other words, first push switches the light on and in order to switch the light off it has to be pushed again. We decided to take a risk and bought one light to see whether is was possible to convert it to a ‘pushbutton’. As it turned out it was an easy task to do the modification so we rushed and got five more as it is shown on the picture below.

Round Lights From Chinese Shop

Round Lights From Chinese Shop

And then it was just a matter of replacing bulbs with LEDs and wiring buttons and LEDs to Arduino board. The table below shows the total cost of materials and parts under $47 which is pretty good as the initial requirement was to stay just below $100. Besides, there are still ways to bring down the total cost even more.

Part Supplier Unit Price, A$ Count Extended Price, A$
Arduino Mega2560 R3 Aliexpress/Ebay
13.00
1
13.00
Mega Enclosure – Blue (PRT-11985) Sparkfun
12.95
1
12.95
Push button (modified wall light) Local Chinese shop
2.00
6
12.00
Huckup Wire Pack (WH3025) Jaycar Electronics
4.95
1
4.95
1.5mm Heatshrink Tubing (WH5570) Jaycar Electronics
1.45
1
1.45
Red/Orange/Green/Blue Through Hole 3mm LED >=2700 mcd Digikey Electronis
0.31
6
1.86
Through Hole 180 Ohm Resistor 0.125W Digikey Electronis
0.09
6
0.54
Total
46.75

LEDs and buttons wiring up is rather asсetic and doesn’t require advanced soldering skills. LEDs are connected as common cathode so they share common ground with buttons. Each pushbutton needs three wires, one for button, one for LEDs and one for ground. The given example uses Arduino Mega2560 w which is a little bit overkill for this project and technically speaking virtually any other Arduino board can be used with minimum changes to the wiring up.

Quiz System Wiring Up

Quiz System Wiring Up

The Arduino sketch provided below shows that the application is really simple: it initialises 6 pins in PWM mode to control LEDs and 6 pins in input mode to read button states. Any change on button inputs triggers an interrupt so that it allows to handle all six buttons independently and without priorities, all users are given equal opportunities. Upon bootup the system enters demo mode when all buttons are lit up sequentially and any button press toggles the gaming mode. In gaming mode a system first waits for a button press. Button which happens to be hit first is illuminated for a few second and the the system is ready for the next round.

/*
 QuizSystem.c - automated quiz system for six participants 

 Copyright (c) 2014 Dmitry Pakhomenko.
 dmitryp@magictale.com

http://magictale.com

 This code is in the public domain.
*/

#define BTN_1 62	//PK0 PCINT16
#define BTN_2 63	//PK1 PCINT17
#define BTN_3 64	//PK2 PCINT18
#define BTN_4 65	//PK3 PCINT19
#define BTN_5 66	//PK4 PCINT20
#define BTN_6 67	//PK5 PCINT21

#define LED_1 8
#define LED_2 9
#define LED_3 10
#define LED_4 11
#define LED_5 12
#define LED_6 13

const char PRODUCT_NAME[] PROGMEM = "QuizSystem";
const char SPACE_CHAR[] PROGMEM = " ";
const char FIRMWARE_REV[] PROGMEM = "V1.0";
const char FIRMWARE_DATE[] PROGMEM = "22.07.14";
const char COMPANY_URL[] PROGMEM = "http://magictale.com";
const char BTN_PREFIX[] PROGMEM = "BTN: ";
const char INITIAL_STATE[] PROGMEM = "INITIAL STATE";

#define MAIN_LOOP_DELAY 2000
#define SECONDS_IN_SHOWING_RESULTS_STATE 5 * 5 //ToDo: not really in seconds at the moment...

enum enum_SysState
{
    sysSelfTest,
    sysInitial,
    sysQuiz,
    sysShowResult,
    sysFailure
};

volatile uint8_t sysState;        //current system state (mode)
volatile uint8_t initStateTimer;  //seconds remaining before going to back to initial state
volatile uint8_t btnTriggered;    //flag indicates that a button press was detected
volatile uint8_t btnReg;          //button register;
volatile uint8_t testState;

void serialEvent()
{
    while (Serial.available() > 0)
    {
        //dump the received byte for now
        Serial.read();
    }
}

void initLEDs()
{
    analogWrite(LED_1, 0);
    analogWrite(LED_2, 0);    
    analogWrite(LED_3, 0);    
    analogWrite(LED_4, 0);    
    analogWrite(LED_5, 0);    
    analogWrite(LED_6, 0);    
}

void setup() 
{ 
    analogReference(DEFAULT);  
    
    //Set up 6 button inputs with pullup resistors
    pinMode(BTN_1, INPUT_PULLUP);
    pinMode(BTN_2, INPUT_PULLUP);
    pinMode(BTN_3, INPUT_PULLUP);
    pinMode(BTN_4, INPUT_PULLUP);
    pinMode(BTN_5, INPUT_PULLUP);
    pinMode(BTN_6, INPUT_PULLUP);
    
    //Enable interrupts for buttons
    PCICR |= _BV(PCIE2);
    PCMSK2 |= _BV(PCINT16);    
    PCMSK2 |= _BV(PCINT17);    
    PCMSK2 |= _BV(PCINT18);
    PCMSK2 |= _BV(PCINT19);    
    PCMSK2 |= _BV(PCINT20);    
    PCMSK2 |= _BV(PCINT21);
    
    pinMode(LED_1, OUTPUT);
    pinMode(LED_2, OUTPUT);
    pinMode(LED_3, OUTPUT);
    pinMode(LED_4, OUTPUT);
    pinMode(LED_5, OUTPUT);
    pinMode(LED_6, OUTPUT);
   
    initLEDs();

    sysState = sysSelfTest;

    Serial.begin(57600);

    Serial.print(reinterpret_cast<const __FlashStringHelper *>(PRODUCT_NAME));
    Serial.print(reinterpret_cast<const __FlashStringHelper *>(SPACE_CHAR));
    Serial.print(reinterpret_cast<const __FlashStringHelper *>(FIRMWARE_REV));
    Serial.print(reinterpret_cast<const __FlashStringHelper *>(SPACE_CHAR));
    Serial.println(reinterpret_cast<const __FlashStringHelper *>(FIRMWARE_DATE));
    Serial.println(reinterpret_cast<const __FlashStringHelper *>(COMPANY_URL));

    initStateTimer = 0;
    btnTriggered = false;
    testState = 0;
}

void handleBtnPress()
{
    Serial.print(reinterpret_cast<const __FlashStringHelper *>(BTN_PREFIX));
    Serial.println(btnReg, BIN);

    if ((btnReg) & 0x1) digitalWrite(LED_6, HIGH);
    else if ((btnReg >> 1) & 0x1) digitalWrite(LED_5, HIGH);
    else if ((btnReg >> 2) & 0x1) digitalWrite(LED_4, HIGH);
    else if ((btnReg >> 3) & 0x1) digitalWrite(LED_3, HIGH);
    else if ((btnReg >> 4) & 0x1) digitalWrite(LED_2, HIGH);
    else if ((btnReg >> 5) & 0x1) digitalWrite(LED_1, HIGH);
}

void demo()
{
    while (1)
    {
      for (uint8_t pwm = 0; pwm < 255; pwm++)
      {
          initLEDs();
          
          if (testState == 0) analogWrite(LED_1, pwm);
          else if (testState == 1) analogWrite(LED_2, pwm);
          else if (testState == 2) analogWrite(LED_3, pwm);      
          else if (testState == 3) analogWrite(LED_4, pwm);      
          else if (testState == 4) analogWrite(LED_5, pwm);
          else if (testState == 5) analogWrite(LED_6, pwm);      
          else if (testState == 6) 
          {
            analogWrite(LED_1, pwm);
            analogWrite(LED_2, pwm);        
            analogWrite(LED_3, pwm);        
            analogWrite(LED_4, pwm);        
            analogWrite(LED_5, pwm);        
            analogWrite(LED_6, pwm);        
          }
          delay(1);
      }
      
      for (uint8_t pwm = 255; pwm > 0; pwm--)
      {
          initLEDs();
          
          if (testState == 0) analogWrite(LED_1, pwm);
          else if (testState == 1) analogWrite(LED_2, pwm);
          else if (testState == 2) analogWrite(LED_3, pwm);      
          else if (testState == 3) analogWrite(LED_4, pwm);      
          else if (testState == 4) analogWrite(LED_5, pwm);
          else if (testState == 5) analogWrite(LED_6, pwm);      
          else if (testState == 6) 
          {
            analogWrite(LED_1, pwm);
            analogWrite(LED_2, pwm);        
            analogWrite(LED_3, pwm);        
            analogWrite(LED_4, pwm);        
            analogWrite(LED_5, pwm);        
            analogWrite(LED_6, pwm);        
          }
          delay(1);
      }
      
      testState++;
      if (testState > 6) 
      {
          testState = 0;
          break;
      }
    }
}

void loop()
{
    if (millis() % MAIN_LOOP_DELAY == 0)
    {
        if (initStateTimer != 0) initStateTimer--;          
    }
  
    switch (sysState)
    {
        case sysSelfTest:
        {
            demo();          
            break;
        }
        
        case sysQuiz:
        {
          
            break;  
        }
        
        case sysShowResult:
        {
       
            if (btnTriggered)
            {
                handleBtnPress();                            
                btnTriggered = false;
            }

            if (initStateTimer == 0)
            {
                sysState = sysQuiz;
                initLEDs();
                Serial.println(reinterpret_cast<const __FlashStringHelper *>(INITIAL_STATE));
            
            }
        }
        break;
       
        
        default:
            ;
        
    }
}

ISR(PCINT2_vect)
{
    cli();
    
    btnReg = 0x7F & ~*portInputRegister(digitalPinToPort(BTN_1));    
    
    if (sysState == sysQuiz)
    {
        sysState = sysShowResult;
        initStateTimer = SECONDS_IN_SHOWING_RESULTS_STATE;
        btnTriggered = true;
       
    }else if (sysState == sysSelfTest)
    {
        sysState = sysQuiz;      
        initLEDs();
    }
    sei();
}

The video below shows the process of building and Quiz System in action:

What is more important, the system has plenty of resources unused and this project can be looked at as a template for more advanced and sophisticated designs. For example, it can be integrated with a PC via USB interface so that the device can control Power Point presentations for instance which would navigate to certain pages depending on user input. Another possible enhancement is addition of a RC interface for master control or a small graphic display to indicate winner number – all those small things which would make children interested in gaming process or maybe even in electronics & programming.

Envisaging Case for Luminardo

Recently we have been busy working on new stylish case for Luminardo project. The challenge is to design something which would cost reasonable price and at the same time have modern look. Making cases of acrylic panels by laser cutting them is relatively inexpensive but all cases look kinda the same – like ugly boxes. On the other hand, 3D-printed parts may potentially have any shape but this technique is still rather expensive. In addition, it is very problematic to make 3D printed surface as shiny and polished as acrylic panels. Besides, it is impossible to mare transparent 3D-printed parts. But what if we combine acrylic and 3D-printed elements in one assembly? The we still should be able achieve stylish look and bring down the overall cost. There are the following prerequisites for this particular project:

a) Front panel has to be transparent (or as a minimum has to have a viewport). This is required not only by the VFD display itself but by light sensor, IR receiver and RGB LED;

b) Rear panel need to have multiple cut offs for numerous connectors (by the way, most of them are optional). The panel can be opaque but having it made of acrylic would let us achieve more dramatic look;

c) As a whole the outer shell should look… streamlined if it is possible to say. Like a pebble.

And here is what we have come up with. Variant “1″ is shown below. The shell is visibly bigger than the PCBAs and therefore requires more material. At the same time, it looks like all the prerequisites are taken care of.

Luminardo Case Experiment Front View

Luminardo Case Experiment Front View

Luminardo Case Experiment Bottom View

Luminardo Case Experiment Bottom View

Luminardo Case Experiment Rear ISO View

Luminardo Case Experiment Rear ISO View

Luminardo Case Experiment Bottom ISO View

Luminardo Case Experiment Bottom ISO View

Luminardo Case Experiment Front ISO View

Luminardo Case Experiment Front ISO View

Luminardo Case Experiment Exploded View

Luminardo Case Experiment Exploded View

Variant “2″ is shown below. It uses an acrylic inset which works like a feet and as an element of design – it glows as it is illuminated by the RGB LED. On the other end of the case there are two small feet which gives the unit asymmetric and unusual look. However, it is not clear whether the IR receiver would be able to work through the thickness of the inset. And the VFD viewport must extent more to the left in order for the light sensor to be functional.

Luminardo Case Experiment Front View

Luminardo Case Experiment Front View

Luminardo Case Experiment Bottom View

Luminardo Case Experiment Bottom View

Luminardo Case Experiment Rear ISO View

Luminardo Case Experiment Rear ISO View

Luminardo Case Experiment Bottomt ISO View

Luminardo Case Experiment Bottomt ISO View

Luminardo Case Experiment Front ISO View

Luminardo Case Experiment Front ISO View

All in all, it is still work in progress. We are waiting for comments and more ideas!

Downloads:

1. Case for Luminardo in SketchUp, experiment “1″

2. Case for Luminardo in SketchUp, experiment “2″

Case for Pilot Display

It is time to put our Pilot Display into a case. A box of appropriate size and shape has been found at Blacktown’s Jaycar store. The dimensions were almost ideal with only one problem – a battery compartment that prevented PCBA from fitting inside. It tuned out not to be a problem for us, using some files we successfully cut off the compartment. With the help of a miniature drill holes on the front panel were made. In order to secure the board inside nylon standoffs have been glued with a glue gun. The board is mechanically secured with two screws. A battery compartment lid conveniently provides access to board’s edge and USB connectors.

Pilot Display Case Modified

Pilot Display Case Modified

Pilot Display Board Fitted In Case

Pilot Display Board Fitted In Case

Pilot Display Fully Assembled Back Lid Off

Pilot Display Fully Assembled Back Lid Off

Pilot Display Fully Assembled

Pilot Display Fully Assembled

Firmware for Pilot Display

The firmware of Pilot Display unit comprises of two parts: bootloader and main application. Bootloader simplifies the process of main application upgrade – once the bootloader is programmed, there is no need in external programmers to do firmware upgrades. Source code of the main application is designed as a sketch for very popular Arduino platform which appeals for its rapid prototyping approach and extremely short induction time when a new member joins to a project or when it comes to knowledge transfer between a contractor and a customer. Currently Arduino IDE doesn’t have official support for ATMega1281P microprocessor but that support can be easily added to the environment. For more details about initial Arduino IDE setup please refer to this link.

The main application has three modes: test mode, configuration mode and normal mode. Test mode is initiated automatically immediately after reboot upon applying power to the unit. There is a few seconds delay after start as initially control is given to the bootloader. The bootloader has a timeout of a few seconds during which it is waiting on activity from serial port. If no requests to perform firmware updates is received then the bootloader launches the main application. Main application initialises LED drivers and switches LEDs one by one until all of the are lit up. Then all LEDs are switched off and the unit enters normal mode. At this point the unit is ready to receive commands from turbine engine ECUs.

Configuration mode is intended for changing unit’s default parameters such as baudrate for both serial ports and current through LEDs. By changing the current flowing through LEDs brightness adjustment is achieved. Brightness for red, green and orange colors is adjusted individually. To enter configuration mode disconnect ‘LEFT ENGINE’ serial port from ECU, connect it to a PC/laptop, run a terminal app like ‘Hyperterminal’ with baudrate set to 2400 bps and type ‘aaaaaa’. The unit should respond with the list of available commands and current settings. Follow the instructions to configure the unit.

The video below shows display’s LED test in progress:

Downloads:

1. Turbine ECU communication protocol V2.14-Vx.35 by AMT Netherlands

2. Pilot Display Bootloader Rev.1.0

3. Pilot Display Production Firmware (Arduino Sketch) Rev.1.0

First Pilot Display PCB is Assembled

First Pilot Display PCB is finally assembled and tested. As it usually happens with revisions 1.0 there are several things that have been overlooked during design. Good news is that all issues are minor and don’t impact main functionality. Here are the discovered issues:

a) First of all, FT232 is not powered when USB cable is not connected resulting TXLED and RXLED outputs being pulled low and as a consequence LD1 and LD2 are constantly switched on. Connecting USB cable puts both LEDs to the desired (switched off) states even when the cable is then unplugged. This is annoying but given that both LEDs are powered with 1K resistors in series, it doesn’t make much of a difference in power consumption even with both LEDs being permanently switched on.

b) The next problem though is ST3232BSOIC16 which shares RX-I and TX-O signals with FT232. ST3232BSOIC16 never puts its outputs into hi-z state and therefore microprocessor ATMEGA1284PA can’t receive data from FT232 which is essence means that USB interface cannot be used for firmware updates, maximum that we can get is a debug output through USB. However, firmware updates still can be done via RS232 interface for the left engine (TX_LEFT and RX_LEFT). In the next revision it will be worth adding a jumper to manually switch between serial and USB interfaces;

c) R5 resistor doesn’t make much sense and better to be replaced with 0.1uF ceramic capacitor;

d) Both MAX6957 are not designed to put their DOUT outputs into hi-z mode when not selected by SS which creates conflicts on the SPI bus when the microprocessor is being programmed via ISP header. We managed to program the microprocessor by cranking up VCC to 5V via ISP header. Once the bootloader is programmed, there is no need in ISP anymore as from that point the firmware can be updated via RS232 interface without need to use external ISP programmer;

e) Really strange and unexpected issue – red LEDs are slightly bigger than green and orange ones despite they all are of the same form factor. Attempts to find another type of red LEDs resulted in the same slightly bigger sizes!

f) LEDs are really difficult to solder precisely so it would be worth using LED spacers for the next boards;

g) At the end of this design cycle the customer expressed a desire to have opto-isolation for both RS232 ports so looks like that ST3232BSOIC16 would need to be replaced with something else;

The DC-DC converter is capable of delivery up to 1A @ 3V3 and was successfully tested under maximum load by switching all LEDs simultaneously at maximum current of 20mA per LED. No excessive power dissipation is under maximum load – all chips remain cold when touching with a finger.

Fully assembled board is shown on the pictures below.

Pilot Display PCBA Bottom

Pilot Display PCBA Bottom

Pilot Display PCBA Top

Pilot Display PCBA Top

Pilot Display PCBs arrived

Pilot Display Rev.1.0 PCBs finally arrived. At a first glance nothing is terribly wrong, everything seems to be fine which is a good start. However, there is always something in rev1.0 boards or schematics so will see what the subsequent assembling process and preliminary test bring us. As always, Itead Studio PCB manufacturing service has been used, they made and shipped 6 PCBs instead of promised 5.

This is what the PCBs look like:

Pilot Display bare PCBs

Pilot Display bare PCBs

Wait for more upcoming updates on this project.

Pilot Display for… a Twin Turbine Engine!

Just recently by a pure accident we became involved into very unusual project. Willing to become known to some degree amongst the local Sydney electronics enthusiasts we decided to take part in one of the community projects and help to build something. It didn’t take much time to wait for someone who posted a request asking for assistance to design a LED display panel for engine health status indication. At a first glance it looked like a toy for a remotely controlled plane equipped with a miniature turbine engine. However, it soon turned out to be more serious: the panel was supposed to indicate health status of a full scale twin turbine engine being part of a jet pack! Display was required to show RPM and temperature for both engines individually and ‘flight time’. All information was requested to be represented by columns of super bright LEDs, be power efficient, receive data streams from both ECUs (Engine Control Units) via regular RS232 ports, have a capability of firmware update and work in wide range of input voltages (with the efficiency peak somewhere in 15…16V range as it is a typical voltage of the jet pack’s onboard lithium battery).

After doing some preliminary experiments with multiplexed displaying technique we got unsatisfactory results for LED brightness under direct sunlight and decided to go for static indication by using specialised MAX6957 chips which also allow controlling current flowing through each individual LED and therefore allow for programmatically adjustable brightness. On top of that we decided to use USB to serial converter for easier firmware updates/debugging and a photo resistor acting as a light sensor. And here is the result of our effort.

The circuit diagram of the display panel is given below.

Pilot Display CircuitDiagram Rev.1.0

Pilot Display CircuitDiagram Rev.1.0

The PCB layout is done on two layers. Component placement for top and bottom sides are given on the pictures below.

PilotDisplay Component Layout Rev1.0

PilotDisplay Component Layout Rev1.0

A 3D model of the PCBA and Sketchup snapshots are given below.

Pilot Display PCBA 3DModel Top View

Pilot Display PCBA 3DModel Top View

Pilot Display PCBA 3DModel Top ISO View

Pilot Display PCBA 3DModel Top ISO View

Pilot Display PCBA 3DModel Bottom ISO View

Pilot Display PCBA 3DModel Bottom ISO View

Pilot Display PCBA 3DModel Side View

Pilot Display PCBA 3DModel Side View

Downloads:

1. Pilot Display Gerber files Rev1.0

2. Pilot Display Eagle files Rev1.0

3. Pilot Display SketchUp 3D Model Rev1.0

4. Pilot Display Component 3D Models

4. Pilot Display BOM (Bill Of Materials)

Firmware source code is yet to come – stay tuned.

Bluetooth for Dummies: Domesticating RFComm

Knowing RFComm port number to connect to we are fully set for our final step – to establish RFComm communication channel. Before we can send RFComm specific commands we need to establish L2CAP connection to RFComm the same way as it has been already described in ‘Establishing connection to SDP’. The only difference is that this time we need to use RFComm PSM (0×03) rather than SDP PSM. Let’s not focus our attention on L2CAP as it is now a technicality of no interest and shift our attention to RFComm specifics.

Make the same preparations and run two consoles with logging hcpdump applications and it was described in this post. Let’s analyze packet exchange immediately after establishing connection to RFComm. Here is what the first console would typically report:

< ACL data: handle 43 flags 0x02 dlen 8
L2CAP(d): cid 0x0041 len 4 [psm 3]
RFCOMM(s): SABM: cr 1 dlci 0 pf 1 ilen 0 fcs 0x1c //1-st SABM command
> ACL data: handle 43 flags 0x02 dlen 8
L2CAP(d): cid 0x0041 len 4 [psm 3]
RFCOMM(s): UA: cr 1 dlci 0 pf 1 ilen 0 fcs 0xd7 //UA response

< ACL data: handle 43 flags 0x02 dlen 18
L2CAP(d): cid 0x0041 len 14 [psm 3]
RFCOMM(s): PN CMD: cr 1 dlci 0 pf 0 ilen 10 fcs 0x70 mcc_len 8 //PM (paramater negotiation) request
dlci 32 frame_type 0 credit_flow 15 pri 7 ack_timer 0
frame_size 55 max_retrans 0 credits 7
> ACL data: handle 43 flags 0x02 dlen 18
L2CAP(d): cid 0x0041 len 14 [psm 3]
RFCOMM(s): PN RSP: cr 0 dlci 0 pf 0 ilen 10 fcs 0xaa mcc_len 8 //PM (parameter negotiation) response
dlci 32 frame_type 0 credit_flow 14 pri 0 ack_timer 0
frame_size 55 max_retrans 0 credits 7

< ACL data: handle 43 flags 0x02 dlen 8
L2CAP(d): cid 0x0041 len 4 [psm 3]
RFCOMM(s): SABM: cr 1 dlci 32 pf 1 ilen 0 fcs 0xca //2-nd SAMB command
> ACL data: handle 43 flags 0x02 dlen 8
L2CAP(d): cid 0x0041 len 4 [psm 3]
RFCOMM(s): UA: cr 1 dlci 32 pf 1 ilen 0 fcs 0x1 //UA response

< ACL data: handle 43 flags 0x02 dlen 12
L2CAP(d): cid 0x0041 len 8 [psm 3]
RFCOMM(s): MSC CMD: cr 1 dlci 0 pf 0 ilen 4 fcs 0x70 mcc_len 2 //MSC (modem status) command
dlci 32 fc 0 rtc 1 rtr 1 ic 0 dv 1 b1 1 b2 1 b3 0 len 0
> ACL data: handle 43 flags 0x02 dlen 12
L2CAP(d): cid 0x0041 len 8 [psm 3]
RFCOMM(s): MSC CMD: cr 0 dlci 0 pf 0 ilen 4 fcs 0xaa mcc_len 2 //MSC (modem status) command
dlci 32 fc 0 rtc 1 rtr 1 ic 0 dv 1 b1 1 b2 1 b3 0 len 0

< ACL data: handle 43 flags 0x02 dlen 12
L2CAP(d): cid 0x0041 len 8 [psm 3]
RFCOMM(s): MSC RSP: cr 1 dlci 0 pf 0 ilen 4 fcs 0x70 mcc_len 2 //MSC (modem status) response
dlci 32 fc 0 rtc 1 rtr 1 ic 0 dv 1 b1 1 b2 1 b3 0 len 0
> ACL data: handle 43 flags 0x02 dlen 12
L2CAP(d): cid 0x0041 len 8 [psm 3]
RFCOMM(s): MSC RSP: cr 0 dlci 0 pf 0 ilen 4 fcs 0xaa mcc_len 2 //MSC (mode status) response
dlci 32 fc 0 rtc 1 rtr 1 ic 0 dv 1 b1 1 b2 1 b3 0 len 0

< ACL data: handle 43 flags 0x02 dlen 9
L2CAP(d): cid 0x0041 len 5 [psm 3]
RFCOMM(d): UIH: cr 1 dlci 32 pf 1 ilen 0 fcs 0xc4 credits 33 //Command with credit

Let’s have a look at the same exchange in raw representation logged by the second console and try to decode it:

< 0000: 02 2a 20 08 00 04 00 41  00 03 3f 01 1c           .* ....A..?.. //1-st SABM on channel 0 (byte @ 0x9 == 0x3)
> 0000: 02 2a 20 08 00 04 00 41  00 03 73 01 d7           .* ....A..s.. //UA response

< 0000: 02 2a 20 12 00 0e 00 41  00 03 ef 15 83 11 20 f0  .* ....A...... . //PM (paramater negotiation) request
0010: 07 00 37 00 00 07 70                              ..7...p
> 0000: 02 2a 20 12 00 0e 00 41  00 01 ef 15 81 11 20 e0  .* ....A...... . //PM (parameter negotiation) response
0010: 00 00 37 00 00 07 aa                              ..7....

< 0000: 02 2a 20 08 00 04 00 41  00 83 3f 01 ca           .* ....A..?.. //2-nd SABM  on channel 0x10 (byte @ 0x9 == 0x83, channel number << 3 = 0x80)
> 0000: 02 2a 20 08 00 04 00 41  00 83 73 01 01           .* ....A..s.. //UA response

< 0000: 02 2a 20 0c 00 08 00 41  00 03 ef 09 e3 05 83 8d  .* ....A........ //MSC (modem status) command
0010: 70                                                p
> 0000: 02 2a 20 0c 00 08 00 41  00 01 ef 09 e3 05 83 8d  .* ....A........ //MSC (modem status) command
0010: aa                                                .
< 0000: 02 2a 20 0c 00 08 00 41  00 03 ef 09 e1 05 83 8d  .* ....A........ //MSC (modem status) response
0010: 70                                                p
> 0000: 02 2a 20 0c 00 08 00 41  00 01 ef 09 e1 05 83 8d  .* ....A........ //MSC (modem status) response
0010: aa                                                .

< 0000: 02 2a 20 09 00 05 00 41  00 83 ff 01 21 c4        .* ....A....!. //Command with credit on channel 0x10

...and... rock'n'roll!

Time to put everything together. Get source code from Download section below, update your installation of USB Host 2.0 library on your machine, run the provided sketch making sure that ELM327 is powered and within range of your Luminardo board. You should see something similar to the output provided below. Note, that very last reply >LM327 v1.5 is transmitted by ELM327 which means that we have established SPP communication channel! The only remaining part now is to put logic which communicates with ELM327 and renders received values on VFD panel.

Luminardo USB Host Bluetooth SPP Test
SPP Bluetooth Library Started
Enabling VBus... enabled
Bluetooth Dongle Initialized
HCI Reset complete
Write class of device
Local Bluetooth Address: 00:19:0E:12:65:6A
The name is set to: Luminardo
Pairing to 'Other' device with predefined address
Device: 40:22:11:00:69:58 has been found
Connecting to 'Other' device...
Connected to 'Other' device
Received Key Request
Bluetooth pin is set too: 1234
Pairing successful with 'Other' device
SDP Connection Request Sent
L2CAP Connection Response
SDP Connection Response
SDP Configuration Request Received
SDP Configuration Response Sent
SDP Configuration Request Sent
SDP Configuration Response Received
SDP Successfully Configured
SDP Service Search Attribute Request 1 Sent
Channel will be in the next packet
SDP Service Search Attribute Request 2 Sent
Channel found in second packet: 16
RFComm Connection Request Sent
L2CAP Connection Response
RFComm Connection Response
RFComm Configuration Request Received
RFComm Configuration Response Sent
RFComm Configuration Request Sent
RFComm Configuration Response Received
RFComm Successfully Configured
RFComm SABM Sent
Received UA RFComm Packet on channel: 00
Sent UAH RFComm Cmd BT_RFCOMM_PN_CMD (Parameter Negotiation Request) on channel: 00
Received UIH RFComm Packet on channel: 00 - BT_RFCOMM_PN_RSP (Parameter Negotiation Response)
RFComm 2-nd SABM Sent
Received UA RFComm Packet on channel: 10
Send UIH RFComm Cmd BT_RFCOMM_MSC_CMD (Modem Status Command)
Received UIH RFComm Packet on channel: 00 - BT_RFCOMM_MSC_CMD (Modem Status Cmd)
Send UIH RFComm Cmd BT_RFCOMM_MSC_RSP (Modem Status Response)
Received UIH RFComm Packet on channel: 00 - BT_RFCOMM_MSC_RSP (Modem Status Response)
RFComm Cmd with Credit Sent
Serial Bluetooth connected

>LM327 v1.5

Now the implemented functionality allows to establish SPP communication between two Arduino boards equipped with USB Host shields. It is also quite remarkable that this particular Bluetooth dongle is not functional under WinXP because it comes with Win8 driver only yet our hardware happily communicates with the dongle!

Downloads:

1. USB Host 2.0 For Arduino – Bluetooth SPPi addon.

Bluetooth for Dummies: Getting Data from SDP

As a result of effort in previous post we now armed with knowdledge what has to be done in order to connect to Bluetooth SDP (Service Discovery Protocol). All we need to get from SDP is a RFComm channel number to connect to, the rest information provided by SDP service while it is a subject of certain interest, not compulsory for our goal. Make the same preparations and run two consoles with logging hcpdump applications and it was described in the previous post. Let’s analyze packet exchange a bit further, immediately after establishing connection to SDP. Here is what the first console would typically report:

< ACL data: handle 43 flags 0x02 dlen 24
    L2CAP(d): cid 0x0040 len 20 [psm 1]
        SDP SSA Req: tid 0x0 len 0xf //Service search attribute request is sent to remote endpoint
          pat uuid-16 0x1101 (SP)
          max 65535
          aid(s) 0x0000 - 0xffff
          cont 00
> ACL data: handle 43 flags 0x02 dlen 50
    L2CAP(d): cid 0x0040 len 46 [psm 1]
        SDP SSA Rsp: tid 0x0 len 0x29 //Service search attribute response is received but it doesn't fit in one packet
          count 36
          cont 02 00 24 //Indication that there is still some data pending
< ACL data: handle 43 flags 0x02 dlen 26
    L2CAP(d): cid 0x0040 len 22 [psm 1]
        SDP SSA Req: tid 0x1 len 0x11 //Service search attribute request is sent again to fetch what didn't fit in the first packet
          pat uuid-16 0x1101 (SP)
          max 65535
          aid(s) 0x0000 - 0xffff
          cont 02 00 24 //Indication that we need to get the pending part of data
> ACL data: handle 43 flags 0x02 dlen 28
    L2CAP(d): cid 0x0040 len 24 [psm 1]
        SDP SSA Rsp: tid 0x1 len 0x13 //Second service search attribute response with remaining data
          count 16
          record #0
              aid 0x0000 (SrvRecHndl)
                 uint 0x10007
              aid 0x0001 (SrvClassIDList)
                 < uuid-16 0x1101 (SP) >
              aid 0x0004 (ProtocolDescList)
                 < < uuid-16 0x0100 (L2CAP) > <
                 uuid-16 0x0003 (RFCOMM) uint 0x10 > >
              aid 0x0009 (BTProfileDescList)
                 < < uuid-16 0x1101 (SP) uint 0x100 > >
          cont 00 //Indication that there is no pending data anymore

Let’s have a look at the same exchange in raw representation logged by the second console and try to decode it:

< 0000: 02 2a 20 18 00 14 00 40  00 06 00 00 00 0f 35 03  .* ....@......5. //1-st SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU (0x06 at address 0x0009)
  0010: 19 11 01 ff ff 35 05 0a  00 00 ff ff 00           .....5.......

> 0000: 02 2a 20 32 00 2e 00 40  00 07 00 00 00 29 00 24  .* 2...@.....).$ //1-st SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU (0x07 at address 0x0009)
  0010: 36 00 31 36 00 2e 09 00  00 0a 00 01 00 07 09 00  6.16............
  0020: 01 35 03 19 11 01 09 00  04 35 0c 35 03 19 01 00  .5.......5.5....
  0030: 35 05 19 00 02 00 24                              5.....$

< 0000: 02 2a 20 1a 00 16 00 40  00 06 00 01 00 11 35 03  .* ....@......5. //1-st SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU (0x06 at address 0x0009)
  0010: 19 11 01 ff ff 35 05 0a  00 00 ff ff 02 00 24     .....5........$

> 0000: 02 2a 20 1c 00 18 00 40  00 07 00 01 00 13 00 10  .* ....@........ //2-nd SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU (0x07 at address 0x0009)
  0010: 03 08 10 09 00 09 35 08  35 06 19 11 01 09 01 00  ......5.5.......
  0020: 00                                                .

Again, it would be worth getting familiar with internal packet structure and payload. An SDP service is basically a hierarchical list of attributes, each attribute has a list of field-value pairs and fields can have different types (byte, short, int – just like modern programming language). Attributes can be grouped and can nest more elements or groups inside. This is pretty much what we need to know about SDP services. Let’s have a closer look at the binary data:

=== HCI header ===
02 2a HCI hanle with PB and BC flags
20 18 HCI ACL total data length (it actually specifies total length 0x0018)
00 14 L2CAP payload length
00 40 SDP channel ID (please note that channel number is not 0x0001 anymore that is why the packet is interpreted as SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU 
  and not as L2CAP_CMD_DISCONNECT_REQUEST (although they both have the same code)

00  ??? Unknown byte, it is present neither in spec nor in packets when sent from Arduino/Luminardo.
  Probably a bug in hcidump as the byte is not even accounted by HCI ACL total data length, just ignore it

=== Actual L2CAP command ===
06 SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU
00 00 Transaction identifier
00 0f Parameter length
{
  35 Data Element Sequence, data size in next 8 bits
  03 Data size
  {
    19 Field type UUID, 2 bytes
    11 01 Field value 0x0100 - L2CAP_UUID
  }
  ff ff Maximum attribute byte count
  35 Data Element Sequence, data size in next 8 bits
  05 Data size
  {
    0a Field type Unsigned int, 4 bytes  - Attribute ID range
    00 00 Attribute range from 0x0000...
    ff ff ... to 0xFFFF
  }
}
00 No more data

In essence, the packet above is requesting all attributes with identifiers from 0 to 0xFFFF from service with UUID = 0×0100.

=== HCI header ===
02 2a 20 32 00 2e 00 40  00 - HCI header, as above, please see the SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU packet structure

=== Actual L2CAP command ===
07 SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU
00 00 Transaction identifier
00 29 Parameter length
00 24 Attribute lists byte count
{
  36 Data Element Sequence, size in next 16 bits
  00 31 Size
  {
    36 Data Element Sequence, size in next 16 bits
    00 2e Size
    {
      09 Unsigned integer in next 16 bits
      00 00 Attr ID 0000 - "Service Record Handle"
      0a Unsigned integer in next 32 bits
      00 01 00 07 Attr Value - handle

      09 Unsigned integer in next 16 bits
      00 01 Attr ID 0001 - "Service Class ID"
      35 Data Element Sequence, size in next 8 bits
      03 Size
      {
        19 UUID, 2 bytes
        11 01 "SERIALPORT  UUID"
      }

     09 Unsigned integer in next 16 bits
     00 04 Attr ID 0004 - "Protocol Descriptor List"
     35 Data Element Sequence, size in next 8 bits
     0c Size
     {
       35 Data Element Sequence, size in next 8 bits
       03 Size
       {
         19 UUID, 2 bytes
         01 00 "L2CAP UUID"
       }

       35 Data Element Sequence, size in next 8 bits
       05 Size
       {
         19 UUID, 2 bytes
         00 ... end of data, 3 bytes are still missing, will be continued in next package
         ...
       }
}
02 Size in next two bytes
00 24 More data to come
=== HCI header ===
02 2a 20 1a 00 16 00 40 00 - HCI header, as above, please see the SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU packet structure

=== Actual L2CAP command ===
06 SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU
00 01 Transaction Identifier
00 11 Parameter length
{
  35 Data Element Sequence, data size in next 8 bits
  03 Data Size
  {
    19 Field type UUID, 2 bytes
    11 01 Field value 0x0100 - L2CAP_UUID
  }
  ff ff Maximum attribute byte count
  35 Data Element Sequence, data size in next 8 bits
  05 Data Size
  {
    0a Field type Unsigned int, 4 bytes  - Attribute ID range
    00 00 Attribute range from 0x0000...
    ff ff ... to 0xFFFF
  }
}
02 Copied from last response,
00 24 in essence it is a request to get the remaining data
=== HCI header ===
02 2a 20 1c 00 18 00 40 00 - HCI header, as above, please see the SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU packet structure

=== Actual L2CAP command ===
07 SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU
00 01 Transaction identifier
00 13 Parameter length
00 10 Attribute lists byte count
{
  03 Continuation of 0x00 from previous packet makes "00 03" - "RFCOMM UUID"
  08 Unsigned integer in next 8 bits
  10 Here it is, a RFComm channel number, that is what we are after!
  
  09 Unsigned integer in next 16 bits
  00 09 Attr ID 0009 - "Bluetooth Profile Descriptor List"
  35 Data Element Sequence, data size in next 8 bits
  08 Data Size
  {
    35 Data Element Sequence, data size in next 8 bits
    06 Data Size
    {
      19 UUID, 2 bytes
      11 01 "SERIALPORT  UUID"
      09 Unsigned int in next 16 bits
      01 00 Profile version number
   }
}
00 No more data

As an attentive reader has probably already noted, in the last package we received a value that we were after, it is RFComm channel number which is 0×10 for this particular case. At this point we know what has to be sent and how in order to get this magic number. Enough theories, time to put our knowledge in practice. As a prototype to validate our ideas with SDP service a limited in functionality class called SPPi has been designed, please refer to the Download section of this post. Copy SPPi.cpp and SPPi.h to USB Host 2.0 folder then compile and run Luminardo_USBHost_Bluetooth_SPPi.ino. The output should be like this (which proves that now we can talk SDP):

Luminardo USB Host Bluetooth SPPi Test
SPP Bluetooth Library Started
Enabling VBus... enabled
Bluetooth Dongle Initialized
HCI Reset complete
Write class of device
Local Bluetooth Address: 00:19:0E:12:65:6A
The name is set to: Luminardo
Pairing to 'Other' device with predefined address
Device: 40:22:11:00:69:58 has been found
Connecting to 'Other' device...
Connected to 'Other' device
Received Key Request
Bluetooth pin is set too: 1234
Pairing successful with 'Other' device
SDP Connection Request
L2CAP Connection Response
SDP Connection Response
SDP Configuration Request Received
SDP Configuration Response Sent
SDP Configuration Request Sent
SDP Configuration Response Received
SDP Successfully Configured
SDP Service Search Attribute Request 1 Sent
SDP data... - SDP Service Search Attribute Response 1: 0B 20 34 00 30 00 41 00 07 00 48 00 2B 00 26 36 00 31 36 00 2E 09 00 00 0A 00 01 00 07 09 00 01 35 03 19
11 01 09 00 04 35 0C 35 03 19 01 00 35 05 19 00 03 08 02 00 26
SDP Service Search Attribute Request 2 Sent
SDP data... - SDP Service Search Attribute Response 2: 0B 20 1A 00 16 00 41 00 07 00 49 00 11 00 0E 10 09 00 09 35 08 35 06 19 11 01 09 01 00 00

Now you can move to the next, final step – to start RFComm transport layer which will be described in detail in next post. Stay tuned and don’t hesitate to ask questions!

Downloads:

1. USB Host 2.0 Add-on Allowing to Initiate Communication with SDP service.

Bluetooth for Dummies: Establishing Connection to SDP

This post is a continuation of our endeavour to enhance USB Host 2.0 for Arduino library making a particular focus on SPP Bluetooth profile and Luminardo’s ability to initiate connection to a remote Bluetooth SPP-aware hardware. Last time it has been described what had to be changed in the library in order for Luminardo to start pairing sequence. Today we are going to progress further and discover what else has to be done. But before we proceed first we need do to some preparations to make sure that we have tools and documentation handy. Note, that this post doesn’t set a goal to make a complete Bluetooth guide, it is rather an example which demostrates where to start and how to learn in order to solve a problem related to BT stack.

First of all, we would need Bluetooth core specification. It sounds obvious but strangely enough it is not such as straightforward as it seems: there are several core specifications with version numbers ranging from 2.0 to 4.1 not to mention numerous specification ‘addendums’. A brief investigation reveals that the library is built around rev.4.0 and the appropriate document location would be here. Two more documents would be also extremely useful: SPP Specification ver.1.2 and GSM 07.10 Specification.

Secondly, having a real working system (e.g. two Bluetooth SPP-aware devices) as a reference point can significantly simplify the learning process as all the above mentioned documents are really difficult to read and even more difficult to comprehend. Being able to watch the actual data exchange is a must. Using a PC with Windows OS gives virtually nothing as the Bluetooth stack is an integral part of OS kernel and apparently there is no way to sniff or log Bluetooth packets. What is even worse, different versions of Windows use completely different Bluetooth stacks but all of them are black boxes – nobody knows what is happening inside. On the other hand, Linux and its Bluetooth stack have everything in place for efficient packet debuging and troubleshooting. Out team used Lubuntu with pre-installed Bluez package. Hovewer, to log packets one more package must be installed: bluez-hcidump. In our environment all that needed to be done to install it was apt-get install bluez-hcidump in command line.

And finally we would need a device to connect to. It could be another PC or laptor with Bluetooth adapter or an ELM327 adapter. In latter case an adapter must be powered up as at this stage there is no need in physical communication with car’s bus and it is enough to apply supply voltage only. Also it is extremely inconvenient to run experiments in a car be it inside a dark garage or a parking spot, a home lab would be a better place. Any 12VDC power supply capable of delivering at least 100 mA will do. During our tests we used a bench power supply as shown on the pictures below. Make sure you get polarity right: brown wire is GND, red wire is +12VDC.

Powering ELM327 off Bench Power Supply

Powering ELM327 off Bench Power Supply

Powering ELM327: wiring polarity

Powering ELM327: wiring polarity

Now a significant amount of time must be spent trying to combine knowledge contained in Bluetooth specs, logic contained in SPP.cpp and BTD.cpp files and common sense. It also takes some time to get familiar with bluez-hcidump peculiarities as it offers wide range of options allowing to log in different formats. The specs are written in a very strange way, while reading you keep asking yourself only one question over and over again: what were those guys smoking when they wrote those specs? Information is scattered across not only pages and chapters but different files as well, it is extremely difficult to make sense out of it. They keep jumping from one topic to another, often using lengthy descriptions of an entity or a process while a single picture-example would be worth a thousand words, you need to read the same passage multiple times trying to understand what they meant. What is even worse, the information often looks ambiguous and inconclusive, there is no way to comprehend it fully unless you have a real hardware and library to conduct some experiments to proof or disproof your guesses after reading the spec. The information available in the Internet is surprisingly scarce: apart from the official Bluetooth specs which are easy to understand only for people who wrote them there is basically nothing else. Documentation and forum discussions for Bluez or btstack are only about API and how to use it, the actual implementations of the stacks are not discussed as such or discussed by BT professionals who already has a deep knowledge of the specifications. It looks like everyone considers the stack as a given without actual understanding how and why it works, and those who know about it prefer to keep the knowledge closed, it is really unclear how overcome this vacuum before you know enough to progress further towards the numbered group of Bluetooth guru.

As we already spent significant amount of time here are some facts that came out of our effort:
1. Reversing the role of our device from acceptor to initiator requires rater major redesign of logic resided in SPP.cpp. Format and content of packets sent by initiator is unknown at the moment and yet to be discovered;
2. In order to establish connection to a remote device via SPP it is required to interrogate SDP service to get a port number to connect to;
3. As soon as port number is known, connection to RFComm service should be initiated.

All interactions with SDP and RFComm services are done trough underlying L2CAP interface, for simplicity sake consider it as a backbone transport layer. Before any requests can be sent to either SDP or RFComm services a connection to that service has to be established by means of standard set of L2CAP commands. The order of these commands is important. In most cases if a command is malformed or sent at inappropriate time it will be ignored and no error packet of any kind will be sent which undoubtedly makes troubleshooting more difficult. A typical sequence of establishing connection to an SDP (RFComm or any other) service is given below:

1. Initiator sends CONNECTION REQUEST;
2. Acceptor replies with CONNECTION RESPONSE and shortly after sends CONFIGURATION REQUEST;
3. Initiator replies with CONFIGURATION RESPONSE and shortly after sends CONFIGURATION REQUEST (or the other way around);
4. Acceptor replies with CONFIGURATION RESPONSE – and from this moment connection is established and service specific commands can be sent;

Let’s confirm our theoretical knowledge with real working system. Power up ELM327 then start Bluetooth Manager in Linux, search for available devices and it should detect ELM327 as ‘OBDII’. Pair with it and enter pin ’1234′ when requested (please refer to user manual of your ELM327 as there could be another value for pin code). Start Linux two consoles and run in first one hcidump command without parameters and in second one hcidump - X -R command. Then go to OBDII’s properties, click ‘Setup’ and select ‘Connect to Serial Port’. You should see data coming in and out logged by hcidump. Here is a portion of data logged by the first console:

< ACL data: handle 43 flags 0x02 dlen 12
    L2CAP(s): Connect req: psm 1 scid 0x0040  //outgoing CONNECTION REQUEST to PSM = 1 (Service Discovery Protocol) from a client with SCID = 0x0040 (Source Channel Identifier, a unique ID of our endpoint or client)
> ACL data: handle 43 flags 0x02 dlen 16
    L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0 //incoming CONNECTION RESPONSE from a remote endpoint with DCID = 0x0040 with result and status set to 0 (success)
      Connection successful
< ACL data: handle 43 flags 0x02 dlen 12
    L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 0 //outgoing CONFIG REQUEST from our endpoint with config parameters (flags) set to 0
> ACL data: handle 43 flags 0x02 dlen 20
    L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 8 //incoming CONFIG REQUEST from remote endpoint
      MTU 60 
      FlushTO 65535 
< ACL data: handle 43 flags 0x02 dlen 18
    L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 4 //outgoing CONFIG RESPONSE from our endpoint
      MTU 60 
> ACL data: handle 43 flags 0x02 dlen 18
    L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 4 //incoming CONFIG RESPONSE from remote endpoint
      MTU 48 

The second console logs data in raw format and gives opportunity to see actual byte stream. The same portion of data will look like this:

< 0000: 02 2a 20 0c 00 08 00 01  00 02 03 04 00 01 00 40  .* ............@ //outgoing L2CAP CMD CONNECTION REQUEST (0x02 at address 0x0009)
  0010: 00                                                .

> 0000: 02 2a 20 10 00 0c 00 01  00 03 03 08 00 40 00 40  .* ..........@.@ //incoming L2CAP CMD CONNECTION RESPONSE (0x03 at address 0x0009)
  0010: 00 00 00 00 00                                    .....

< 0000: 02 2a 20 0c 00 08 00 01  00 04 04 04 00 40 00 00  .* ..........@.. //outgoing L2CAP CONFIG REQUEST (0x04 at address 0x0009)
  0010: 00                                                .

> 0000: 02 2a 20 14 00 10 00 01  00 04 4c 0c 00 40 00 00  .* .......L..@.. //incoming L2CAP CONFIG REQUEST (0x04 at address 0x0009)
  0010: 00 01 02 3c 00 02 02 ff  ff                       ...<.....

< 0000: 02 2a 20 12 00 0e 00 01  00 05 4c 0a 00 40 00 00  .* .......L..@.. //outgoing L2CAP CONFIG RESPONSE (0x05 at address 0x0009)
  0010: 00 00 00 01 02 3c 00                              .....<.

> 0000: 02 2a 20 12 00 0e 00 01  00 05 04 0a 00 40 00 00  .* ..........@.. //incoming L2CAP CONFIG RESPONSE (0x05 at address 0x0009)
  0010: 00 00 00 01 02 30 00                              .....0.

At this stage it would be worth to get familiar a bit with packet structure and payload. So far there are four packet types: L2CAP CONNECTION REQUEST, L2CAP CONNECTION RESPONSE, L2CAP CONFIG REQUEST and L2CAP CONFIG RESPONSE. All four structures are given below.

=== HCI header ===
02 2a HCI hanle with PB and BC flags
20 0c HCI ACL total data length (it actually specifies total length 0x000C 
   according to Bluetooth Core Spec. rev.4.0, Host Controller Interface Functional Spec)
   and be 0C 00 instead of 20 0C but we don't worry about it now - it works for us anyway
00 08 L2CAP payload length 
00 01 L2CAP channel ID (0x0001 for ACL-U, please refer to Bluetooth Core Spec. rev.4.0, 
  Logical Link Control and Adaptation Protocol Specification, page 54)

00 ??? Unknown byte, it is present neither in spec nor in packets when sent from Arduino/Luminardo. 
  Probably a bug in hcidump as the byte is not even accounted by HCI ACL total data length

=== Actual L2CAP command ===
02 L2CAP_CMD_CONNECTION_REQUEST
03 Packet identifier
04 00 Command payload Length
01 00 SDP_PSM
40 00 SCID, Source Channel Identifier
=== HCI header ===
02 2a 20 10 00 0c 00 01  00 - HCI header, as above, please see the L2CAP CONNECTION REQUEST packet structure

=== Actual L2CAP command ===
03 L2CAP_CMD_CONNECTION_RESPONSE
03 Packet identifier
08 00 Command payload length
40 00 DCID (Destination Channel Identifier, in other words, a remote endpoint ID)
40 00 SCID (Source Channel Identifier, in other words, local endpoint ID)
00 00 Result 0 (success)
00 00 No more data
1-st case in our log generated by sniffer
=== HCI header ===
02 2a 20 0c 00 08 00 01 00 - HCI header, as above, please see the L2CAP CONNECTION REQUEST packet structure

=== Actual L2CAP command ===
04 L2CAP_CMD_CONFIG_REQUEST
04 Packet identifier
04 00 Command payload length
40 00 DCID (Destination Channel Identifier, in other words, a remote endpoint ID)
00 00 Flags



2-nd case in our log generated by sniffer
=== HCI header ===
02 2a 20 14 00 10 00 01 00 - HCI header, as above, please see the L2CAP CONNECTION REQUEST packet structure

=== Actual L2CAP command ===
04 L2CAP_CMD_CONFIG_REQUEST
4c Packet identifier
0c 00 Command payload length
40 00 DCID (Destination Channel Identifier, in other words, a remote endpoint ID)
00 00 Flags
01 Config Opt: type = MTU (Maximum Transmission Unit) - Hint
02 Config Opt: length
3c 00 MTU
02 Config Opt: type = Flush Timeout
02 Config Opt: length
ff ff Flush timeout
=== HCI header ===
02 2a 20 12 00 0e 00 01 00 - HCI header, as above, please see the L2CAP CONNECTION REQUEST packet structure

=== Actual L2CAP command ===
05 L2CAP_CMD_CONFIG_RESPONSE
4c Packet identifier
0a 00 Command payload length
40 00 SCID (Source Channel Identifier, in other words, local endpoint ID)
00 00 Flag 0
00 00 Result 0 (success)
01 Config Opt: type = MTU (Maximum Transmission Unit) - Hint
02 Config Opt: length
3c 00 MTU

Now we have a bit of understanding on how to establish connection to SDP service (same applies to RFCom) and we are ready to comminicate with SDP service. What should be done and how to do it will be discussed in our nest post. Stay tuned and ask questions if you have any!