Sensing tipping spoon rain gauge

If you have a tipping spoon rain gauge to sense besides other SDI-12 sensors, you can use a basic SDI-12 USB adapter equipped with an analog/digital input terminal block to sense it. You can sense up to 4 pulse sensors such as a tipping spoon rain gauge, a flow meter etc.

In the following picture you can see the 12-terminal long green terminal block between my thumb and index finger with markings “3 + – 2 + -” etc. You can either purchase this option on tindie.com when you place your order or purchase a 0.1 inch (2.54mm) pitch 12-pole terminal block and solder to the basic adapter yourself but you need to check the connectivity between the processor and the terminal block yourself because I only check that if you order this option. Connect 0,1,2,3 to + and perform analog read, you should always get near 4.995V and connect 0,1,2,3, to – you should always get 0.

With this terminal block, you get up to 4 digital counters although you will unlikely deploy 4 rain gauges within close proximity unless I imagine you want to see how an agriculture pivot irrigation system distributes water in your field. It’s still nice to have if you need one rain gauge.

Here is an adapter with the terminal block unpopulated, with the space between my index finger and ring finger. You can see more clearly the markings.

Each group of 3 pins on the terminal block is one counter so there is a total of 4 counters. There are many pulse sensors such as tipping spoon rain gauges and flow meters. In theory each tip of the spoon creates a pulse of voltage from low to high then back to low. Rotation of a flow meter creates these pulses continuously. The following shows some low-to-high and high-to-low level transitions that form pulses:

But this is only the the theory. The actual situation of mechanical contacts bouncing off and on until they settle looks like this:

So there are multiple bouncing from the time marking 1 when the signal has been low but just start to become high until time marking 2 when the signal finally settles at high. The duration of the multiple bouncing between high and low is about half a millisecond. If you feed this signal directly to the counter, the counter will register all of these pulses instead of just one.

To count a rain gauge, you usually need to condition the input because it tends to be noisy. The cause of the noise is the electrical contact bouncing of the reed switch inside the gauge. When the spoon tips, it waves a magnet across a reed switch, which short circuits the two connections on the wire. This reed switch mechanically bounces back and forth before making the contact when the magnet is near and then breaking the contact when the magnet moves far enough. The bouncing causes the two wires to make repeated connections and disconnections so each tipping of the spoon causes multiple (inconsistent) number of contact making and breaking. To condition the input, you can use a resistor and capacitor. This causes the electrical signal from multiple contact making and breaking to smooth out into a single pulse.

The rain gauge you have most likely has two contacts in the cable. Either contact can act as the signal wire and the other as the ground. The diagram below assumes there is a power wire but if you don’t have one you can ignore it. You can start with a 10k ohm resistor and 1uF capacitor and follow this diagram to solder the resistor inline with the signal wire (usually either wire), and place the capacitor between the signal and ground (the other wire). The following diagram assumes you are connecting to input 0.

Now you are almost home free. There is one more detail. The counter registers both high-to-low and low-to-high transitions instead of just counting pulses because some sensors use both transitions and others like the rain gauge only use one to indicate a pulse. If you count tipping spoon rain gauges for instance, your tip count is exactly half of the reported transition count because each tipping corresponds to a pulse that has both H-to-L and L-to-H transitions that both get counted.

Here is a photo of a setup provided by one of the users. You can see a pair of twisted green/white wires coming off one side of the 12-pole terminal block. The wires are connected to the 2 by 3 clear plastic wire terminal blocks on the top and center of the right side terminals. This type of terminal blocks connect the left side to the corresponding right side to save you from having to solder wire ends together. So the green/white wire pair connect to the pair of orange/white wire that are connected to a 1uF capacitor seen on the second photo wrapped in translucent protective tubing. Then the rain gauge with thick black wire on the right side of the 2 by 3 is connected to the white wire going to the counter terminal. The red one on the other hand connects to a short red heat-shrink tube in the shape of a side-way U with a resistor of 10k ohm inside connected to the left side of the 2 by 3. This makes the resistor inline between the red wire and the green wire. I think I might want to design a small breakout board to make this connection easier.

This shows how the resistor is inline connected.
This shows the capacitor in translucent protective tubing that is connected to the pair of orange and white wires.

SDI-12 USB adapter mechanical drawings

I’ve added the mechanical drawings to the adapter’s home page. You can print out a copy and use it as a guide to drill mounting holes and plan wiring of your data loggers. Make sure you print with “actual size” and download the file corresponding to your paper size, Letter or A4.

Mechanical dimension in mm units of the latest SDI-12 USB adapter (Letter)

Mechanical dimension in mm units of the latest SDI-12 USB adapter (A4)

Update to SDI-12 USB adapter adds more protection

It’s been a while since my last post. So here is an update. Based on inputs of a number of users, I have made some updates to the basic SDI-12 USB adapter. Here are the new features:

Zener diode between SDI-12 bus line and ground.

This protects the microcontroller from voltage spikes due to power spikes or other interference, as well as accidentally short circuiting power with SDI-12 bus. Some SDI-12 sensors come with stereo plugs, they are notorious for short circuiting all three contacts when plugging in. This causes shocks to the SDI-12 bus if you are using external 12V power, which eventually causes it to die or at least its pin to degrade.

Inline 510 ohm resistor

This also helps with transient protection. I’ve made a few prototypes and plan to test them. Here is a photo:

This update shouldn’t affect the logging script or your existing loggers. I’ll consider adding these components to the SDI-12 USB + Analog and GPS adapters later after I identify space for them since there’s not a lot of space on those variants. I recommend the 1N4737 Zerner diode if you wish to add some transient protection yourself. Just make sure to place it correctly, with the black stripe side connected to the SDI-12 and the other side to ground, on a spare SDI-12 terminal or solder between the S and – pins on the middle 8-pin add-on header:

FT231X USB IC

This IC replaces the FT232RL that is a bit outdated. The new IC will not affect any existing logging scripts, which only identifies the adapter by its vendor ID of 0x0403. If you have modified my scripts to identify the adapter by its USB vendor ID:device ID, the old device ID for FT232RL is 0x6001 while the new device ID for FT231X is 0x6015.

Summer consulting projects

The summer is finally coming! This year we had a lot of snow and I was busy during the semesters. I anticipate to do some travel this summer and further develop my data logger solution but still I have the bulk of May to August open for consulting projects. Let me know if you need my help!

Video extension cables and standoffs

If you ever opened up a computer tower, you know that there are many different size screws. You also may realize that some screws aren’t even imperial or English, which is what we use in USA. Same situation outside a computer. A video cable such as VGA or DVI for instance, usually has two screws on the connectors so you can screw the end to your computer. What happens if you use an extension cable? You get screws on both the regular cable and extension cable, hardly any use when both sides are screws. I recently had to extend a VGA cable and was faced with this nonsense. The connectors on the regular cable and extension cable will easily get loose unless I tape them together. So I started looking for standoffs that can bridge two screws together. It turns out that these screws aren’t imperial either. They are M3! Good thing I grabbed a few M3 standoffs with threads on both sides so I just used two of those. Next time you want a perfectly secure extension cable, look for M3 standoffs. The bronze standoffs are in between two VGA connectors, both of which feature screws.

2019-02-12 10.53.44

Open FTDI USB-serial UART port by ID

In this post, I will explain how to open serial port to your Arduino or SDI-12 USB adapter by its unique ID so you always open the correct port even when there are multiple such devices on your computer or raspberry pi.

For Arduino and SDI-12 USB adapter users, I have a nice trick to help you manage multiple Arduinos or SDI-12 USB adapters on the same computer or raspberry pi. On raspberry pi, as on a typical linux system, your device shows up as a serial port, such as /dev/ttyUSB0. This serial port designation is usually bound by the order that the device is discovered at boot time, which may not be the same even if you keep your adapter plugged into the same USB port. This means if you have more than one device on your raspberry pi, you may open the wrong port at times, which should be a big issue. To prevent your program from opening the wrong port, you need a unique ID for each device. Luckily FTDI chips already come with unique IDs. We just have to find those IDs and possibly change them into more meaningful things for us to remember. Assume for the moment you are making a data logger for your test fields. There is one field that can be called “NORTH”. The following steps will help you change the ID of the FTDI chip on your device so you can later open its port by that ID, instead of a port name. Here is a list of which devices are using FTDI’s chips that have the reprogrammable ID feature:

  • Liudr SDI-12 USB adapter (all types)
  • Sparkfun Redboard
  • Certain Arduino clone boards
  • Lots of other devices such as GPS etc.

Here is the FT_PROG tool FTDI provides. It’s windows only but I’m sure you can find a windows machine to run it. I’ve not tested it in a virtual machine whose host is linux or macos. I’ll do that when I have more time. If you are unsure whether your device has an FTDI chip, a quick scan using the program will tell you.

http://www.ftdichip.com/Support/Utilities.htm#FT_PROG

First, press the scan icon (magnifying glass). If you have a device with FTDI chip, it will show up. See the screen grab below:

So I have an FT232R chip with a chip serial number “A106DHE5”. I can open port with this serial number but I’d rather change it to “NORTH”. Click on the “SerialNumber” from the left side.

Uncheck the “auto generate serial no” so you can edit the serial number to “NORTH”. You have up to 16 characters to name the adapter. Once done, press flash icon (thunder bolt).

Now that you have programmed your chip, you can read the information back using “scan” again to verify that the ID has changed:

Now that you have this nice ID, let’s open port by this ID.

Here is a small complication. On linux, the chip ID is returned, such as “NORTH”. On window, the port ID is returned, such as “NORTHA“. The addition of the “A” indicates the port “A” on chip “NORTH”. This is because some FTDI chips have two serial ports. The port IDs will be “NORTHA” and “NORTHB“. Even for FTDI chips that have only one port, such as for our case, the “A” is still there. So I recommend comparing chip ID instead of port ID. If you only work on linux/rpi systems, this doesn’t seem to concern you. But if you wish to make your code platform independent, i.e. running on windows without an incident, you will only extract whichever ID you receive with the stored chip ID, up to the length of the stored chip ID. Note: in the platform-independent code, you can’t slice a port’s serial_number with your stored ID because some internal ports don’t have serial numbers thus returns empty that will throw an error when you try to slice an empty array.

The following is a snippet that works ONLY on linux/rpi systems:


import serial.tools.list_ports  # For listing available serial ports
import serial  # For serial communication

my_ID='A817EQLG'
port_device=''
# List ports for user to select
a = serial.tools.list_ports.comports()
print('\nDetected the following serial ports:')
for w in a:
    print('Port:%s\tID#:=%s' % (w.device, w.serial_number))
    if (w.serial_number==my_ID): # Match ID with the correct port
        port_device=w.device # Store the device name to later open port with.
if len(port_device)!=0:
    print('\r\n%s is the correct port.' %(port_device))
else:
    print("Port with ID: %s is not found!" %(my_ID))

The following is a snippet that works on ALL OS:

import serial.tools.list_ports  # For listing available serial ports
import serial  # For serial communication

my_ID='A817EQLG'
port_device=''
# List ports for user to select
a = serial.tools.list_ports.comports()
print('\nDetected the following serial ports:')
for w in a:
    print('Port:%s\tID#:=%s' % (w.device, w.serial_number))
    if (w.serial_number.__str__()[:len(my_ID)]==my_ID): # Match ID with the correct port
        port_device=w.device # Store the device name to later open port with.
if len(port_device)!=0:
    print('\r\n%s is the correct port.' %(port_device))
else:
    print("Port with ID: %s is not found!" %(my_ID))

Your choice, simplicity of code or cross-platform compatibility.

Here is the cross-platform code’s result on my windows machine:

Detected the following serial ports:
Port:COM23	ID#:=A817EQLGA
Port:COM3	ID#:=None


COM23 is the correct port.

As you can see, there is an added “A” at the end of the ID reported by windows, which the python code ignored to produce a match.

I’ve also attempted to do this using Processing 3.0. Unfortunately, the Serial.getProperties() function that should return similar information returns blank (possibly not implemented on windows and yet to be tested on linux). If you have tested Processing method with success, please reply below with your results. I’ll add your comment to the post.

Here is the code I used in Processing 3.0:

import processing.serial.*;
import java.util.Map;
 
void setup() {
  String[] ports = Serial.list();
 
  for (int i=0; i < ports.length; i++) {
    Map<String, String> props = Serial.getProperties(ports[i]);
    print(ports[i]+": ");
    println(props);
  }
}

Results:

COM3: {}
COM23: {}
:(

Closing note: even if you work on Windows that assigns unique COM port number to your arduino or adapters, the assignment relies entirely on the currently available port numbers. If you develop your project on one windows PC and deploy on another windows PC, you WILL get different COM port numbers. On a Mac, the ID is embedded on the port name such as /dev/ttl.usbserial-A103RU9T so you are better off. But, will you be willing to shell out the money to get a mac and have it sit somewhere to collect data just because of this feature? If you are a linux wiz, you can bind names with serial numbers using some scripts. That’s beyond the scope of our general discussion, which assumes minimal experience with linux administration.

Calibrate a magnetic sensor

I recently worked on a project that required a magnetic sensor (MPU-9250) be calibrated to get best accuracy. I had some basic understanding on how to calibrate a magnetic sensor, although I’ve not done calibration before. The calibration turned out to be a bit complicated and took a while to understand. I got some help, including method to get best calibration, from the author of the MPU9250 Arduino library, Kris Winer. I thought that if I shared my experience here, others that are planning to calibrate their magnetic sensors may find it useful.

First of all, what is calibration? In a general sense, calibrating a sensor makes the sensor provide the most accurate readings allowed by the sensor’s own precision. As an example, let’s assume for a moment that the earth’s magnetic field and any other stray magnetic fields are shielded and you have a uniform magnetic field generated artificially for the sole purpose of calibration. Let’s say that the field strength is 400 mG (milliGauss), equivalent to 40,000 nT (nanoTesla). Now if you align one axis of your magnetic sensor parallel to the direction of the field, it should read 400mG. If you then carefully rotate your sensor so that the axis is anti-parallel with your field, it will read -400mG. If you didn’t do a good job in either alignments, you will read less values, say 390mG, if you’re off by about 13 degrees, because only a portion of the field, which is a vector, is projected along your magnetic sensor’s axis.

In the diagram above, the thick blue arrows represent the constant magnetic field of 400mG pointing to the right. The thin arrows represent various orientations your sensor could take. If your sensor + axis is also pointing to the right, you get the full 400mG. If your sensor + axis points to the left, you get -400mG. If your sensor + axis makes an angle, it reads a projection of the field, which is less. You can figure out the angle:

The above was assuming that there IS a constant magnetic field and the sensor’s reading IS symmetric along its positive and negative axis, meaning with zero magnetic field, the sensor reads zero. When not calibrated, the sensor reading will NOT be zero under zero field. It could read say 10mG. As a result, you might get say 510mG and -490mG with the field on. You know what that means. There is an offset (bias) of 10mG that should be subtracted from your reading to get the correct reading of +-400mG.

The above was the basis for calibration to remove the offset (bias) on each axis. In order to get the maximal and minimal value, you need to write your code to store max/min out of a stream of live data while you rotate your sensor in space, trying to maximize or minimize the readout. Then repeat two more times for a 3-D magnetic sensor. Since you don’t have a magnetic shield, you are relying on the earth’s magnetic field as the constant field. The earth’s magnetic field is not horizontal, or pointing from north to south. In most areas, the field either has a vertical up component, or a down component. And in most cases the field points from south to north as the rotational north pole (AKA the north pole) is near the magnetic south pole, where field lines go in, not coming out. In my area for example, the earth magnetic field points primarily downwards, only slightly towards north, making an angle over 70 degrees with the horizontal. The magnetic field has very little component in the east direction. The relative strength between East, North, and Downward is about 1:64:195. The angle the magnetic field vector makes with the horizontal is called magnetic inclination, with downward being positive. This is approximately atan(195/64)=72 degrees. The angle the magnetic field vector’s horizontal component makes with the true north is called magnetic declination, with east being positive. This is approximately atan(1/64)=0.9 degrees. The properties of magnetic field varies greatly from place to place and also changes from time to time. To find out the magnetic field in your area, visit noaa.gov:

https://www.ngdc.noaa.gov/geomag-web/#igrfwmm

The following is from my area:

The next calibration is for sensitivity. The sensor either returns an analog voltage or a digital value. How do we convert this return into actual magnetic field in mG? This means finding the relation between the sensor readout and actual physical values. Say the sensor is digital and returns values between 0 and 32767, which represents magnetic field between 0 and 49150mG. Then you can use the conversion vactor 49150/32767=1.5mG/LSB to convert your readout. Here LSB means one digit (least significant bit). For an analog sensor, you will need an x.xx mG/V.

All sensors provide this factor in their spec sheets so you can just use this factor to get the actual magnetic field. But since not all sensors were made identical, some sensors should use larger or smaller values than the spec’s factor. Some manufacturers test their sensors at factory and store a correction factor for each axis in the sensor for better accuracy. For example, the MPU-9250 sensor (the magnetic sensor is AK8963) has digital magnetic field sensor output. One of the sensors I got has the following factory trims:

X-Axis sensitivity 1.18

Y-Axis sensitivity 1.19

Z-Axis sensitivity 1.14

So instead of a straight 1.5mG/LSB, the x-axis has 1.5*1.18=1.77mG/LSB. We’ll multiply this factor to the x-axis readout to get x magnetic field in mG. Same for y and z axes.

And yet sometimes these adjustments are still not able to make all 3 axes read the “400mG” value. They are very close to be identical already so we can apply a small correction. We average the maximal readings from all three axes, then divide by the maximal reading of each individual axis to get three factors. If say the x-axis reads slightly higher maximum than y and z axes. Then the avg_max/x_max will be slightly less than 1. We apply this factor to the the final result:

x_field=(1.5mG/LSB)*(x_sensitivity)*(avg_max/x_max)*x_readout

For one of my sensors, this yields 1.5*1.180*0.975*x_readout=1.726*x_readout(mG)

If the sensor you’re using doesn’t have the factory trim, then you’re out of luck unless you have both a magnetic shield and a nice uniform magnetic field inside the shield so you can find out the trim.

The following are steps for the AK8963 magnetic sensor calibration I did, using Kris Winer’s MPU9250 Arduino library:

  1. Extract factory sensitivity factor by calling initAK8963().
  2. Call readMagData() to extract raw data repeatedly enough to get accurate max/min for all axes.
  3. Calculate bias in raw counts by (max+min)/2, such as (510+ (-490))/2=10
  4. Combine factory sensitivity factor and Kris’s scale factor described above.
  5. Keep these biases and factors in program.

As a testimony to the method that works, here are two graphs.

This graph has three separate plots from raw data that represent mx vs. my, mx vs. mz, and my vs. mz. I rotated my sensor as much as I could for a few minutes before I got bored and couldn’t think of any other ways of rotation. The fact that all three plots are near circular means that the sensor’s three axes have very similar sensitivities. I was simply rotating the sensor around, giving all three axes opportunities to read the whole magnetic field. This means when the x axis reads the whole field, y and z axes read nothing. Some trigonometry can show that the result is a series or circles making a disc. But the discs were not centered as I expected, because the z axis had a very large bias (this means the blue is mx vs. my). After my bias calibration, here is what I got:

Now all three plots are centered at zero pretty well visually, although I didn’t multiple the factory sensitivity or the final factor from Kris’s calculation. This shows that bias calibration is the most crucial. If your sensor doesn’t have stored factory sensitivity factors, getting it calibrated for bias alone will go a long way.

FYI, this is my sensor board inside an enclosure. I found it much easier to hold and rotate when it’s in a box. The cable became much less of an issue when I was rotating the box. The black square board is my SDI-12 USB adapter. The purple board is the sensor board. I customized the SDI-12 USB adapter by gluing the sensor board to it and connecting it to the I2C bus on the adapter.

SDI-12 USB adapter and manual updates

If you have been looking at the SDI-12 USB adapters lately, you may have noticed that the manual got updated a bunch of times between February and March (latest version being 3/19/2018). I’ve been updating the manual to:

1. Introduce the newer SDI-12 USB adapter (black) that replaced the original adapter (green).

Original SDI-12 USB adapter since 2015:

slide2

Updated SDI-12 USB adapter:

SDI-12 USB + adapter

Notice that now the updated adapter is the same size as the other flavors of the adapters, such as the SDI-12 + Analog adapter and the SDI-12 + GPS adapter. These are the features the updated adapter has:

  • Four SDI-12 sensor connectors
  • External power connection and sensor power selector (5V USB or external)

2. I updated several sections of the manual to make the manual easier to understand.

  • Bookmarks in the .PDF file
  • More detailed description of the updated adapter
  • Details of how to configure an SDI-12 adapter
  • Other places have been tidied up as well

By the way, did I mention that I reduced all my adapters by $5 to $10?

  • SDI-12 USB adapter (updated) $45 ($5 cheaper)
  • SDI-12 + GPS USB adapter $60 ($10 cheaper)
  • SDI-12 + Analog USB adapter $80 ($10 cheaper)
  • There will be discounts for bulk purchases, such as 10, 20, 50 etc. Please contact me if you intend to buy more adapters so I can send you an accurate quote of prices with discounts and shipping cost.
  • If you still need the green adapter for its simplicity or form factor, you can contact me to do bulk orders, 10 adapters or more.
  • If you need something else to be on your adapter to connect to a particular sensor, contact me to see how I can help you achieve that. The sensor would definitely not be SDI-12 but I’ll make it so that reading from it is just like reading from any other SDI-12 sensor so your program needs little to no change.

Since I am assembling and testing these adapters myself and use trustworthy US parts vendors, I will have to get more efficient in my work to make up the difference. I made some improvements on a reflow oven that I will be using for new batches of adapters. I hope the effort pays off. I would be able to assemble a batch, load into the reflow oven, and assemble the next batch or do cleanup or soldering thru-hole components such as the screw terminal blocks while the oven reflows automatically. I’ve been using a manual control (Variac transformer) for reflow for the past several years, which requires constant attention.

One negative impact from the US postal service, since the end of January 2018, USPS will no longer ship merchandise via international first-class letter. The only alternative is first-class package, which is a much more expensive service. A package containing one adapter that used to cost less than $4 will now cost $9 to ship to Canada (come on!) or $14 to most of the rest of the world. I felt that I’m being squeezed out of the international market by the postal service. The only upside is that now the package seems to come with complete tracking in both countries. I made a small change to the first-item shipping cost and additional item shipping cost.

I am also considering designing a complete low-cost data-logging system so it would be more integrated than the adapters. The adapters have proved to be very successful indeed. It shows up as number-1 search for keywords “SDI-12 adapter” or “SDI-12 USB adapter”. I didn’t pay google anything for the free advertisement. Everyone that searched and read my blog helped spread the word! Thank you!

I plan to keep improving the adapters so that anyone that is integrating SDI-12 sensors will find it easy to just get an adapter and add SDI-12 sensors. For anyone else that is interested in low-cost data logging solutions but are more interested in a turn-key solution instead of investing time and effort in learning python programming or raspberry pi, this logger will make things easy for you!

Any comments, suggestions, your use case, including what sensors and telemetry solutions you want that you would like to share? Leave a comment! The design of the data logger is still very fluid so that your opinions WILL influence its development!

The development of a turn-key solution WILL positively affect the adapter development as well. I expect to have a 3-prone approach ultimately: USB adapters to add SDI-12 sensors to existing logger projects that use computers/raspberry pi, serial dongles/shields to do the same for Arduino-based existing logger projects, and a complete logger for those seeking turn-key solutions.

Have a nice spring/fall day!

JLCPCB – A new PCB fabrication house

I was recently approached by a new PCB fabrication house JLCPCB (technically they’ve been in business for some time but just started advertising to USA customers) to write some reviews on their service. Given my experience designing PCB and using PCB fabrication services (batchpcb, seeedstudio, iteadstudio, oshpark etc.) over the past decade, writing a review shouldn’t be too hard. If they are any good, I could use their service myself and recommend them to others in the hobby electronics community. So I sent them two of my designs and got them back relatively fast, with DHL. Since I still have some older versions of one of the boards, I could make a comparison with the two fabrication services. JLCPCB has offered their service free of charge for the exchange of a fair review. So I decide to not mention which competition I pitched JLCPCB against. What I primarily looked at was how accurately each layer of board is printed and how well they are aligned with one another from each fab service. JLCPCB‘s results are very good. Its competition, a long-standing name among DIYers, doesn’t look quite as good.

In case you’re still learning how to design PCBs, especially surface-mount components, here are some terms I’m going to use:

Top layer: this logic layer contains all copper traces, pads, and vias on the top side of the two-sided circuit board. A process is used to protect all aforementioned features specified in this layer when the entire board is etched in acid. If the process isn’t accurate, then your features aren’t exactly where they are supposed to be.

Top solder paste: this logic layer contains only pads for surface-mount components. It is a subset of top layer and is used to generate stencils for reflow soldering. Again if the fabrication isn’t very good, these features tend to not align with other features.

Top solder resist: this logic layer contains similar information to the pads and vias contained in top layer but the sizes of the features in this layer are slightly enlarged to a peel-back amount so they don’t accidentally apply solder resist on top of your pads where solder should go. Solder resist is a lacquer that prevents solder from adhering to the copper traces. They also protect the bare copper from rusting away. You will want them to be applied to all your traces and only leave the pads exposed so later process will cover them with a very thin layer of solder (the shiny looking stuff) and you then reflow solder your components to these pads. If the registration of this layer of lacquer is not well registered with the top layer, you will see visually under a magnifier. This is why fabrication houses usually do quite a bit of peel-back so they leave room for themselves to be less-aligned but still the lacquer won’t cover up the pads.

The following images are from an FTDI chip (FT232RL). The pitch was the finest on my board so I selected these pads for comparison of how well the different layers register with one another on these two service providers.

Top: JLCPCB Bottom: competition


They look similar. I’ve unfortunately scratched the pads on the competition’s board. I applied solder paste to that board and later cleaned the paste off so I could photograph its pads. My bad.
On a closer look there is a difference:

SMD pads (SSOP 0.65mm pitch):

Top: JLCPCB Bottom: competition
You can see that JLCPCB‘s boards (top) have SMD pads (shiny metal pads) that are very symmetrically situated inside the solder-resist masks (slightly larger dark rectangle). The solder resist also goes closer to the board than its competition. These are manufactured from the same designs! The engineers at the competition must have increased the peel-off (sizes of feature not covered by solder resists) to offset their less-perfect layer registrations and/or accuracy on each layer. As you can see, not all pads and their solder resist have the same offset. Some look better than others. This is clear with the second to the last pad and the last pad. The pads are not centered at all. This is very consistent across the board made by competition. JLCPCB has better overall registration than competition. Better registration translates into better chance to prevent solder bridges and less chances to reworking on your boards after you reflow them, that means time and money saved.

Thru-hole pads (o.1″ spacing):

Top: JLCPCB Bottom: competition

The top one has less size solder resist layer, the edges of the red lacquer surrounding the shiny pads (almost same size as the thru-hole pads) and very symmetrical. The competition has again increased the solder resist layer and couldn’t keep the layer registered well with the pad.

The via to the right of the bottom right through hole pin hole will be compared next. On JLCPCB‘s board, its solder resist has 2.8mm diameter when displayed on my computer monitor. On the competition board, it is 3.1mm. The images were taken under a high-magnification lens and I checked the images to be exactly the same zoom, measuring the same across the same features on screen. What this means is that if you have a lot of vias in one area, very close to one another, you may get some solder bridges between the vias if there is not enough solder resist to separate them. Not a problem on this board but a problem if you happen to have vias very close and also close to thru-hole components. You solder the thru-hole and inevitably fill the adjacent vias with some solder. This could short vias.

Overlay (white texts):

The quality of the white overlay texts are about the same between the two fabricators, although at some places you see one board having better quality than the other board while at different places the quality is reversed. This is not a crucial feature to look at though.

JLCPCB: top, Competition: bottom

With the explanation above, you can easily distinguish these two photos. The bottom one has so much space between the edges of the shiny pads and the red lacquer (less qualty). The top one has so much less and so symmetric.

So the results are clear. JLCPCB is a pretty decent PCB fab house and I will order my next batch from them. The shipping cost is also slightly less than competition if you use DHL. I always use DHL. They do a good job delivering to small cities like the one I live in!

Here is a link to JLCPCB’s website:

www.jlcpcb.com

I am not getting any commissions for your purchases. The link has no “trackers” 🙂

P.S.: I had a research student hand solder this board (yes, every single chip resistor and the FTDI chip) as a good test of his skills and it turned out fine. This is a proof that having the right size solder resist helps, really. This is what the board looks like after assembly:

Augmented reality sandbox control box updated

This slideshow requires JavaScript.

I constructed a couple of these control boxes for my ARsandbox project using Arduino Micro and Adafruit Feather 32u4. The main goal is to make the interaction between a user and the software more intuitive. The two flashing buttons add and remove water from the simulation, which is pretty nice. I wanted to add another feature, to move the elevation up and down. Say you have a lot of sand in the box but you want to display some “water”. You have to remove a lot of sand (time consuming) from the box or edit a config file (near impossible for an average user). On the other hand, if I can change the base plane location in the calculation, I can achieve the above goal without removing sand or editing files. With help of the original developer Dr. Oliver Kreylos, I started messing with the source code. It took me a day to understand what I could do to change the base plane and how to do it. I ended up creating a tool to manipulate the plane. Now that the plane can be moved, I added a rotary encoder to the control box to emulate the keyboard keys assigned to move the plane. Voila!

The photo gallery above shows 5 shots of different elevation values. You can clearly see the “sea” on bottom right is shrinking after each turn of the knob.

Here is a short video. I was slowly raising the elevation so more and more land became above water. Then I quickly returned the land back into water:

Here is a photo of the inside of the box:

I added a breakout board for rotary encoder and buttons. It’s very tightly sandwiched between the two buttons. There is no space between the board and the buttons, not even a fraction of millimeter. I don’t know how these pieces just managed to fit. Here is the breakout board:

I made this a while ago as an interface for my phi-panels LCD backpacks. This breakout board is pretty simple. There are two encoder channels and one shaft button, plus six push buttons, which I am not using. I just connected to the encoder channels, shaft button, and the common. I had to cut off a strip on the right side of the board to fit into the box. I was using my phi_interfaces library to read the encoder and emulate a button push if the encoder is rotated one way, another button if the encoder is rotated the other way. I didn’t assign any function for the shaft button. I am thinking that I should use it to switch between elevation adjustment and water speed adjustment. If you want one, I have a few available, with rotary encoders (I don’t have knobs). Leave me a message. I’ll break off a strip of the board so it will fit in the same box I’m using. Otherwise, you can get it printed yourself if you know how to do that. Here is the design file in EAGLE CAD:

Keypad circuit

If you know how to design boards, you can easily I haven’t updated the Adafruit Feature 32u4 version but will probably design a new printed circuit board so assembling will be easier, and not involving cutting off part of a board. Some code in case anyone wants to replicate it.


/*
 * Credit: Dr. John Liu
 * Purpose: This sketch emulates keyboard keys "1" and "2" with two push buttons with LEDs. It also flashes the LEDs
 * If a rotary encoder is present, it will emulate '5' and '6' if you rotate the encoder's shaft. This combined with my modification on ARsandbox source code will shift color mapping up and down.
 * This version uses p-channel mosfets and open drain with 10Kohm pull-up to 5V to control LEDs with the 3.3V Adafruit Feather32u4.
 * Notes: On Adafruit Feather 32u4, pin 9 is connected to battery sensing voltage divider.
 * 2018-01-28
 * Visit https://liudr.wordpress.com for more information
 */
#include "Keyboard.h"
#include 
#define USING_MICRO
#ifdef USING_MICRO
const int button_1=2; // 11 for Adafruit feather 32u4, 2 for Arduino Micro;
const int button_2=3; // 10 for Adafruit feather 32u4, 3 for Arduino Micro
const int EncoderChnA=7;
const int ChnCommon=8;
const int EncoderChnB=9;
const int ShaftBtn=10;
const int EncoderDetent=18;
#endif

#ifdef USING_FEATHER
const int button_1=11; // 11 for Adafruit feather 32u4, 2 for Arduino Micro;
const int button_2=10; // 10 for Adafruit feather 32u4, 3 for Arduino Micro
#endif

const int led_1=6;
const int led_2=5;
const unsigned long led_on_ms=300;
const unsigned long led_off_ms=1700;
const unsigned int button_1_key='1'; //KEY_LEFT_ARROW;
const unsigned int button_2_key='2'; //KEY_RIGHT_ARROW;
const unsigned int UpKeyOut='5';
const unsigned int DownKeyOut='6';

int prev_1=HIGH;
int prev_2=HIGH;
int led_stat=LOW;

unsigned long prev_1_ms=0;
unsigned long prev_2_ms=0;
unsigned long blink_timer_0_ms=0;
unsigned long blink_timer_1_ms=0;

int debounce_ms=25;

char mapping[]={'U','D'}; // This is a rotary encoder so it returns U for up and D for down on the dial.
phi_rotary_encoders MyEncoder(mapping, EncoderChnA, EncoderChnB, EncoderDetent);
//multiple_button_input* dial1=&my_encoder1;

void setup()
{
  // make pin 2 an input and turn on the
  // pullup resistor so it goes high unless
  // connected to ground:
  pinMode(button_1, INPUT_PULLUP);
  pinMode(button_2, INPUT_PULLUP);
  pinMode(led_1,OUTPUT);
  pinMode(led_2,OUTPUT);
  pinMode(ChnCommon,OUTPUT);
  digitalWrite(led_1,LOW);
  digitalWrite(led_2,LOW);
  digitalWrite(ChnCommon,LOW); // Using this pin as ground since some prototypes don't have enough gnd pins.
  Keyboard.begin();
  blink_timer_0_ms=millis();
}

void loop()
{
  unsigned char ch=MyEncoder.getKey(); // Rotary encoder emulates two buttons.
  if (ch==mapping[0])
  {
    Keyboard.write(UpKeyOut);
  }
  else if (ch==mapping[1])
  {
    Keyboard.write(DownKeyOut);
  }

  switch (led_stat)
  {
    case LOW:
    if (millis()-blink_timer_0_ms>led_on_ms)
    {
      blink_timer_0_ms=millis();
      led_stat=HIGH;
      pinMode(led_1,INPUT); // Open drain to let pull-up resistor pull drain to 5V.
      pinMode(led_2,INPUT); // Open drain to let pull-up resistor pull drain to 5V.
    }
    break;

    case HIGH:
    if (millis()-blink_timer_0_ms>led_off_ms)
    {
      blink_timer_0_ms=millis();
      led_stat=LOW;
      pinMode(led_1,OUTPUT); // Pull drain to GND.
      pinMode(led_2,OUTPUT); // Pull drain to GND
    }
    break;
  }

  if ((digitalRead(button_1) == HIGH)&&(prev_1==LOW))
  {
    if (millis()-prev_1_ms>debounce_ms)
    {
      prev_1_ms=millis();
      Keyboard.release(button_1_key);
      //Serial.println("1 released");
    }
    prev_1=HIGH;
  }

  if ((digitalRead(button_1) == LOW)&&(prev_1==HIGH))
  {
    if (millis()-prev_1_ms>debounce_ms)
    {
      prev_1_ms=millis();
      Keyboard.press(button_1_key);
      //Serial.println("1 pressed");
    }
    prev_1=LOW;
  }

  if ((digitalRead(button_2) == HIGH)&&(prev_2==LOW))
  {
    if (millis()-prev_2_ms>debounce_ms)
    {
      prev_2_ms=millis();
      Keyboard.release(button_2_key);
      //Serial.println("2 released");
    }
    prev_2=HIGH;
  }

  if ((digitalRead(button_2) == LOW)&&(prev_2==HIGH))
  {
    if (millis()-prev_2_ms>debounce_ms)
    {
      prev_2_ms=millis();
      Keyboard.press(button_2_key);
      //Serial.println("2 pressed");
    }
    prev_2=LOW;
  }
}

Here is the instruction of the software update:
Instruction

Here is the link to the two files you need for the update:

Files

Without my button box, you can still use keyboards such as 5 and 6 to invoke the feature.

%d bloggers like this: