You are browsing as a guest. Sign up (or log in) to start making projects!

james-lian

@james-lian

Joined June 8th, 2026

  • 28Devlogs
  • 6Projects
  • 2Ships
  • 17Votes
Amateur software dev + aspiring hardware dev
Open comments for this post

5h 32m 30s logged

Devlog #6: Getting throttled

Attempting to complete my PCBs, and I’m going through it, fr.

Fried transistors + attempted fixes

After receiving my PCBs, I got to work to solder my components. I had ordered PCBA for any components I didn’t have, such as inductors, the barrel jack connection, etc., but I was planning to save money by hand-soldering components I DID already have, such as transistors and my entire MCU system. This also gave me the added benefit of testing these components beforehand, and it allowed me to go through several iterations of code on the ESP-01S.

The first issue I encountered with hand-soldering was with soldering the tiny, microscopic transistor holes (it was my first time doing so). The copper pads for my PN2222 breadboard transistors are super close together, and with my shaky hands, I accidentally whiffed, put too much solder, and left a solder connection between all three pads. Whoops!

I eventually managed to fix it with a solder sucker.
But after soldering, I decided to test the transistors with a multimeter to check if they were damaged, and both transistors were duds. I probably spent too long trying to fix it with the soldering iron and ended up frying them both.

After this, I attempted to salvage the project by desoldering some of the more expensive components from the board and using it on the second backup PCBA I ordered from JLCPCB. I managed to salvage the OLED screen, but while desoldering the ESP-01S, I broke off several pins, essentially leaving the chip unusable. I decided not to salvage the bigger chips (e.g. ESP-01S, ENS160+AHT21), and decided to just reorder some components instead for the second attempt.


New attempt: flip-flop switch fix + MCU not working

I managed to solder all components to the second PCB cleanly, without any hiccups. However, on trying my project out, I encountered two issues:

  1. The flip flop switch turns on the fan, but it doesn’t turn it off.
  2. The 3.3V MCU system (including the OLED, ESP-01S, and ENS160+AHT21 sensor) doesn’t seem to turn on.

The first issue was relatively easy to fix. Turns out, I had forgotten to add a pull-up resistor near the bottom of the flip-flop switch, and thus my steering capacitors had no path to VCC to “refill” (reset) their charge after the initial button press. I quickly soldered a pull-up resistor to VCC to fix it.

The second issue stumped me for a while. I found out that I had forgotten to connect pin 4 (EN) of the ESP-01S to 3V3, so I quickly soldered a connection, but it still wasn’t working. Eventually, I took a look with my multimeter and found the problem: my 3V3 logic chips were actually running on 1.23V for some reason.

It turns out, JLCPCB had sent me PCBA’s with the WRONG BUCK CONVERTER. I had ordered an TDSEMIC LM2596S-3.3, which would normally work perfectly, but JLCPCB had sent me PCBA’s with TDSEMIC LM2596S-ADJ instead.

I’ll have to either contact JLCPCB for a refund, or attempt to salvage the PCB myself by modding it. I’ve burnt enough allowance on this ;-;


Below: desoldering fail, attempted fixes (2), close-up of LM2596S-ADJ vs what I actually ordered (https://www.lcsc.com/product-detail/C42421958.html?s_z=n_q_C42421958&globalKeyword=C42421958)

0
0
40
Open comments for this post

9h 6m 45s logged

Devlog #8

Database schemas + API planning

I’ve mostly been working on database schemas and models since the last devlog, and I’ve created APIs to allow the frontend to access this information from the database as well. A lot of systems design work has been going on in the background. As I’m still a beginner to such concepts, a lot of rewriting code has also been going on in the background!

To be honest, progress has slowed since I’ve been splitting myself between working on the frontend and backend. I often find myself getting a bit stuck or lost in what’s happening. I think I’ll work on completely finishing one thing before moving onto the next for the next devlog.

Below are snippets of what I’ve been working on, including API methods for the frontend, the API endpoints for the backend, and some peeks at some of the changes I’ve made to existing models/new models that I’ve created.

FileSystemWatcher

I’ll be using the built-in C# FileSystemWatcher in order to track which files and folders the user has made changes to. The alternative is polling for these changes on-demand (e.g. checking the folder’s “Last-Modified” date). However, the problem with such an approach is that Windows does not automatically update a folder’s “Last Modified” date exactly when a file inside that folder is changed. Thus, to confirm whether any changes actually occurred to a folder or not, the program would have to poll ALL subdirectories and files, which could be performance-intensive. On the other hand, FileSystemWatcher is an abstraction over OS-level kernel APIs. The kernel itself registers a listener on an attached folder when FileSystemWatcher is used. Thus it is very efficient and much more accurate than polling a folder’s properties.

0
0
12
Open comments for this post

2h 44m 1s logged

Devlog #3

Reconfigured PCB to use surface-mount 74HC595

As the teeth of a through-hole 74HC595 would block the sliding action of the stator, I took some time to redesign the PCB with a surface mount component instead.


3D CAD casing

I also spent some time designing the casing that would hold everything together. It’s a little bulky, but I think it’ll work for now. I will be adding finishing touches (including mounting holes) later.


1
0
40
Open comments for this post

48m 38s logged

Devlog #2

Almost completed PCB Design for slider + stator (design error)

I redesigned my PCB several times to achieve the optimal layout for all the components. I ran into several issues along the way.

1) Rx electrode and Tx transmitter positioning

The Rx electrode and Tx transmitter array are sensitive capacitive devices. When positioned close to other electrical components, parasitic capacitance can be introduced, which can disrupt our weak signal and create crosstalk. It may also affect the integrity of our 8-phase signal, causing phase delays and shifts.

2) Connecting the Tx array pads to the 74HC595

Wiring the Tx array pads (under the Rx electrode) in a way so that every 8th pad was connected was a nightmare. I couldn’t directly draw traces over the copper pads as that would disrupt the ground plane, which is crucial for blocking noise from other components and external sources. For example, the human body is essentially a giant capacitor, and a human placing their hand over an unshielded Tx array would completely ruin our delicate signal. Therefore, it is crucial that the copper traces avoid cutting up as much of the ground plane as possible.

Thus, I ended up reducing the width of both the Rx electrode and Tx array to make extra room for running the copper traces to the side. I also added a small grounded copper pad between the traces and the Rx electrode just to shield it from any parasitic capacitance.

3) Design mistake

I realized just now after looking at the PCBs I ordered that I made a tiny mistake with my op-amp circuit. To the left of my Seeed MCU, I have an op-amp functioning as an ultra-high input impedance buffer. I’ve also attached an image of the schematic.

An op-amp normally takes a weak voltage signal, compares its two inputs, and multiplies the difference to create a much stronger output voltage. On my circuit, Pin 4 and Pin 1 (VOUT) of my circuit are connected to create a 1:1 voltage ratio, creating a unity-gain buffer feedback loop, so no amplification is actually happening. What DOES happen is that we’ve exposed the op-amp’s high input impedance, meaning it draws incredibly little current from our incredibly weak Rx electrode capacitor. This prevents our Rx electrode from just straight discharging our AC signal our circuit - preserving its original timing and phases. We feed the signal into pin 3 of the op-amp, allowing it to ride the 1.65 V (introducing micro-fluctuations, e.g. 1.64V-1.66V), and our op-amp outputs this signal directly to our MCU pin.

My mistake was that I connected the Rx electrode straight to pin 4 instead of pin 3.
I will see if I can fix it with some quick mods. Fingers crossed.


0
0
35
Open comments for this post

5h 19m 57s logged

Devlog #5

CAD Modeling

With the code and PCB done, I needed a frame to hold all of the hardware together. Therefore, I spent a couple hours designing two cases in Fusion360 - one to house the PCB, and one to house the fan and the carbon filter I was using.

PCB Case

This PCB case is a little different than the ones I’ve designed so far. For one, the MOSFET and the capacitor that arrived on the PCB assembly I ordered from JLCPCB were much larger than I’d imagined, so I needed to design holes in my casing to accommodate for their size. Additionally, the ENS160 + AHT21 sensor needs generous access to air in order to provide accurate readings. I couldn’t encase them in the casing entirely, so I needed several unusual holes in my design so as not to interfere with the placements on my PCB board.

I ended up designing a snap-fit housing with two parts - top and bottom (my first time designing a snap-fit joint!). The top part has several holes to ensure enough spacing for PCB components such as the OLED screen and keyswitch. It’s not air tight, but I really only need it to keep the PCB isolated and make things more presentable.

Fan Holder Case

I also needed to model a CAD case for the desktop fan to hold it and the active carbon filter in place. The fan case itself is actually a very simple design - three separate “panels” held in place with 4 screws and heat inserts.

0
0
52
Open comments for this post

3h 12m 22s logged

Devlog #4

Finalizing MCU program

I used the breadboard prototype I had from Devlog #1 to finalize a working version of the ENS160 + AHT21 sensor program on the ESP-01S. I use the Adafruit libraries for these components to extract their readings, and then display it on a scrolling background on my 0.91 inch OLED SSD1306.


This program is the final step before I solder the remaining components onto my PCB, which conveniently arrived as I was on vacation.

It displays the following information:
Line 1: Temp (deg C), Temp (deg F), Humidity (relative %)
Line 2: Air Quality Index (AQI), Total Volatile Organic Compounds (ppb - TVOC), eCO2 (ppm - estimated CO2)

0
0
48
Open comments for this post

9h 31m 6s logged

Devlog #7

API errors fixed - OAuth now works 100%

In my specific architecture, API calls from frontend to backend work like so:

fetch() call from Vite frontend --> intercepted by WPF WebService2 middleware shell --> forwards down NamedPipe --> received by Kestrel (cross-platform web server) on the ASP.NET Core C# backend. 

For the longest time, I just couldn’t get API calls from the frontend to reach the backend. I would always end up with a TypeError: failed to fetch(), which, as I found out, could mean a lot of things. Initially, I thought it was a CORS issue with the backend. I added builder.Services.AddCors() to allow cross-origin requests, and also adjusted the content-headers included with the fetch() request on the frontend, but I still experienced the same issue.

Eventually, I discovered that it was a CORS issue with the MIDDLEWARE. It turns out after the WPF WebView2 shell forwards the fetch() request down the NamedPipe and retrieves a response from the backend, it actually creates a new web request to return it to the frontend with createWebResourceRequest(). This new response DOESN’T COPY the CORS headers from the backend, meaning the Access-Control-Allow-Origin header was missing from the response despite the backend returning a 200 OK status code. This blocks the frontend from reading the response, and a generic TypeError: Failed to fetch is thrown.

Since the desktop app is essentially a proxy/interceptor - it’s creating its own HTTP response, so it needs to handle CORS headers itself.

I was also missing an OPTIONS preflight responder on the backend, so I added that in as well.


Listener callback dynamic port access

When OAuth is requested from the frontend to the backend, the backend adds a listener to the provider’s OAuth function call. This way, say when the user completes Google’s OAuth process, Google redirects to our listener url, and we can display a Success page to the user.

I had a minor logic error in my listener callback implementation where the same port was always used, even if it was unavailable. For this reason, if the user were to leave the OAuth page and retry the OAuth process, they would encounter an error if the listener was still not cleaned up. I implemented a dynamic port searcher to automatically serve the user a port that was free and unused.


Implementing EF Core Backend System and FileSystem schemas

I’m currently planning to implement the core of the backend functionality, which is to monitor file changes occuring to “watched” folders and upload them asynchronously and autonomously to providers the user has signed into.

A great way I’ve found to map out future app functionality is to map out schemas for anything that needs to be stored locally. Currently, I’m working on schemas for SystemSettings, SyncEvents, and FileSystemWatcherWorker.


Below is a short overview of the current basic features of the app, including OAuth account management + a notification/error-logging system.

0
0
7
Ship

I made a custom 3x4 layout macropad configured with 5 keyboard profiles! It also features a unique keylogging mechanism using a custom class KMK extension that places itself within the KMK firmware Display update loop to display to the user their inputs and key combinations.
Everything is my own design, including the PCB and 3d printed CAD parts. Made with parts from the hack club hackpad kit.

The keyboard profiles include:

  1. Default (includes commonly-used keys like HOME, END, arrow keys, and some Fn keys)
  2. Numpad (includes numbers)
  3. Art layer (includes common Krita hotkeys like B for brush, E for eraser, A, S, R, etc.), activated through the Toggle key
  4. Art-CTRL layer (includes common CTRL-related commands for Krita, including CTRL+A and CTRL+S), activated by holding the bottom-left modifier key on the Art layer
  5. Art-CTRL-SHIFT layer (includes common CTRL-SHIFT-related commands for Krita, including CTRL+SHIFT+A and CTRL+SHIFT+Z), activated by holding the bottom-left modifier key AND the key directly above it on the Art layer
  • 7 devlogs
  • 14h build
  • 5.00x multiplier
  • 69 Stardust
  • Hackpad
Video of Project → See source code →
Open comments for this post

5h 9m 12s logged

Final Devlog (build phase)

PCB soldering + Assembly

I’ve successfully soldered my PCB and assembled my parts using some 3d prints and the kit that arrived a while ago.
There was an issue with my design where 2 out of the 6 screws in my design ended up being redundant, but it still works as-is!


KMK fixes + added input display functionality

I tested the KMK code that I wrote earlier and due to several errors in the code, it didn’t work.

I refactored the code to fix the errors, and I also added additional functionality:

  1. Expanded the number of profiles (KMK layers) from 2 to 5
  2. Added modifier key-like functionality (e.g. Ctrl and Shift) using the said profiles
  3. Added a custom input display class for the OLED that logs the user’s keypresses and displays the following action!

Input display

The OLED screen features an input display that keylogs the user the key commands and combinations that they press.
Making it work was actually surprisingly hard. For one, KMK does not provide much built-in functionality to perform an action after a specific key is pressed. I decided to create my own custom class - OLEDManager - to manage the keyboard’s TextEntry’s. OLEDManager can be placed itself in the actual firmware loop of KMK by adding it to keyboard.modules - allowing it to activate after keys are pressed or after the matrix is scanned. However, this took a while for me to figure out - there was some boilerplate required that I only corrected after some trial and error.
Initially, the keylogging functionality didn’t work if multiple keys were pressed at once, as KMK did not store key press events in some sort of log. I made a workaround that tracked the keypresses by adding and removing them incrementally based on the keypress and keyrelease events. After that, I ran into the issue where some keys wouldn’t be logged at all, or would only show up on the display AFTER the keys were released. This took me a really long time to figure out, but any keys that aren’t associated with layer change keys (e.g. KC.MO and KC.TG) don’t prompt any KMK Display rerenders. I could not simply update the text property of the TextEntry objects, I had to actually force a rerender after every keypress in order for the logging functionality to perform as expected.

0
0
17
Open comments for this post

9h 43m 49s logged

Devlog #6

These past days I’ve been focused on cleaning up the auth code and setting up frontend-backend communication.

IScopeFactory - Singletons and Scoped Objects

.NET (modern C#) features native dependency injection, where abstractions can be mapped directly to a class, in order to allow that class to receive its required objects from an external source in a more efficient and cleaner manner.

In ASP.NET Core C#, you can register classes as a couple of different types of services to be injected into other classes. I mainly use singletons and scoped objects. Singletons, like their name suggests, are long-lived classes, of which a single instance exists throughout the application’s lifecycle. By contrast, scoped objects are created once per client request before being destroyed.

The problem is that when you use dependency injection, you cannot inject a shorter-lifespan class into a longer-lifespan class (e.g. a scoped into a singleton). This necessitates the use of IServiceScopeFactory to automatically manage the lifetimes of these child classes.


Thread-Safe Singletons

By default, the System.Collections.Generic library in C# is NOT threadsafe. That means that, if multiple scoped objects or singletons access the same Dictionary, that could throw an error. Thus, I switched them out for ConcurrentDictionaries throughout my app.


Multi-Account Functionality Integration

I further integrated functionality that allows my app to store MULTIPLE accounts per provider (e.g. 3 OneDrive accounts, 4 Google Drive accounts). This required refactoring the CloudToken object to include a new AccountId prop, retrieving that prop from the OAuth2.0 protocol, and rewriting some logic in TokenManager to include AccountId validity checks.


RevokeOAuthSync

I added a method to revoke OAuth by terminating the refresh and access tokens using the revoke OAuth2.0 endpoint.


Frontend-Backend Communication

With the basics of the auth protocol API and TokenManager API setup, I’ve begun attempting to facilitate API calls between the React Vite frontend and the ASP.NET Core C# backend. It’s still a WIP, but things are moving! I also added some additional UI to allow the user to select and manage their accounts on different providers.


0
0
4
Open comments for this post

9h 54m 42s logged

Devlog #5

I’m currently on vacation, but I’m trying to stay productive by working on some software projects.

OAuth2.0 Token Management + EF Core SQLite Setup + HTTPClient/DPAPI

The past few days, I’ve been working on setting up some basic OAuth functionality for Google and Google Drive. I thought it would be very simple at first, but perhaps since I’m new to C# and on vacation, it took me a bit to get used to the C#/ASP.NET features and development patterns, such as dependency injection.

Additionally, there’s plenty more to using OAuth 2.0 that I was not aware of at first. Step 1) involved acquiring ClientId and Clientsecret values from the Google Dev Console page. These were stored in User Secrets in Visual Studio along with the project.

For desktop clients, clients use the provider’s OAuth API link to apply for a long-lived Refresh Token. This Refresh Token, once encrypted and stored securely, is used to continuously request for short-lived Access Tokens, which is what is actually used for Google’s API services, like the Google Drive API.

Encryption was setup using the .NET data protection API, DPAPI. I created two classes: a GoogleDriveProvider to manage Google-specific OAuth setup and Drive API calls, and a generic TokenManager that handles the refresh-access token exchange for all OAuth-related providers, including ones I will add in the future such as OneDrive and Dropbox.

For the HTTPClients used to make the web requests, I decided to add an HTTPClient to GoogleDriveProvider using builder.Services.AddHttpClient().

Finally, all pertinent information to the app is stored within an SQLite database using EF Core. Refresh Tokens are stored within an object called CloudTokens.

I have the basic functionality for the OAuth setup, so my next steps will just be to test it out and debug for any errors.

Below are some snapshots from the code I wrote.


0
0
15
Open comments for this post

1h 42m 56s logged

Devlog #3 - PCB design

Once I had the design finalized from the schematic, it was straightforward enough to convert it into a PCB. I spent some time rearranging components to try and use as little space as possible.

I had to leave some extra space for the ENS160+AHT21 air quality sensor, as it doesn’t work without consistent exposure to air.

2
0
58
Open comments for this post

1h 37m 22s logged

Devlog #2 - schematic design

After confirming that the ESP-01S was a viable microcontroller for this project, I continued with designing the schematic for the PCB.


Power Management

ESP-01S is a 3.3V native controller, and the ENS160+AHT21 and the I2C OLED both use 3.3V logic. If I plug my 12V DC adapter directly into the ESP-01S, I’ll fry all my MCU components. Thus, I’m using an LM2596 buck converter to step-down some of the charge to 3.3V.


Air Quality Sensor + MCU

This section contains the sensor components and MCU. The air quality sensors and the OLED both share the same SCL/SDA I2C communication pins, which we established was possible in the previous devlog.


Flip-Flop Switch + MOSFET

Since the pins on the ESP-01S are being used, I decided to try exploring other options to turn my fan ON/OFF. I decided to use a flip-flop switch circuit. This allows me to persistently alter the fan’s ON/OFF state by simply clicking one pushbutton.

The flip-flop switch circuit is a bistable multivibrator. It has two stable states where it can output a persistent signal (either 0 or 1), thus why we say it can represent a single bit of information.

One of the states (transistors) is connected to a MOSFET (STP55NF06L), a heavy-duty voltage-controlled component which handles heavy loads much better than a simple transistor. The MOSFET helps control the fan.

I attached a flyback diode in parallel to the fan. Because a fan is fundamentally wire coiled around magnets (an inductor), when the flip-flop switch suddenly turns off, the magnetic field of the fan collapses, converting back into an extremely high-voltage electric signal that, if not protected against, could instantly fry my MOSFET. The flyback diode allows the current to loop back around through the inductor until it is harmlessly dissipated as heat.


Power-On Circuit

On its own, the flip-flop switch’s initial state is hard to control . It’s based on chance, and small manufacturing differences between identical resistors and capacitors used in the circuit.
To ensure the flip-flop switch always initializes to an OFF state, I added a tiny 10nF capacitor to one of the transistors. This makes one of the transistors slightly slower, thus ensuring the other one will always “win” the race condition.


Next, I’ll be designing the PCB and programming the ESP-01S!

0
0
9
Open comments for this post

46m logged

Devlog #1 - breadboard prototype

I’ve been soldering a lot more recently thanks to starting a variety of electronics projects, and I’m starting to feel the need to get a fume extractor and some sort of air quality sensor to monitor my workspace.
After some consideration, I decided making my own fume extractor would be a fun project idea, and also a great way to use up some electronics components that I have lying around.


Components

To make this project, I ordered some cheap $6 12V cooling fans meant for PC setups. For the microcontroller, I’ll be using an ESP-01S, an ENS160+AHT21 (which can measure air quality and VOCs), and an I2C 0.91 inch OLED display, all components I already have. I spent some time desoldering the OLED display from an older PCB project to reuse it in this one.


Breadboarding

The ESP-01S is an incredibly small controller, with only two pins (GPIO0 and GPIO2) available as digital pins (apart from RX and TX, but I plan to use those for debugging). Since both the OLED and the ENS160+AHT21 are I2C components that communicate with Serial Data (SDA) and Serial Clock (SCL), theoretically these components could share GPIO0 and GPIO2 as SDA and SCL pins if they had different device addresses.
However, to be sure, I breadboarded and flashed a quick program to output the addresses and confirm that I had three separate, unique identifiers.


Now that I have this information, I’m ready to design the PCB next.

0
0
7
Open comments for this post

3h 27m 59s logged

Devlog #3

PCB Design Mistakes

After soldering the components on last time, I found several issues with the design. First, it appears that I wired the 555 timer wrong - I ordered an NE555 timer which takes 5V logic instead of one that was compatible with 3.3V logic. Even after soldering a wire to fix that, something else with the design was wrong, which led to the component constantly outputting a HIGH signal.

Instead of spending time trying to fix it, I decided to just scratch out all the traces connecting it and removing the component altogether. Rather than retrieving a frequency from the touch capacitance sensor via hardware, I opted for a pure software route instead. I soldered a wire directly from the capacitance sensor’s copper pad to pin 1, and then using software, I would repeatedly drive the pin HIGH, and then measure the amount of time it takes for the pin to reach LOW (measuring the amount of time it takes for the copper pad to discharge).

This allows me to approximate the capacitance of the copper pad via the formula t = R * C. The capacitance will increase when my finger approaches the pad, and so by counting the cycles it takes for the capacitor to discharge, our program can tell when the capacitive sensor was touched.

The soil moisture capacitive sensor is basically the same concept in action. When the sensor is placed in dry soil, the capacitance will be low (as soil is dielectric), and timing for discharge will be low as well. When the soil is watered, capacitance will increase (as water is a strong dielectric), which will thus increase the time it takes for the soil capacitive probes to discharge.

The only problem was that it turned out I had wired the RC circuit for the soil capacitive probes incorrectly as well, which I thus spent time fixing.


Shipping

After testing the code and ensuring it worked, I went back and redesigned the PCBs to fix the mistakes I found in these physical prototypes. I’ll be shipping this project soon!

0
0
6
Open comments for this post

55m 12s logged

Devlog #2

Soldering

The PCBs and components arrived, so I spent today soldering everything together. This was the first time I’ve soldered in a couple years, so apologies if it looks rusty. I realized belatedly as I was soldering that I made one mistake: I forgot to order the I2C OLED display. I’ll be attaching it as it arrives in the coming days.

Additionally, I’ve kept the legs of most of the components intact for now, in case I want to reuse them for other projects in the future.

0
0
9
Open comments for this post

1h 24m 29s logged

This is the initial schematic for a solar cell that charges a lithium ion coin cell via the BQ25504 Texas Instruments battery charger. This one took quite a while to setup as I had completely fudged the resistor value calculations at the start in order to get my desired output voltage. I believe this will still need more tuning in the future so that the proper voltage will output under indoor lighting conditions.

0
0
3
Open comments for this post

5h 12m 1s logged

Devlog #1

I’ve completed a schematic and an initial PCB layout for my own DIY capacitive sensor calipers, although I will definitely redo the layout to ensure a more efficient trace design.

The design of the calipers and how they work

Calipers are fundamentally capacitive sensors, and so I will recreate these precise sensors with carefully placed copper pads on the PCB. The Xiao Seeeduino RP2040 in my design will control the copper pads, and an LCD screen. I’m also using an 74HC595 to convert 3 pin outputs into eight (for the Tx transmitter array discussed later).

The slider and the stator

All the MCU and electrical components will be placed on the slider (head of the calipers). The stator (ruler body) will be a separate, long PCB “stick” that doesn’t contain any electrical components, consisting of only floating copper islands.

The capacitive pads: How is the signal received?
I will be placing several copper islands to form an Receiver (Rx) electrode and a Transmitter (Tx) array on the bottomside of the slider head. The copper islands on the stator will be 2.5mm wide, and placed 2.5mm apart (forming one 5mm cycle). The Tx array on the stator head will consist of a repeating pattern of 8 copper pads spaced apart evenly within every 5 mm cycle.

  1. This configuration essentially forms a capacitor - two conductors separated by a layer of insulating material (a thin sheet of air).
  2. To bridge the air gap between slider and stator, I need to generate an AC signal (which radiates electromagnetic radiation much better than DC). By generating a square wave signal with the 8 copper pads, we can simulate an AC signal without actually using digital-to-analog converters.
  3. I use the 8-pad Tx array to generate an 8-phase signal (driving half of them HIGH and half of them LOW, and then rapidly shifting the square wave right, going from 0deg phase shift to 360deg in 45deg increments). Using the RP2040, I can generate the 8-phase signal at very high speeds (e.g. 100 kHz) with the PIO state machines. A high frequency is needed to limit capacitive reactance (resistance to AC current), whose formula is given by Xc = 1/2πfC. Our homemade capacitor has an extremely low capacitance (C) value (picofarads), and thus my frequency (f) must be high for me to get any usable signal.
  4. No matter the position of our slider, the copper islands on the stator line up most with only certain copper pads on our 8-phase Tx array. When these copper pads are driven HIGH, we read the biggest signal from our Rx electrode. When the copper pads are misaligned, we read a weak signal from our Rx electrode.

The logic behind position

We take two samples (I and Q) at specific phases in the 100 kHz cycle to find the output voltage of the Rx array. Samples that return a strong signal (high output voltage) are samples whose phase aligns strongly with the copper pads of the stator. If the stator is shifted slightly and thus misaligned, the returned signal will be weaker. We can use two samples and some math to easily determine position.


All ICs are to be fitted with decoupling capacitors placed very close by to ensure the voltage supply (VCC) is not overwhelmed by the 100 kHz frequency and that a stable voltage supply is maintained at all times.

0
0
4
Open comments for this post

2h 52m 1s logged

Devlog #1

Schematic + PCB design

Finished setting up the schematic + PCB of my capacitive soil moisture sensor display. It features a capacitive sensor for detecting soil moisture, and also a capacitive touch sensor for interacting with the OLED display. The PCB features a two copper probe-fingers; one which connects to the MCU and forms a RC circuit, and the other which connects to ground.


How it works

The idea is that when the PCB is embedded in the dielectric soil, and the humidity level increases, the capacitance of the copper probes will naturally increase as well with the presence of water. Through the equation Time = Resistance * Capacitance, we can detect the change in how long the capacitor (the copper probes) take to charge/discharge, and thus approximate the humidity level, with some calibration of course.

The touch capacitance sensor (shown on f.cu) works similarly. When a finger approaches the copper pad, the capacitance of the circuit changes, and that is something we can measure. Only difference is that, for added robustness, I added a 555 timer to convert the capacitance into a frequency that the MCU can read.


Next steps

Since the PCB is already finished, the next step for me is to finish the code and order the PCB to test it out. As this is one of my very first PCBs ever, fingers crossed it’ll work 🤞

0
0
2
Loading more…

Followers

Loading…