Sunday, April 23, 2023

Hints on injecting input into Windows (use 'ScanCode' for Unicode!)

 Use ScanCode to inject Unicode characters!

Got a Unicode string and need to inject it using the InputInjector class? Wondering how to inject it since the InjectedInputKeyboardInfo class has a ScanCode and a Virtual Key but no Unicode char? It turns out to be easy!

In the InjectedInputKeyboardInfo class, set the KeyOptions to Unicode and then fill in the ScanCode with the unicode char. Since you've got a string, you'll make a list of InjectedInputKeyboardInfo items and then send them all with the InjectKeyboardInput method.

I know this works because my new keyboard will happily inject  Unicode strings like this: 😋👩‍👩‍👧‍👧!

Handy Links

MSDN learn.microsoft.com ScanCode
Old [UWP]InjectedInputKeyboardInfo with Unicode forum questions that says they have an answer (but don't say what it is): here



Friday, April 14, 2023

Hints on using CircuitPython's adafruit_ble Bluetooth

 Hints on using CircuitPython's adafruit_ble Bluetooth

Some APIs and libraries for Bluetooth are a joy to use: they fit right into our basic concepts of how the protocol work, and match the kinds of tasks we want to do.

And then there's the adafruit_ble library. 

I've been working on a little project using the very nifty AdaFruit  Feature nRF52840, and there's a lot to like about it. They got a ton of the details just *french kiss*, starting with the font on the main device (the 840 is in extra-large letters so you can quickly tell one from the other) and running to their library of compatible "feather" devices and integration into CircuitPython.

But the ble library? They have clearly spent a ton of time and effort on it (which I thank them for). But everything in it is just that little bit backwards from everything I know about Bluetooth.

Example: why doesn't this code work?

Here's a simple example: I've found a device and I know that it has supports the Current Time Service that I want to read. I know it does that because I check to make sure that the service's GUID (0x1805) is part of the device advertisement. This is done with the CircuitPython statement: 

        if BtCurrentTimeServiceClient.uuid in connection

which means I should be able to get that service, right? So this code should work?

    service = connection[BtCurrentTimeServiceClient.uuid]

Because that's what a collection is for: you can check to make sure a key is in the collection, and then pull the value out of the collection. But that's not how CircuitPython works. You can check the services via a guid, but to get a value, you can only do that by providing the type of the object you want out (and it had better derive from Service)

Why this is horrific, and how much time I wasted

Given code that doesn't work, the next step is to figure out how to fix it. Potential fixes I tried included:
  • using a different kind of UUID (UUID versus StandardUUID)
  • getting the UUID from a different place (afafruit_ble versus bleio)
  • testing to make sure that the 'in' wasn't just always returning true by trying a fake UUID
  • connecting at different times
  • stopping the scan before getting data
  • doing a time.sleep(5) before connecting or getting data
  • connecting to the address versus the advertisement
  • getting the service twice
  • pairing
  • iterate through the services (this was really weird)
  • disconnecting when I was done
  • putting it in a try/except block to investigate the exception

That's a lot of investigation just to learn that what's going on is "magic": the CircuitPython library, instead of just boringly reporting on a connected devices's services and characteristics, demands that you carefully create a complete-enough CircuitPython replica of the device you're connecting to.

Doc shortcomings examples

Little snippets of code would have been super handy. All of the documentation assumes that I'm already a Python expert and will instantly understand the Pythonesque ways to getting information

For example, there's a handy "StructCharacteristic" that will put chunk of data out of a Bluetooth characteristic. This is handy when you need to read something like the Current Time Service where the time data is split into 9 different fields, mostly unsigned byte, but one is a 2-byte unsigned short.

But how does one actually read the data? It's not mentioned in the docs or in the source code. Turns out this will read the data:

    char = service.data
    print("    value=", char)
    print("    year=", char[0])
    print("    mon=", char[1])
    print("    day=", char[2])

This works because the data is the characteristic, and seemingly when you get it you get the unpacked version of the raw data.

What should they have done?

Take a look at the Windows Bluetooth (UWP) API. Once you have a device, you can list all the services, and for each service, list all of the characteristics.

Or, if you know more about the device you're connecting to (often the case), you can get just the services and characteristics you're interested in, which is almost certainly going to be faster (no point in poking the Bluetooth device for a bunch of service and characteristic data that you don't care about).

Handy Links

Link to Adafruit Feather nRF52840 Express
Link to the Characteristic class on GitHub which includes Struct































Friday, March 31, 2023

WTF is “Exact time 256” : diving into Bluetooth SIG documents

 

WTF is “Exact time 256”    

The Bluetooth Special Interest Group (SIG) has a metric ton of Bluetooth LE device specs in a massively confusing pile. In this walkthrough I’ll show how I figured out the details of a fairly simple LE protocol. In particular, I’ll be creating something that needs to match the “Current Time Service”, services # 0x1805.

TL/DR: the good document is the “Gatt Specification Supplement”.

On the Bluetooth.com site, you want “Specifications” and under specification you’ll need a tab open for both the “Assigned numbers” and the “Specifications” directory.

In “Assigned numbers”, there’s two important documents. The first is “Assigned Numbers Document”. It’s a giant list of all of the GATT services by name and all of the characteristics. The second is “Gatt Specification Supplement”.

In the “Specifications” directory, find the listing for the "Current Time Service” and click the link to get the service page. It's got a bunch of the lest useful programming documents ever. The only document that’s interesting for most developers is the Current Time Service 1.1 PDF file. Click the link to read the long, complicated document.

Page 9 starts to be interesting: the Current Time Service supports three characteristics: the “Current Time” (page 10), “Local Time”, and “reference Time”. On page 10 it’s mentioned that we’ll be reading the Exact Time 256 field. This is the first bit of useful (and critical) information in the spec.

BTW, when they say “Unknown”, it’s not clear to me which “Unknown” value they mean. But in the Assigned Numbers document there are 8 time that “Unknown” is mentioned; all of the numeric values (6 of them) are zero. The others are weird strings for something having to do with telegrams. Or not, it’s a Bluetooth SIG spec, so nothing is clear.

At this point you might wonder what the actual bytes are. The Bluetooth SIG doesn’t care that you’re wondering. You might even try typing “Exact Time 256” into the main page search box on the main page, but you won’t get any hits. However, you can find it in the Assigned numbers where it has number 0x2A0C in the “Characteristics by name” and “Characteristics by UUID” section.

Note that in the “Specifications” it lists “Current Time” as the characteristic; that’s characteristic 0x2A2B.

And then finally, take a look at the GATT Specification Supplement, page 78, section 3.62, “Current Time”. It lists the bytes: there’s an Exact Time 256 and then a U8 “Adjust Reason” which says why the time changed. Even better, there's a decent overview of the Exact Time 256 fields!

And here the path through the forest of all Bluetooth knowledge ends. If only there was some way to know that the "supplement" is in fact the useful document, and not the pile of other docs.

Wednesday, March 15, 2023

It takes a lot of people to ship a complex product

 I'm a Program Manager (PM) for one of Those Big Companies. I don't actually program except as a hobby, and I don't manage. Where I add value is making sure that what we're building is what our customers need.

"I could build a replacement with just ___ people"

No, you couldn't, if it's a complex project. I was recently reminded of the true history of the automobile: a long, hard slog by an enormous number of people to get each element of a car to actually work. In particular, take a look at Motor-Car Principles on Project Gutenberg. Among the many, many things that had to be painfully figured out


How to stop a car. The book lists a whole series of brake types (none of them disk brakes like in modern cars) 



How an axle works. Car axles are not wagon axels, and you can't use one instead of the other.

Building a transmission. This is something steam engines, for example, simply don't have. A steam engine on a train is directly connected to the wheels with no intervening transmission.



Making a piston. Steam engines, in contrast to car engines, are forgiving: as long as the timing is roughly correct, the steam engine will work. A gas engine has much higher tolerances.

The book goes on with a long, long list of technologies that are unique to cars.

Moral of the story: some projects are pretty simple and can be done with a small team (or even just one person). But once you get to new technology, it's a quagmire that requires an extraordinary amount of work just to know what needs to be done, and from there to co-ordinate the work so that all the parts show up on time.

Thursday, March 2, 2023

Fixing "The type or namespace 'Windows' could not be found" error CS0246

"The type or namespace 'Windows' could not be found" solution!

Every time I install a new version of Visual Studio, I run into just a metric hand-full of errors from previously working projects. The projects haven't changed, but suddenly Visual Studio is seemingly incapable of finding the "Windows" namespace.

My Solution: run the Visual Studio Installer". Pick your installed version and click Modify. The modification you probably want is to under Universal Windows Platform development in the Optional section. Look at the list of available Windows SDK and just install them all (assuming you have enough disk space).

There's no harm (AFAICT) in getting them all, and it will save you heartache and frustration down the line.

This entire process is just a cluster. What's more common than building apps for Windows using the compiler tools from Microsoft that are specifically designed to build Windows apps? Well, the only thing more common then developing windows apps is seeing yet another place where Visual Studio once again makes development more painful.

For me, the #1 reason I get the error is that I don't have enough of the Windows SDKs installed. By default, the Visual Studio installer only installs the "most recent" SDK. You might think that's enough, but it's not.

Each project will have a reference path that looks like this:

    <Reference Include="Windows.Foundation.UniversalApiContract">
      <HintPath>..\..\..\..\..\..\Program Files (x86)\Windows Kits\10\References\10.0.22010.0\Windows.Foundation.UniversalApiContract\14.0.0.0\Windows.Foundation.UniversalApiContract.winmd</HintPath>
    </Reference>

Note how the HintPath specifies exactly one particular SDK. If you have a more recent version, it doesn't count even though the contracts are specifically designed to be upwards compatible.

What's worse, Visual Studio loves to kind of silently update your project files, so you'll silently be updated, potentially breaking the project on a different computer.

Error CS0246 The type or namespace name 'Windows' could not be found (are you missing a using directive or an assembly reference?)

Sunday, February 5, 2023

Wi-Fi Performance samples

 Wi-Fi Performance, broadly

Number 2 in a series on Wi-Fi performance. This time, Wi-Fi (and networking in general) is compared in difference places is compared. Sneak preview: Bluetooth PAN is the slowest :-)


I ran a simple speed test and gathered latency information (round-trip UDP times) in a variety of places and network. No surprise, Ethernet is the fastest and Bluetooth is the slowest. Airport Wi-Fi had generally high throughput (it's higher throughput than my house Wi-Fi network, although note that I have fiber with a low bandwidth cap) but with medium latency.

PAN? Bluetooth Personal Area Network? What's that?

Bluetooth PAN (Personal Area Network) is a technology that lets you share an internet connection from one laptop to another using Bluetooth instead of Wi-Fi. I'm not quite sure why people would do this, but it's still supported in Windows. It's got a non-winning combination of low speed and high latency. 

To actually share a network connection using PAN:

1. On the "host" laptop, open the Mobile Hotspot settings, and share your Wi-Fi (or Ethernet) connection with Bluetooth. 

2. On the "using" laptop, you need to get to the PAN settings page. AFAICT, there's only one way to do this: 

  • right-click on the Bluetooth icon and click "Join a Personal Area Network"
  • in the resulting Vista-era "Devices and Printers", select the "host" computer whose network you want to join. You might need to first pair with the "host" computer.
  • click connect using and select direct connection.


Wi-Fi: Is it faster on AC? Is it slower on Battery?

 Wi-Fi Performance: AC versus DC

One of a series of comparisons to learn what makes a difference in Wi-Fi performance.

TL/DR: they aren't difference, at least on my setup.

In the screenshot, I've done several latency, download, and upload tests against one specific server.

Test methodology: With the laptop plugged in, run all the speed tests twice with "AC" as the note. Then remove the docking station (the part that is plugged in) and run the same tests against the same server and mark them "DC".

Data Analysis: 
  • Download mean throughput increased from 16.2 Mbps to 18.4 Mbps going from DC to AC. This isn't a large difference.
  • Upload mean throughput decreased from 31.3 Mbps to 31.28 Mbps going from DC to AC. This is almost certainly not a real difference.
  • Mean Latency decreased from 12 msec to 10.3 msec. My stats program tells me this is probably a real difference. The DC had a little bit less jitter, but not by a real amount.
Conclusion: Wi-Fi is about the same speed on DC as it is on AC.