Friday, October 16, 2026

Decoding the MEET MS-BDTP1A Multimeter Bluetooth

 Meet the MEET MS-BDTP1A multimeter!

The MEET MS-BDTP1A multimeter is a cute battery-powered, very portable, pen-style multimeter. The maker has a handy web page  with the full specs. This is my second successful multimeter; the previous one was the ANENG 9002 which has the BDM protocol described up on GitHub.

The DMM itself is pretty nice. It handles all the things a portable DMM should, like resistance, volts, and current, plus the typical add-ons like a diode reading and continuity.

Investigating the services and characteristics

Using my new Bluetooth app showed that the device uses service FFF0 like the BDM type multimeters, but using characteristic FFF1 and not FFF4 like the BDM. Setting up the notification for FFF1 shows a stream of data coming in, all 15 chars long, looking like this: "18 27 3D 4E 5B 66 72 86 92 A2 B1 CA DD EE FF"

Copilot and Github both report failure in decoding it. Copilot says it looks like an LCD type interface, so it's similar to the BDM protocol, but it's a different length. Also, looking at the hex dump, the top 4 bits are just the numbers 1 to 9 and A to F, in order, so clearly they are some kind of check for bad transmissions.

LCD type interface

A bunch of these multimeters used an LCD screen interface for their Bluetooth. There's companies that will make a nice one-chip DMM solution, but without a Bluetooth interface. Instead, the company making the DMM has to jury-rig something in. Since the only data they have access to is the data bus leading to the LCD screen on the DMM, they just dump the raw LCD data straight to Bluetooth.

This means that each segment of the 7-segment display gets it's own separate bit in the output, and they aren't going to be neatly bundled together. 

BTW, if you aren't familiar with 7-segment nomenclature, each segment is given a letter A to G, starting at the top segment A and going clockwise and ending the  middle segment called G. I've added a picture from the Fortune spec sheet. To the left of each digit is a decimal point (or, for the leftmost digit, a negative sign).

The Bluetooth interface is over Bluetooth, of course. But since some these devices used to provide data over a RS-232 serial port interface, they are often documented as an RS-232 interface. 

Reverse-engineering the LCD interface

After some thrashing around, the successful steps for reverse engineering were:

Start by exploring some data sheet. Asking Copilot for DMM chip spec sheets. This brought up the Fortune FS9731 device where the RS-232 interface matched the style of the data I was seeing. Notably, the top four bits are doing the "counting up" thing. 

Here's the Fortune RS9731 spec sheet RS-232 data. You can see the 1X 2X 3X etc which matches the kind of data the Notify is providing.


When I compared my notify data against the LCD data, it absolutely didn't match at all. 

Find a spec sheet that's a better match. I found a spec from the same Fortune company where the spec sheet matches the specs of the MEET device. The MEET device says it's a "2000 count" aka "3½ digit" display, which matched the Fortune FS 9952_LP2 spec. That spec sheet didn't include any RS-232 interface but it did include a LCD screen map. And their LCD screen picture kind of looked like the screen in the MEET device.

Here's the Fortune FS 9952 LCD display spec.



To convert this into code, I just had to decode the bottom table. The first four columns aren't useful; it's the next that have the real data. The data as transmitted is the SEG1 in the bottom bits of the first byte, the SEG2 in the bottom bits of the second byte, and so on.

Note how the data is scattered seemingly at random. There's some hints of a pattern, but not enough to really write code for.

And it all worked!

 After a bit of fiddling, I could update my BDM Multimeter display to also support the MEET MS-BDTP1A multimeter. Here it is in action:



Total time: Exploring the protocol and comparing to BDM protocol was < 8 hours. Finding the right spec sheet was about 2 hours of work. And the actual code modifications was 4 hours.


Sunday, October 11, 2026

Your Bluetooth is bad, October 2026 edition

Your Bluetooth is bad, time setting edition for October 2026

 

Adding a "set the time" feature? Use 64-bit times, and two-byte, minute-precise time zone offsets with +/- 24 hours. Also consider listening to the Bluetooth clock value

 

I'm adding another small environment sensor to my new Windows Bluetooth app, and there's new-to-me issues with setting the time.

 

The obvious issues: the time is a Unix epoch time, but shoved into 32 bits. This is silly; the Y2038 is coming for all of us when a Unix time won't fix into a 32-bit number any more.

 

And then there's two different weird problems with time zones. Firstly, the time zone is defined to be an integer, in hours. But there's plenty of time zones around the world that are offset by 15, 30, or 45 minutes. Far better is to just allow a minute-by-minute adjustment.

 

But you also can't have a +13 hour time zone, even though those exist. The only workaround is to use -11.

 

But wait, there's another issue: if you're in a negative time zone like I am, the clock will be 15 minutes off for no reason. The solution (thanks to Set clock on Xiaomi LYWSD02 · Issue #643 · esphome/feature-requests) is to add 256 to the negative value. Which is extra weird because it's set with a 2-byte value, so why is there any dependency on bytes?

 

Far better would be to just have a signed integer of the number of minutes for a time zone offset, and then don't apply any corrections.

Tuesday, August 25, 2026

Fun with icons and Vista compliance

 Fun story today about that time I tried to make my company's program comply with the Vista requirements. Spoiler alert: it's literally not possible to comply with Vista requirements.

At the time I worked for WildTangent, a leading provider of games and a nifty game-catalog program that many companies pre-installed on their computers. We also did 3D on the web before realizing that the market for 3D on the web was very limited :-(

Vista had the noble and sensible goal of getting companies to stop using weird, undocumented hacks (read The Old New Thing for many, many blog posts by Raymond Chen about compatibility). Enter a Vista requirement: "Everything must follow the written standards". 

Vista also wanted to be big, fresh and exiting. And in Vista, that meant "large icons". Windows programs were accustomed to making tiny little 8x8 and 16x16 icons. These were appropriate for the tiny screens of the time, but in the Vista timeframe more and more people were getting big monitors.  Enter our second requirement, "all Vista programs must include a 256x256 icon".

And the last bit of knowledge: the officially documented Icon spec in Windows is clear about two things: the icon's size must be included and fit into a 1-byte field, and that there is a limited number of valid icon sizes. 

Both of these are problems. A value of 256 (for a 256x256 icon size) is to large to fit into a 1-byte field. In practice, Windows code just decides that a value of zero means 256, but at the time it was completely undocumented. The icon size of 256x256 not being on the official list of acceptable icon sizes was just the icing on the cake, as it were.

The Microsoft response? They didn't care. 

 

Monday, August 3, 2026

Your Bluetooth is bad, August 2026 (Battery and Name)

 Your Bluetooth is bad, and you should feel bad

Alternate title: Your Bluetooth is OK, but it could be better

 Just two gripes this time!

Always include a name. 

I'm looking at you, Ruuvi. The Ruuvi sensors are interesting little devices. They started out back when the Eddystone protocol was an exciting new thing (fun fact: Copilot thinks there are still major Eddystone devices in the real world!), then made essentially the same sensors but with their own advertising format. And the Ruuvi company includes good documentation on their GitHub!

But ... why don't the tags advertise their names? Normally the way my app can tell one device from another is just from the name (and so far, without any overlaps) or for standard devices like Bike sensors, from the services exposed. Now I have to paw deep into every advertising packet just to see if an advert might be from a Ruuvi.

Use battery percentages, not voltages

Providing a battery voltage and not a percent, IMHO, is just weaseling your way out of a small bit of analysis. And it's an analysis that you, the maker, can do best.

There is no user out there who wants to know that the battery is at 2.45 volts. But everyone wants to know if the battery is running low. You can either do a conversion in your app, or you can just do the conversion on the device.

Not only that, but end users just expect a "ballpark" figure. This is confirmed by Microsoft, which expects devices to provide 11 steps (0..100% in 10% steps including the 0 and 100), and by the Bluetooth SIG which made the standardized battery levels.





Wednesday, July 8, 2026

API Rules for designers: use one encoding, not two

 

API Rules for designers: use one encoding, not two

 

"If you haven't tested your code, it's probably wrong"

 

Today's bad API example is from the Bluetooth Heart Rate system. Most of the protocol is fine: you get a notification every so often with updated heart rate information (e.g.,  heart rate in beats per minutes (BPM) plus optional stuff for overall energy and the "RR Interval" for fine-grained heart data).

 

Testing this protocol is extra expensive because the heart rate (BPM) can be sent in two ways. It might be a single byte, and it might be two bytes. There's a single bit in a flags structure to say which way it is. If the person's heart is 255 BPM or less, it's one byte. If it's 256 or more, it's two bytes. The app code (and device code) has to handle this.

 

For an app, the code is hard to test for two reasons. Most importantly, a human doesn't ever have a heart rate that high (according to Copilot, "255+ BPM is possible, but it’s always pathological"). The second is that even if I could find a person with a heart rate that high, I have no confidence that any specific consumer-grade device will ever produce this data. As just a person coding in their spare time, I can buy a couple of heart monitors. But imagine I still worked at Microsoft: what are the changes that a VP (that's the clearance it takes for approving purchases) would sign off on an unknown number of devices on the chance that one of them would produce this data?

 

In the end, I've got code in my new Bluetooth app (not on the Microsoft app store yet) to handle the special flag. But my confidence that it works isn't great.

 

The right API choice was to always send 2 bytes of data and not try to make something in the name of spurious efficiency.

 

For the nay-sayers: the awkward protocol doesn't support any interesting new scenarios, and doesn't have any appreciable amount of energy.

 

If they had kept the protocol as-is, developers would have the same abilities. The RR Interval data size would shrink from 9 entries to 8 -- but in typical use, there's only 3 or 4 entries. And if there's a case for needing more than 8 entries, the Bluetooth device could simply transmit more often.

 

Takeaway: Protocol designers should always include "how will a developer test their code" when considering complicated APIs.

 

Friday, April 3, 2026

IL2104 IL2026 TRIM and JSON with WinUI3 and newer C#

 JSON: why is C# TRIM so horrible for no reason 

Trim analysis warning IL2026: SerializeExtra.Demonstrate_Bug_Program.Demonstrate_Bug_Main(): Using member 'System.Text.Json.JsonSerializer.Serialize(!!0, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.

In the old days, C# had a simple JSON story: always use the Newtonsoft JSON library, helpfully packaged up in NuGet, and never use anything else, ever. But then I tried making an app with WinUI3 and the more recent C# (like version 10?), and now it completely sucks, and it's ten times harder than it should be. And it's all Microsoft's fault

C# introduced a new concept: TRIM. This is a link time option. It's on by default for Release mode and off by default for Debug mode. What it does it go through your app and removes all of the "unneeded" code. The problem is that it means that the normal way Newtonsoft's JSON works doesn't work; it will just plain crash.

Note the horrific discovery mechanism for this, by the way: there you are, coding happily in a shiny new app, using shiny new features, and a switch you didn't set causes weird-ass warnings when you compile, and then your app crashes, but only when you're almost done and are now testing in Release mode.

The only solution is to switch to the System.Text.Json.JsonSerializer and Deserializer classes. But even these are completely broken with the new switch, and delightfully (sarcasm) the official Microsoft documentation guides you right into the absolute wrong way to use them.

Worse, the C# classes also guide you into the wrong way. Those shiny new templated classes? Those won't work at all.

Luckily, I've researched it so you can just jump right into the pit of success.

  • When you make your SourceGenerationContext from JsonSerializerContext, always add a TypeInfoPropertyName option 
  • When you call Serialize of Deserialize, you must use the non-templated versions. Pass in your object (or string, for deserialize), the typeof(yourclass) and your SourceGenerationContext.Default object. 
  • Never new up a SourceGenerationContext object. When you do, it won't have the JsonSourceGenerationOptions that the Microsoft docs tell you to use
  • Always specify a [JsonSourceGenerationOptions(WriteIndented = true)]. If you wanted a compressed serialization format, you'd have used Protocol Buffers, not JSON. When you use JSON, it's because your users want a human readable result, not some one-line nonsense.

See my complete project on Github for more details. The About file is particularly helpful. Look at the WeatherForecast_Fix1.cs file for full details. The bug is reported to Microsoft .NET team.


Thursday, March 19, 2026

Fixing the dreaded XAML parsing failed

Fixing my XAML Parsing Failed bug (subfolders)


This is a doozy, and is about the last possible bug one would imagine in the new WinUI3 that Microsoft is pushing for us to use when making apps for Windows.

The background when you make an app using C# (and really, why wouldn't you?), you will use the XAML mini-language for describing your layout. XAML has conceptually been around for years, but Microsoft being Microsoft, they can't help but radically change it and rename it every few years. The XAML for WPF isn't the XAML for SilverLight, which isn't the XAML for UWP, which isn't the XAML for the most recent WinUI3

And a problem that a ton of people have discovered is that somewhat randomly they will write some XAML and it will just fail. But it won't fail at compile time (they do compile-time work to make it run fast; the XAML is not interpreted at runtime). Instead, it fails when your XAML page or control is displayed! The exception generally just says "XAML Parsing Error".

Well, I've figured what's going on. Like any seasoned developer, I use folders to organize my controls. Guess what programming system from a major OS company can't handled "putting a control into a folder"? That's right, Microsoft's WinUI3!


And I've got a simple repro [on GitHub](https://github.com/pedasmith/bug_WinUI3_usercontrol_in_folder_xaml_parse)