AGROSAGE - Your soil health companion

Published Nov 28, 2025
 72 hours to build
 Intermediate

AgroSage is a solution for farmers to monitor soil health using Bluetooth-enabled sensors. It connects to ESP32-based soil probes to collect real-time data (moisture, temperature, pH, EC, NPK, light, air conditions) and provides AI-powered insights for crop management. The app works offline and supports English, Hindi, and Bengali. It helps farmers make data-driven decisions to improve soil health and crop yields.

display image

Components Used

ESP32-C6-DEVKITC (Espressif Systems)
1
NPK sensor 7 in 1
1
DHT 11 Sensor
Collects local environmental telemetry.
1
LDR module
1
TTL to RS485 Converter
1
LiIon battery
1
LiIon Battery case
1
Liion Charging module
1
Description

 

AgroSage: Smart Soil Monitoring for Modern Farmers

 

 

The Challenge

Farmers often lack real-time, actionable soil data. Traditional methods are slow, expensive, and don’t provide immediate insights. Small-scale farmers need an affordable, easy-to-use tool to monitor soil health and make timely decisions.

The Problem:

  • No real-time soil data
  • Expensive lab testing
  • Language barriers
  • Limited internet in rural areas
  • No actionable recommendations

 

The Solution:

AgroSage — a mobile app that connects to Bluetooth-enabled soil sensors, works offline, provides AI insights, and supports multiple languages.

 

Project Goals & End Result

What We Achieved:

  • Real-time soil monitoring via Bluetooth
  • Offline-first functionality
  • AI-powered recommendations
  • Multi-language support (English, Hindi, Bengali)
  • Cross-platform (iOS & Android)
  • Cloud sync for data backup
  • Reading history and analytics

End Result:

A production-ready mobile app that helps farmers monitor soil health, get actionable insights, and improve crop yields.

 

Step-by-Step Build Process:

HARDWARE BUILD:

Check for components here - Digikey MyList 

Components Requires :

  1. ESP32-C6-DEVKITC (Espressif Systems)
  2. DHT 11 Sensor
  3. LDR Sensor
  4. NPK 7-1 Sensor
  5. RS0485 - TTL Converter
  6. Soldering Iron

 

Connect the parts with the circuit diagram provided below :-

 

 

After that open Arduino IDE, 
Choose Board as - ESP32C6 Dev Module
USB CDC on Boot - Enable


And Upload the following Sketch :-

/*
 * AgroSage ESP32-C6 BLE Server + RGB Status - Ayan Ghosh
 *
 * States (RGB LED on GPIO 8):
 * - Bluetooth NOT connected: RED
 * - Bluetooth connected & IDLE/IN_SOIL: BLUE
 * - MEASURING: YELLOW
 * - PROCESSING: PURPLE
 * - RESULT_READY: GREEN
 * - ERROR_STATE: WHITE
 */
#include <DHT11.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <ArduinoJson.h>
#include <Adafruit_NeoPixel.h>
#include <SoftwareSerial.h>


// BLE Service and Characteristic UUIDs (must match the app)
#define SERVICE_UUID           "12345678-1234-5678-1234-56789abc0000"
#define STATUS_CHAR_UUID       "12345678-1234-5678-1234-56789abc0001"
#define COMMAND_CHAR_UUID      "12345678-1234-5678-1234-56789abc0002"
#define RESULT_CHAR_UUID       "12345678-1234-5678-1234-56789abc0003"

// Device name (must start with "AgroSage-")
#define DEVICE_NAME "AgroSage-001"

// Onboard RGB NeoPixel (ESP32-C6-DevKitC)
#define RGB_PIN    8
#define RGB_COUNT  1

// Define the RX and TX pins for Software Serial 2
#define NPK_RX 22
#define NPK_TX 23

const byte temp[] = {0X01,0X03, 0X00,0X12,0X00,0X02,0x64,0x0E};
const byte soil_ph[] = {0x01,0x03, 0x00, 0x06, 0x00, 0x01, 0x64, 0x0B};
const byte ec[] = {0x01,0x03, 0x00, 0x15, 0x00, 0x01, 0x95, 0xCE};
const byte nitro[] = { 0x01, 0x03, 0x00, 0x1E, 0x00, 0x01, 0xE4, 0x0C };
const byte phos[] = { 0x01, 0x03, 0x00, 0x1f, 0x00, 0x01, 0xb5, 0xcc };
const byte pota[] = { 0x01, 0x03, 0x00, 0x20, 0x00, 0x01, 0x85, 0xc0 };

SoftwareSerial mod(NPK_RX,NPK_TX);////// TX AND RX

Adafruit_NeoPixel rgb(RGB_COUNT, RGB_PIN, NEO_GRB + NEO_KHZ800);

// Status states
enum ProbeStatus {
  IDLE,
  IN_SOIL,
  MEASURING,
  PROCESSING,
  RESULT_READY,
  ERROR_STATE
};

// Forward declarations
void setStatus(ProbeStatus status);
void performMeasurement();
void updateLed();           // update LED based on status + connection state
void setLedColor(uint8_t r, uint8_t g, uint8_t b);

const int LDR_PIN = 0;   // change if needed
// ADC / Voltage settings
const float VCC = 3.3;
const int ADC_MAX = 4095;
// Voltage divider fixed resistor on the module (likely 10k)
const float R_FIXED = 10000.0; // ohms
// LDR characteristics (tune for accuracy)
const float GAMMA = 0.7;   // slope
const float RL10  = 50.0;  // kΩ at 10 lux

float humidity_val = 0;
float temperature_val = 0;

DHT11 dht11(20);

// Global variables
BLEServer* pServer = NULL;
BLECharacteristic* pStatusCharacteristic = NULL;
BLECharacteristic* pCommandCharacteristic = NULL;
BLECharacteristic* pResultCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
ProbeStatus currentStatus = IDLE;

// =========================
// Server callbacks
// =========================
class MyServerCallbacks: public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    deviceConnected = true;
    Serial.println("Client connected");
    updateLed();  // update LED on connection change
  }

  void onDisconnect(BLEServer* pServer) {
    deviceConnected = false;
    Serial.println("Client disconnected");
    updateLed();  // update LED on connection change
  }
};

// =========================
// Command characteristic callback
// =========================
class CommandCallbacks: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pCharacteristic) {
    // On this core getValue() returns an Arduino String
    String value = pCharacteristic->getValue();

    if (value.length() > 0) {
      String command = value;
      Serial.println("Received command: " + command);

      if (command == "HELLO_V1") {
        Serial.println("Handshake received");
        setStatus(IDLE);
      }
      else if (command == "MEASURE_NOW") {
        Serial.println("Starting measurement...");
        performMeasurement();
      }
    }
  }
};

void setup() {
  mod.begin(9600);
  Serial.begin(115200);
  delay(2000);
  Serial.println("\n=== AgroSage ESP32-C6 Starting ===");

  // --- RGB LED init ---
  rgb.begin();
  rgb.setBrightness(60);  // 0-255
  rgb.show();             // turn off initially
  setLedColor(255, 0, 0); // red while booting / not connected

  // Initialize BLE
  BLEDevice::init(DEVICE_NAME);

  // Create BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create Status Characteristic (Notify + Read)
  pStatusCharacteristic = pService->createCharacteristic(
                            STATUS_CHAR_UUID,
                            BLECharacteristic::PROPERTY_READ |
                            BLECharacteristic::PROPERTY_NOTIFY
                          );
  pStatusCharacteristic->addDescriptor(new BLE2902());

  // Create Command Characteristic (Write)
  pCommandCharacteristic = pService->createCharacteristic(
                             COMMAND_CHAR_UUID,
                             BLECharacteristic::PROPERTY_WRITE |
                             BLECharacteristic::PROPERTY_WRITE_NR
                           );
  pCommandCharacteristic->setCallbacks(new CommandCallbacks());

  // Create Result Characteristic (Notify + Read)
  pResultCharacteristic = pService->createCharacteristic(
                            RESULT_CHAR_UUID,
                            BLECharacteristic::PROPERTY_READ |
                            BLECharacteristic::PROPERTY_NOTIFY
                          );
  pResultCharacteristic->addDescriptor(new BLE2902());

  // Start the service
  pService->start();

  // Start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();

  Serial.println("BLE Service started");
  Serial.println("Device Name: " + String(DEVICE_NAME));
  Serial.println("Waiting for a client connection...");

  setStatus(IDLE);
}

void loop() {
  // Handle disconnecting
  if (!deviceConnected && oldDeviceConnected) {
    delay(500);
    pServer->startAdvertising();
    Serial.println("Start advertising again");
    oldDeviceConnected = deviceConnected;
  }

  // Handle connecting
  if (deviceConnected && !oldDeviceConnected) {
    oldDeviceConnected = deviceConnected;
  }
  delay(100);
}

// =========================
// LED helper functions
// =========================

void setLedColor(uint8_t r, uint8_t g, uint8_t b) {
  rgb.setPixelColor(0, rgb.Color(r, g, b));
  rgb.show();
}

void updateLed() {
  // 1. If Bluetooth is not connected → RED
  if (!deviceConnected) {
    setLedColor(255, 0, 0);  // red
    return;
  }

  // 2. If connected: color based on currentStatus
  switch (currentStatus) {
    case IDLE:
    case IN_SOIL:
      // Connected but idle / probe in soil → BLUE
      setLedColor(0, 0, 255);    // blue
      break;

    case MEASURING:
      // Measuring → YELLOW
      setLedColor(255, 150, 0);  // yellow/orange
      break;

    case PROCESSING:
      // Processing → PURPLE
      setLedColor(150, 0, 150);  // purple
      break;

    case RESULT_READY:
      // Result ready → GREEN
      setLedColor(0, 255, 0);    // green
      break;

    case ERROR_STATE:
      // Error → WHITE
      setLedColor(255, 255, 255); // white
      break;
  }
}

// =========================
// Set and notify status
// =========================
void setStatus(ProbeStatus status) {
  currentStatus = status;

  String statusStr;
  switch(status) {
    case IDLE:
      statusStr = "IDLE";
      break;
    case IN_SOIL:
      statusStr = "IN_SOIL";
      break;
    case MEASURING:
      statusStr = "MEASURING";
      break;
    case PROCESSING:
      statusStr = "PROCESSING";
      break;
    case RESULT_READY:
      statusStr = "RESULT_READY";
      break;
    case ERROR_STATE:
      statusStr = "ERROR";
      break;
  }

  Serial.println("Status: " + statusStr);

  // BLE status notify
  if (deviceConnected && pStatusCharacteristic != nullptr) {
    pStatusCharacteristic->setValue(statusStr.c_str());
    pStatusCharacteristic->notify();
  }

  // Update LED for this status
  updateLed();
}

// =========================
// Perform measurement and send results
// =========================
void performMeasurement() {
  if (!deviceConnected) {
    Serial.println("No client connected, skipping measurement.");
    return;
  }

  // Update status: IN_SOIL
  setStatus(IN_SOIL);
  delay(1000);

  // Update status: MEASURING
  setStatus(MEASURING);
  delay(2000);


  int temperature = 0;
  int humidity = 0;
  int result = 0;
  while (temperature == 0 && humidity == 0) {
    result = dht11.readTemperatureHumidity(temperature, humidity);
    delay(500); // small delay to avoid rapid retries
  }
  delay(250);

  // Read sensors (simulated values for now)
  readSoilMoistureAndTemperature();
  delay(250);
  float ph           = readPH();
  delay(250);
  float ec           = readEC();
  delay(250);
  float airTemp      = (float) temperature;
  delay(250);
  float airHumidity  = (float) humidity;
  delay(250);
  float light        = readLight();
  delay(250);
  float n            = readNitrogen();
  delay(250);
  float p            = readPhosphorus();
  delay(250);
  float k            = readPotassium();
  delay(250);

  // Update status: PROCESSING
  setStatus(PROCESSING);
  delay(250);

  // Create JSON object
  DynamicJsonDocument doc(1024);
  doc["ts"]     = millis() / 1000;
  doc["dev_id"]     = DEVICE_NAME;
  doc["soil_moist"] = humidity_val;
  doc["soil_temp"]     = temperature_val;
  doc["ph"]            = ph;
  doc["ec"]            = ec;
  doc["air_temp"]      = airTemp;
  doc["air_hum"]  = airHumidity;
  doc["light"]         = light;
  doc["n"]             = n;
  doc["p"]             = p;
  doc["k"]             = k;

  // Serialize JSON to string
  String jsonString;
  serializeJson(doc, jsonString);

  Serial.println("Sending result:");
  Serial.println(jsonString);

  // Send result via BLE
  if (deviceConnected && pResultCharacteristic != nullptr) {
    pResultCharacteristic->setValue(jsonString.c_str());
    pResultCharacteristic->notify();
  }
  // Update status: RESULT_READY
  setStatus(RESULT_READY);
  delay(500);
  // Return to IDLE
  setStatus(IDLE);
}

// ============================================
// SENSOR READING FUNCTIONS (SIMULATED)
// Replace these with actual sensor readings
// ============================================

void readSoilMoistureAndTemperature() {
  byte receivedData[9];
  delay(10);
  if(mod.write(temp,sizeof(temp))==8){
    mod.readBytes(receivedData,sizeof(receivedData));
    unsigned int humidity = (receivedData[3]<<8|receivedData[4]);
    unsigned int tempe = (receivedData[5]<<8|receivedData[6]);
    tempe = tempe/10;
    humidity = humidity/10;
    humidity_val = (float) humidity;
    temperature_val = (float) tempe;
  }
}

float readPH() {
  byte receivedData[9];
  delay(10);
  if(mod.write(soil_ph,sizeof(soil_ph))==8){
    mod.readBytes(receivedData,sizeof(receivedData));
    unsigned int ph_val_sensor = (receivedData[3]<<8|receivedData[4]);
    return (float) ph_val_sensor/100;
  }
}

float readEC() {
  byte receivedData[9];
  delay(10);
  if(mod.write(ec,sizeof(ec))==8){
    mod.readBytes(receivedData,sizeof(receivedData));
    unsigned int ec_soil = (receivedData[3]<<8|receivedData[4]);
    return (float) ec_soil;
  }
}

float readNitrogen() {
  byte receivedData[9];
  delay(10);
  if(mod.write(nitro,sizeof(nitro))==8){
    mod.readBytes(receivedData,sizeof(receivedData));
    unsigned int nitrogetn = (receivedData[3]<<8|receivedData[4]);
    return (float) nitrogetn;
  }
}

float readPhosphorus() {
  byte receivedData[9];
  delay(10);
  if(mod.write(phos,sizeof(phos))==8){
    mod.readBytes(receivedData,sizeof(receivedData));
    unsigned int phosphorus = (receivedData[3]<<8|receivedData[4]);
    return (float) phosphorus;
  }
}

float readPotassium() {
  byte receivedData[9];
  delay(10);
  if(mod.write(pota,sizeof(pota))==8){
    mod.readBytes(receivedData,sizeof(receivedData));
    unsigned int potassium = (receivedData[3]<<8|receivedData[4]);
    return (float) potassium;
  }
}

float readLight() {
  int adcValue = analogRead(LDR_PIN);
  float voltage = adcValue * (VCC / ADC_MAX);
  float R_ldr = R_FIXED * (voltage / (VCC - voltage));  // ohms
  // Lux approximation
  float lux = pow((RL10 * 1000.0 / R_ldr), (1.0 / GAMMA));
  return lux;
 
}

 

 

Open the Serial Monitor, Change the baud-rate to 115200 and you will able to see that the system has started it's bluetooth system. 
 

 

Now we are ready with the Hardware part, lets move ahead with the software part

Software build:

Required Software

  • Node.js >= 20.19.4
  • npm >= 10.0.0
  • Expo CLI (installed via npm or npx)
  • EAS CLI (for production builds): npm install eas-cli --save-dev

Platform-Specific Requirements

iOS Development

  • macOS (for iOS builds)
  • Xcode 14+ with Command Line Tools
  • CocoaPods (installed via sudo gem install cocoapods)
  • Apple Developer Account (for device testing and App Store distribution)

Android Development

  • Android Studio with Android SDK
  • Java Development Kit (JDK) 11+
  • Android SDK API level 33+
  • Gradle (included with Android Studio)

Hardware Requirements

  • iOS: iPhone 6s or later (for ExecuTorch Neural Engine support, iPhone 8+ recommended)
  • Android: Android 6.0 (API 23) or later
  • Bluetooth: BLE-capable device

 Installation

1. Clone the Repository

git clone https://github.com/Ayanghosh-agno/agrosage-main/tree/main cd agrosage-main

 

2. Install Dependencies

npm install

 

3. Environment Configuration

Create a .env file in the root directory:

EXPO_PUBLIC_SUPABASE_URL=your_supabase_project_url EXPO_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key

 

Note: For production builds, these values are hardcoded in the source code. You can also configure them via EAS Secrets:

eas secret:create --scope project --name EXPO_PUBLIC_SUPABASE_URL --value your_url eas secret:create --scope project --name EXPO_PUBLIC_SUPABASE_ANON_KEY --value your_key

 

4. iOS Dependencies (macOS only)

cd ios pod install cd ..

 

5. Metro Bundler Configuration

The project includes metro.config.js configured to handle .pte model files. No additional setup needed.

⚙️ Configuration

App Configuration (app.json)

Key configuration options:

  • App Name: "AgroSage"
  • Version: 1.0.1
  • Bundle Identifiers:
    • iOS: com.anonymous.agrosage
    • Android: com.anonymous.agrosage
  • Icons & Splash: Configured to use ./assets/images/icon.png
  • Permissions: Bluetooth, Location, Internet

Supabase Setup

  1. Create Supabase Project
    • Visit supabase.com
    • Create a new project
    • Note your project URL and anon key
  2. Deploy Edge Function

    cd supabase/functions/clever-task supabase functions deploy clever-task

     

  3. Update Environment Variables
    • Add credentials to .env file
    • Or configure via EAS Secrets for production builds

ExecuTorch Model Setup

The app includes a pre-trained ExecuTorch model for on-device AI inference. The model is located at:

  • assets/models/agrosage_insights.pte

To train and export a new model:

  1. Navigate to models/python/
  2. Install Python dependencies: pip install -r requirements.txt
  3. Train the model: python train_model.py
  4. Export to ExecuTorch: python export_executorch.py
  5. Copy the generated .pte file to assets/models/

🏃 Running the App

Development Mode

Start the development server:

npm run dev

 

Run on iOS Simulator/Device:

npm run ios # or npx expo run:ios

 

Run on Android Emulator/Device:

npm run android # or npx expo run:android

 

Important Notes:

  • BLE functionality requires a development build. Expo Go does not support native BLE modules.
  • For first-time setup, run npx expo prebuild to generate native projects.

Creating a Development Build

iOS:

npx expo run:ios

 

Android:

npx expo run:android

 

This will:

  1. Generate native iOS/Android projects
  2. Install native dependencies
  3. Build and launch the app

📦 Building for Production

Using EAS Build (Recommended)

EAS Build is the recommended way to create production builds.

  1. Install EAS CLI (if not already installed)

    npm install eas-cli --save-dev

     

  2. Login to Expo

    npx eas login

     

  3. Configure Build (if not already configured)

    npx eas build:configure

     

  4. Build for iOS

    npx eas build --platform ios --profile production

     

  5. Build for Android

    npx eas build --platform android --profile production

     

Environment Variables for Production

Configure environment variables via EAS Secrets:

eas secret:create --scope project --name EXPO_PUBLIC_SUPABASE_URL --value your_url --type string eas secret:create --scope project --name EXPO_PUBLIC_SUPABASE_ANON_KEY --value your_key --type string

 

Or add them to eas.json:

{  "build": {    "production": {      "env": {        "EXPO_PUBLIC_SUPABASE_URL": "your_url",        "EXPO_PUBLIC_SUPABASE_ANON_KEY": "your_key"      }    }  } }

 

Local Builds

iOS:

  1. Run npx expo prebuild
  2. Open ios/AgroSage.xcworkspace in Xcode
  3. Select your development team
  4. Product → Archive (for App Store) or Build (for ad-hoc)

Android:

  1. Run npx expo prebuild
  2. Open project in Android Studio
  3. Build → Generate Signed Bundle / APK
  4. Follow the signing wizard

📁 Project Structure

agrosage-main/
├── app/                          # Expo Router pages
│   ├── index.tsx                # Home screen (main dashboard)
│   ├── history/                  # Reading history
│   │   ├── index.tsx            # History list view
│   │   └── [id].tsx             # Reading detail view
│   └── settings/                 # Settings
│       └── index.tsx            # Settings screen
├── assets/                       # Static assets
│   ├── images/                   # Images and icons
│   │   └── icon.png             # App icon
│   └── models/                   # AI model files
│       └── agrosage_insights.pte # ExecuTorch model
├── src/
│   ├── api/                      # API clients
│   │   ├── client.ts            # Base API client
│   │   ├── insights.ts          # AI insights API (HTTP + ExecuTorch)
│   │   └── readings.ts           # Readings API
│   ├── ble/                      # Bluetooth functionality
│   │   └── bleClient.ts          # BLE client implementation
│   ├── components/               # Reusable UI components
│   │   ├── InsightsCard.tsx     # AI insights display
│   │   ├── MetricCard.tsx       # Metric display card
│   │   ├── StatusBadge.tsx      # Status indicator
│   │   ├── HealthSummary.tsx    # Data summary component
│   │   └── PrimaryButton.tsx    # Primary action button
│   ├── context/                  # React Context providers
│   │   ├── BLEContext.tsx        # BLE state management
│   │   ├── LanguageContext.tsx   # Language state & translation
│   │   └── ReadingContext.tsx    # Reading state management
│   ├── i18n/                     # Internationalization
│   │   └── translations.ts       # Translation dictionary (EN/HI/BN)
│   ├── services/                 # Utility services
│   │   ├── executorchInsights.ts # ExecuTorch AI service
│   │   ├── onDeviceInsights.ts   # Rule-based fallback
│   │   └── locationService.ts    # GPS location service
│   ├── storage/                  # Local storage
│   │   └── historyStorage.ts     # Reading history persistence
│   └── types/                    # TypeScript definitions
│       └── index.ts              # Type definitions
├── models/                       # AI model training
│   └── python/                   # Python scripts
│       ├── train_model.py        # Model training script
│       ├── export_executorch.py  # ExecuTorch export script
│       └── requirements.txt     # Python dependencies
├── supabase/                     # Supabase configuration
│   └── functions/
│       └── clever-task/          # Edge Function for AI insights
│           └── index.ts          # Function implementation
├── ios/                          # iOS native code
├── android/                      # Android native code
├── app.json                      # Expo configuration
├── package.json                  # Dependencies
├── tsconfig.json                 # TypeScript configuration
├── metro.config.js               # Metro bundler config
└── eas.json                      # EAS build configuration

 

API & Backend

Supabase Edge Function

The clever-task Edge Function processes sensor data and returns AI insights.

Endpoint: POST /functions/v1/clever-task

Request Body:

{  "sensorData": {    "timestamp": 1732105932000,    "atmoTemp": 28.5,    "humidity": 65.2,    "light": 450,    "ec": 1.2,    "soilTemp": 24.3,    "moisture": 38.7,    "n": 40.5,    "p": 12.3,    "k": 30.1,    "ph": 6.5  } }

 

Response Format:

{  "success": true,  "recommendations": [    {      "type": "practice",      "title": "Increase Irrigation",      "description": "Soil moisture is below optimal levels...",      "priority": "high",      "confidence": 85,      "actionable": true,      "reasoning": "Current soil moisture at 38.7% is below optimal range..."    }  ],  "timestamp": "2024-11-20T12:00:00Z",  "source": "watsonx" | "fallback" }

 

On-Device AI (ExecuTorch)

The app includes an ExecuTorch model for on-device inference on iOS devices:

  • Model Format: .pte (ExecuTorch portable format)
  • Input: 10 sensor values (moisture, soilTemp, ph, ec, atmoTemp, humidity, light, n, p, k)
  • Output: 40-dimensional feature vector representing insight probabilities
  • Inference Time: ~2ms on iPhone 14 Pro Max
  • Hardware Acceleration: Apple Neural Engine

🌍 Internationalization

AgroSage provides comprehensive multi-language support with offline translation.

Supported Languages

  • English (en) - Default
  • Hindi (hi) - हिन्दी
  • Bengali (bn) - বাংলা

Translation Coverage

  • All UI elements and labels
  • AI insight titles and descriptions
  • Summary messages
  • Priority labels (High, Medium, Low)
  • Reasoning messages (WHY section)
  • Error messages and alerts
  • Settings and navigation

Language Selection

  • Accessible from Settings screen
  • Instant UI update on language change
  • Persistent across app restarts
  • Automatic re-translation of ExecuTorch insights when language changes

 

Development

Type Checking

npm run typecheck

 

Linting

npm run lint

 

Regenerating Native Code

npx expo prebuild --clean

 

Debugging

Enable Debug Logs:

  • ExecuTorch inference logs are automatically enabled
  • BLE connection logs in bleClient.ts
  • API request/response logs in insights.ts

Common Debug Commands:

# Clear Metro bundler cache npx expo start --clear # Reset iOS simulator xcrun simctl erase all # Clear Android build cache cd android && ./gradlew clean && cd ..

 

Permissions

Android

  • BLUETOOTH - Connect to BLE devices
  • BLUETOOTH_SCAN - Scan for BLE devices (Android 12+)
  • BLUETOOTH_CONNECT - Connect to BLE devices (Android 12+)
  • ACCESS_FINE_LOCATION - Required for BLE scanning
  • ACCESS_COARSE_LOCATION - Required for BLE scanning
  • INTERNET - Cloud sync and AI insights

iOS

  • NSBluetoothAlwaysUsageDescription - BLE connectivity
  • NSBluetoothPeripheralUsageDescription - BLE peripheral mode
  • NSLocationWhenInUseUsageDescription - Location for GPS coordinates

🐛 Troubleshooting

BLE Connection Issues

  • Device not found: Ensure device name starts with "AgroSage-"
  • Connection fails: Check Bluetooth is enabled and device is in range
  • Data not received: Verify ESP32 is sending data in correct JSON format
  • Fragmented data: App automatically handles fragmented JSON packets

Build Issues

  • Metro bundler errors: Run npx expo start --clear
  • iOS pod install fails: Update CocoaPods: sudo gem install cocoapods
  • Android build fails: Clean build: cd android && ./gradlew clean
  • Dependency conflicts: Delete node_modules and package-lock.json, then npm install

AI Insights Issues

  • ExecuTorch model not loading: Ensure metro.config.js includes .pte in assetExts
  • Model inference fails: Check model file exists at assets/models/agrosage_insights.pte
  • HTTP insights fail: Verify Supabase Edge Function is deployed and .env has correct credentials
  • Network errors: Check internet connection and Supabase project status

Language/Translation Issues

  • Translations not updating: Restart the app after changing language
  • Missing translations: Check src/i18n/translations.ts for all required keys

 

App Screenshots :- 

 

  • App Opening Page

 

  • Device searching page

 

  • List of Agrosage device

 

  • Connect Device with Measure Now button

 

  • Soil and Environment Insights upon clicking button

 

  • Setting Page

 

  • AI Insights using ARM AI with trained model

 

  • Multi-Lingual Capability

 

 

HARDWARE RGB INDICATION :

  • Device Ready to connect with mobile

 

  • Device connected with mobile and ready to measure

 

  • Device measuring soil data

 

  • Measuring Done

 

  • Sending to the app

 

VIDEO DEMO:

Codes

Downloads

3D Case Download
Agrosage Circuit Diagram Download
Agrosage Document Download
Comments
Ad