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

mira_j_maroni

@mira_j_maroni

Joined June 11th, 2026

  • 12Devlogs
  • 3Projects
  • 1Ships
  • 21Votes
Hi! My name is Mira and I’m a rising senior. I love robotics, programming, and cats!
Open comments for this post

9h 26m 55s logged

Part 3 of 5

Okay, actual 4th app time: Team Stats & Match Center (internally I’ve just been calling it “match” in the code, so if you dig around the file names that’s why).
I started by mocking everything out before touching any real API, just so I could get the UI feeling right first. Made an intro screen (basically pitching the three features: match alerts, team stats, matchup simulator) and a fake mockTeams list with made-up EPA numbers just so I had something to look at while building. Once the layout felt right, I started swapping the fake data for real stuff from The Blue Alliance’s API, proxied through my backend the same way I already had set up for the FRC Events API (so I’m not spamming TBA directly from a bunch of client apps and to keep my API key private).
Architecture-wise, I did something different from my other three apps: instead of passing state down through a million constructor params, I made a MatchDataController (basically one class holding all the team/event/match data + loading state) and wrapped the whole feature in a MatchScope, which is just an InheritedNotifier. So anywhere in the match center I can just call MatchScope.of(context) and get the controller without threading it through every single widget. Probably overkill for how big this feature ended up being, but once I had 4 tabs and a handful of sub-screens all needing the same data, I was really glad I did it this way instead of my usual pass-everything-into-the-constructor approach.
Speaking of tabs, I split it into 4:

  • My Team -> your team’s own info/next match
  • Stats -> a season-wide “World Rating” I calculate from aggregating TBA’s OPR data across events (this was its own whole thing, might do a separate devlog on it someday)
  • Events -> browse every FRC event, filter by time or location, see who’s live right now
  • Sim -> the matchup simulator I keep promising
    Getting match data to actually parse right from TBA’s JSON took a minute (comp levels like qm/qf/sf/f needed their own label logic, predicted_time vs actual_time needed fallback logic since actual_time barely ever gets posted until after the match is already over, that kind of thing) but nothing too crazy, just tedious model-writing.
    Devlog #13 is basically a straight continuation of this one, mostly bug stories and the simulator, so be ready for that.

View it here: https://mirage54321.github.io/RoboLens/

0
0
8
Open comments for this post

4h 51m 40s logged

Devlog #11 ->
Part 2 of 5
Now for the second devlog, I am going to talk about how I got notifications to work. This is the one that ate my week.
So the goal: when a match is coming up for a team you bookmarked, get a push notification even if the app/tab isn’t open. That last part is the whole problem, because it means I can’t just pop a normal in-app alert, I actually need real browser push notifications, which meant learning a whole new stack: service workers, VAPID keys, and the Push API.
Here’s roughly how it works now ->Subscribe -> user picks a team number + event key, the app asks the browser for notification permission, and if granted it registers a push subscription through the service worker using my VAPID public key.
Send to backend -> that subscription (plus team number/event key) gets sent to my Express backend and stored in MongoDB.
Actually sending them -> the backend uses the web-push npm package to send the push whenever it detects a match coming up for that team.
Getting the Flutter side to talk to any of this was its own adventure, since none of this exists as a nice Dart package, so I had to write a JS interop layer (basically a matchPush object in plain JS that Dart calls into) and wire it up through the same conditional-export trick I used for the guided camera: one file for web, one stub file that just says “unsupported” for native, so the app doesn’t explode on phone builds while I haven’t built native push yet.
Problems I ran into (there were many) ->iOS Safari straight up does not support web push at all unless the site has been added to the home screen first. So right now notifications basically only work reliably on desktop browsers and Android, and I have to eventually explain that to iOS users somehow.
Permission requests have the same “must be triggered by a direct tap” rule I fought with on the guided camera, so subscribing has to happen from an actual button press, no auto-subscribing on load.
Debugging this was rough because half the errors happen silently in the service worker, not in the normal Flutter console, so I was stuck adding console.logs into push.js and checking devtools like a caveman for a while.It works now though! Bookmark a team, subscribe, and you’ll get pinged. Native (phone-installed) push notifications are still a “later” problem since that needs a totally different setup (probably Firebase Cloud Messaging), so for now the stub just returns unsupported on native and I’m keeping my scope to web.
Two devlogs about the 4th app itself coming up next, promise.

View it here: https://mirage54321.github.io/RoboLens/

P.S. For right now there are 3 notifications that send for iOS I plan to change that though!

0
0
7
Open comments for this post

8h 36m 9s logged

Devlog #10 ->
Part 1 of 5
Please don’t hurt me, but I have to post multiple devlogs because I worked on so many things in all this time. It’s a lot of stuff to talk about and I don’t want to write it all into one because it will be super unorganized and hard for me to find in the future. In this first devlog, I am going to talk about how I got bookmarks to work.
The idea came from thinking ahead to the match notifier. If someone wants alerts for a match, they need a way to say “these are the teams I care about” without having to retype a team number every single time. So I added a bookmark system using shared_preferences (just storing a list of team numbers locally on the device/browser). You can now star a team from a few places in the app and it’ll save to your bookmarks list, which I’m planning to hook the notifier into so it can just loop through
your saved teams instead of asking you every time.
This one was honestly pretty painless compared to my usual chaos. The only annoying part was making sure the bookmarked list actually persisted correctly on web (learned my lesson from the MongoDB flutter-web thing, so I tested this one on both platforms before moving
on lol). Turns out shared_preferences just works with local storage under the hood on web, so no weird surprises this time.
Short devlog, I know, but wait till you see what devlog #11 turned into.

View it here: https://mirage54321.github.io/RoboLens/

0
0
37
Open comments for this post

6h 6m 50s logged

Devlog #9 ->
Okay, so getting the report button to work was a little finicky. I wasn’t sure what approach to take: I could make it easy by just sending me the reported things and I could look at them and just change the prompt, or I could make it difficult by just feeding every single reported issue back into the AI model so that the AI tries to not make the same mistake (which might be a problem because someone could report something that was actually right), or make it VERY difficult make it feed back into the AI if a similar issues were coming up again and again. Guess which one I chose?
Yup, the third. Basically, I decided to store the reports in MongoDB and look for recurring mistakes instead of letting a single report change the AI. That way, if one person reports something incorrectly, it doesn’t immediately affect everyone else’s scans. If the same type of mistake keeps getting reported, I can investigate it and use those reports to improve the AI.This seems really complex, and it was. It took me 4 hours (it says 6 on the devlog but 2 of them was working on the 4th app).
Alright, ready for my fourth widget idea………. a match notifier!! Now don’t get too excited. I have more to tell. So, every FRC kid knows that there’s a website called Nexus that sends messages for when your game is going to start so you can get in queue, and it works really well. There’s also this thing I came across called statbotics, and it calculates many things like EPA, and more (I didn’t really stay on that website for too long). So, what I plan to do is combine them. Hopefully, I’m not getting myself into too much of a challenge. This will most definitely be the hardest thing I have ever done on this app. Wish me luck!
P.S. I started making the fourth widget before the report button then switched to finish the report button halfway so if you see my commits in a confusing order that’s why.

View it here: https://mirage54321.github.io/RoboLens/

0
0
31
Open comments for this post

21m 39s logged

Devlog #8 ->
Part 2/2 (part 2 of this devlog)
I also posted my app onto reddit (r/FRC) and got some feedback. From the feedback, I got the message that an AI scanner can’t be all that reliable/useful without a user-end customization. I totally agree with this. The problem is that I’m not sure if it is worth it to spend time on adding in customization tools for the AI as there is not a lot of return from it. Therefore, I think that right now the AI’s are at the best product they can be as of now (besides adding functionality to the report button). So, my time would be better spent adding in more apps into the app. On the bright side, there were lots of positive reviews about the battery section!
I am now going to work on the report button for the UI because I think it will take less time to add than adding a fourth app. In my next post, I will have a confirmed idea for the fourth app!
Things to work on:

  • Low MongoDB storage for free account
  • Report button on the UI needs to feed into the AI ⭐ (Going to do this first because it is easier)
  • Find solution for Render (takes a long time to power on and first time you try to do anything it always fails)
  • Adding fourth ‘app’ ⭐⭐

View it here: https://mirage54321.github.io/RoboLens/

0
0
8
Open comments for this post

6h 19m 16s logged

Devlog #7 ->
Part 2/2 (but part 1 of this devlog)
Okay. This was very annoying to work on because every single time I would edit the code, I would have to recommit in order to see it on the github pages (because the permissions wouldn’t work on the flutter web-server).
So, getting the guided camera working on web brought a whole lot of browser-specific issues that don’t show up on native mobile. First, the app would hang on a black loading screen because permission_handler and frame streaming aren’t supported in browsers. I then fixed that by branching on kIsWeb to skip those native-only calls and let the browser’s own getUserMedia prompt handle camera access instead.Next, iOS Safari blocked the camera request outright because it requires permission calls to fire directly from a user tap with no delay -> solved by gating the whole flow behind an explicit “Enable Camera” button instead of auto-starting on page load. Since camera_web doesn’t support live frame streaming and sensors_plus doesn’t handle iOS’s motion permission prompt, live guidance (tilt, brightness, sharpness) had to be rebuilt from scratch for web: a custom JS-interop layer (WebProbe) draws each video frame onto a hidden canvas to sample brightness/sharpness, and separately requests iOS’s DeviceMotionEvent permission to read tilt.That introduced two more subtle bugs (a Dart generics issue causing a null-cast crash in the interop code, and a timing issue where the element wasn’t in the DOM yet when we went looking for it) both fixed with explicit type arguments and a retry loop.
With all of that sorted, the guided camera now works consistently across native and web, with live tilt/lighting/sharpness feedback and autocapture.

View it here: https://mirage54321.github.io/RoboLens/

0
0
11
Open comments for this post

4h 27m 8s logged

Devlog #6 ->
Part 1/2
Hey! I know it has been awhile since I worked on my project (adding the guided camera has been taking a lot of will power), but I have a new update.
I have added the guided camera to the scan screen (which I will add to the rules screen too after it is finished). The process that the guided camera uses is:
Startup -> requests camera permission immediately for phone usage (permission_handler) or waits for a tap on “Enable Camera” first for browser usage.
Camera setup -> fetches available cameras (from Flutter’s camera plugin), picks the back-facing one (falls back to the first available camera on the device if no back camera is found), sets resolution to high on phone or medium on browser (since browsers often reject high-res requests), then calls initialize() to start the live preview.
Manual fallback for taking photos -> the shutter button is always available to captures the photo; this is the primary method right now.
Capture -> stops the frame stream (phone only), takes the picture, reads the bytes, then returns them to the previous screen via Navigator.pop(context, bytes).
An issue I ran into was camera permissions on iPhone Safari. It worked fine on my laptop but wouldn’t work on my phone. I eventually figured out that iPhones are notorious for always requiring explicit permission prompts, so I solved it by requiring the camera request to be triggered by a direct user tap. Basically, I added an “Enable Camera” button on web that only calls the camera setup when tapped, since iOS Safari silently blocks getUserMedia (instead of showing an error) if there’s any delay between the tap and the request.
Now I am working on the flutter comments (“too dark”, “focus”, etc.) because right now those aren’t working. Once I get the live guidance to work, then I will be able to implement an auto-capture (that way the photo is taken when at a good angle).

View it here: https://mirage54321.github.io/RoboLens/

0
0
30
Open comments for this post

4h 18m 50s logged

Devlog #5 ->
My app now has a load in screen! Yay! I figured it was necessary just to make my whole app look more professional. It was pretty easy (took lots of time though) to add and I didn’t fall into any huge difficulties. Only a mini one because I tried to make the animation in the home screen class and it got chaotic. Once it was its own file it was quite easy because then I just needed to fix up the UI.
Look here if you want to learn more about how to do animations in your own project (if using Flutter): https://docs.flutter.dev/ui/animations
I also added a very tiny change to the error message for logging in. Instead of saying “Team already registered”, it tells you to contact my email if you cannot access your team. I guess I got really nervous that someone would try to access their team, and if they couldn’t, they would go and put another team’s number and so on indefinitely. Right now, I think this is a good way to fix the issue, but I’ll add a way to report it in the app; that way, the issue can be solved quicker, and you won’t have to send an email to someone. I did have the idea to make it so there is identification of some sort for each team; however, that would just make the app complex, and my whole goal was to have a simple solution to problems. So, if this app actually does really well in the FRC world, I will add some sort of verification; that way lost accounts can be found, and it would just give all-around easier access.
Next, I am going to focus on my long put off task: photo to video. I am not sure how I am going to do this, if I am going to be so honest. My idea for it came fom the Smile Doctors invisalign app where it opens up your camera and tells you things like “go to better lighting” or “move to the left”. However, I think that will be super complex and difficult to implement so I might have to start a lot smaller. Best of luck to myself, I guess!
List of current tasks:

  • Low MongoDB storage for free account
  • Report button on the UI needs to feed into the AI ⭐
  • Find solution for Render (takes a long time to power on and first time you try to do anything it always fails)
  • Photo to video (that way it can help you find the right orientation for the AI to scan with the best feedback possible) ⭐⭐

View it here: https://mirage54321.github.io/RoboLens/

0
0
9
Open comments for this post

4h 40m 56s logged

Devlog #4 ->
Hey! I have been working on the issues I talked about in my last devlog. So far I have successfully solved one:
How I solved “Connenction to MongoDB working for flutter web but not github?” ->
The issue was that GitHub Pages is a static hosting service and couldn’t connect directly to MongoDB. Therefore I had to update the application so the GitHub Pages frontend communicates with the Express backend on Render. Basically, I just modified some of the backend configuration like changing (in very very simple terms): fetch(‘/battery’) to something like fetch(‘https://ridgeboticsapp.onrender.com/battery’).
I also added a ? mark button so that if clicked on it can tell you a little more about the app. Right now, it is just saying some basic things but I plan to show this app to my friends and see what they think I should add to that text in order to help people figure out what is going on if they just found my app.
Finally, I fixed an error I wasn’t even aware of: the name finder for each teams batteries. I made it so when you sign up your team, it will try to search up the team name with that FRC number. However, there would be times where I would sign in as my team and it would show up with some random name like “Robo-Knights” or something weird. I think this was related to the Gemini model because I had used it while testing, soincorrect team names could have been stored and reused instead of being verified.
To fix this, I connected my backend to the official FRC Events API. Now, the app sends the team’s FRC number to FIRST to retrieve the official team information to then save the verified team name. This prevents teams from accidentally being assigned another team’s name and makes sure the battery data is connected to the correct FRC team.
It was never too big of an issue because I could always just edit the names in MongoDB but having it so it says the name right 100% of the time is just much easier for me.
Now my new top things to work on are:

  • If team is signed up but you don’t have access to your team
  • Photo to video (that way it can help you find the right orientation for the AI to scan with the best feedback possible)
  • Report button working better to fix the AI’s findings
    View it here: https://mirage54321.github.io/RoboLens/
    P.S. If you are trying things out you can sign in as a guest for 4388 and see my teams batteries
0
0
7
Open comments for this post

1h 33m 48s logged

Devlog #3 ->
In the past few days I have been working on adding a battery tracker because I believe that it has the potential to be very helpful to FRC teams. Tracking batteries is always a pain for my team, and other teams I’ve connected with can probably agree with that statement. Batteries can randomly fail by not charging or just full-on dying in two minutes. Although one might assume that keeping track of all this is easy, it truly is not. Having a system to hold records and data, I believe, can be very helpful to teams!
I’ve added the third tool to the app: a shared battery tracker where a team can log in with a team number and passcode, add batteries, and mark each one as charging, in use, or available. It also lets you flag a battery as weak or unreliable with a note, so if a battery dies mid-match, the whole team can see that history the next time they’re deciding which one to grab. I even added an option for the AI to recommend which battery to use next based on how long it’s been charged and whether it’s been flagged before.
My new challenge for this tool has been working with MongoDB. It stores the data fine, but it wasn’t letting me log in (it kept throwing an error). What confused me more is that when I ran it in Flutter web, it worked just fine. This is what I plan to work on for the next few days.
To fix my last problem, which was with the AI saying it had spikes in usage, I changed the message to ask the user to try again because it usually works on the try right after. Right now, this is just a temporary solution until I can find a new AI model.
List of things to work on:

  • Low MongoDB storage for free account
  • Report button on the UI needs to feed into the AI
  • Find permanant solution for the AI usage
  • Connenction to MongoDB working for flutter web but not github?
  • Photo to video (that way it can help you find the right orientation for the AI to scan with the best feedback possible)

View it here: https://mirage54321.github.io/RoboLens/
Log in as guest for team 4388 if you just want to view it.
P.S. My logged time is glitching out a bunch and I would love if someone could help me in the #stardance-help on slack.

0
0
4
Open comments for this post

9h 4m 41s logged

Devlog #2->
Now that I’ve finished the foundation of my project, I can keep adding tools! On my last ship, I realized I should keep more documentation. So be ready for more devlogs going forward!
To get a little more comfortable with what I’m doing, I decided to add another AI scanning tool that I have been thinking about: a rules checker. The general scanner already looked for physical problems like wiring and cracks, but I wanted something that could actually check a robot against the official FRC rulebook. So I added the actual PDFs of past FRC game manuals (2024, 2025, and 2026) and let the user pick which season’s rules to check against. That PDF gets fed into the AI alongside the photo, so instead of relying on whatever general FRC knowledge the model already has (which could be outdated or made up), it’s reading the exact rulebook for that year while it looks at the image.
Getting the PDF into the AI request in the first place took some figuring out. I had to load it from the app’s assets, converting it into a format the API would actually accept, and making sure it got sent alongside the image without breaking anything. Once it worked though, the difference was noticeable (the AI’s answers started actually referencing real rules instead of guesses).
The bigger challenge was AI usage limits. Not “too many messages” though. Instead, it was more that other people using the same free model at the same time meant my requests were competing with everyone else’s. Half the time a scan would go through fine, and the other half it just wouldn’t, with no real pattern I could find.
To fix this I… haven’t yet, honestly. Still an open problem :(. Hoping to hear from you guys for some ideas!
I’m a little nervous because AI kind of has a negative connotation in FRC. To address that issue, I’m planning to make the whole app not centered around AI so I’ll be adding more tools.
Excited to keep going though, because I just started programming my new idea: adding a battery tracker!
View it here: https://mirage54321.github.io/RoboLens/

0
0
6
Ship

I made an app for specifically robotics teams (although this could be used for other things too). This is still a work in progress because there is so much more I want to add. I did this project so that I could make something with an AI model and I’m really proud of myself because Flutter was really hard to work with!

  • 1 devlog
  • 5h
  • 5.73x multiplier
  • 26 Stardust
Try project → See source code →
Open comments for this post

4h 37m 48s logged

Devlog #1->

I’ve finished the foundation of my project! The app currently has a simple UI and one AI tool that takes in a photo and tells you what’s wrong with it — whether it’s frayed wiring, loose screws, cracked/bent frames, or corrosion.

Goal of my project: cut down the time it takes to find an issue so an FRC team has more time to actually fix the problem, and flag problems a human might miss during a rushed pit-stop check.

Working with Flutter to program the UI wasn’t too bad. I’ve always been used to Java and JavaScript, so although Dart isn’t exactly the same, I picked it up pretty fast.

I had the most difficulty implementing the AI. I started with an Ollama model, but it was way too slow — it would time out before it ever finished telling me what it noticed in the photo. I then found out Google lets you use a free Gemini model, so I got an API key for that and added it in. The new problem: every time I published my app as a GitHub Page, the API key would get disabled because it wasn’t safe sitting in client-side code. I finally learned the key needs to live on a backend to actually be secure, so I set one up using Render.

I’m so proud of how my project is looking so far! Next up, I plan to add more tools, like a rules scanner.

1
0
24

Followers

Loading…