🐈 Github
https://github.com/ekkezia/conndev/tree/main/wk11
🖍️ Notes

Note in class:

Nginx just provides a single gateway, a single contact so it reduces the risk of having so many ports like in NodeJS.

Nginx responds to only static file requests.
Nginx and Node communicate with each other via common protocol HTTP.

For testing, we're using Arduino Nano 33 IoT, GPS, and Waveshare LCD 1.69" / Adafruit LCD. Though for production,

Description

A mini device that returns images from nearest traffic camera to user's current location and allow to capture and save the image.


API

webcams.tmcnyc.org

- GET all cameras: /api/cameras

- GET the image from the camera ID: /api/cameras/[id]/image

Usage

1. Bring your device to outdoor, the GPS will only be able to work when it is outside a building or window.

2. Press power button to turn on, the device GPS will get your location by default and show you the footages of the nearest 5 traffic cameras.

3. Scroll through the cameras by rotating the rotary encoder.

4. Press the rotary encoder to refresh the camera footage.

5. Press the capture button to snap the current camera footage and save it internally.

Learn

  1. Learn how to display image from API response on controller
  2. Learn how to use GPS
  3. Learn how to provision using personal device as hotspot


Materials

- XIAO ESP32S3 or ESP32C6

- Adafruit GPS Breakout v3

- rotary encoder

- button

Pins

Adafruit_ST7789 TFT LCD or Waveshare

- CS: D3

- DC: D2

- RST: D1

- BL (optional): D0

Encoder / Button

- ENC_CLK: D4

- ENC_DT: D5

- ENC_SW: D6

- CAPTURE_BTN: D9

GPS

- GPS_RX_PIN: D7

- GPS_TX_PIN: D6

Battery Reading

- A_PIN: D0

A lot issues I encounter have to do with wrong wiring. Fried an Arduino because i also plugged the controller 1 row off, since ESP32C6 GND and 3.3V pin is side by side.

Code

1. WiFiNINA -> store latest connection result in lastHttpCode

2. TFT Screen (Width: 280, Height: 240) -> Adafruit ST7789

3. GPS to define USER_LAT & USER_LON

-> Use haversine formula to calculate the distance between user lat & lon to the nearest camera lat & lon:

calculate the great-circle distance (the shortest distance over the surface of a sphere) between two points given their latitude and longitud

a = math.sin(delta_phi / 2.0) ** 2 + math.cos(phi_1) * math.cos(phi_2) * math.sin(delta_lambda / 2.0) ** 2

c = 2 * Radius of earth * math.atan2(math.sqrt(a), math.sqrt(1 - a))

4. Check with hardcoded camera list at cameras.h to find nearest cameras

5. Requesting the NYCTMC HTTP API for nearest cameras:

netFile.client.print("GET " + path + " HTTP/1.1\r\n");

netFile.client.println("Host: webcams.nyctmc.org");

The complete path is:

path = webcam.tmcnyc.org/api/cameras/[cam.id]/image

Content will be stored in netFile.contentLength.

6. Processing the image

- jpegOpen: start HTTP Request

- jpegRead: reads byte and buffer the file

- jpegClose: close the connection and clear data

Flow

jpegOpen → return &netFile

JPEGDEC stores it in f->fHandle

jpegRead gets f

↓you cast back to NetJPEGFile*

read from WiFi → write into buf

7. Rotary encoder to scroll through camera list, and press to refresh. Rotating the rotary encoder and pressing it will both redraw the image, hence refreshing the image shown on display. The fn drawImage will draw the image that is currently being the selectedNearest.

8. Capture button


Getting user’s network to connect individually to the camera

Thanks to Sky’s documentation!

  1. Create and display QR code on the device. This QR code contains information for user’s personal device to connect to the controller’s hotspot.
  2. Website will be triggered open on user’s personal device and it will show 2 fields: Wifi username and password.
  3. This information is then will be used to provision the controller to connect to user’s personal device.
  4. Use the Preference library to remember the last personal hotspot connected.

Think of what happened when device lost connection or do not have connection?

Keep searching?


Note

Upload SSL certificate to Arduino and make sure Serial Monitor is closed everytime doing so.

Tools > Upload SSL Root Certificates

SSL Certificate is the public key that is used to decrypt the content from the HTTPS API



Issues

1. Storing allCameras in a JSON file cameras.h internally may cause issue as it takes a lot of storage. Thinking to just fetch it from API.

2. GPS data is not being read even after being brought outdoors.

GPS

We learnt from the example file (wired) to test the GPS is using the UART & Serial. It is a two-way data bridge:

PC <--USB (Serial, 115200)--> XIAO ESP32-C6 <--UART (Serial1, 9600)--> GPS

- Serial: Computer -> GPS (sends command to GPS)

- GPSSerial (UART): GPS -> Computer (forwards raw NMEA)

On ESP32-C6, we may need to explicit the pin definition since it is not auto-assigned like other boards.

GPSSerial.begin(9600, SERIAL_8N1, D7, D6);

1. Power button

2. Sound module & haptics for feedback

3. Battery indicator

Blog image

What's inside?

Blog image


Printed instruction sheet

Blog image

Ideally want to make a packaging box too as if it is a real product, but for another time.

Elizabeth Kezia Widjaja © 2026 🙂