ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A college and career planner for Davidson College students.
MakeItSo started at hack@DAVIDSON 2026. The goal is one place for what is scattered today across Handshake, WildcatSync, Davidson One, RateMyProfessors and email: the course catalog, a four-year plan, careers, verified alumni and campus events.
Status: being rebuilt. The hackathon version is being replaced on a tested foundation (Next.js 16, React 19, Mongoose 9, Anthropic Claude for AI features). Right now the app has the landing page, sign-up/sign-in and the signed-in shell with every page in place (Today, Courses, My plan, Careers, Events, Alumni, Profile). Those pages say what they will show; the course catalog, plan, careers, alumni and events data arrive in the next waves. Degree requirement information in the app is a planning aid: always verify in Degree Works and with your advisor.
| Layer | Technology |
|---|---|
| App | Next.js 16 (App Router, Turbopack) · React 19 · TypeScript 5.9 (strict) |
| UI | Tailwind CSS 4 (design tokens) · Radix UI primitives · lucide-react · sonner |
| Auth | NextAuth.js v4 (email + password credentials, JWT sessions) |
| Data | MongoDB + Mongoose 9 |
| AI | Anthropic Claude via @anthropic-ai/sdk (being wired up; AI_PROVIDER=mock for tests) |
| External data | Davidson College public course API, RateMyProfessors |
| Tests | Vitest + Testing Library · mongodb-memory-server · Playwright |
| Hosting | Vercel (Root Directory = frontend) |
MakeItSo/
├── .github/workflows/ci.yml CI: npm ci → typecheck → lint → format → test → build → CSS check → e2e
├── .nvmrc Node 24
└── frontend/ the Next.js app
├── proxy.ts request proxy: passes the requested path to layouts, so a signed-out deep link
│ goes to /login?callbackUrl=<path> and back
├── app/
│ ├── page.tsx landing page
│ ├── (auth)/ /login, /register, /verify (mailbox code), /forgot-password
│ ├── (marketing)/ /privacy
│ ├── (hub)/ signed-in pages in the AppShell (layout requires a session): /today,
│ │ /courses, /courses/[term]/[code], /plan, /careers, /careers/[slug], /events,
│ │ /alumni, /profile
│ ├── globals.css design tokens (the only place raw colours live)
│ └── api/ route handlers, all built with defineRoute (auth, register, profile, search)
├── server/ server-only modules
│ ├── env.ts zod-validated environment, read lazily, with production checks
│ ├── db.ts lazy MongoDB connection (getDb), sanitizeFilter on
│ ├── http/ defineRoute (auth, CSRF, limits, validation, caching), typed errors,
│ │ fetchExternal (the only way to call outside services) + fixtures mode
│ ├── auth/ NextAuth options, sessions (requireUser() / requireApiUser()), Davidson-only
│ │ sign-up with mailbox verification, passwords, profile, mail (console / Resend),
│ │ password reset, rate limits
│ ├── clock.ts now(): the server's "now" (pinned by FIXTURES_NOW in fixtures mode)
│ ├── sync.ts recordSync / getSourceStatuses: the shell's out-of-date notice
│ ├── account/ per-user data registry for export and account deletion (incl. the student's
│ │ own rows in the hackathon-era collections)
│ ├── catalog/ programs/ rmp/ feeds/ content/ the live course catalog, Acalog majors and minors,
│ │ ratings, campus feeds and curated content; plan/ is a typed stub until it lands
│ └── search/ global search over pluggable providers
├── components/ui/ UI primitives (Button, Card, SourceNote, CourseCode, Dialog, ...)
├── components/app/ AppShell: TopBar, Sidebar, source notice, BottomTabs, theme, user menu
├── lib/ shared (client + server): term, sources, routes, flags, format, event-location
│ ├── types/ zod schemas + types for catalog, plan, feeds, content, ratings, AI
│ └── api/ one contract per route (method, path, auth, schemas) + a typed client
├── models/ Mongoose models (new collections; models/legacy/ is read-only)
├── docs/CONTRACTS.md the frozen contracts in one page
├── types/ type augmentation (next-auth)
└── tests/ Vitest unit/integration tests; tests/e2e = Playwright;
tests/fixtures/external = recorded upstream responses
Shared interfaces are frozen and documented in frontend/docs/CONTRACTS.md:
term rules (lib/term.ts), sources (lib/sources.ts), domain types (lib/types/*), route contracts
(lib/api/*, served with defineRoute and called with callApi), fetchExternal, recordSync, the account
data registry, feature flags and the service signatures. Outside services are reached only through
fetchExternal (ESLint forbids raw fetch in server/, lib/ and app/api/); with EXTERNAL_MODE=fixtures
(tests, e2e, CI) it serves frontend/tests/fixtures/external and never touches the network. Server code reads
"now" from server/clock.ts, which tests and e2e pin to the fixtures' day with FIXTURES_NOW.
The look is the classic MakeItSo type with Davidson's official colours (the owner's decision of 2026-10-25, which replaced the earlier "Lakeside" palette): a white ground, Davidson Black (#0F1012) text, cool grey structure and Davidson Red (#D42121, PMS 3546 C) as the one accent, from the College's Marketing Toolbox. Red marks exactly four things: the one primary action on a screen, the current page (a red bar beside the nav item, not only a tint), selection, and "now". Text links are near-black and underlined, never red. Since red is the accent, urgency and danger never rely on colour: the now pill has a clock icon beside the time, an item due today an alarm icon and its words, errors an icon, and destructive buttons are outlined (crimson, never a red fill) with a trash icon and words, and ask first. Planned work and the next term are never red, and courses in progress keep their course colour; only this term's own marks (its "now" label, its credits on the progress bar, a requirement it fills) are red, as "now". Focus rings are Lake Blue, never red.
frontend/app/globals.css as CSS variables for light and dark. With no data-theme on
<html> the theme follows the system; the toggle stores an explicit choice in localStorage (mis-theme),
and a small inline script applies it before first paint. Tailwind 4 is configured in that CSS file (@theme);
there is no tailwind.config.ts.bg-surface, text-fg-2, border-line, bg-primary-fill, text-urgent,
bg-course-pine-wash... Tailwind's default palette, type scale, radii and shadows are switched off.
tests/design/no-raw-colors.test.ts fails on raw hex/rgb colours, palette classes (bg-red-500) or dynamically
built class names in app/, components/ and lib/. tests/design/tokens.test.ts checks WCAG AA contrast for
every token pair in both themes.link utility (Davidson Black, a grey underline that darkens on
hover); the current sidebar item and phone tab carry a bar in --nav-mark; field errors use <FieldError>
and inline messages <Notice> (icon + words); a list with an add button on every row uses
emphasis="secondary", and a page's other buttons are variant="secondary", so a screen keeps one red button
(the UI budgets in tests/e2e/ux fail on a second).font-display) for the wordmark, page titles and card
and section titles only; IBM Plex Mono (font-mono) only for course codes, CRNs and the verification code.
All three are self-hosted from the @fontsource packages through next/font/local, so the build needs no
network and the browser never calls Google. Sizes text-xs (12px, the floor) to text-2xl
(32px), plus text-3xl (44px) for big numbers.md = 720px (bottom tabs below, sidebar above), lg = 900px (two content columns), xl =
1180px (full 240px sidebar; a 96px rail of icons with their labels between 720 and 1180px).Requirements: Node.js 24 (see .nvmrc; nvm use picks it up; Node 22.12+ also works), npm, and a MongoDB you can reach (a local
mongod, Docker mongo:8, or a free MongoDB Atlas cluster).
git clone https://github.com/Hades-c/MakeItSo
cd MakeItSo/frontend
npm ci
cp .env.example .env.local # then fill in the values below
npm run dev
Open http://localhost:3000 and create an account.
Set these in frontend/.env.local locally, or in the Vercel project settings. None of them are needed to
build; they are validated on first use at runtime (server/env.ts), with an error that names what is missing.
| Variable | Required | Description |
|---|---|---|
MONGODB_URI | yes | MongoDB connection string, e.g. mongodb://127.0.0.1:27017/makeitso or mongodb+srv://… |
NEXTAUTH_SECRET | yes | Session signing secret: openssl rand -base64 32. In production ≥ 32 characters, not the placeholder |
NEXTAUTH_URL | yes* | Public URL of the app, e.g. http://localhost:3000. *Optional on Vercel |
APP_ORIGIN | production | Public origin checked against the Origin of every POST/PUT/PATCH/DELETE (not needed on Vercel previews) |
ANTHROPIC_API_KEY | for AI | Anthropic API key, used by AI features when AI_PROVIDER=anthropic |
AI_PROVIDER | no | anthropic (default) or mock (tests and e2e, answers as model mock; rejected on Vercel production) |
AI_ENABLED | no | All AI features on/off. Default true (AI also needs a verified @davidson.edu account and consent) |
AI_DAILY_TOKEN_BUDGET | no | Tokens per day across all users before AI pauses. Default 2000000 |
AI_ALLOW_UNVERIFIED | no | Temporary (remove once mail works): true lets unverified @davidson.edu accounts use AI. Default false |
MAIL_PROVIDER | no | none, console (codes in the server log; dev/test default) or resend. Default none in production |
MAIL_API_KEY | for resend | Mail provider API key |
MAIL_FROM | for resend | Sender, e.g. MakeItSo <[email protected]> |
CRON_SECRET | for cron | Bearer secret Vercel Cron sends (≥ 16 characters). Cron routes answer 503 without it |
Moodle calendar import. On /syllabi a student can paste the calendar URL Moodle gives them (Calendar → Import or
export calendars → Export calendar → All courses, Recent and next 60 days → Get calendar URL); its due dates come in
as plan deadlines marked Moodle (frontend/server/moodle). That URL carries the student's Moodle token, so it is
stored encrypted (AES-256-GCM with a key derived by HKDF from MOODLE_FEED_KEY, or from NEXTAUTH_SECRET when it is
unset), never logged and never sent back to the browser. Set MOODLE_FEED_KEY (openssl rand -base64 32) to be able
to rotate NEXTAUTH_SECRET without touching the Moodle links. Changing whichever key is in use makes every saved link
unreadable: those students are asked to paste their calendar URL again, and their imported deadlines stay until then.
Syncs happen on connect, on "Sync now" and when the student opens Today or /syllabi six hours after the last one (no
cron). MOODLE_IMPORT_ENABLED=false turns it off without deleting anything.
AI_ALLOW_UNVERIFIED exists only while email verification cannot work in production (no mail provider). With it
on, an @davidson.edu account that has not verified its address may use AI after consenting (18+ attestation
included); cold-email drafts and the alumni network stay verified-only, legacy non-Davidson accounts stay locked,
quotas and the daily token budget are unchanged, and "Report this" from an unverified account never hides a shared
answer (frontend/server/ai/access.ts, frontend/docs/CONTRACTS.md). When the switch is removed, that report rule
stays: reports kept from unverified accounts must go on not counting (CONTRACTS.md, "Removing the switch").
Cost risk while it is on: the per-student quotas (20 generations a day) then limit addresses nobody has proven
they own. Sign-up sends no mail, accepts any @davidson.edu address and allows 5 accounts an hour per IP, so each
throwaway account gets its own quota, and AI_DAILY_TOKEN_BUDGET (input, output and cache tokens of everyone) is
the only real cost limit. A handful of such accounts can use up the whole budget (one plan-suggestions or
career-plan model call may use up to 12,000 output tokens), which costs the full budget that day and shows "AI is
paused for today" to every student until midnight in Davidson. Set the budget to what you are willing to spend in a
day, and keep the Anthropic workspace's spend limit, while the switch is on.
Production (next start and every Vercel deployment) also checks that the Vercel production environment does not
use AI_PROVIDER=mock, EXTERNAL_MODE=fixtures, the console mailer or FIXTURES_NOW, and RATE_LIMITS=off is
refused anywhere without EXTERNAL_MODE=fixtures. A bad value fails the request that needs it with a 500 and a
server log naming the variable; it never fails the build.
frontend/)| Command | What it does |
|---|---|
npm run dev | Development server on port 3000 |
npm run build | Production build (needs no environment variables) |
npm run check:css | After a build: no font size below 12px, and CSS under 80 KB gzipped |
npm start | Serve the production build |
npm run typecheck | Generate Next.js route types, then tsc --noEmit |
npm run lint | ESLint 9 (flat config: eslint.config.mjs) |
npm run format | Prettier (with the Tailwind class sorter); npm run format:check to verify only |
npm test | Vitest unit + integration tests (tests/**/*.test.ts[x]) |
npm run test:e2e | Playwright end-to-end tests against next build && next start -p 3210 |
npm run test:ux | Only the UI budgets (tests/e2e/ux): target-versus-actual tables, axe, overflow |
npm test): server modules are tested directly; database tests use
mongodb-memory-server via tests/helpers/db.ts, so no
MongoDB setup is needed. The first run downloads a mongod binary (about 100 MB) into the local cache.
Tests run with EXTERNAL_MODE=fixtures and FIXTURES_NOW=2026-09-30T12:00:00-04:00, and any real network
call from a test fails.npm run test:e2e): builds the app, then tests/e2e/serve.mjs starts an in-memory MongoDB and
next start on port 3210 with AI_PROVIDER=mock, EXTERNAL_MODE=fixtures, FIXTURES_NOW, RATE_LIMITS=off
and a throwaway secret; the server process blocks any outbound fetch to a non-local host. Install the browser once with
npx playwright install chromium. Set E2E_SKIP_BUILD=1 to reuse an existing build, E2E_MONGODB_URI to use
your own database, or PW_CHROMIUM_EXECUTABLE to use a Chromium you already have.tests/e2e/ux, part of test:e2e; npm run test:ux alone): a seeded junior's pages measured at
390, 768, 1024 and 1440 px against the UI-simplification targets (height, words, type, red, rail labels, tap
targets, focus behind fixed bars, h2 order, provenance in words). It prints a table per page (and writes it to
the CI job summary). Misses are warnings; axe serious/critical findings, sideways scrolling at 390, the budgets
of landed items and an h2 order that differs from screen order fail. The pages whose h2 order is out today are
listed in tests/e2e/ux/h2-order-baseline.ts (H2_ORDER_WARN_ONLY, each with the item that fixes it) and only
warn; once one reads in order it passes with a note to remove its entry, which makes it fail on the next
mismatch.CI (.github/workflows/ci.yml) runs all of the above on every pull request and on pushes to main, plus
npm audit --omit=dev --audit-level=high. It needs no secrets.
Known dev-only advisory. A full npm audit (dev dependencies included) reports
GHSA-vfj7-8cjw-p6xm (high: stack exhaustion on deeply nested
brace patterns) in braces, through eslint-config-next → @next/eslint-plugin-next → fast-glob → micromatch.
It waits for an upstream fix: the advisory covers every release up to 3.0.3, the latest braces, so an npm
overrides entry has nothing safe to pin; npm audit fix --force would downgrade eslint-config-next to 14,
which does not support Next.js 16; and swapping fast-glob for another globber under the ESLint plugin is not an
API-compatible override. The package only runs at lint time on this repository's own glob patterns, never ships
to users and never sees request input. Revisit when braces publishes a fix (an override to it is then enough)
or @next/eslint-plugin-next drops fast-glob.
frontend. Vercel picks Node.js 24, the newest major allowed by
engines.node (>=22.12 <25) in package.json.MONGODB_URI, NEXTAUTH_SECRET, APP_ORIGIN, CRON_SECRET and
ANTHROPIC_API_KEY (and NEXTAUTH_URL if you use a custom domain).Scheduled jobs live in frontend/vercel.json (the Root Directory's config; the repository-root vercel.json is not
read when the Root Directory is frontend). Vercel Cron calls each route with Authorization: Bearer $CRON_SECRET,
so CRON_SECRET must be set in Production. The schedules are Hobby-plan safe (at most daily); reads also refresh
stale data in the background, so the jobs only keep data warm. On the Pro plan you can raise the feeds job to every
30 minutes.
| Route | Schedule (UTC) | Job |
|---|---|---|
/api/cron/feeds | daily 10:23 | campus feeds (WildcatSync, Hurt Hub…) |
/api/cron/rmp | daily 07:17 | RateMyProfessors: the roster (weekly: skipped while under 6 days old), then tag counts (up to 4 minutes) |
/api/cron/catalog | daily 07:13 | course schedule: the terms list, past terms and the first-run backfill from Fall 2022 (up to 5 minutes) |
/api/cron/programs | Mondays 06:23 | majors and minors from the Acalog catalog |
The current and registration terms also refresh in the background on reads (every 15 minutes), so the catalog job matters most for past terms: "Other terms" availability and the first ingest of a new deployment.
Production: https://make-it-so.vercel.app
lib/course-color.ts); the code is always printed next to the colour.lib/sources.ts, components in
components/ui/source-tag.tsx): no boxed tags. A single-source list says its source once, in one quiet line
(<SourceLine>: "Course schedule · seats as of Oct 28, 12:00 PM"); a mixed list says it in each row's meta line
(<SourceNote>: "via Registrar", "via Moodle"), once per run of rows from one source, and only where it is not
obvious (the student's own deadlines say nothing; a syllabus deadline says "From your syllabus"). Every item still
carries data-source and "Source: …" for screen readers, and every AI output shows <AiChip> ("AI · verify with
your advisor"). docs/CONTRACTS.md ("Look") has the rule; expectAllAttributed checks it.ADMIN_EMAILS | no | Comma-separated addresses allowed on admin routes (with a verified mailbox) |
EXTERNAL_MODE | no | live (default) or fixtures (serve tests/fixtures/external; tests, e2e, CI) |
FIXTURES_NOW | tests only | With fixtures: pins server "now" (server/clock.ts); vitest and e2e use 2026-09-30T12:00:00-04:00 |
RATE_LIMITS | tests only | on (default) or off (skip rate limits; needs EXTERNAL_MODE=fixtures; the e2e server sets it) |
FEATURE_CAREERS | no | Careers pages on/off. Default true |
FEATURE_EVENTS | no | Events page and feeds on/off. Default true |
FEATURE_ALUMNI | no | Alumni directory on/off. Default true |
APP_TIMEZONE | no | IANA time zone for "today" logic. Default America/New_York |
RMP_ENABLED | no | RateMyProfessors ratings on/off. Default true |
RMP_SUMMARIES_ENABLED | no | AI summaries of RateMyProfessors reviews on/off. Default false (owner opt-in) |
MOODLE_IMPORT_ENABLED | no | Moodle calendar import on /syllabi on/off. Default true (off: no connecting or syncing) |
MOODLE_FEED_KEY | no | Key (≥ 32 characters) for the encrypted Moodle calendar URLs. Default: derived from NEXTAUTH_SECRET |