I have been deeply involved in both Shack-Hartmann and lateral-shear polarization interferometers. Now I want something simple and slow for hobby projects and had the same question. I don’t think such a project is as daunting as might be imagined. One would like 12 bits in the camera, the Sony CMOS cameras are so good, the last bit is nearly noise free.
The camera need not be expensive: Any amateur telescope web store will have dozens for less than $400. You want monochrome, preferably USB 3, with the largest diagonal focal plane size you can afford. A Global shutter is nice if you have a dynamic situation. Rolling shutters are generally lower noise.
A lens array will be expensive. Thorlabs, Seuss, and RPC Photonics, and others sell them for \$400-\$600. The Thorlabs arrays are mounted. In the end you want the focal spot to be at least 2 pixels across. Choose your micro lens F/# accordingly, usually quite slow, F/20 or more. Likely you want the closest pitch you can find as this sets the spatial resolution. A 4 x 5 mm focal plane will view 40 x 50 micro lens spots. Good enough.
You will need to craft a mount that mates the camera and the lenslet array at the micro lens focus distance. They are slow, so the depth of field should be forgiving.
The real crux is the software. There is public domain software that is close (git has AOtools) and there may be more. You will need to take a dark exposure for the shutter time used (I typically take 100 frames and average them). Then the reaL GOTCHA issue: What to use as a reference? You can use a shear plate to focus a collimated light source (a single mode VCSEL works well), but you are unlikely to have a shear plate about. Place the VCSEL or other point source bright enough to see (an LED?) as far away in a dark room as you can manage. Don’t use a mirror or lenses, turn off heating and cooling, etc. This gives you a known curvature. for 100 μm lenslets at F/20 the focal length is 2mm so a tilt of 1/2 a subaperture (to keep things easy) or 50 μm is only 25 milliradians (mr), something over a degree. The edge of the camera must see the source as no larger than this angle. So if you have 50 subapertures across your camera the edge is 2.5 mm from the middle. Thus 2.5e-3 meters / d = 2.5e-3 radians so the source needs to be more than a meter away. Easy! With the distance from the source to the focal plane known you can now easily calculate the tilt distribution (should look spherical).
Use openCV or your favorite utility to get images into your computer and a) subtract background b) make sure the camera does not saturate (adjust exposure time, retake the dark background and try again) c) calculate the peak intensity of each spot and which pixel coordinates that is in d) Draw a box around each focal spot which is several spot diameters across each spot e) calculate the centroid of each spot using only pixels in the box (the rule is add no pixels that do not contribute to your signal, the centroid). You can play with the box size. An F/20 blue-green 500 nm wavelength spot from a 100 μm lenslet will be about lambda F/# across (just 20 wavelengths, about 10 μm) which should be 3 to 4 pixels so a box 16 x 16 pixels should be a good place to start. The centroids should be good to a small fraction of a pixel. I have done better than 1/100 pixel with a really good focal plane array and 1/20 pixel for standard cameras such as the above. Now you can calculate the centroid location within the box, and reference that to the whole array. A plot across a slice will give you a linear function which is somewhat less than the lenslet pitch. Do this in both X and Y. You may have a rotation in the spot coordinates. You can physically correct this, or use math. The slope in X and Y should be the same. If not, the lens array is tilted. If you move the light further away, the spots should move to be more directly behind the lenslets.
In passing I would note that I found the accuracy of the lithography on the imager, and used to make the lenslet array was better than my best tilt calibration! Thus for a 3.5 μm pixel and 100 μm lenslet the spots should be 100/3.5 pixels apart. This will be independent of focus etc. I also found trying to uniformly illuminate the array to correct for the variation in responsivity pixel by pixel generated more noise, not less. Silicon is awfully good.
The last calibration needed is to define where zero tilt is. This should be close to normal to the focal plane array, so pick the middle lenslet and the pixel you declare is on boresight. Now you have the subaperture tilts calibrated.
The last messy task is to convert from wavefront subaperture tilt to wavefront. There are books written on the subject. Notionally if you start at your zero tilt and add up the subaperture tilts as you move across the array, subtracting the zero tilt position of each subaperture, you have wavefront. Note you can take any path from the center to the subaperture you want to know and add up the X and Y tilts and you should get the same answer. Ideally you want to take the average of all possible paths (that are illuminated) as that would be the best average value you could get. This is referred to as the minimum RMS error. There are many ways to calculate this. If you think of the tilts as the gradient in phase, and calculate the divergence of the gradients, you have a measure of the wavefront curvature. It is also simply Laplace’s equation which can be solved by successive over-relaxation, or if you like fancy algorithms, use a multigrid method (it’s way faster). Greg Allen’s thesis has another approach, https://dspace.mit.edu/bitstream/handle/1721.1/120381/1084482108-MIT.pdf?sequence=1&isAllowed=y . There are many more.
In summary, you can build a Shack-Hartmann wavefront sensor for about $1000 and a lot of software development. The Thorlabs sensor seems cheap given the effort, at least until someone comes up with an open source sensor and software.