I'm having fun with an NXP Rapid IOT kit as part of a Hackster.io contest. This is the first time I've really tried to deal with a magnetometer and how to convert the seemingly random output into something that can be used for a compass. Here's what I've learned!
You have to calibrate your code to match the magnetometer values. I simply looked at the range of X and Y values as I rotated the IOT kit (range in this case meaning to grab the highest and lowest values). This gives you a midpoint for the X and Y values.
Calculate an adjusted X and Y: Xadjust = (Xmidpoint - Xraw) where the values have the obvious meaning. I suppose a better adjustment would also divide by the overall range so that the X and Y values are also normalized, but so far I haven't needed to do that.
Calculate an angle with the always handy atan2(x, y) function. You might remember from trigonometry class that the tangent of an angle is equal to the opposite side of the triangle divided by the adjacent side. If you know opposite/adjacent, then you automatically know the tangent of the angle, and by using the arc-tangent, can get the original angle back. In computers, we use atan2 and provide the X and Y (aka, adjacent and opposite) values directly; otherwise, there would be a divide-by-zero problem when the adjacent is zero (aka, when pointing magnetic north/south)
In computers we always get the angle in radians; multiply by 180/3.1416 to get the angle in degrees. No, that value for PI is a little off, but take a look at the results you get; they jitter by several degree no matter what.
But then there was a big discovery: my laptop has magnets that really, really interfere with the readings! This isn't helped by the short USB cable provided!
TL/DR: compass heading is atan2 (xadjust, yadjust) where the adjusted values are relative to the actual midpoint that you measure. You have to do the measure locally; it's not a globally-valid amount all over the world. The phrase "all over the world" includes "especially near a modern laptop with magnets"
Fun historical fact: iron ships included "deviascopes" to adjust the magnetic compass and to offset the residual magnetism left over from building the ship. A ship's magnetic field will potentially change every time the ship goes in for servicing (and on battleships, every time the big guns are moved!)