diff --git a/Gemfile b/Gemfile index 2a5e32c..0f885b3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,13 +1,12 @@ source "https://rubygems.org" -# remove once dependencies updated +# remove once added to jekyll dependencies gem "csv" gem "base64" -gem "bigdecimal" -gem "rake" gem "jekyll", "~> 4.0" gem "webrick" +gem "faraday-retry" group :jekyll_plugins do gem "jekyll-feed" diff --git a/Gemfile.lock b/Gemfile.lock index 72e0620..fe25009 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,26 +1,55 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) base64 (0.2.0) - bigdecimal (3.1.6) + bigdecimal (3.1.8) colorator (1.1.0) - concurrent-ruby (1.2.3) - csv (3.2.8) + concurrent-ruby (1.3.3) + csv (3.3.0) em-websocket (0.5.3) eventmachine (>= 0.12.9) http_parser.rb (~> 0) eventmachine (1.2.7) - faraday (2.9.0) + faraday (2.9.2) faraday-net_http (>= 2.0, < 3.2) faraday-net_http (3.1.0) 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) - 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) - i18n (1.14.1) + i18n (1.14.5) concurrent-ruby (~> 1.0) jekyll (4.3.3) addressable (~> 2.4) @@ -59,7 +88,7 @@ GEM kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) liquid (4.0.4) - listen (3.8.0) + listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.4.0) @@ -70,22 +99,65 @@ GEM sawyer (~> 0.9) pathutil (0.16.2) forwardable-extended (~> 2.6) - public_suffix (5.0.4) - rake (13.1.0) + public_suffix (6.0.0) + rake (13.2.1) rb-fsevent (0.11.2) - rb-inotify (0.10.1) + rb-inotify (0.11.1) ffi (~> 1.0) - rexml (3.2.6) - rouge (4.2.0) + rexml (3.3.0) + strscan + rouge (4.3.0) rubyzip (2.3.2) safe_yaml (1.0.5) - sass-embedded (1.70.0-x86_64-darwin) - google-protobuf (~> 3.25) - sass-embedded (1.70.0-x86_64-linux-gnu) - google-protobuf (~> 3.25) + sass-embedded (1.77.5) + google-protobuf (>= 3.25, < 5.0) + rake (>= 13) + 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) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) + strscan (3.1.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) unicode-display_width (2.5.0) @@ -93,21 +165,45 @@ GEM webrick (1.8.1) 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-android + x86_64-linux-gnu + x86_64-linux-musl DEPENDENCIES base64 - bigdecimal csv + faraday-retry jekyll (~> 4.0) jekyll-feed jekyll-gist jekyll-include-cache jekyll-paginate jekyll-remote-theme - rake webrick BUNDLED WITH - 2.5.5 + 2.5.14 diff --git a/README.md b/README.md index 2ffd393..624ff7f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ blog + resume ``` -gem install bundler +gem install bundler jekyll bundle update bundle install bundle exec jekyll serve diff --git a/_config.yml b/_config.yml index 377aa26..6954f2c 100644 --- a/_config.yml +++ b/_config.yml @@ -12,7 +12,7 @@ linkedin_username: matthewlamtran permalink: /:year/:month/:title/ show_excerpts: true -remote_theme: "mmistakes/minimal-mistakes@4.24.0" +remote_theme: "mmistakes/minimal-mistakes@4.26.2" minimal_mistakes_skin: dark plugins: - jekyll-include-cache diff --git a/_pages/todo.md b/_pages/todo.md index 02ec0c0..f876528 100644 --- a/_pages/todo.md +++ b/_pages/todo.md @@ -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. - **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 - **Linux board** - probably an NXP or STM32 chip, maybe a Zynq - **software** + - **Zephyr USB Rubber Ducky** - **autodbconv** - LDF/DBC parser built in Rust - **JABI mobile apps** - built in Flutter for cross-platform deploy - **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 - **MCUboot FSBL** - updatable bootloader! - - **bootloader dynamic linking** - app calls functions provided by bootloader to save flash - **acoustic keylogger** - lots of articles online, seems interesting # 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 - **3D printer** - silent, fast, use linear rails - **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 - **Sine Wave Encoder** - worked with these before so document it - **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 - **Closed Loop Current Clamp** - http://www.ti.com/lit/ug/tidu674/tidu674.pdf - **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 - **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 -- **Component Storage System** - storing a bunch of bags in a box is not a solution - **Zynq breakout** - **Intel SoC breakout** - **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 - **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 - SIM card - PCIe - - POE - other - embedded Rust - - lwIP - LVGL diff --git a/_posts/2024/2024-6-22-solder-dispenser.md b/_posts/2024/2024-6-22-solder-dispenser.md new file mode 100644 index 0000000..d28194b --- /dev/null +++ b/_posts/2024/2024-6-22-solder-dispenser.md @@ -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 +--- + + + + +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" %} diff --git a/assets/img/2024/solder_dispenser_1.jpg b/assets/img/2024/solder_dispenser_1.jpg new file mode 100644 index 0000000..0e96d9c Binary files /dev/null and b/assets/img/2024/solder_dispenser_1.jpg differ diff --git a/assets/img/2024/solder_dispenser_10.jpg b/assets/img/2024/solder_dispenser_10.jpg new file mode 100644 index 0000000..e097138 Binary files /dev/null and b/assets/img/2024/solder_dispenser_10.jpg differ diff --git a/assets/img/2024/solder_dispenser_11.jpg b/assets/img/2024/solder_dispenser_11.jpg new file mode 100644 index 0000000..2e98d37 Binary files /dev/null and b/assets/img/2024/solder_dispenser_11.jpg differ diff --git a/assets/img/2024/solder_dispenser_2.jpg b/assets/img/2024/solder_dispenser_2.jpg new file mode 100644 index 0000000..bb3a3fd Binary files /dev/null and b/assets/img/2024/solder_dispenser_2.jpg differ diff --git a/assets/img/2024/solder_dispenser_3.jpg b/assets/img/2024/solder_dispenser_3.jpg new file mode 100644 index 0000000..feb0428 Binary files /dev/null and b/assets/img/2024/solder_dispenser_3.jpg differ diff --git a/assets/img/2024/solder_dispenser_4.jpg b/assets/img/2024/solder_dispenser_4.jpg new file mode 100644 index 0000000..77cca02 Binary files /dev/null and b/assets/img/2024/solder_dispenser_4.jpg differ diff --git a/assets/img/2024/solder_dispenser_5.jpg b/assets/img/2024/solder_dispenser_5.jpg new file mode 100644 index 0000000..f06d914 Binary files /dev/null and b/assets/img/2024/solder_dispenser_5.jpg differ diff --git a/assets/img/2024/solder_dispenser_6.jpg b/assets/img/2024/solder_dispenser_6.jpg new file mode 100644 index 0000000..1cc8821 Binary files /dev/null and b/assets/img/2024/solder_dispenser_6.jpg differ diff --git a/assets/img/2024/solder_dispenser_7.jpg b/assets/img/2024/solder_dispenser_7.jpg new file mode 100644 index 0000000..296f8ba Binary files /dev/null and b/assets/img/2024/solder_dispenser_7.jpg differ diff --git a/assets/img/2024/solder_dispenser_8.jpg b/assets/img/2024/solder_dispenser_8.jpg new file mode 100644 index 0000000..994b5cd Binary files /dev/null and b/assets/img/2024/solder_dispenser_8.jpg differ diff --git a/assets/img/2024/solder_dispenser_9.jpg b/assets/img/2024/solder_dispenser_9.jpg new file mode 100644 index 0000000..d4a346f Binary files /dev/null and b/assets/img/2024/solder_dispenser_9.jpg differ