Sunday, September 28, 2025

Custom PCBs for ESP32 I2C sensors to use with Home Assistant

Background 

I have a post from a few years ago about how I use I2C sensors with ESP32s to make relatively cheap and easy smart home sensors.  These all run software called ESPHome, which is really easy to use for these DIY sensors, and if you're running Home Assistant and have any interest in DIYing sensors I'd highly recommend you check it out.

I won't go into too much detail about how these work here, since you can go back and read that post, but the key is that all my sensors use a protocol called I2C which is just a way to do short distance low speed communications over 4 wires.  The nice thing is that I2C uses a bus, where many sensors all share those same 4 wires, so you don't need dedicated pins for every sensor.  All you need to do is find a sensor that detects what you want to measure, and uses I2C (and confirm the voltage is 5V or 3V3, more on that later), then you can hook multiple sensors up to 4 pins on the ESP32, and run ESPHome on it.

The only tricky part is how you physically connect those 4 wires from multiple sensors to the same 4 pins on the ESP32.  That's not hard to do for a one off, where you don't care what it looks like, but if you're trying to make a lot of these, easily, and have something that looks semi-presentable it becomes a lot harder.

 

An example of my previous DIY solution to I2C sensors
 

Above you can see what I came up with before.  It's just some protoboard with bare copper wires (scavenged from ethernet cables).  It works fine, but it's more annoying to wire everything up than you might think.

For a long time I've figured someone has to make a basic circuit board for exactly this purpose.  Just connecting the correct 4 pins on the ESP32 to a bus of 4 pin sockets you can plug in whatever I2C sensors you want.  Theses types of boards are commonly called "hats" and while they seem more common for Raspberry Pis they exist for ESP32s as well.

Either way, I could never find one, and so I decided to make this my first foray into designing and printing custom PCBs.

Getting the PCBs 

I'm trying not to make this post into one of my trademark long sagas, so I'll skip over a lot of the design details; I'm no expert anyway.  I will say, I used KiCAD to do the design, starting with the schematic editor, and then switching to the PCB editor.  The only items I'm placing are pin sockets and the board only needs 2 layers.  If you're interested in doing something similar there are many tutorials online.

The revised board I made

I then used JLCPCB to have the boards printed.  There's another company called PCBWay which I've also heard good things about, and the prices were similar.  Both these sites want you to upload "Gerber files" which I exported from KiCAD (including exporting drill files, that was not obvious).  Then I just zipped the files I got from the export, and that's zip file is the gerber file.

Order details of my custom PCB from JLCPCB

This is a screenshot of my order details from JLCPCB.  Mainly, just in case anyone else wants to get some of these boards made, they can use it as a reference.  But I didn't change much, besides choosing lead free solder and black as the board color, because I want my PCB all black.

Tariffs 

There was a lot of uncertainty for me as to what, if any, tariffs I'd have to pay to import these boards from China to the US.  So allow me to post another data point here.  I ordered the boards on Aug 11th, 2025, and it cost $5.22 total shipped, after a first time customer type discount of $9.46.  They were delivered on Aug 20th, and the government has yet to come to my house demanding import tariffs, so I think I'm in the clear.  Of course, this info is already like 3 iterations of tariffs out of date, so only time will tell what you will pay.

20 Custom PCBs, printed and shipped from China for $5

My one regret

I had one regret in the design, which I've since updated for if I ever get more of these printed, or if anyone else uses my design.  I mentioned above that when buying I2C sensors you have to check if they are 5 V or 3.3 V (called 3V3).  The ESP32 supplies both voltages (if you power if from USB) so you can use either, but if you're powering all the sensors from the same bus they all need to be the same.  All the sensors I've bought are 5V, but I figured it might be nice to have the option to use either.  So I designed a solder jumper in the middle of the board.  The 5V supply comes in from one side, and the 3V3 comes in on the other, then the output is in the middle.  You need to connect one of those two sides with a blob of solder to the middle pad, but not all 3 together.

I didn't really think much about this, other than patting myself on the back for being so clever to design the flexibility into my boards, but when I got them I realized how annoying it was to actually bridge the solder.  It's not impossible, but it isn't trivial.  Searching around for how to do this lead to a lot of discussions basically saying, "yeah, it sucks, so don't use solder bridges".  So, I redesigned the boards to just have 3 through holes there, that you can solder in male pins and just use a normal jumper cap to select the voltage, or even just solder a wire between the two, which would be pretty easy to do.

It's not worth getting new boards made, but if/when I ever get more made I will use the new design.

The boards with headers soldered on and one attached to an ESP32

Here you can see 5 boards with the female headers soldered on.  The bottom left board shows the solder jumper bridged.  The stack on the right and up top show unsoldered boards.

Side view of the finished board, with 3 sensors connected

Top view of the finished board

 

These pictures show the finished product.  There are 3 sensors attached to this board, with 2 spare available sets of pins I could attach 2 more (although I'd have to solder on the headers).  The sensors here are a BH1750 light sensor, a SHT45 temperature and humidity sensor, and a SCD41 CO2 sensor.

The eagle eyed among you may have noticed 2 of these sensors are dangling off cables losing a lot of the nice presentation the board should provide.  A few reasons for that, first I don't really care as this board went behind a TV in my bedroom where you can't see it.  Second, I always dangle the temperature sensors off a wire so they get less heat from the ESP32 itself.  Third though, the SCD41s seem to all have the slightly wrong pin order from what I need.  Most I2C sensors seem to use the order of VCC, GND, SCL, and SDA (in either direction is fine, you can solder the pin header to either side).  But the SCD41s all swap the GND and VCC, which means I need to use the wires so I can swap the order there.

What you need if you want to do this yourself?

If you want to do something similar, you can download my gerber files here and upload them to JLCPCB or PCBWay to get them printed.  Refer to my above screenshot for the options I chose.

This is the old version I had printed, with the solder jumper, and this is the newer version that I haven't actually tested, but has the easier to use through hole jumpers.

Then you need to buy ESP32s.  I get my ESP32s and sensors on Aliexpress these days, although you can also find them on Amazon for a bit more.   This is the ESP32 listing I used, but these don't tend to last long.  If you need to search for your own ESP32, just make sure it is the 30 pin version.  Just literally count the pins and make sure it's 15 on each side.  Then, imagine you are holding the board with the USB port facing down, the bottom left pin should be VIN/VCC, and the pin above that should be GND.  The bottom right pin should be 3V3 (and the one above that should be GND, but that doesn't actually matter for my board).  Then in the upper right the top 5 pins should be labeled D23, D22, TX0, RX0, and D21.  Of those D21 and D22 are the ones used, and technically those pins can be changed in the ESPHome config.  I don't think I've ever seen a 30 pin version of a ESP32 that didn't have the pins arranged like that, but it's good to double check.  Also note they make micro USB and USB C powered versions.

When buying sensors, it's going to depend on what you want to measure.  For temperature, BME280s are good sensors, with temperature, humidity and pressure.  The SHT45s are supposed to be a bit more accurate, with the trade off of not having pressure.  SCD41 are CO2 sensors.  The BH1750 is a light sensor.  They make SHT41 and SHT40 for temperature and SCD40 for CO2 which detect the same things but are less accurate for less money.  Any sensor you get make sure it is I2C, and then try to get all either 5V or 3V3.  Most will be compatible with either voltage, but check the listing.  Then try to get ones with pins in the order of VCC, GND, SCL, and SDA, it's not the end of the world if they don't match that order, but you'll have to use dupont wires to swap the order.  Sometimes they will have more pins other than those, but they don't matter, you can just leave them unconnected.

There are a lot of types of I2C sensors available, just search AliExpress or Amazon for "I2C sensor" to see what is out there.  Here's a $24 LiDAR sensor that works with ESPHome.  Mostly any should work with this board and an ESP32 running ESPHome, but you'll want to do a bit of research if you're buying something I didn't mention above.

Then you need 15 pin female headers and 4 pin female headers.  A kit like this has both size, but if you're going to be make a bunch of these, it's probably cheaper to buy a pack of like 100 of the 4 pin and 20 of the 15 pin ones.

You'll also need power, but the ESP32 power requirements are very low, so any spare USB power supply you have should work.  You should even be able to power them from any USB ports on the back of your TV or any other appliances you have.

Then you'll have to solder on the headers.  Refer to my above pictures as a guide.

Then you setup ESPHome and flash it to the boards.  There are a bunch of guides on how to do that, and you can refer to my previous post for an example of my YAML config. 

Sunday, August 31, 2025

WeatherStar 4000+

https://weatherstar.netbymatt.com/

A really good simulation of the 90s style local on the 8s forecast, complete with smooth jazz. 


 

Wednesday, July 2, 2025

Xfinity using WiFi signals in your house to detect motion

Subject to applicable law, Comcast may disclose information generated by your WiFi Motion to third parties without further notice to you in connection with any law enforcement investigation or proceeding, any dispute to which Comcast is a party, or pursuant to a court order or subpoena. 

https://news.ycombinator.com/item?id=44426726

Friday, May 16, 2025

A leap year check in three instructions

 https://hueffner.de/falk/blog/a-leap-year-check-in-three-instructions.html

With the following code, we can check whether a year 0 ≤ y ≤ 102499 is a leap year with only about 3 CPU instructions:

bool is_leap_year_fast(uint32_t y) {
    return ((y * 1073750999) & 3221352463) <= 126976;
}

How does this work? The answer is surprisingly complex. This article explains it, mostly to have some fun with bit-twiddling; at the end, I'll briefly discuss the practical use.

 

Bonus link:

That article links out to this site several times that lets you input high level code like C/Ruby/Javascript/etc and see the compiled/assembly output.

https://godbolt.org/z/PWs8saMYd

Saturday, May 3, 2025

The one interview question that will protect you from North Korean fake workers

https://www.theregister.com/2025/04/29/north_korea_worker_interview_questions/

The first time in a long time I can think of a "one weird trick" style tip which is actually one weird trick that does seem like it'd be effective.

North Korean infiltrators are bagging roles worldwide throughout the year. Thousands are said to have infiltrated the Fortune 500.

They're masking IPs, exporting laptop farms to America so they can connect into those machines and appear to be working from the USA, and they are using AI – but there's a question during job interviews that never fails to catch them out and forces them to drop out of the recruitment process.

"My favorite interview question, because we've interviewed quite a few of these folks, is something to the effect of 'How fat is Kim Jong Un?' They terminate the call instantly, because it's not worth it to say something negative about that," he told a panel session at the RSA Conference in San Francisco Monday.

Sunday, March 23, 2025

Bed Weight Sensor v3

One of my favorite sensors I'm using with Home Assistant is a home made bed weight sensor.  This lets me know when someone is in bed to do things like dim lights, and stop automations that would announce things.  I also have a DIY burglar alarm set up to detect when a door opens while everyone is in bed.  If you search Amazon for "weight sensor" a lot of listings like this one show up.  That listing includes the strain gauges themselves and also the HX711 board you use to combine 4 of those into a single reading.  If you search around the internet you'll see a lot of tutorials for how to wire those up to measure with an ESP32 (I'm using ESPHome to get that into Home Assistant).  People commonly use 4 gauges, in each of the 4 corners of a platform, but using 2 or 1 is also possible and you can find examples of those as well.

weight gauges and typical wiring

This picture from the above Amazon listing shows how 4 gauges are commonly wired together in what is called a "Wheatstone bridge".

The problems with Bed Weight Sensor v1 and v2

When I first set these up for my bed, I just did a test by putting 2 gauges between the frame and my foam mattress.  That worked ok; it was enough to sense "someone is in bed", but it wasn't very accurate beyond that.

Version 2 used 4 gauges in a row across the bed, with little wooden squares cut out and placed under each, which were then all attached to the frame.  The sensor measured the total weight as a whole, rather than trying to measure the left side of the bed vs the right side, which is another common solution I've seen.

The v2 sensor worked pretty well; it was very accurate at knowing if the bed was empty or not, and was pretty good at knowing how many people were in bed.  However, it didn't do a great job at knowing who was in the bed.  I didn't have many automations that relied on knowing which person was in bed, but there were some.

The sensor also drifted over time, and every few months I had to recalibrate it, which wasn't too much work, but was annoying to have to do.  Frankly, it was amazing it worked at all, considering the gauges were just under the foam mattress, with plenty of the weight on the mattress able to spread out past the gauges and go directly to the frame.

The dream that became Bed Weight Sensor v3

My dream was to build a sensor which put the gauges between the legs of the bed and the floor, so that 100% of the weight of the bed was going through them, and so both sides of the gauge were on a hard surface.  The one problem I had preventing me from doing that was that my bed frame has 12 legs.  While the gauges are cheap enough, I could never find an example for how to wire 12 of them together to get one measurement.  I even asked this question and the advice I got was just to set up 3 sensors, each with 4 gauges, and then just sum their weights in software.  That seemed silly and wasteful to me, but the main problem I had with it was that I had no way to calibrate each of those 3 sensors independently.

To calibrate one of these sensors, you just put two known weights on it, and record the reading each gives you.  Generally you use 0 as one weight, and then some other known weight for the other one, which would be "me" in this case.  So, with 3 of these, one for the left side, one for the right side, and one along the middle, it would be easy enough to get a 0 reading, but how was I supposed to know how much weight was on just the left hand side when I got into bed?  My best hope would be to try to lay across the bed and assume my weight would be evenly spread, but that just didn't seem like a path to success.

Instead, I was sure there was a way to just wire up all 12 gauges to give a single unified weight measurement.  I knew these gauges were just varying their resistance when a strain was applied, and the HX711 was just a high precision ADC that would measure the voltage across the combined resistors and convert that into a digital reading.  So, it seemed pretty clear that the resistors could be wired up to allow the HX711 to read all 12 the same as if they were a group of 4.  Certainly, the specific resistance for a given weight would change, but the calibration step would account for that.

Testing

4 weight gauges set up to measure the change in resistance when weights are put on them

I began just by confirming how these gauges worked with 4 arranged in the common Wheatstone bridge.  I used this StackExchange answer as a reference quite a bit while working on this, and the below image from it shows how one gauge works.

datasheet for a weight gauge

There are 3 wires coming out of each gauge, and there are two resistors inside the gauge.  One resistor is between the white and red wires and its resistance goes up when a weight is applied down on the gauge.  The other resistor is between the red and the black wire, and its resistance goes down as weight is applied down on the gauge.  This means the overall resistance across the entire gauge, from the white wire to the black wire is always the same (2 kΩ for these).  That's why these are often arranged in a Wheatstone bridge, using 4 gauges, each with their black and white wires connected together to form a square, with a voltage applied across 2 of the red wires and then the resulting voltage measured across the other two tells us what the overall reading is.  This image (also from that StackExchange post) shows an example.

example of a Wheatstone bridge with 4 gauges and a weight on 1 of them

This image shows what happens when gauge 4 (G4), on the right hand side has some weight applied to it, which causes one half of it to increase in resistance slightly and the other side of it to decrease slightly.  The other 3 gauges have no weight on them.  Without knowing much about electronics you can trace the path from the positive voltage at G1Red and see that it has less resistance to go through to get to the left hand side (labeled Sig+), and more resistance to get to the right hand side (Sig-).  This means we'd expect the voltage at Sig+ to go up relative to Sig-.  Then tracing up from the bottom, where the negative voltage is applied, we see that the path to Sig- is less resistance, so we'd expect the voltage at that spot to drop further relative to Sig+.  That is how we use a Wheatstone bridge to measure the change in these 4 gauges.

How can we do that for 12 gauges though?  Well if you connect two resistors in parallel you just get a new combined resistor with a lower overall resistance.  There's no reason simply connecting two of these Wheatstone bridges in parallel wouldn't work, you might just lose out on some precision.  I tested this by buying a bunch of strain gauges and wiring up two independent Wheatstone bridges, then connecting them in parallel (so the same points on the red wires from each bridge connected together), and taking a bunch of measurements with different weights.

needlessly complicated data collection

I paid special attention to making sure that the measured voltage was the same however the weight was spread between the two bridges.

the data in question

two Wheatstone bridges, labeled A and E with various weights and the combined voltage

Plotting that data showed exactly what I wanted to see, the voltage scaled linearlly as the weight increased.  It didn't matter which side the weight was applied to, if it was evenly balanced or all on one side or the other, the output voltage was the same.

The next thing to solve for was that I needed some sort of stands for the gauges.  The gauges have rivets sticking out of the bottom of them and the center portion needs to be able to flex slightly, so you have to mount them supported along the outer edge, with the middle unsupported.  This was another thing I didn't consider while making v2 of the bed sensor, and another reason I'm surprised it worked as well as it did.

These gauges are common, so it wasn't hard to find a 3d print model for them.  I used this one, which I slightly modified to remove the screw holes since I wouldn't be using them.  I got a bunch of those printed at my library, and spray painted them and the gauges black so they'd blend in better with my bed frame.

weight gauges in 3d printed trays

Once I had those, I wired up the full system for the first time.  All 12 gauges, on the plastic stands, wired up in 3 parallel Wheatstone bridges, all going to a single HX711 which was connected to an ESP32 running ESPHome configured to measure weight from the HX711.  Each of the 3 bridges had their 4 gauges under the corners of a scrap piece of plywood.  Then I essentially had 3 plywood scales all wired up together reporting a single weight across all of them.

testing the full setup

This worked very well, and it was immediately clear the overall idea would work fine.  The above picture shows 55 lbs total on the system, with 50 of those spread evenly between two Wheatstone bridges and the remaining 5 lbs on the third bridge.  I moved that weight around in different arrangements, and if it was evenly spread or focused on different platforms I still got accurate measurements, generally to within a 1/10th of a pound.  I also left it running overnight and came back the next day to see very little variation.

Putting the bed in Bed Weight Sensor

With the testing done it was now time to install this sensor on my bed.  I planned on making this install a bit more robust and more hidden than v2.  My bed frame has 3 groups of 4 legs each, with one set along the left side, one set along the right side, and one set up the middle.  Thinking about it as rows instead of columns, there's one row along the foot of the bed, two in the middle, and one up at the head.

soldering in the bedroom

This meant each Wheatstone bridge of 4 gauges would have G1 on the leg by the foot, G2 in the middle, G3 above that, and finally G4 up near the head.  Each gauge connected to its neighboring gauge, using either its black or white wire.  If the black wire went down, the white wire went up.  With the exception of G1 and G4, which "wrapped" around and went along the length of the bed from the foot to the head.  That was how each bridge was wired independently, and that gave a red wire coming up each leg.  Then all the similar red wires were connected together, along the rows.  So the red wire from G1 on the left was connect to the red wire from G1 in the middle and G1 on the right.  Each group of 3 red wires was connected to a fourth wire which connected it to a single spot on the HX711.

I don't have any close up pics of the wiring.  You might think it would be helpful, but it's just a confusing mess of wires running all over the bed frame.

I started this process around 4pm, hoping to have it done by dinner time.

At this point, let's stop and do some math.  Each bridge had 4 connections between the black and white wires.  However, only one of those was short enough that the wires from the gauge could be directly connected to each other.  The other 3 needed an extension between them, which doubled the number of connections, giving 7 connections.  For the red wires, these generally also needed to be extended to reach where they all landed in the center of the bed; only the two legs in the center didn't need that.  So this means there were (3 * 7) + 10 = 31 wire to wire solders that needed to be done.  And that only got me a mess of 12 wires in the center of the bed.

Another thing you might want to notice about my bed frame is that it's a dense mesh of bars such that you can't really walk between them, but also can't really walk on them.  This led to me constantly carefully stepping through the bars to get to the center of the frame, absolutely sure I would fall at some point and break my leg.  In retrospect, sliding around under the bed, on my back, might have been a better strategy, but that didn't occur to me until I was done.

The wires on the gauges were very tiny 28 gauge stranded wire, which made them a nightmare to work with.  Once I had all 12 wires at the center of the bed, all I had to do was land them all on some prototype board.  After all the wire to wire soldering I figured that just doing some through hole soldering would be simple, and it probably would have, it it wasn't being done in the center of the bed frame, bending over to butt level.  This is the part that for sure would have been easier to do from underneath.

Anyway, I finally finished up, and had managed to not forget a single heat shrink tube, either on the connections or the legs.  I did a pretty through testing of all the connections with a multimeter, measuring both connectivity, and the resistance between various points.  At this point I had a pretty ingrained sense for what resistance value I would expect from any given two points in the network of wires.  Much to my surprise, everything checked out.  So I powered up the ESP32 and started monitoring the measurements it was reporting.  After a basic calibration, I was delighted to see it was very accurate.  I could put the roll of masking tape on the frame and easily detect that.  I sat on different points along the frame and got very consistent weights for me.

I did have a little bit of cleaning up to do, but I purposely left things not fully done in case I had to make repairs.  I left the heat shrink unshrunk, and didn't tape up the wires as much as I had intended.  I also left the v2 sensor on the bed for now, as a fall back and to collect some parallel data.

the mostly finished v3 sensor with the v2 also still attached with the beige masking tape
I proudly stood and admired a job well done.  It was 2am, my wife was asleep in the guest room, and aside from a very quick cheesesteak break, I had worked continuously on this for 10 hours.

Also visible in the above picture is the old v2 sensor still attached.  All the beige masking tape was either temporary or related to the v2 sensor.

a closeup of a weight gauge, in its stand, with unshrunk heat tubing hiding the wires

This picture shows the nearly finished product, just without the tube shrunk or a bit more black masking tape I used to keep it tight to the leg.  With the overhang of the mattress, the gauges aren't visible at all.  One thing you can see is some electrical tape I used to hold the gauge to the leg.  I expected the gauges to slide on the floor and had some rubber I was prepared to put under them, but actually the leg slipping off them was the bigger problem.  I tried a few things, including hot glue, but electrical tape worked very well, and has held up for months at this point.

Where's the data?

I know you all came here for data, so without further ado.

comparison of v2 and v3 weight sensor data over about a day
This graph shows a comparison of the two sensors over the same day.  Yellow is v2 and blue is v3.  You can see how the yellow graph jumps all over the place, basically anytime someone rolls over.  While it is "clear" there is a pattern of one person, then both, then the other person, you can imagine how hard it would be to attempt to draw a horizontal line where we can say "below this weight is person 1, and above is person 2".  And in fact, the highest point of the yellow graph on the left (my wife) is higher than the lowest point on the right (me).  Now compare that to the blue line representing v3 and it's a stark difference.  The blue line is not perfectly flat, but the slight variation you can see amounts to a pound or two of difference.  The larger square wave pattern you can see (especially visible on the far right end, but also throughout the first phase on the left and in other spots) is about 10 lbs of variation, and is our cat coming and going.  Note the v2 sensor doesn't even acknowledge that.

closeup of v3 weight sensor data over a few hours showing the variation

Here is shorter period of time, from a different day, about 2 months later with no recalibration since the initial set up.  It's a very un-cherry picked period, there are better looking sections, but this is about the worst case I see.  There is a bit of variation there, but accounting for the cat, it's within a few pounds of the same measurement.  After what I had before, I am very happy with this setup.  I would love it if the sensor were accurate enough to monitor our weights over time, but this seem unlikely, even with much more precise gauges, just due to stuff like the headboard touching the wall, or the pillows, blankets, or clothing changing, sometimes during the night.

Either way, I consider this a smashing success, and removed the v2 sensor and taped up the lose wiring after about a week.  At this point the sensor has been working for over 2 months and seems as accurate as the first day.