ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
An anonymous question board for Cal Poly Pomona CS tutoring. Students scan a QR code, ask a question, and
answer each other — no account, no login. A second QR code at the tutoring desk handles walk-in check-ins,
so the department gets traffic numbers without a paper sign-in sheet.
It runs on free tiers and is meant to be something a tutor can forget about for a month. Keeping it free,
small, and anonymous is the point. Read Scope and Student privacy
before adding anything.檢視完整 README ↗An anonymous question board for Cal Poly Pomona CS tutoring. Students scan a QR code, ask a question, and answer each other — no account, no login. A second QR code at the tutoring desk handles walk-in check-ins, so the department gets traffic numbers without a paper sign-in sheet.
It runs on free tiers and is meant to be something a tutor can forget about for a month. Keeping it free, small, and anonymous is the point. Read Scope and Student privacy before adding anything.
Contents: Scope · Student privacy (FERPA) · How it works · Running it locally · Contributing · Deploying · Handing it off · Maintainers
/admin for tutors and admins, and badges for teachers or others a tutor vouches
for.| Not doing | Why |
|---|---|
| Student accounts, campus sign-in (SSO), emails | Ties posts to identifiable students — see Student privacy. Also the whole flow is "scan and type". |
| Names or student IDs on check-ins | Same. Hashing names doesn't help: with a class roster they match back in seconds. |
| Replies / comment threads | A third kind of post to report, hide, edit, delete, and badge — and then people expect notifications. Answers + upvotes + "solution" cover it. |
| Notifications (email, push, SMS) | They cost money and need contact details. |
| Private messages | Unmoderatable, and a harassment channel. |
| Video uploads, rich-text editing | Storage and moderation cost; plain text plus code blocks is enough for CS questions. |
| Analytics or visitor tracking | Beyond the check-in counts, nobody has needed it, and it collects data we'd rather not have. |
| Background jobs, cron, queues | Scheduled work (password rotation, the daily summary) runs after a page view instead. |
| Anything paid | The site must never send anyone a bill. |
If tutors have asked for something twice and it fits the above, open an issue. If it doesn't fit, the answer is probably no, and that's fine.
The one hard rule: this site must never store anything that identifies a student.
That means no names, no student (Bronco) IDs, no emails or phone numbers, no campus sign-in, no class rosters, no grades, and nothing that links a check-in or a post to a specific person — not even hashed or "just for staff".
Why. FERPA (the Family Educational Rights and Privacy Act) is the U.S. law that protects students' education records: records directly related to a student and kept by the university or by someone acting for it. "Which student came to tutoring, when, and for what class" is the kind of record that can fall under it. Such records belong in systems the university has approved, with the access controls and agreements that come with them. This site runs on personal free-tier accounts (Vercel, Neon, Discord) with no data agreement with the university, so the only safe design is not to hold that information at all. Whether some particular data would count as an education record is a question for the university, not for us — this project avoids ever having to ask it.
If you think a feature needs student-identifying data, stop. Ask the tutoring coordinator and Cal Poly Pomona's IT / information security office first. The answer will almost always be to use a system the university already runs.
| Data | What's kept | Identifies a student? |
|---|---|---|
| Posts | The text, an optional nickname, an optional photo, a random browser id, a salted hash of the IP | Not by design. Students can still type personal info — the footer asks them not to, and tutors delete it (see below). |
Browser id (qa_visitor cookie) | A random id, so people can edit their own posts and vote once | No — random, not linked to anything. |
| IP addresses | Only as a salted hash, for spam limits and "delete all from this network". Raw IPs are never stored. | No. |
| Votes, reports, recovery codes | Browser id, IP hash, report reason; recovery codes as a hash only | No. |
Rate-limit log (activity) | Action type, IP hash, browser id — deleted after 2 days | No. |
| Check-ins | Year, class, reason, first visit, time, and a short "Other" text. No browser id, no IP. | No. The "Other" box is 60 characters and staff-only, but it's free text — don't add more of those. |
| Bug tickets | The note, the page path, and a browser summary like "Chrome 128 · Windows" | No. No contact info is asked for. |
| Badge holders | A badge title, a color, an admin's note, a hash of the code | Badges are for staff and teachers. Don't create one named after a student, or put a student's name in the note. |
| Discord | Copies of post and ticket text in alerts; check-in totals only | Same as posts. |
Vercel keeps its own request logs, which include IP addresses; the app doesn't add anything to them.
Before you open a pull request, answer these in its description:
If the answer to 2 is "yes" or "maybe", the change doesn't go in.
A tutor deletes the post from /admin or the post itself. Deleting removes the post, its answers, votes,
reports, and photos for good. Hiding only takes it off the page; the text stays in the database. Also
delete the matching Discord alert if one was sent.
``` fences render as code blocks, `x` as inline code.
Everything else is plain text.XXXX-XXXX-XXXX); entering it at /recover
unlocks that post on another browser or device. Only a hash of the code is stored.uploads/<uuid>.webp), so photos can move to
another store without touching posts./admin and can close, reopen, or delete them; closed
ones are deleted after 180 days./qr: a printable poster with a QR code pointing at the site./login (linked quietly from the footer), with one box for everything: the admin
password, the tutor password, or a badge code. The header then shows ADMIN, TUTOR, or the person's
badge, and staff get a Moderation button with the number of posts waiting for them. Students never need
to sign in, so there's deliberately no sign-in button in the header.ADMIN_PASSWORD makes you an admin; signing in with the
rotating password makes you a tutor. Tutors do the day-to-day moderating. Admin-only: editing the
defaults, managing badge holders, rotating the password by hand, and deleting everything from one network
(which on campus Wi-Fi can catch bystanders). Posts written while signed in as staff are labeled
ADMIN or TUTOR./admin: an emergency read-only switch, temporary setting changes that expire on
their own, the word filter, the check-in numbers, bug tickets, and a review queue with approve / hide /
delete and bulk delete.ADMIN_PASSWORD stays valid as a break-glass key. Rotation is skipped
entirely when no Discord webhook is set, so nobody gets locked out./admin does — do that when a tutor leaves. Changing ADMIN_PASSWORD in Vercel signs admins out./login. Posts made while signed in with that code carry the badge, and the
code also lets that person edit those posts from another device. Per badge holder, an admin can allow
higher posting limits or skip the review queue. Handing out a new code keeps the badge and the posts but
signs out whoever had the old one — that's the fix if a code leaks. Badges are a label, not a power: they
can't moderate anything./checkin (poster at /qr/checkin): year, class, what they need help with, and whether it's their
first time. "Other" opens a short text box. No checkout./admin: counts for today / 7 / 30 days, breakdowns by class, reason, hour, weekday,
and year, a CSV download for any date range (opens in Excel), and a way to remove a joke entry.New posts run through a profanity filter, a link cap, and a duplicate check. Campus Wi-Fi puts many people behind one IP address, so per-IP limits are loose on purpose, and per-browser limits do the real work.
| What | Limit |
|---|---|
| Posting | 6 per browser / 10 min, 40 per IP / 10 min (deleted posts still count) |
| Photos | 100 per IP / hour, 150 MB site-wide per day (protects the free 1 GB store) |
| Votes | 50 per IP per post / hour |
| Edits | 20 per browser / 10 min |
| Wrong recovery codes | 20 per IP / 15 min |
| Failed sign-ins | 5 per IP / 15 min; past 30 site-wide / 15 min, ADMIN_PASSWORD is paused (the tutor password and badge codes still work) |
| Reports | 30 per IP / 10 min |
| Bug tickets | 3 per browser / 10 min, 30 per IP / hour |
| Check-ins | 3 per browser / 3 hours (more say "already checked in"), 300 per IP / hour |
f.u.c.k and sh1t. Tutors can add words (e.g. chegg), un-block built-in words,
and test a sentence right on the admin page.Every setting has a default and an optional temporary change that reverts on its own (1, 2, 6, 12, 24 hours, or until cleared). Tutors can only change the first six settings below, for at most 24 hours; the rest, and "until cleared", are admin-only. "Clear all temporary changes" reverts everything (for a tutor, everything they could have set); STOP EVERYTHING makes the site read-only until a tutor turns it back on.
| Setting | Default |
|---|---|
| Read-only mode | Off |
| Hold new posts for approval | Off |
| Pause photo uploads | Off |
| Profanity filter (off / censor / hold for review / reject) | Censor |
| Reports before auto-hide (different networks) | 3 |
| Discord alert for every new post | On |
| New tutor password every … days | 7 (0 turns rotation off) |
| Daily check-in summary on Discord | On |
Badge holders with "higher limits" get five times the per-browser posting allowance and skip the per-network photo cap; the site-wide daily photo budget still applies to everyone.
| Role | Service |
|---|---|
| Code | GitHub |
| Hosting | Vercel Hobby (Next.js 16, React 19) |
| Database | Neon Postgres (added from Vercel's Storage tab), via postgres.js |
| Photos | Vercel Blob |
| Alerts | Discord webhooks |
Free-tier notes: Vercel's Hobby plan is for non-commercial projects, which this is. A free Neon database sleeps when nobody is using it and wakes on the next request (your data stays). Watch the Usage tab in the Vercel dashboard.
You need Node.js 20.9 or newer, Git, and a Postgres database of your own for development.
Never develop against the production database, and never use real student data as test data. Make up your test posts. If you're a maintainer with Vercel access,
vercel env pullmay hand you the productionDATABASE_URL— replace it with your own before running anything.
Get the code. Fork the repository on GitHub, then:
git clone https://github.com/<you>/bronco-overflow.git
cd bronco-overflow
npm install
Get a database. Any recent Postgres works. Pick one:
docker run --name bronco-db -e POSTGRES_PASSWORD=dev -p 5432:5432 -d postgres:17
Your connection string is then postgres://postgres:dev@localhost:5432/postgres.Configure. Copy the example file and fill in two values:
cp .env.example .env.local
DATABASE_URL — your development database from step 2.ADMIN_PASSWORD — any long value; you'll use it to sign in as admin.Leave the rest out. Without BLOB_READ_WRITE_TOKEN, photos are saved to public/uploads/. Without a
Discord webhook, alerts are skipped. If you want to test alerts, use a webhook on your own Discord
server — never the real tutors' channel.
Run it.
npm run dev
Open http://localhost:3000. Tables are created on the first request, so there's
no migration step. Sign in at /login with your ADMIN_PASSWORD to see /admin.
id that posts already useid is what's storednode_modules/next/dist/docs/ — see AGENTS.md.CREATE TABLE IF NOT EXISTS skips a database that already exists, so a new column
also needs an ALTER TABLE ... ADD COLUMN IF NOT EXISTS line at the bottom of
src/lib/db.ts. Without it the live site breaks and your local one looks fine.deleteQuestion
for the full list.server-only, next/headers, or the database can't be pulled
into a "use client" file — the build fails with a confusing Pages Router error. Shared constants and
types live in their own module (src/lib/member-types.ts,
src/lib/settings.ts) for exactly this reason."use server" files can only export async functions. Constants go somewhere else
(src/lib/report-reasons.ts exists because of this).secrets table): the session key, the IP-hash salt, and the tutor
password. Swapping in a different database signs everyone out, resets IP-based rate limits, and
invalidates badge codes. Back it up before migrating.IMAGE_BASE_URL or
else the Blob store behind BLOB_READ_WRITE_TOKEN. To move photos, copy the files with the same paths and
point one of those at the new place. If it's not Vercel Blob, IMAGE_BASE_URL also opens the
Content-Security-Policy for that host.The goal is a board that a tutor can forget about for a month and that never sends anyone a bill. Small, boring changes are the good ones.
main in your fork (fix-empty-state, add-cs1200-tag, …). One change per pull request.npm run lint
npx tsc --noEmit
npm run build
main. In the description, say:
Every push to main deploys to production, so the maintainer merges only after trying the change.
Good first issues: fix the class list in tags.ts, improve the empty states, make the admin page nicer
on a phone.
The repository is public, so don't open a public issue for a security problem or for anything exposing personal information. Contact the maintainer privately instead.
git remote add origin https://github.com/<you>/bronco-overflow.git
git push -u origin main
DATABASE_URL. Prefer Neon's pooled connection string (the host contains
-pooler); if only POSTGRES_URL exists, copy its value into DATABASE_URL.BLOB_READ_WRITE_TOKEN. If photo uploads fail, try one while signed in as staff: the error then
says why (for example a missing token or a private store).ADMIN_PASSWORD under Settings → Environment Variables: at least 16 random characters
(/admin warns if it's shorter). This is the break-glass key; day to day, tutors use the rotating
password from Discord.DISCORD_WEBHOOK_URL. Use a channel only tutors can read — the tutor
password is posted there. To split alerts across channels, see Discord channels.main deploys automatically.https://<project>.vercel.app/qr and print the poster. The poster and Discord links use the
production domain Vercel reports (your custom domain, once you add one). Anywhere other than Vercel, set
NEXT_PUBLIC_SITE_URL.| Variable | Required | What it is |
|---|---|---|
DATABASE_URL | Yes | Postgres connection string |
ADMIN_PASSWORD | Yes | The admin (break-glass) password |
BLOB_READ_WRITE_TOKEN | In production | Vercel Blob store for photos |
DISCORD_WEBHOOK_URL | Recommended | Alerts, and the rotating tutor password (no webhook, no rotation) |
DISCORD_WEBHOOK_* | No | Per-kind alert channels — see below |
NEXT_PUBLIC_SITE_URL | Off Vercel | The site's address, for posters and links |
IMAGE_BASE_URL | No | Where photos are served from, if not the Vercel Blob store |
DB_POOL_MAX | No | Database connections per server instance (default 5) |
Changing a variable needs a redeploy.
One webhook is enough: everything goes to DISCORD_WEBHOOK_URL. To send a kind of alert to its own
channel, create a webhook there and set its variable. Anything unset (or blank) falls back to the main one.
/admin shows where each kind currently goes.
| Variable | What goes there |
|---|---|
DISCORD_WEBHOOK_POSTS | New questions and answers |
DISCORD_WEBHOOK_MODERATION | Reports, auto-hides, posts waiting for review, "board paused" |
DISCORD_WEBHOOK_PASSWORD | The new tutor password — must be a tutors-only channel |
DISCORD_WEBHOOK_CHECKINS | The daily check-in summary |
DISCORD_WEBHOOK_TICKETS | Bug reports and tickets |
The list lives in src/lib/channels.ts.
When a new admin takes over. Do it in this order; nothing here needs downtime.
.vercel.app address, so printed posters keep working. You have to own the project's team
and be a member of the target one; if their account isn't offered (a Hobby account is one person), see
If a transfer isn't possible.secrets table holds the session
key, the IP-hash salt, and the tutor password, so a fresh one signs everyone out and breaks every
badge code.uploads/…), and connect it to the
project (that sets BLOB_READ_WRITE_TOKEN). Posts store only keys, so nothing in the database changes./qr to check.ADMIN_PASSWORD: they set a new, long one in Vercel and redeploy. That signs out every admin
session — including yours./admin.DISCORD_WEBHOOK_* variables, and redeploy; then
delete the old webhooks.The new admin imports the GitHub repository into their own Vercel account and adds their own Neon database and Blob store, then copies the data over:
pg_dump --no-owner --no-acl "$OLD_DATABASE_URL" | psql "$NEW_DATABASE_URL"
Use Neon's direct connection strings here (the host without -pooler). Copying the whole database keeps
the secrets table, so badge codes keep working. Then copy the photos with the same paths, set the
environment variables, and deploy. The .vercel.app address changes, so the posters need reprinting — a
custom domain avoids that, since it can be pointed at whoever runs the site.
| Role | Who |
|---|---|
| Current admin | @suisarbre |
Update this table when the site changes hands.
NEXT_PUBLIC_SITE_URLcampusTime() from
src/lib/format.ts so people see Pomona time.member-types.ts and the class map in
src/components/MemberBadge.tsx. An unknown color falls back to green.ILIKE '%…%' and "Hot" sorts in SQL over the whole table. Fine for thousands of
questions, worth revisiting around tens of thousands.NEXT_PUBLIC_* variable — those are compiled into the browser bundle.