Build your own robot app with MyApp¶
MyApp is your own web app that controls the PhysiCar. Run it on port 5000 and it
appears in the app's MYAPP tab (at /myapp/).
1. A minimal app¶
Create app.py in /home/physicar/physicar_ws:
# app.py (install once: pip install flask requests)
import requests
from flask import Flask, Response
app = Flask(__name__)
ROBOT = "http://localhost:8000" # the robot's local Web API
@app.route("/")
def home():
return """
<button onclick="fetch('./forward')">Forward</button>
<button onclick="fetch('./stop')">Stop</button>
<img src="./photo">
"""
@app.route("/forward")
def forward(): requests.post(f"{ROBOT}/speed", json={"value": 0.5}); return {"ok": True}
@app.route("/stop")
def stop(): requests.post(f"{ROBOT}/speed", json={"value": 0.0}); return {"ok": True}
@app.route("/photo")
def photo(): return Response(requests.get(f"{ROBOT}/camera").content, mimetype="image/jpeg")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Use relative paths
nginx serves your app under /myapp/ and strips the prefix — write links and fetch()
as ./..., not /....
2. Control with the Web API¶
The robot's local Web API (port 8000) drives everything:
requests.post(f"{ROBOT}/speed", json={"value": 0.5}) # m/s
requests.post(f"{ROBOT}/steering", json={"value": 0.2}) # rad (left +)
jpeg = requests.get(f"{ROBOT}/camera").content # JPEG bytes
state = requests.get(f"{ROBOT}/states").json() # speed, battery, etc.
3. Add an AI brain¶
Call the chat API with the signed-in user's session:
when the user signs in to the PhysiCar app, every request to your app carries a
physicar_session cookie (AI usage draws on that user's credits):
import base64
from flask import request
@app.post("/ask")
def ask():
token = request.cookies.get("physicar_session") # set by signing in to the PhysiCar app
b64 = base64.b64encode(requests.get(f"{ROBOT}/camera").content).decode()
r = requests.post("https://api.physicar.ai/chat",
headers={"Authorization": f"Bearer {token}"},
json={"user_message": {"contents": [
{"type": "image", "mime": "image/jpeg", "base64": b64},
{"type": "text", "text": "Is there a person ahead? yes or no."}]},
"prompt": {"model": "gemini-flash"}})
answer = r.json().get("text") or ""
if "yes" in answer.lower():
requests.post(f"{ROBOT}/speed", json={"value": 0.3})
return {"answer": answer}
Read the token per request
Read the physicar_session cookie from each request — never cache one token
globally: on a shared robot, everyone's requests would bill one account.
In the browser too
nginx injects a small helper into every /myapp/ HTML page, so your page's own
JavaScript can read the same token with window.physicarSession.token() — no setup.
4. Start it on boot¶
Put the launch command in /opt/physicar/userdata/myapp.sh:
The robot runs this file as a service: if myapp.sh exists at boot, it starts
automatically, and output goes to myapp.log next to it. The script does not run from
your workspace — use absolute paths inside it. Until your app answers on port 5000, the
MYAPP tab shows a "MyApp is not running" page that reloads by itself.
To apply changes without rebooting, use the management API under /settings/myapp
(browse it all in /docs — replace the script, delete it, stream the log):
curl -X POST http://localhost:8000/settings/myapp/restart # also /start, /stop
curl "http://localhost:8000/settings/myapp/log?tail=50" # read the log
Checklist: app on port 5000, relative paths, launch command in
/opt/physicar/userdata/myapp.sh.
🛠 Mission: build a Follow-me or doorway-greeter robot. The same app runs unchanged on a real kit.
Learn more
Robot endpoints → Web API. AI requests → Chat API spec.