Week 15 โ€“ Interface and Application Programming

This week is about learning Application programming and Interfacing that with the input/output device that I build for my final project. .

The objective is to build an application that interacts with my projects and supports the features that I intend to integrate with my final project. I intend to have BLE based control of my outfits Neopixels and Meshtastic integrated in the mobile Application.

AI prompt: " This is Interface and Application Programming week, I am developing the mobile application that will connect to my final project (Smart Navratri Outfits) with BLE, using Application I can also control the embroidery Neopixel color, brightness and fade in and out. Second, feature is set the smart Navratri outfit on the mode that it blinks with sound/Audio Reactivity. Can you please give me a animated image of me making mobile application? "


Assignment Overview

Individual Task:
  • Write an application that interfaces a user with an input &/or output device that you made.
Group Task:
  • Compare as many tool options as possible.

Weekly Activity Log
Day Activity Status
Thursday Class - Interface and Application Programming Completed
Friday Public Holiday , Worked on previous pending Documentation ๐Ÿ“ In progress
Saturday Final Project - Components list, Possible production process In progress
3D design ๐Ÿ“ - Mold for Wayfinding - A 21-LED circular "Badge" Completed
Sunday Final Project Requirement Document ๐Ÿ“ Completed
Firmware and Mobile Application Development with BLE Completed
Monday Integrating Meshtashtic in Mobile Application Completed
Tuesday Regional Review ๐Ÿ‘จโ€๐Ÿซ๐Ÿ’ฌ Completed
Milling the Mold Final Project ๐Ÿ›  Pending
Wednesday Casting ๐Ÿ›  Pending
Documentation ๐Ÿ“ In progress

Group Assignment

The link to our group assignment is below:

Group Assignment


Application Development Workflow

Functional Requirements Document

First, prepared a function requirement document by prompting the Gemini with all necessary minimum features that I want ot include in my final project.

Project Overview

Project Name: Smart Navratri Outfits

Goal: To create a pair of interactive traditional costumes that synchronize LED patterns to folk music and use off-grid LoRa communication to help users locate each other in high-density crowds where mobile networks fail.

Core Functional Requirements

Focusing on what the system must do:

Functional Requirements
Feature ID Function Description Priority
FR-01 Audio Reactivity The system must use a digital microphone to animate WS2812B LEDs (blink, fade, color shift) in rhythm with live music. High
FR-02 BLE Control A mobile app must allow users to manually adjust color, brightness, and patterns via Bluetooth Low Energy. High
FR-03 Sync Mode One mobile app must be able to "bond" two costumes to synchronize their LED behavior in real-time. Medium
FR-04 Off-Grid Comms Use Wio-SX1262 (LoRa) to send location data between costumes over several kilometers. High
FR-05 Wayfinding A 21-LED circular "Badge" must act as a visual compass, lighting up the specific clock-position (1โ€“12) of the other user. High
FR-06 Proximity Alert The blinking frequency of the direction indicator must increase as the distance between users decreases. Medium

System Architecture

Following the Input โ†’ Process โ†’ Output workflow:

  • Inputs:
    • Digital Microphone (Sound waves/Rhythm sensing).
    • Wio-SX1262 LoRa Module (Location data from the peer).
    • GPS Module (To determine current coordinates for the compass).
  • Processing:
    • Custom PCB (likely ESP32 for its built-in BLE and processing power).
  • Outputs:
    • WS2812B Neopixel strips (Integrated into machined embroidery).
    • 21-LED Compass Badge (Circular arrangement: 1 center, 8 mid-ring, 12 outer-ring).

User Interaction (UI/UX)

  • The App: Used for selecting color palettes and pairing with a friend's costume.
  • The Badge: Provides haptic or visual feedback. When a friend is at "3 o'clock" relative to you, the LED at that position blinks.
  • Form Factor: Wearable electronics integrated into traditional fabric; the badge is a detachable or pinned-on unit.

Non-Functional Requirements

  • Fabrication: The badge housing must be 3D printed or CNC milled, and the embroidery must be machine-produced in the lab.
  • Durability: The electronics must be protected from sweat and the vigorous movement of Garba folk dance.
  • Connectivity: Must operate reliably in an environment with 40k+ people (off-grid LoRa is essential here).

The "Spiral" Development Plan

To ensure you graduate on time, we break the complexity into "spirals":

  • Spiral 1 (The Must-Haves): Create one PCB that controls Neopixels based on microphone input (Sound-reactive mode).
  • Spiral 2: Develop the BLE Mobile App to change colors and brightness for a single suit.
  • Spiral 3: Integrate the Wio-SX1262 modules to send a "ping" between two boards and light up the direction on the 21-LED badge.
  • Spiral 4 (The Reach Goal): Full Meshtastic integration and synchronized "Master/Slave" control of two suits from one app.

Since, I am in the interfacing and application development week I an starting with Spiral 2 Development of a android based Mobile App - first, I listed down what I want as user work-flow and then given it to AI for refinement and guidance with the MIT APP Inventor so that I can build the App.

AI prompt: " This is Interface and Application Programming week, I am developing the mobile application that will connect to my final project (Smart Navratri Outfits) with BLE, using application. I want three screens, first screen - welcome screen with connect option abd connection status, second screen - brightness control, color picker, audio-reaction and third for integrating Meshtectic."

Application User Workflow

Screen 1: Welcome to Garba-Sync

  • Action: Big button labeled "Scan for Bheru".
  • Discovery: A list showing available costumes (e.g., "Cloth1" or "Cloth2").
  • Feedback: A status label that switches between "Searching...", "Connected to Bheru", and "Lost Connection".

Screen 2: Aura Settings (The Fun Word!)

  • Audio-Responsive: A toggle switch to enable/disable the "Dance Mode" (Audio-Reactive blinking).
  • Brightness: A slider (0โ€“255) to adjust the intensity so you don't blind people on the dance floor.
  • The Color Wheel: 12 specific buttons arranged in a circle (or a list) matching your requested palette:
    • Warm: Red, Red-Orange, Orange, Yellow-Orange, Yellow.
    • Nature: Yellow-Green, Green, Blue-Green.
    • Cool: Blue, Blue-Purple, Purple, Red-Purple.
    Sync Mode: A special button that sends a LoRa signal to all nearby clothes.
  • Friend Finder Toggle: The master switch to start/stop the LoRa location broadcast.

Screen 3: Radar (The "Magic")

  • Avatar Selection: Two icons at the top (Boy/Girl). You tap one to represent the friend you are looking for.
  • The Compass: A central rotating needle or arrow.
  • Animation: The Boy/Girl icon sits at the tip of the needle, pulsing as the LoRa signal strength updates.

I decided to use MIT App Inventor to make the application.

AI prompt: "I am going to use MIT App Inverter for first time, can you guide be step by step lest start with screen 1, I just opened the MIT App Inventor website, I have not yet started - and not even imported BLE Extension."

MIT App Invertor : Designer Screen

Step 1: Start a New Project

  • Click the "Create Apps!" button on the MIT App Inventor website and log in with your Google account.
  • Click Projects > Start new project.
  • Name it: Garba_Sync_Bheru (Note: App Inventor doesn't allow spaces in project names).

Step 2: Import the BLE Extension

Since you've already downloaded the .aix file:

  • Look at the Palette (left column). Scroll to the very bottom to find Extension.
  • Click Import extension > Choose File > select your .aix file.
  • Once it appears in the Palette, drag and drop the "BluetoothLE" component onto the white phone screen.
  • It won't show up on the screen; it will appear at the bottom as a "Non-visible component."

Step 3: Setup the "Vertical Arrangements" (The Screen Strategy)

Instead of 3 screens, we will make 3 "folders" on one screen.

  • Go to the Palette > Layout.
  • Drag 3 VerticalArrangement components onto the screen.
  • Rename them in the Components (right column) to:
  • Welcome_Layout
  • Aura_Layout
  • Radar_Layout

For all three, go to the Properties (far right) and set:

  • Width: Fill Parent
  • Height: Fill Parent

Crucial: Uncheck the Visible box for Aura_Layout and Radar_Layout. Only Welcome_Layout should be checked.

Step 4: Design Screen 1 (Welcome_Layout)

Designing: Welcome_layout

Now, letโ€™s add the buttons and labels for your "Bheru" connection:

  • Drag a Label into Welcome_Layout. Rename it lbl_Header.
  • Set Text to: "Welcome to Garba-Sync"
  • Set Font Size to: 24 and Bold.
  • Drag a Button into Welcome_Layout. Rename it btn_Scan.
  • Set Text to: "Scan for Bheru"
  • Set Background Color to something vibrant!
  • Drag a ListView into Welcome_Layout. Rename it list_Devices.
  • This is where "Chaniya Choli" or "Kediyu" names will appear.
  • Drag another Label at the bottom. Rename it lbl_Status.
  • Set Text to: "Status: Not Connected"

Step 5: The "Scan" Logic (The Blocks)

MIT App Inventor : The Blocks
The Blocks for Welcome_Layout

Now, click the Blocks button in the top right corner to start the "coding."

  • Find the btn_Scan blocks: Drag out when btn_Scan.Click.
  • Find the BluetoothLE blocks: Inside the button click, snap in call BluetoothLE1.StartScanning.
  • Update Status: Drag out set lbl_Status.Text to and attach a text block that says "Searching for Bheru...".

Step 6: Design Screen 2 (Aura_Layout)

Designing : Aura_layout

Now, let's build the Aura Settings page. Follow these steps in the Designer tab:

Switch Visibility:

  • In the Components list, uncheck Visible for Welcome_Layout.
  • Check Visible for Aura_Layout. (It will look like a blank canvas now).

Add Audio Toggle:

  • Drag a HorizontalArrangement into Aura_Layout.
  • Inside it, drag a Label ("Audio-Responsive") and a Switch (Rename to sw_Audio).

Add Brightness Slider:

  • Drag a Label ("Brightness") into Aura_Layout.
  • Drag a Slider (Rename to sld_Brightness) below it.
  • Properties: MaxValue = 255, MinValue = 0, ThumbPosition = 50.

Add the Color Palette (The 12 Colors):

  • Drag a TableArrangement into Aura_Layout.
  • Set Columns to 4 and Rows to 3 (this gives you your 12 slots).
  • Drag a Button into each cell. Rename them btn_Red, btn_Orange, etc.
  • Set the Background Color of each button to match your wheel.

Add the Sync Button:

  • Drag a Button (Rename btn_Sync) at the bottom.
  • Text: "โœจ Sync with Nearby Bherus โœจ".
  • Make it Bold and a distinct color like Gold.

Add Radar Navigation:

  • Drag a Button (Rename btn_GoToRadar).
  • Text: "Open Friend Finder Radar".

Step 7.1: The List Update (Clock)

  • Gold Block: when Clock1.Timer
  • Inside: set list_Devices.Elements to โ†’ BluetoothLE1.AdvertiserNames

Step 7 : Setting up the Code Block for Bluetooth Connection

The Blocks for Bluetooth Connection

Step 7.2: The Handshake (AfterPicking)

  • Gold Block: when list_Devices.AfterPicking
  • Inside (Top): call BluetoothLE1.StopScanning
  • Inside (Bottom): call BluetoothLE1.ConnectWithName (or ConnectWithAddress)
  • Socket: list_Devices.Selection

Step 7.3: The Success Switch (Connected)

  • Gold Block: when BluetoothLE1.Connected
  • Inside (1st): set Welcome_Layout.Visible to โ†’ false
  • Inside (2nd): set Aura_Layout.Visible to โ†’ true
  • Inside (3rd): set lbl_Status.Text to โ†’ "โœจ Connected to Bheru! โœจ"

Application Testing and Validation

MIT AI2 Companion

The MIT AI2 Companion is a free app that acts as a "live bridge" between my computer and my phone.

Instead of having to download and install an app file (.apk) every time I make a change, the Companion app shows my project live. When I move a button on my computer screen, it moves on my phone screen instantly. It is essential for testing Bluetooth because computers usually can't "talk" to BLE devices directly through the browserโ€”they need my phone's hardware to do it.

Download the MIT AI2 Companion --> Connect computer and phone to the same WiFi network --> In my computer browser click "connect" --> AI Companion --> Scan QR code using my mobile --> Wait for Mobile Screen to get updated.

What Went Wrong - The screen resolution and icon size mismatch, Keep Searching... for device

The Screen design and pixel size on MIT App Inventor was not completely on my phone's screen when I download .apk

What I Learned - Test Step by Step

DO not weight for entire design to be done and then testing all features together, it is better to test as we design and build.

Step 1: So here, I should have first make welcome screen block, check the actual size and tested the BLE connect function first.

Step 2: Then test the Aura Screen block, check the actual size and Neopixel Brightness control function.

Step 3: Then I should have test color selector function change the Neopixel color change.

Spiral 2 - Develop the BLE Mobile App

AI prompt: "Can you please help to start development with spiral 2 : Develop the BLE Mobile App to control color and brightness for a single costume. we can come back to Spiral 1 of hardware development later. Right Now, I have XIAO ESP32S3 and Neopixel Strip of with 25 Neopixels. can you help me develop the BLE Mobile App to control color and brightness for these 25 Neopixels."

1. Hardware Setup

I connected XIAO ESP32S3 to Neopixel ring as follows VCC --> 3V3, GND --> GND, and DATA PIN --> D9.

2. ESP32S3 Firmware (The "Server")

I will need to use the ArduinoBLE library and a Neopixel library (like Adafruit_NeoPixel). The ESP32S3 will act as a BLE Peripheral, exposing "Characteristics" for color and brightness that the mobile app can write to.

Required Logic for the Firmware: I will need native ESP32 BLE library, which is much more robust for the S3 architecture, do not need to download it because it is built into the ESP32 board manager you already installed. The ESP32S3 will act as a BLE Peripheral, exposing "Characteristics" for color and brightness that the mobile app can write to.

  • Service: Create a custom BLE Service for "Garba-Sync".

Characteristics:

  • Color Characteristic: A 3-byte array (Red, Green, Blue) that the app updates.
  • Brightness Characteristic: A single byte (0-255) to control intensity.
  • Callback: When the app writes a new value, the ESP32 updates the Neopixel strip immediately.

3. Mobile App Development (The "Client")

For a custom mobile application, I can use MIT App Inventor (Android) with the BLE Extension.

UI Elements to Create in the App:

  • Scan/Connect Button: To find and pair with your "Garba-Sync" costume.
  • Color Picker or RGB Sliders: Three sliders (0-255) to define the color.
  • Brightness Slider: One slider to send a value to the brightness characteristic.

4. Integration Steps

  • Flash the ESP32S3: Upload the code that initializes the BLE stack and sets the Neopixel strip to 25 LEDs.
  • Test Connection: Use a generic BLE scanner app (like nRF Connect) to ensure I can see the device and manually write hex values to change the LED colors.
  • Build the App: Once manual control works, build the interface in MIT App Inventor to automate the data sending.

AI prompt: "Lets first work on Arduino code for XIAO ESP32S3 to handle the BLE, I will do the hardware setup VCC, GND and DIN to D9 pin of XIAO ESP32S3 and also XIAO ESP32S3 will be my main controller of the project. Lets use the generic BLE scanner app like nRF connect to ensure I can see the device and manually write hex values to change the LED colors."

Arduino Code - Testing BLE with nRF Connect and Color Service
  
  #include <BLEDevice.h>
  #include <BLEUtils.h>
  #include <BLEServer.h>
  #include <Adafruit_NeoPixel.h>

  // Use the GPIO number directly for the XIAO S3
  #define LED_PIN    D9      
  #define LED_COUNT  25

  Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

  // Unique IDs for your Garba Costume
  #define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
  #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

  class MyCallbacks: public BLECharacteristicCallbacks {
      void onWrite(BLECharacteristic *pCharacteristic) {
        // FIX: Use Arduino 'String' instead of 'std::string'
        String value = pCharacteristic->getValue(); 
        
        if (value.length() == 3) {
          // Accessing indices works the same way
          uint8_t r = (uint8_t)value[0];
          uint8_t g = (uint8_t)value[1];
          uint8_t b = (uint8_t)value[2];
          
          for(int i=0; icreateService(SERVICE_UUID);
    
    BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                          CHARACTERISTIC_UUID,
                                          BLECharacteristic::PROPERTY_READ |
                                          BLECharacteristic::PROPERTY_WRITE
                                        );

    pCharacteristic->setCallbacks(new MyCallbacks());
    pService->start();
    
    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->addServiceUUID(SERVICE_UUID);
    pAdvertising->setScanResponse(true);
    pAdvertising->setMinPreferred(0x06);  
    pAdvertising->setMinPreferred(0x12);
    BLEDevice::startAdvertising();
    
    Serial.println("Characteristic defined! Now gradient-scanning on your phone...");
  }

  void loop() {
    // Nothing needed here for now, callbacks handle the work!
    delay(2000);
  }
        

What Went Wrong

The XIAO ESP32 was not flashing and giving the upload error - PermissionError(13), I tried several times.

What I Learned

While flashing, please ensure below steps.

  • The "Manual Bootloader" Trick (Most Likely Fix), Press and hold the BOOT button and While holding BOOT, press and release the RESET button once and then Release the BOOT button. Now, select the COM Port and Upload the code.
  • Close the Serial Monitor
  • Check for "Ghost" Connections - If you have other software open that talks to serial ports
  • Swap the Cable
  • Check "USB CDC On Boot" (Again) - Make sure Tools --> USB CDC On Boot is still set to Enabled.

Testing with nRF Connect

Now, I Installed the nRF Connect mobile Application to check if the XIAO is able to connect via BLE.

I followed these steps to test:

  • Open nRF Connect: Enable Bluetooth on my phone and open the app.
  • Scan: I looked for a device named "Garba-Sync-01" and tap Connect.
  • Find Characteristics: Under that service, we can see a characteristic with UUID: beb5483e-36e1-4688-b7f5-ea07361b26a8.

Change Color:

  • Tap the Write icon (up arrow).
  • Select BYTE ARRAY.
  • Enter 3 hex values (e.g., FF 00 00 for Red, 00 FF 00 for Green).
Test With nRF Connect App

Change Brightness:

  • Tap the Write icon.
  • Enter 1 hex value between 00 and FF (e.g., 32 for roughly 20% brightness).
Arduino Code - Both Color and Brightness Services


    #include <BLEDevice.h>
    #include <BLEUtils.h>
    #include <BLEServer.h>
    #include <Adafruit_NeoPixel.h>

    #define LED_PIN    D9      
    #define LED_COUNT  25
    Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

    // Unique IDs
    #define SERVICE_UUID           "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
    #define COLOR_CHAR_UUID        "beb5483e-36e1-4688-b7f5-ea07361b26a8"
    #define BRIGHTNESS_CHAR_UUID   "d211246c-7e61-456a-93a0-f80076a598c1"

    class ColorCallbacks: public BLECharacteristicCallbacks {
        void onWrite(BLECharacteristic *pCharacteristic) {
          String value = pCharacteristic->getValue();
          if (value.length() == 3) {
            strip.setPixelColor(0, strip.Color((uint8_t)value[0], (uint8_t)value[1], (uint8_t)value[2]));
            for(int i=1; igetValue();
          if (value.length() > 0) {
            uint8_t b = (uint8_t)value[0];
            strip.setBrightness(b);
            strip.show();
            Serial.printf("Brightness set to: %d\n", b);
          }
        }
    };

    void setup() {
      Serial.begin(115200);
      strip.begin();
      strip.setBrightness(50); // Start dim for safety
      strip.show();

      BLEDevice::init("Garba-Sync-01");
      BLEServer *pServer = BLEDevice::createServer();
      BLEService *pService = pServer->createService(SERVICE_UUID);

      // Color Characteristic
      BLECharacteristic *pColorChar = pService->createCharacteristic(COLOR_CHAR_UUID, BLECharacteristic::PROPERTY_WRITE);
      pColorChar->setCallbacks(new ColorCallbacks());

      // Brightness Characteristic
      BLECharacteristic *pBrightChar = pService->createCharacteristic(BRIGHTNESS_CHAR_UUID, BLECharacteristic::PROPERTY_WRITE);
      pBrightChar->setCallbacks(new BrightnessCallbacks());

      pService->start();
      BLEDevice::startAdvertising();
      Serial.println("Ready! Connect via nRF Connect.");
    }

    void loop() { delay(10); }

    

What Went Wrong - not able to see the second service of Brightness control on nRF Connect App

I was not able to see the second service/characteristic (d211...) of brightness control. It is very likely that nRF Connect is showing you a "cached" version of your device from the previous code upload. Since the phone remembers the first time it connected to "Garba-Sync-01" (when it only had one characteristic), it doesn't always look for new ones automatically.

What I Learned

If we face this then :

  • Refresh Services in nRF Connect --> Connect to XIAO --> click top right corner three dots (โ‹ฎ) --> click "Refresh Services".
  • Toggle Bluetooth of my phone

Color and Brightness Control Services

Spiral 3 - LoRa/GPS - starting with the "Ping" test code

XIAO ESP32S3 Sense with the B2B (Board-to-Board) connector is perfect for rapid prototyping my Spiral 3, and my plan to integrate the Wio-SX1262 directly onto a custom PCB for the final version.

Wio-SX1262 module is strictly a LoRaยฎ transceiver module. Its job is to send and receive long-range radio packets for "find friends" feature, I need two distinct pieces of data.

  • Location Provided by a GPS Module, it uses UART (Serial) - TX and RX pins. I installed arduino library TinyGPSplus (by Mikal Hart) to parse the GPS data.
  • Communication: Provided by the LoRa Module (Wio-SX1262) to send that location to others. The Wio-SX1262 uses SPI communication - MOSI, MISO, SCK, and a CS (Chip Select) pin. I installed arduino library RadioLib (by Jan Gromes) - for the SX1262 LoRa chip.

Test Code for GPS Module - this code will read the GPS coordinates and print them to the Serial Monitor. How this works "Under the Hood" - The GPS module (NEO-6M) is constantly screaming text called NMEA sentences (they look like $GPRMC,123519,A,4807.038,N...).

  • The SerialGPS.read() grabs those characters.
  • The gps.encode() function acts like a translator, turning that text into usable variables like gps.location.lat().
  • Because the ESP32S3 is powerful, it can handle this translation while simultaneously running the BLE and Neopixels without any lag.
Arduino Code - GPS Module

// Test Code for GPS Module - this code will read the GPS coordinates and print them to the Serial Monitor.
// --- LIBRARIES ---
#include          // The library that turns messy GPS "sentences" into numbers
#include     // Allows us to create a second serial port for the GPS
#include  // Controls your 25 LEDs

// --- PIN DEFINITIONS ---
#define LED_PIN    D3    // Data pin for Neopixels (moved to D3 to avoid LoRa conflict)
#define LED_COUNT  25   // Total number of LEDs in your strip
#define GPS_RX     D7    // XIAO Pin D7 (Connect to GPS TX)
#define GPS_TX     D6    // XIAO Pin D6 (Connect to GPS RX)

// --- OBJECT INITIALIZATION ---
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); // Create the LED strip object
TinyGPSPlus gps;                // Create the GPS "parser" object to handle data
HardwareSerial SerialGPS(1);    // Create a new Serial port (Serial1) on the ESP32 hardware

void setup() {
  // Start the standard Serial for the Computer (Serial Monitor) at 115200 baud
  Serial.begin(115200);
  
  // Initialize Neopixels
  strip.begin();
  strip.setBrightness(50);
  strip.fill(strip.Color(0, 0, 255)); // Set color to BLUE (indicates "System OK, searching for GPS")
  strip.show();

  // Initialize GPS communication. 9600 is the factory standard for NEO-6M modules.
  // We tell the ESP32 to use Pins 7 (RX) and 6 (TX) for this specific serial link.
  SerialGPS.begin(9600, SERIAL_8N1, GPS_RX, GPS_TX);
  
  Serial.println("Spiral 3: GPS Test Starting...");
}

void loop() {
  // 1. DATA FEEDING: This loop checks if the GPS module is sending any bytes.
  while (SerialGPS.available() > 0) {
    // gps.encode() takes the raw characters one-by-one and looks for a valid GPS "sentence."
    // It returns 'true' only when it has a complete, valid packet of information.
    if (gps.encode(SerialGPS.read())) {
      displayGPSInfo(); // If a valid packet is ready, go to the display function
    }
  }

  // 2. ERROR CHECK: If 5 seconds pass and no data characters have been processed,
  // it usually means the TX/RX wires are swapped or loose.
  if (millis() > 5000 && gps.charsProcessed() < 10) {
    Serial.println("No GPS detected: check wiring (TX to D7, RX to D6)");
    delay(2000);
  }
}

// --- DISPLAY FUNCTION ---
void displayGPSInfo() {
  // Check if the library successfully calculated a location from the satellites
  if (gps.location.isValid()) {
    // Print Latitude and Longitude with 6 decimal places of precision
    Serial.print("Location: ");
    Serial.print(gps.location.lat(), 6);
    Serial.print(",");
    Serial.println(gps.location.lng(), 6);
    
    // VISUAL FEEDBACK: Change LEDs to GREEN to show the user the "Friend-Finder" is ready
    strip.fill(strip.Color(0, 255, 0));
    strip.show();
  } else {
    // If we have a connection to the module but not enough satellites yet
    Serial.println("Waiting for Satellites... (Go near a window)");
  }
}
  
GPS Module Test

LoRa Integration code - Sending your location through the air! This code will take my GPS coordinates and broadcast them as a LoRa packet. With second device, it would be able to "hear" my location from up to several kilometers away. LoRa Settings - LoRa uses different "Open" radio bands depending on your local laws, the code line radio.begin(868.0) shall be 868.0 for Europe/India, and 915.0 for Americas.

  • The Serial Monitor will say "[SX1262] Initializing ... success!"
  • Once my GPS gets a fix, the Serial Monitor will say "Transmitting packet ... success!"
  • Now I am officially "Broadcasting" its location into the air.

What Went Wrong - X1262 initialization... failed, code -2

The error code -2 in RadioLib means RADIOLIB_ERR_CHIP_NOT_FOUND. Essentially, the XIAO is looking for the LoRa chip over the SPI bus and Wio-SX1262 is answering.

What I Learned "We need to direct AI"

The Ai did not know that there is B2B connection and it is enough for the connection it was directing me to connect 3V3, GND, DIO1, BUSY, NRST to (D0, D1, etc..)pins using jumper wires, which was not required and I send a lot of time debugging it.

Then I gave AI, the links of Seeed WIKI page and after reviewing the after reviewing the official Seeed Studio documentation for the XIAO ESP32S3 + Wio-SX1262 kit, it found the "missing link."

The documentation reveals that when using the B2B connector, the LoRa module uses non-standard pins for its control signals (CS, Busy, Reset, and DIO1) compared to the standard stacking headers. This is why our previous attempts with D0, D1, etc., were failing with Code -2."

The Correct B2B Pin Mapping : CS: 41, DIO1: 39, Busy: 40, Reset: 42

LoRa Initialization

Integration : GPS + LoRa + LEDs

Arduino Code - Integration : GPS + LoRa + LEDs

        
        #include <RadioLib.h>
        #include <TinyGPS++.h>
        #include <HardwareSerial.h>
        #include <Adafruit_NeoPixel.h>

        // --- 1. PIN DEFINITIONS ---
        // LoRa Pins (Official Seeed B2B Mapping for XIAO ESP32S3)
        #define LORA_CS    41
        #define LORA_DIO1  39
        #define LORA_BUSY  40
        #define LORA_NRST  42

        // GPS Pins (XIAO Side Headers)
        #define GPS_RX     D7    // Connect to GPS TX
        #define GPS_TX     D6    // Connect to GPS RX

        // Neopixel Pin (Move to D3 to stay clear of I2C/SPI)
        #define LED_PIN    D3    
        #define LED_COUNT  25

        // --- 2. OBJECT INITIALIZATION ---
        SX1262 radio = new Module(LORA_CS, LORA_DIO1, LORA_NRST, LORA_BUSY);
        TinyGPSPlus gps;
        HardwareSerial SerialGPS(1); // Use UART1
        Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

        // Timing variables
        unsigned long lastSendTime = 0;
        const int sendInterval = 10000; // Broadcast every 10 seconds

        void setup() {
          Serial.begin(115200);
          while(!Serial); // Wait for monitor
          delay(1000);

          // Initialize LEDs
          strip.begin();
          strip.setBrightness(50);
          strip.fill(strip.Color(0, 0, 255)); // Start Blue (Searching)
          strip.show();

          // Initialize GPS Serial
          SerialGPS.begin(9600, SERIAL_8N1, GPS_RX, GPS_TX);

          // Initialize LoRa Radio
          Serial.println(F("\n--- Garba-Sync Spiral 3 Boot ---"));
          Serial.print(F("[LoRa] Initializing... "));
          
          // 868.0 for India/Europe, 915.0 for Americas
          int state = radio.begin(868.0); 

          if (state == RADIOLIB_ERR_NONE) {
            Serial.println(F("Success!"));
          } else {
            Serial.print(F("Failed, code ")); Serial.println(state);
            strip.fill(strip.Color(255, 0, 0)); // Red for Radio Failure
            strip.show();
            while (true);
          }
        }

        void loop() {
          // 3. AGGRESSIVE GPS FEED
          // We read every single character from GPS to prevent buffer overflow
          while (SerialGPS.available() > 0) {
            gps.encode(SerialGPS.read());
          }

          // 4. PERIODIC DATA UPLOAD (Every 10 Seconds)
          if (millis() - lastSendTime > sendInterval) {
            
            // Print Debug Info to Serial Monitor
            Serial.print(F("Satellites: ")); Serial.print(gps.satellites.value());
            Serial.print(F(" | Lat: ")); Serial.print(gps.location.lat(), 6);
            Serial.print(F(" | Lng: ")); Serial.println(gps.location.lng(), 6);

            if (gps.location.isValid() && gps.location.age() < 2000) {
              // Create the packet string
              String msg = "ID:Sync-01,LAT:" + String(gps.location.lat(), 6) + ",LNG:" + String(gps.location.lng(), 6);
              
              Serial.print(F("[LoRa] Broadcasting Location... "));
              int state = radio.transmit(msg);

              if (state == RADIOLIB_ERR_NONE) {
                Serial.println(F("Done!"));
                flashLED(strip.Color(0, 255, 0)); // Flash Green on success
              } else {
                Serial.print(F("Error: ")); Serial.println(state);
              }
            } 
            else {
              // Visual feedback while waiting for lock
              if(gps.satellites.value() > 0) {
                strip.fill(strip.Color(255, 255, 0)); // Yellow = Seeing sats, but no lock yet
              } else {
                strip.fill(strip.Color(0, 0, 255));   // Blue = No satellites seen
              }
              strip.show();
              Serial.println(F("[GPS] Waiting for valid 3D Lock..."));
            }
            
            lastSendTime = millis();
          }
        }

        // Function to flash LEDs briefly
        void flashLED(uint32_t color) {
          strip.fill(color);
          strip.show();
          delay(300);
          strip.fill(0);
          strip.show();
        }
        
LoRa Broadcasting My Location ! - "Please come and save me from FAB Academy Final Project Pressure!!
LoRa Broadcasting

Master Firmware (BLE, NEO, GPS and LoRa) Key Takeaways :

  • Concurrency: Explain how the code handles three protocols at once (BLE, LoRa, GPS) using the "non-blocking" millis() timer instead of delay().
  • BLE GATT: Mention that you used a Peripheral role with a GATT Service to allow your mobile app to interact with the hardware.
  • Pin Mapping: Highlight that you used the B2B pins (39-42) for LoRa to save the external headers for your GPS and LEDs.
Arduino Code - BLE NEO GPS LoRA (Master Firmware)

  /* * GARBA-SYNC MASTER FIRMWARE (Spiral 3.5)
  * Integrated GPS, LoRa (868MHz), and BLE (Bluetooth Low Energy)
  */
 
  #include <BLEDevice.h>         // Enables Bluetooth Low Energy functionality
  #include <BLEUtils.h>          // BLE helper functions
  #include <BLEServer.h>         // Enables the XIAO to act as a BLE Server (Peripheral)
  #include <RadioLib.h>          // Controls the SX1262 LoRa Module
  #include <TinyGPS++.h>         // Parses raw NMEA data from GPS into readable Lat/Lng
  #include <HardwareSerial.h>    // Manages UART communication for the GPS module
  #include <Adafruit_NeoPixel.h> // Controls the RGB LED ring

  // BLE UUIDs: These are unique "addresses" for your app to find specific features.
  // Service UUID: The main "folder" for your device's features.
  #define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
  // Brightness Characteristic UUID: The specific "file" the app writes to for LED control.
  #define BRIGHT_CHAR_UUID    "beb5483e-36e1-4688-b7f5-ea07361b26a8"

  // LoRa Pin definitions for the XIAO ESP32S3 B2B (Board-to-Board) connector
  #define LORA_CS    41   // Chip Select
  #define LORA_DIO1  39   // Data Input/Output 1 (Interrupt)
  #define LORA_BUSY  40   // Busy status pin
  #define LORA_NRST  42   // Hardware Reset

  // GPS and LED Pins connected to the XIAO headers
  #define GPS_RX     D7   // Connect to GPS Module TX
  #define GPS_TX     D6   // Connect to GPS Module RX
  #define LED_PIN    D3   // Data pin for the Neopixels
  #define LED_COUNT  25   // Number of LEDs in the ring

  // --- OBJECT INITIALIZATION ---
  // Create LoRa instance using the SX1262 module and specific pins
  SX1262 radio = new Module(LORA_CS, LORA_DIO1, LORA_NRST, LORA_BUSY);

  // Create GPS parser instance
  TinyGPSPlus gps;

  // Define a Hardware Serial port (Serial1) for the GPS module
  HardwareSerial SerialGPS(1);

  // Initialize Neopixel strip
  Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

  // Global variables for system state
  int currentBrightness = 50;     // Initial LED brightness (0-255)
  unsigned long lastSendTime = 0; // Stores the timestamp of the last LoRa transmission

  // --- BLE CALLBACK CLASS ---
  // This class "listens" for data coming from your Mobile App
  class MyCallbacks: public BLECharacteristicCallbacks {
      // This function runs automatically whenever you send data from the App
      void onWrite(BLECharacteristic *pCharacteristic) {
        // Retrieve the data sent from the phone app
        String value = pCharacteristic->getValue(); 

        if (value.length() > 0) {
          // We take the first byte of data and use it to set LED brightness
          currentBrightness = (int)value[0];
          strip.setBrightness(currentBrightness);
          strip.show(); // Update the physical LEDs immediately
          
          Serial.print("App Update -> New Brightness: ");
          Serial.println(currentBrightness);
        }
      }
  };

  void setup() {
    // Start the standard Serial Monitor for debugging
    Serial.begin(115200);
    
    // 1. INITIALIZE LEDs
    strip.begin();
    strip.setBrightness(currentBrightness);
    strip.fill(strip.Color(255, 0, 255)); // Set Purple color (Indicates "Booting/BLE Active")
    strip.show();

    // 2. INITIALIZE BLE (Bluetooth)
    BLEDevice::init("Garba-Sync-Badge");             // Set the name seen by your phone
    BLEServer *pServer = BLEDevice::createServer();    // Create the BLE server instance
    BLEService *pService = pServer->createService(SERVICE_UUID); // Start the main service
    
    // Create the "Brightness" characteristic so the app can talk to the badge
    BLECharacteristic *pChar = pService->createCharacteristic(
                                  BRIGHT_CHAR_UUID, 
                                  BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE
                              );
                              
    pChar->setCallbacks(new MyCallbacks());            // Link the app logic to the code above
    pService->start();                                 // Start the service
    BLEDevice::getAdvertising()->start();              // Start "broadcasting" so phone can find it

    // 3. INITIALIZE GPS & LoRa
    SerialGPS.begin(9600, SERIAL_8N1, GPS_RX, GPS_TX); // Start GPS serial communication
    int state = radio.begin(868.0);                    // Initialize LoRa radio at 868MHz
    
    if (state == RADIOLIB_ERR_NONE) {
      Serial.println("System Online: BLE, GPS, and LoRa Active.");
    } else {
      // If radio fails, print error code (check B2B connection if this happens)
      Serial.print("LoRa Failed, code: "); Serial.println(state);
    }
  }

  void loop() {
    // --- GPS LOGIC ---
    // Constantly "feed" every character coming from the GPS module into the TinyGPS parser
    while (SerialGPS.available() > 0) {
      gps.encode(SerialGPS.read());
    }

    // --- LoRa BROADCAST LOGIC ---
    // Use a non-blocking timer to send data every 10 seconds (10000ms)
    if (millis() - lastSendTime > 10000) {
      
      // Check if the GPS has successfully found its location (Lock)
      if (gps.location.isValid()) {
        // Create a data packet: ID of badge, Latitude, and Longitude
        String msg = "ID:01,LAT:" + String(gps.location.lat(), 6) + ",LNG:" + String(gps.location.lng(), 6);
        
        // Send the location packet into the air via LoRa
        radio.transmit(msg);
        
        Serial.println("LoRa Broadcast Sent: " + msg);
      } else {
        // If GPS isn't ready yet, we wait (usually requires being near a window or outside)
        Serial.println("Waiting for GPS fix to broadcast...");
      }
      
      lastSendTime = millis(); // Reset the 10-second timer
    }
  }
  

Meshtastic Web Flasher

So I also tried the Meshtastic Web Flasher and the kit was successfully flashed.




2.Ask AI's Execution Plan - Gemini and Codex

3.Ask AI's to research - Gemini and Codex

4.Ask AI to share example UI - Gemini and Codex

5.Validate "My wanting" - and iterate if required

6.Ask AI for Final Prompt Document - Gemini and Codex

7.Maintain Change log