add solder dispenser post

This commit is contained in:
Matthew Tran 2024-06-22 19:51:13 -07:00
parent fee3fd780f
commit 9af690eebc
17 changed files with 182 additions and 37 deletions

View File

@ -1,13 +1,12 @@
source "https://rubygems.org" source "https://rubygems.org"
# remove once dependencies updated # remove once added to jekyll dependencies
gem "csv" gem "csv"
gem "base64" gem "base64"
gem "bigdecimal"
gem "rake"
gem "jekyll", "~> 4.0" gem "jekyll", "~> 4.0"
gem "webrick" gem "webrick"
gem "faraday-retry"
group :jekyll_plugins do group :jekyll_plugins do
gem "jekyll-feed" gem "jekyll-feed"

View File

@ -1,26 +1,55 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
addressable (2.8.6) addressable (2.8.7)
public_suffix (>= 2.0.2, < 6.0) public_suffix (>= 2.0.2, < 7.0)
base64 (0.2.0) base64 (0.2.0)
bigdecimal (3.1.6) bigdecimal (3.1.8)
colorator (1.1.0) colorator (1.1.0)
concurrent-ruby (1.2.3) concurrent-ruby (1.3.3)
csv (3.2.8) csv (3.3.0)
em-websocket (0.5.3) em-websocket (0.5.3)
eventmachine (>= 0.12.9) eventmachine (>= 0.12.9)
http_parser.rb (~> 0) http_parser.rb (~> 0)
eventmachine (1.2.7) eventmachine (1.2.7)
faraday (2.9.0) faraday (2.9.2)
faraday-net_http (>= 2.0, < 3.2) faraday-net_http (>= 2.0, < 3.2)
faraday-net_http (3.1.0) faraday-net_http (3.1.0)
net-http net-http
ffi (1.16.3) faraday-retry (2.2.1)
faraday (~> 2.0)
ffi (1.17.0)
ffi (1.17.0-aarch64-linux-gnu)
ffi (1.17.0-aarch64-linux-musl)
ffi (1.17.0-arm-linux-gnu)
ffi (1.17.0-arm-linux-musl)
ffi (1.17.0-arm64-darwin)
ffi (1.17.0-x86-linux-gnu)
ffi (1.17.0-x86-linux-musl)
ffi (1.17.0-x86_64-darwin)
ffi (1.17.0-x86_64-linux-gnu)
ffi (1.17.0-x86_64-linux-musl)
forwardable-extended (2.6.0) forwardable-extended (2.6.0)
google-protobuf (3.25.2) google-protobuf (4.27.1)
bigdecimal
rake (>= 13)
google-protobuf (4.27.1-aarch64-linux)
bigdecimal
rake (>= 13)
google-protobuf (4.27.1-arm64-darwin)
bigdecimal
rake (>= 13)
google-protobuf (4.27.1-x86-linux)
bigdecimal
rake (>= 13)
google-protobuf (4.27.1-x86_64-darwin)
bigdecimal
rake (>= 13)
google-protobuf (4.27.1-x86_64-linux)
bigdecimal
rake (>= 13)
http_parser.rb (0.8.0) http_parser.rb (0.8.0)
i18n (1.14.1) i18n (1.14.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jekyll (4.3.3) jekyll (4.3.3)
addressable (~> 2.4) addressable (~> 2.4)
@ -59,7 +88,7 @@ GEM
kramdown-parser-gfm (1.1.0) kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0) kramdown (~> 2.0)
liquid (4.0.4) liquid (4.0.4)
listen (3.8.0) listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0) mercenary (0.4.0)
@ -70,22 +99,65 @@ GEM
sawyer (~> 0.9) sawyer (~> 0.9)
pathutil (0.16.2) pathutil (0.16.2)
forwardable-extended (~> 2.6) forwardable-extended (~> 2.6)
public_suffix (5.0.4) public_suffix (6.0.0)
rake (13.1.0) rake (13.2.1)
rb-fsevent (0.11.2) rb-fsevent (0.11.2)
rb-inotify (0.10.1) rb-inotify (0.11.1)
ffi (~> 1.0) ffi (~> 1.0)
rexml (3.2.6) rexml (3.3.0)
rouge (4.2.0) strscan
rouge (4.3.0)
rubyzip (2.3.2) rubyzip (2.3.2)
safe_yaml (1.0.5) safe_yaml (1.0.5)
sass-embedded (1.70.0-x86_64-darwin) sass-embedded (1.77.5)
google-protobuf (~> 3.25) google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.70.0-x86_64-linux-gnu) rake (>= 13)
google-protobuf (~> 3.25) sass-embedded (1.77.5-aarch64-linux-android)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-aarch64-linux-gnu)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-aarch64-linux-musl)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-aarch64-mingw-ucrt)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-arm-linux-androideabi)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-arm-linux-gnueabihf)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-arm-linux-musleabihf)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-arm64-darwin)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-riscv64-linux-android)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-riscv64-linux-gnu)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-riscv64-linux-musl)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-x86-cygwin)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-x86-linux-android)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-x86-linux-gnu)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-x86-linux-musl)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-x86-mingw-ucrt)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-x86_64-cygwin)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-x86_64-darwin)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-x86_64-linux-android)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-x86_64-linux-gnu)
google-protobuf (>= 3.25, < 5.0)
sass-embedded (1.77.5-x86_64-linux-musl)
google-protobuf (>= 3.25, < 5.0)
sawyer (0.9.2) sawyer (0.9.2)
addressable (>= 2.3.5) addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3) faraday (>= 0.17.3, < 3)
strscan (3.1.0)
terminal-table (3.0.2) terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3) unicode-display_width (>= 1.1.1, < 3)
unicode-display_width (2.5.0) unicode-display_width (2.5.0)
@ -93,21 +165,45 @@ GEM
webrick (1.8.1) webrick (1.8.1)
PLATFORMS PLATFORMS
x86_64-darwin-23 aarch64-linux
aarch64-linux-android
aarch64-linux-gnu
aarch64-linux-musl
aarch64-mingw-ucrt
arm-linux-androideabi
arm-linux-gnu
arm-linux-gnueabihf
arm-linux-musl
arm-linux-musleabihf
arm64-darwin
riscv64-linux-android
riscv64-linux-gnu
riscv64-linux-musl
ruby
x86-cygwin
x86-linux
x86-linux-android
x86-linux-gnu
x86-linux-musl
x86-mingw-ucrt
x86_64-cygwin
x86_64-darwin
x86_64-linux x86_64-linux
x86_64-linux-android
x86_64-linux-gnu
x86_64-linux-musl
DEPENDENCIES DEPENDENCIES
base64 base64
bigdecimal
csv csv
faraday-retry
jekyll (~> 4.0) jekyll (~> 4.0)
jekyll-feed jekyll-feed
jekyll-gist jekyll-gist
jekyll-include-cache jekyll-include-cache
jekyll-paginate jekyll-paginate
jekyll-remote-theme jekyll-remote-theme
rake
webrick webrick
BUNDLED WITH BUNDLED WITH
2.5.5 2.5.14

View File

@ -5,7 +5,7 @@
blog + resume blog + resume
``` ```
gem install bundler gem install bundler jekyll
bundle update bundle update
bundle install bundle install
bundle exec jekyll serve bundle exec jekyll serve

View File

@ -12,7 +12,7 @@ linkedin_username: matthewlamtran
permalink: /:year/:month/:title/ permalink: /:year/:month/:title/
show_excerpts: true show_excerpts: true
remote_theme: "mmistakes/minimal-mistakes@4.24.0" remote_theme: "mmistakes/minimal-mistakes@4.26.2"
minimal_mistakes_skin: dark minimal_mistakes_skin: dark
plugins: plugins:
- jekyll-include-cache - jekyll-include-cache

View File

@ -10,17 +10,15 @@ Going into industry and college, I did not expect the sheer number of things tha
Projects I'll get to next. Projects I'll get to next.
- **hardware** - **hardware**
- **solder dispenser** - level up from the tweezer and hand-squeezed syringe
- **word watch** - inspired by those word clocks
- **wireless solar keyboard** - full size, swappable keys, ch32 w/ nrf24, solar powered, usable in UEFI/BIOS - **wireless solar keyboard** - full size, swappable keys, ch32 w/ nrf24, solar powered, usable in UEFI/BIOS
- **Linux board** - probably an NXP or STM32 chip, maybe a Zynq - **Linux board** - probably an NXP or STM32 chip, maybe a Zynq
- **software** - **software**
- **Zephyr USB Rubber Ducky**
- **autodbconv** - LDF/DBC parser built in Rust - **autodbconv** - LDF/DBC parser built in Rust
- **JABI mobile apps** - built in Flutter for cross-platform deploy - **JABI mobile apps** - built in Flutter for cross-platform deploy
- **ePaper Zephyr support** - made a breakout board a while ago, try adding to Zephyr - **ePaper Zephyr support** - made a breakout board a while ago, try adding to Zephyr
- **CAN/LIN DFU for Zephyr** - perhaps build it on UDS or devise custom protocol - **CAN/LIN DFU for Zephyr** - perhaps build it on UDS or devise custom protocol
- **MCUboot FSBL** - updatable bootloader! - **MCUboot FSBL** - updatable bootloader!
- **bootloader dynamic linking** - app calls functions provided by bootloader to save flash
- **acoustic keylogger** - lots of articles online, seems interesting - **acoustic keylogger** - lots of articles online, seems interesting
# Future Projects # Future Projects
@ -29,11 +27,10 @@ Projects I'll get to once I finish the others.
- **CNC v2** - do it properly this time, make sure it can cut metal - **CNC v2** - do it properly this time, make sure it can cut metal
- **3D printer** - silent, fast, use linear rails - **3D printer** - silent, fast, use linear rails
- **Nerf Gun** - precise, magazine fed, mountable and electric - **Nerf Gun** - precise, magazine fed, mountable and electric
- **Auto Nerf Turret** - become a Jedi with a reobot deliberately shooting at your lightsaber - **Auto Nerf Turret** - become a Jedi with a robot deliberately shooting at your lightsaber
- **Robotic Actuator** - improve the one I designed in 2017 - **Robotic Actuator** - improve the one I designed in 2017
- **Sine Wave Encoder** - worked with these before so document it - **Sine Wave Encoder** - worked with these before so document it
- **Solder Station** - try doing what GreatScott did and make a cheap good one - **Solder Station** - try doing what GreatScott did and make a cheap good one
- **Solder Paste Dispenser** - auto squeezing, maybe throw it on a 3D printer and integrate with KiCad
- **Pick and Place Machine** - auto placing, deep integration with KiCad - **Pick and Place Machine** - auto placing, deep integration with KiCad
- **Closed Loop Current Clamp** - http://www.ti.com/lit/ug/tidu674/tidu674.pdf - **Closed Loop Current Clamp** - http://www.ti.com/lit/ug/tidu674/tidu674.pdf
- **Isolated Differential Oscilloscope Probe** - **Isolated Differential Oscilloscope Probe**
@ -44,11 +41,9 @@ Projects I'll get to once I finish the others.
- **Self-balancing Robot** - super simple but I've never done it - **Self-balancing Robot** - super simple but I've never done it
- **Hacked Labelmaker** - hack Dymo and make it print whatever we want with a proper desktop app - **Hacked Labelmaker** - hack Dymo and make it print whatever we want with a proper desktop app
- **Microphone Triangulation** - like the EE16A lab but an actual product - **Microphone Triangulation** - like the EE16A lab but an actual product
- **Component Storage System** - storing a bunch of bags in a box is not a solution
- **Zynq breakout** - **Zynq breakout**
- **Intel SoC breakout** - **Intel SoC breakout**
- **CAN Sniffer** - no cutting wires, https://www.csselectronics.com/products/cancrocodile-contactless-can-adapter, inject a signal too? - **CAN Sniffer** - no cutting wires, https://www.csselectronics.com/products/cancrocodile-contactless-can-adapter, inject a signal too?
- **Zephyr USB Rubber Ducky** - make a USB HS board first
- **Pi Zero Cluster** - got lots of them lying around, no SD cards - **Pi Zero Cluster** - got lots of them lying around, no SD cards
- **SDR** - deploy the BLE/802.15.4 Chisel code you wrote (zero IF?) - **SDR** - deploy the BLE/802.15.4 Chisel code you wrote (zero IF?)
@ -84,8 +79,6 @@ Things I have to come up with a project for.
- protocols - protocols
- SIM card - SIM card
- PCIe - PCIe
- POE
- other - other
- embedded Rust - embedded Rust
- lwIP
- LVGL - LVGL

View File

@ -0,0 +1,57 @@
---
title: Solder Dispenser
date: 2024-6-22 4:20
categories: projects
excerpt: Compact, battery-powered, and uses ripple counting for encoderless closed-loop motor control. Not perfect, but consistent enough for 0402 parts.
header:
teaser: /assets/img/2024/solder_dispenser_3.jpg
gallery:
- image_path: /assets/img/2024/solder_dispenser_11.jpg
- image_path: /assets/img/2024/solder_dispenser_10.jpg
- image_path: /assets/img/2024/solder_dispenser_4.jpg
- image_path: /assets/img/2024/solder_dispenser_9.jpg
- image_path: /assets/img/2024/solder_dispenser_8.jpg
- image_path: /assets/img/2024/solder_dispenser_7.jpg
gallery2:
- image_path: /assets/img/2024/solder_dispenser_5.jpg
- image_path: /assets/img/2024/solder_dispenser_6.jpg
- image_path: /assets/img/2024/solder_dispenser_1.jpg
- image_path: /assets/img/2024/solder_dispenser_2.jpg
---
<https://github.com/dragonlock2/miscboards/tree/main/wch/solder_dispenser>
<https://github.com/dragonlock2/kicadboards/tree/main/projects/solder_dispenser>
Prior to this project, the way I applied solder paste was by using tweezers to move tiny globs around. This worked well, but was tedious, slow, and inconsistent. For fine-pitch parts, I often just placed a line of paste across the pads and dealt with the bridging after. Solder stencils work but are rather expensive if they're only used for one-off boards. Solder paste dispensers like the I-EXTRUDER are the perfect middle ground and can potentially be mounted to a CNC for automated paste dispensing. Every design I found online was some combination of too large, externally powered, difficult to manufacture, not ergonomic, and not aesthetic. Thus, I decided to design my own compact battery-powered version.
## electrical
As always, the electrical design was relatively straightforward. Since this is a frequently used project with lower sleep current requirements, it was a good time to try out the CH32V003. This was also my first time using the STSPIN250 as a motor driver and not an [audio amplifier](../../../2023/07/lightsaber-v5). The layout wasn't too bad, I fit it into 2 layers as usual.
The most interesting part of this circuit is the ripple counter which looks at current ripples caused by motor brush commutation to measure position. Both [TI](https://www.ti.com/lit/ug/tidud30a/tidud30a.pdf) and [Microchip](https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/Sensorless-Position-Control-of-Brushed-DC-Motor-Using-Ripple-Counting-Technique-00003049A.pdf) have near identical reference designs. Wanting to use fewer parts, I ended up designing my own signal conditioning circuit using LTspice. I modeled the ripple current as a sine wave current source using actual motor measurements as the parameters. Since we need to amplify the ripple frequency while filtering out the PWM frequency and load current DC bias, we essentially need a bandpass filter. To not miss steps caused by sudden load changes, we need a sharp cutoff below the ripple frequency. In fact, this is so important that my cutoff frequency is right at the ripple frequency. In the end my filter design consisted of a differential RC filter piped into a differential bandpass amplifier, into an inverting bandpass amplifier, into an RC filter, into a comparator with hysteresis.
## mechanical
The key to keeping the device small is a good mechanical design. I went as small as I could with cheap, off-the-shelf parts. I got the high-torque geared motor with attached M3 lead screw off Aliexpress and the glass syringes off Amazon. Looking at the syringe plunger, I reasoned that it was a hollow pipe with caps held on with glue or pressed fit. After a trip to the gas stove, I was able to get the cap off to confirm. This allowed me to put the lead screw far further down the syringe, saving quite a bit of room. I was a bit lazier with the rest of the design as my switches mount was hard to assemble and the whole thing needs to be disassembled to refill. While the final design is thicker than desired since I only had larger batteries on hand, it's still more compact than anything else I've seen.
{% include gallery %}
## firmware
Thanks to my prior work building an [SDK](../../../2023/12/baremetal-c-cpp-on-ch32v/) for baremetal CH32V development, I was able to focus more on application development. The UI is simple and allows the user to select an amount to extrude as well as continuously extrude if needed. The ripple counter worked surprisingly well for closed-loop position control after realizing I needed a small delay when switching direction. I'm sure it's still missing a step here and there, but it works well enough.
The hardest part was actually getting low-power sleep down to the advertised 9uA. The key was setting pullups on unused pins and disabling the ADC. I also needed to enable the SWIO as a wakeup pin to allow flashing while asleep, which interestingly also lowered current draw further. Importantly, the chip needs a full power cycle for sleep to work, otherwise it just stays awake. The 9uA also only applies to 3.3V as I saw it go as high as 26uA at 4.2V. Even more, I had one chip that drew far more sleep current, likely due to overheating while soldering. While the CH32V003 is certainly not perfect, for $0.10 a piece it has its place.
## tuning
The most difficult part of this entire project was getting the paste to dispense even somewhat consistently. At one point I suspected my ripple counter being inaccurate and tested with water. To my surprise, it was quite precise so I continued trying.
{% include video id="iy7foi0kO0E" provider="youtube" %}
For reference, I'm currently trying Chip Quik TS391SNL as my solder paste of choice. It's room temperature stable and by keeping it in airtight containers, I hope it lasts longer than a year. To refill a syringe, I found that removing the plunger and injecting from there works best. When putting the plunger back, use a thin wire to break the airtight seal so you can remove the air that gets trapped between the plunger and paste. Since my paste is T4 type, down to a 25AWG needle works well. Make sure to use the plastic conical needles as the gentle ramp prevents clogging.
For dispensing a precise amount, my initial idea was to overextrude and then retract. Since solder paste is so viscous, I imagined that adding more pressure initially would get it to dispense faster. That idea was impossible to tune well as I kept getting extra paste oozing out after the initial dispense. Inspired by 3D printers and the I-EXTRUDER, I eventually found that the prime/retraction system works far better. It's actually the same implementation as my initial idea, but with a different thought process. At rest, we relieve pressure on the paste by retracting a certain amount, preventing oozing. To dispense, we extrude the amount we want plus a prime amount equal to the retraction, then retract. The key to making this idea work was adding a continuous extrude option to the UI so I can prime the needle after a refill. Upon stopping the continuous extrude and retracting, the paste would be at the perfect position for dispensing tiny amounts afterwards. With that implemented, I was able to find a prime/retraction amount that worked well. It's far from perfect and not as consistent as I'd like, but the end result is functional.
{% include gallery id="gallery2" %}

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB