DEV Community

Cover image for DIY Holding Tank Sensors Part 2: "The React Native App"
Leif
Leif

Posted on

DIY Holding Tank Sensors Part 2: "The React Native App"

In my previous article, https://dev.to/mergewithcare/diy-holding-tank-sensors-part-1-or-mommy-the-ai-made-me-code-in-c-4llg , I went over the process of "vibe coding" a working ESP32 app in C using the Espressif toolchain.

I'm going to go over the journey of "vibe coding" out a React Native app to read values from the sensors on the microcontroller as described in Part 1, and as always my code is up at https://github.com/leifdroms/tank-level-public for anyone that so desires to download it themselves.

We are off to a rocky start.

Claude thinks our React Native project structure is incorrect, and asks us to eject the project. I don’t want to do that- I want to build it with the latest snazzy version of Expo, the framework that sits on top of React Native to make things easier. It suggests that we install the package expo-dev-client, and work from there, which I agree with because a limitation of using React Native with Expo Go: Expo Go on its own only allows you to work with native modules included with Expo Go. Unfortunately, a Bluetooth client is not one of them.

…And unfortunately things did not work out.

Each time my bundler would crash, Claude would take some feedback and output something like the following:

There came a point where I stopped auditing each change it made, crossed my fingers, and hoped that it would all sort itself out. This message was the result of the culmination of several changes Claude made without explicitly approving each one as I had been doing for the microcontroller app section of the project:

It was to no avail.

Why are files in my node_modules folder corrupted?? Something isn’t right. Maybe Claude should stick to its “day job” and not try to replicate the function of command line tooling to bootstrap projects, when that tooling is available, mature, and free. I think to myself to pivot at this point, start over from scratch with a proper React Native project, and have Claude do what it does best – work from there. I will take a peek at the app structure as we have it:

And again we have one giant application file that’s about 700 lines of code long as well:

I have finally hit a point where I’m putting on my judgement and deciding to get things done “the traditional way” and bootstrap the app in the traditional means rather than hoping an LLM will replicate this on its own. Maybe someday when context windows and training data is much better, but for now I can feel myself getting into an AI-driven “coding death spiral” and it’s important to notice when you’re going in circles and to pull yourself out. Sorry Claude, I’ll be back to you in a moment.
And now we’re back to sanity, after setting up Expo and the dev client the “normal” way on the command line:

Ok. Let’s let Claude do some of its automagic. I’m in a salty mood while writing this blog entry, so I’m going to make Claude earn its keep today. Before I created a new app with the official command line utilities, I changed the old app’s folder name to mobile-app-backup. Opening up the new app folder and a new Claude instance in the VS Code terminal, the “annoyed Dad” side of me comes out:

At least Claude can pretend to understand my feelings:

Hmm doesn’t look bad after some iteration, although we don’t need the “Explore” tab anymore that came with Expo. Will get rid of that in the next iteration, and probably drop the "tank history" view because it's really not that important.

Which brings up an interesting point - why did Claude put that in there? It just sort of hallucinated a product requirement, and now I have the associated cruft along with it.

Oh no! We can’t connect to Bluetooth!

And unfortunately Claude wasn’t able to get it spinning after a few more prompts. Time for me to get my hands dirty on my own.

After some juggling and coaxing, we’re connected! We have an app that shows tank levels and changes based on sensor input!

But now we have even more questions, like -

  1. Why is it asking me to authenticate with a pin at the beginning, but this seems to do absolutely nothing?!

  2. Why can’t I set an “admin” pin?

  3. Why can’t I change configuration settings on the ESP32 (which was the whole point of an admin pin in the first place)?

Thus we are really starting to see the pitfalls of “vibe coding”. This thing, while completely, wholly impressive and saving me days or weeks, will NOT crank out a production-ready app just by waving my fingers and making a wish.

Let's focus on the bright side though, and maybe I'm being more critical of the phone app/"front end", because that's been the direction my career has taken the past few years.

Mid-way Conclusions

I was able to go from zero to a working microcontroller and companion phone app that would serve my purposes well in about 2-3 days. This is insane and my mind is still a little blown. The initial product WAS pretty rough and nowhere near production grade, and required quite a bit of human intervention, but I was particularly impressed with how AI was able to handle the embedded/C code. This was a huge weight off my shoulders since it is out of my usual comfort zone, although upon inspection it appears fine without memory leaks. Like my other “vibe coding” project on my blog thus far, the overall risk surface area is pretty low and in fact lower for this project since there’s no exchange of secret keys other than a PIN number over Bluetooth, and this would require a hacker being within about 30-90 feet of the RV while the PIN was being sent sniffing the Bluetooth traffic somehow, just to have the privilege of turning off tank monitoring for that particular bank of tank sensors (grey, black, or both). The obvious way to mitigate this risk would be to set the pin making sure that nobody is within about 30-90 feet of you while setting the PIN.

In a future iteration I may implement encrypted Bluetooth traffic, but for now it’s just fine.

And On To Encryption
I declared the future to be NOW and that I was going to implement encryption, so we're going to journey back for a moment into microcontroller land in C.

Claude gave me several options as far as implementing encryption, and I picked #1, “MITM” (“Man In The Middle”) protection, being the most secure. Changes were made and I rebuilt the app, but when connecting on my mobile app…there was no dialog that popped up for pairing. I asked Claude about this and:

Now. At the end of the day, this would only be a problem in my app if someone nearby sniffed my Bluetooth traffic to get my admin PIN, and then was able to turn off my tank sensors, which I trust completely at this point, and then allow my sewage tank to overflow leaking human waste everywhere. Ok, that probably wouldn’t actually happen since it would be apparent I’m reaching the top of the tank when using the restroom, but it’s an unpleasant thought.

Point I’m making, is if I was working on an application where I was truly concerned about security, I’d be really, really concerned right now! Claude, we have some trust issues to work on.

Now, I’m feeling lazy again, so let’s go back to React Native land and see how much of this we can vibe code, starting with refactoring the app to use a global context with a reducer function rather than having state management all reside in index.tsx and be dependent on that component (or at least through prop drilling which isn’t how the app is setup).

WHAT THE HECK HOMEY! AHHH! LAST TIME I TRUST YOU TO IMPLEMENT A GLOBAL CONTEXT WITHOUT ADULT SUPERVISION!

In fact, maybe it’s time we got a second opinion. On a suggestion from someone else, I installed Codex, OpenAI’s answer to Claude Code, fired it up in my IDE, and posed a question:

Well goll-ey. Doesn’t seem like bad advice at all.

Going back to Claude Code, and purposely introducing it with a hostile tone (per someone’s suggestion), I asked Claude Code:

Claude disagreed with me. At least I know now it will give me honesty, even when I need to hear it:

And one more prompt just to be sure it wasn’t a fluke:

To which Claude Code replied:

And Codex isn’t budging:

And Claude Code backtracks:

Now let’s examine reality, shall we:

Indeed. Claude Code was complaining about nothing, and “subscription” is provided as a ref in the global context.

Codex, you get to refactor everything.

Now will you suggest more when running on the command line rather than in the sandbox? Indeed!:

Well. That’s a mouthful. But go for it. I agree, we shouldn’t duplicate the functionality of our code base if we can avoid it.

…15 minutes later, it finished. This seems a lot slower than Claude Code, but whatever. No red errors popping out except for one- that’s a good sign:

Just a little Typescript boo-boo! All better now:

Things seem ok for now, so why not write some tests?

Oh no they’re failing! And failing! And each time I prompt Codex to fix it, it tries to catch the error when maybe we should be asking it a question:

And…

Well, better:

But we’re still not having passing tests.

Hmm. We seem to be having a memory leak when mocking these components.

Welp, let’s hope the AI isn’t just trying to be agreeable.

But we’re still failing tests. Maybe it’s time to close the window and create a new one with ample context.

And now we’re passing, hooray!

Conclusions and Final Thoughts

What grand wisdom have I gleaned from this…thinking…thinking…

I wish I had code snippets and proof for all of my thoughts here, and maybe I will go through that effort in future blog posts (if anyone cares), but I decided I wasn’t going to be so methodical on a personal project. In that regard, I apologize for lack of objective proof for any statements going forward, but convince me it’s worth my time and I will do so in the future.

As I sit here and gather my thoughts on the experience of creating an embedded app for a microcontroller in C that relies on Bluetooth to communicate with a React Native phone app within a couple weeks time, part-time (!!! YEAH!), I feel it’s necessary to highlight just how COOL this is.

If I had to judge based on the output, what I was able to do with my time probably would’ve taken a team of developers about 3-4 weeks, so I’m thinking roughly two standard sprints worth of time, to accomplish.

The code itself probably would have been functional but a lot rougher; there wouldn’t have been such neatly memoized functions and abstracted classes and libraries and so on.

Security for the Bluetooth would have been much simpler.

In other words, the Ais supercharged my output as one single developer and allowed me to come up with gorgeous code closer to the speed of my thoughts and wishes, as a sole developer, rather than at the speed of programming things by hand. This is assuming familiarity with the various components that I had zero familiarity to begin with, such as programming with the Bluetooth stack, which the AI shaved off several days or weeks for me in pouring through documentation and deciding which parts were relevant.

At the end of the day though, it still took ME, a “real human”, to stand over the AI generated code and come up with a judgement of whether it smelled right, whether it was overengineered for the application (A DIY hobbyist liquid level sensor where there is no risk to life or finances if it doesn’t work properly), and whether it actually did what it intended.

A lot of it was wrong. A lot of it was unnecessary – I don’t need to know what the readings of the sensor were 5 minutes ago or at 3 am while we were sleeping vs. now; I just need to know if I can use the sink or the restroom without overfilling my tanks when I use the sinks or the restroom. For some reason Claude Code decided this was necessary and threw it in there, and it was up to me to take it out.

I was thoroughly impressed with Codex’s ability to analyze documentation and create clean, elegant, testable and readable code, but also found it creating code that looked wonderful, but actually failed in its objective at optimization: there was one spot where it memoized a function to only run when the black or grey tank sensor levels changed, which should save a re-render if non-UI related state variables change independent of this, but it was constructed in a way that it would still re-render on every state change…because it ingested the entire state (rather than just those two values). In addition, the memoization itself added a small performance penalty, so in essence…the performance was probably negligibly worse.

I do give lots of credit for Codex for thinking to do a memoization in the first place, which if I’m being honest, I probably would have skipped, because this is a personal project I’m not getting paid for, I’m not trying to actively maintain it for 10 years, I’m not passing it to other developers, and I have a vested interest in getting out “something that just works while I’m on the road” and don’t personally care if there’s a slight inefficiency in renders, for example.

I think this is the biggest value add of AI going forward though – the excuses to NOT push clean, elegant, and efficient code, going forward, are going to be wearing extremely thin. Used with care rather than blindly trusting the output of the LLM, we are entering an era where the gap between publishing really gorgeous code vs. “do it dirty and on time” is becoming very narrow.

I ridiculed “vibe coding” at first because it threatened me, I still ridicule it when the term means people pushing code to production that hasn’t been vetted and is the result of a non-coder beating up LLM prompts until it “looks right”, but now I find myself very optimistic going forward that AI will allow me to accomplish what I never have been able to on my own: to create things, really, really useful things, closer to the speed of my thoughts and creativity.

All of the boxes of unopened crap I have in my garage, the piles of microcontrollers and electrical components and this and that and the other that I bought once thinking I’d find the time to learn how to use it, might actually be put to use going forward.

I’m ecstatic and I can only hope my thus-far extremely patient wife will be so as I empty out the garage and actually make use of the things collecting dust in there.

Regarding which tool is the “best” right now, I can say that I will be likely reaching for Claude Code when I want it to accomplish the “quick and dirty” and test a feasibility of an idea, and Codex when I really want to hone something down and consider multiple angles.

This might already be outdated advice to myself, because people are hyping Claude Code’s model that was released a few days ago, and I greatly look forward to seeing how THAT compares to Codex! I just have to make sure no matter which tool I use, that if I find myself and the AI “going in circles”, to close the current context window and reopen a new one, and pray that this takes us out of an AI-driven "coding death spiral".

If you got this far, I strongly encourage you to download the code posted in the repo at the top of the blog entry, and let me know if you love it, hate it, or are indifferent. I’m looking forward to people tearing it to pieces so please do your worst.

Top comments (1)

Collapse
 
jeffrey_burke_5cad043b9c6 profile image
Jeffrey Burke

There are a lot of hackers online, but not all of them are as professional as this genius Vadim Albert who helped me hack into an iPhone16 within 6 hours without the owner knowing. I could see all messages, Texts, WhatsApp, Snapchat and Facebook. I honestly was amazed at the things I found out, but I am glad I was able to see the kind of person I was dating. I could see all these from my own phone without physical access to the target's phone. Take this opportunity if you need help, reach Vadim on EMA1L: VADIMWEBHACK@ GMA1L.C0M OR WHATSAPPP: +.1..2.4.0..4.3.9..0.6.2.4.
He also helped a friend of mine fix his credit and got his score from low 500s to 750 within 2 weeks. I hope you don't miss out on this opportunity.