A complete pothole detection and reporting platform:
- React Native + Expo mobile app detects abrupt bumps while driving and lets users submit manual reports with photos and severity.
- PHP + MySQL backend stores users, devices, and events with a simple, framework‑free router.
- Optional Python/Keras service can verify photos for manual submissions.
src/– Mobile app (Expo React Native)App.js– app shell (auth, detection pipeline, tabs/navigation, theming)components/– UI components (MapPotholes,EventList,ManualRegisterForm,UserLoginForm,UserRegisterForm)lib/– services and clientsGyroService.js– gyro sampling, EMA baseline, delta, jerkLocationService.js– GPS watch, speed smoothing, vehicle gatebackendClient.js– REST client (health, auth, events)authStore.js– token persistence withexpo-secure-store(fallback to memory)ManualBacheService.js– camera + severity helper for manual reports
android/,ios/– native projects generated via Expo prebuildassets/– icons and splash assets
SDBdb/– Backend (PHP)public/index.php– single entrypoint + router for/api/*api/index.php– rewrite bridge for hosts without.htaccesssrc/Controllers/–HealthController.php,EventController.php,UserController.phpsrc/Auth.php,src/Database.php,src/Response.phpconfig.php(+ optionalconfig.local.phpoverrides)schema.sql– MySQL schema (users, sessions, devices, events)public/uploads/– image upload root (date‑partitioned subfolders)
SDBdb/BackendPy/– Optional Python ML verifier (Keras model,requirements.txt)
- Mobile: Expo SDK 54, React Native,
expo-sensors,expo-location,expo-secure-store, WebView (Leaflet) - Backend: PHP 8+, MySQL 5.7+/8, PDO, simple router, CORS
- Optional ML: Python 3.10+, TensorFlow/Keras
- Create DB and tables
- Import
SDBdb/schema.sqlinto MySQL (Workbench/CLI).
- Import
- Configure
- Base config:
SDBdb/config.php(sane defaults for local dev) - Local overrides: create/edit
SDBdb/config.local.php(DB creds, features, CORS, uploads) - Important settings:
features.require_auth_events: false by default for local dev. Set true in production.uploads.dir,uploads.url_prefix,uploads.max_bytes: disk location and served URL prefix for imagescors.allow_origin,cors.allow_headers: tighten for production
- Base config:
- Serve the API
- PHP built-in server (from
SDBdb/):php -S 0.0.0.0:8080 -t public public/index.php
- Or use Apache/Nginx with docroot pointing to
SDBdb/public/
- PHP built-in server (from
- Verify
- Open
http://<host>:<port>/SDBdb/public/index.php/api/health→{ "status": "ok" }
- Open
- Directory:
SDBdb/BackendPy/ - Install:
pip install -r requirements.txt - Expose endpoints used by
ManualRegisterForm:GET /→ JSON healthPOST /predict/(multipartfile) →{ "es_bache": true, "distancia": 0.12 }
- Update the Python API URL in
ManualRegisterForm.jsif you enable ML verification.
- Install dependencies
cd src npm install - Configure backend base URL
- Default:
http://localhost:80/SDBdb/public/index.php - Override via environment:
EXPO_PUBLIC_API_BASE="http://192.168.0.45:80/SDBdb/public/index.php" npx expo start - Or edit
extra.apiBaseinsrc/app.config.js - Or use the runtime input on the initial "Conectando al backend…" gate (writes to a runtime override)
- Default:
- Run
npx expo start # Press a (Android) / i (iOS) / w (Web) or scan QR in Expo Go - Register/Login (optional in dev)
- Use the Register tab to create a user; then Login. The session token is persisted using
expo-secure-storeif available.
- Use the Register tab to create a user; then Login. The session token is persisted using
GyroServicesamples device gyroscope at ~50ms intervals and computes:- |ω| magnitude, EMA baseline, delta = |ω| − baseline
- Jerk ≈ |Δω|/Δt (angular acceleration magnitude)
LocationServicesmooths GPS speed and providesisVehicle()- A detection is recorded when jerk and delta exceed thresholds while
isVehicle()is true - The UI list updates immediately, then the app posts to the backend and refreshes from the server on success
Notes:
- Detections require a valid GPS fix (lat/lon) to be sent to the backend.
- Thresholds and cooldowns are configured in
src/App.js.
Base is the configured URL (usually ends with /public/index.php). All routes live under /api/*.
GET /api/health→{ status: "ok" }POST /api/usersbody{ email, password, name? }→ creates a userPOST /api/loginbody{ email, password, device_id?, platform? }→{ token, user, device_id? }GET /api/me(auth) →{ user }GET /api/events?limit=50&since_ts?=...→{ items: [...] }POST /api/events→ create event- Auto example:
{ "type": "auto", "lat": -17.78, "lon": -63.18, "ts": 1710000000000, "delta": 1.6, "mag": 2.1, "jerk": 80.5, "speedKmh": 35.2, "platform": "android", "device_id": "android-abc123" } - Manual example (with image):
{ "type": "manual", "severity": "Severa", "lat": -17.78, "lon": -63.18, "ts": 1710000000001, "platform": "ios", "device_id": "ios-xyz789", "image_base64": "<base64-jpeg>", "image_mime": "image/jpeg" }
- Auto example:
- Dev default:
features.require_auth_events = false(no token required) - Production: set
features.require_auth_events = trueinconfig.local.php - When auth is required, the backend accepts any of:
- Headers:
Authorization: Bearer <token>,X-Authorization: Bearer <token>,X-Auth-Token: <token> - Body/query fallback:
tokenfield in JSON body or?token=...(useful on hosts that stripAuthorization)
- Headers:
users(id, email, name, password_bcrypt, password_sha512, created_at)sessions(token, user_id, created_at, expires_at)devices(id, device_uid, platform, created_at)events( id, user_id?, device_id?, type('auto'|'manual'), severity?, lat, lon, ts(ms), delta?, mag?, jerk?, speed_kmh?, image_path?, created_at )
Import SDBdb/schema.sql into your database to create all tables and indexes.
EXPO_PUBLIC_API_BASEenvironment variable for base URLsrc/app.config.js→extra.apiBase- Runtime override field on the health gate screen (testing only)
config.php– defaults;config.local.php– overridesdb.*– MySQL connectionuploads.*– upload directory, URL prefix, size limitcors.*– allowed origins/methods/headersfeatures.require_auth_events– auth toggle for posting events
- URL configured inside
ManualRegisterForm.js
Create user:
curl -X POST "http://localhost:8080/api/users" \
-H 'Content-Type: application/json' \
-d '{"email":"test@example.com","password":"Password123","name":"Test"}'Login and test me:
curl -X POST "http://localhost:8080/api/login" -H 'Content-Type: application/json' \
-d '{"email":"test@example.com","password":"Password123"}'
curl -H 'Authorization: Bearer <token>' "http://localhost:8080/api/me"Post an event (dev: auth not required):
curl -X POST "http://localhost:8080/api/events" -H 'Content-Type: application/json' \
-d '{"type":"auto","lat":-17.78,"lon":-63.18,"ts":1710000000000}'- Health gate shows HTML/security challenge
- Some free hosts inject a JS challenge (e.g., InfinityFree). The app detects this and offers an unlock flow; prefer a clean/local host for development.
- 401 Unauthorized on event POST
- For dev, set
require_auth_events=falseand restart PHP server - For prod, ensure token arrives via supported headers or
tokenin the JSON body
- For dev, set
- Detection appears then disappears in app
- The app only refreshes from server after a successful insert. If POST fails, the local item remains.
- No location / vehicle gate never passes
- Grant location permission and wait for GPS fix; detections require lat/lon.
- Images not saving/serving
- Ensure
uploads.diris writable andpublic/uploadsis served viauploads.url_prefix.
- Ensure
- Android:
npm run android(fromsrc/) - iOS:
npm run ios(fromsrc/) - Web:
npm run web(limited sensors in browsers)
For production builds, use EAS Build or native tooling as needed.
See LICENSE.