This guide explains how to connect your Frontend (Mobile/Web) and IoT devices to the AgniSutra Backend.
Since the backend runs on your local machine, you cannot use localhost from other devices (like a phone or ESP32). You must use your PC's Local IP Address.
- Find your IP: Run
ipconfig(Windows) orifconfig(Mac/Linux) in a terminal. Look for IPv4 Address (e.g.,192.168.1.5). - Backend URL:
http://<YOUR_IP>:8000
1. Login
- Endpoint:
POST /auth/login - Content-Type:
application/x-www-form-urlencoded - Body:
username=<email>&password=<password> - Response:
{"access_token": "...", "token_type": "bearer"}
Flutter Code (using Dio):
final dio = Dio();
final response = await dio.post(
'http://192.168.1.5:8000/auth/login',
data: {'username': email, 'password': password},
options: Options(contentType: Headers.formUrlEncodedContentType),
);
String token = response.data['access_token'];
// Save 'token' securely2. Register
- Endpoint:
POST /auth/register - Body (JSON):
{ "name": "Farmer John", "email": "john@example.com", "password": "securepassword" }
3. Logout
- Endpoint:
POST /auth/logout - Headers:
Authorization: Bearer <token> - Action: Call this API, then delete the token from your app's storage.
For all other endpoints (Profile, Yield Prediction, Chat), you must send the token in the header.
Header Format: Authorization: Bearer <your_access_token>
Example: Generic Authenticated Request
final response = await dio.get(
'http://192.168.1.5:8000/some/protected/route',
options: Options(headers: {'Authorization': 'Bearer $token'}),
);1. Get Profile
- Endpoint:
GET /auth/me - Headers:
Authorization: Bearer <token>
try {
final response = await dio.get(
'http://192.168.1.5:8000/auth/me',
options: Options(headers: {'Authorization': 'Bearer $token'}),
);
print(response.data);
// Response: { "id": 1, "name": "...", "profile_photo": "/static/uploads/...", ... }
} catch (e) {
print('Error fetching profile: $e');
}2. Update Profile
- Endpoint:
PUT /auth/me - Headers:
Authorization: Bearer <token> - Body: JSON with fields to update (name, city, latitude, longitude).
try {
final response = await dio.put(
'http://192.168.1.5:8000/auth/me',
data: {
"name": "New Name",
"city": "New City",
"latitude": 22.5,
"longitude": 85.2
},
options: Options(headers: {'Authorization': 'Bearer $token'}),
);
print('Profile updated: ${response.data}');
} catch (e) {
print('Error updating profile: $e');
}3. Upload Profile/Cover Photo
- Endpoint:
POST /auth/me/profile-photoor/auth/me/cover-photo - Headers:
Authorization: Bearer <token> - Body: Multipart Form Data with key
file.
// Pick file using image_picker package first
// File imageFile = ...;
String fileName = imageFile.path.split('/').last;
FormData formData = FormData.fromMap({
"file": await MultipartFile.fromFile(imageFile.path, filename: fileName),
});
try {
final response = await dio.post(
'http://192.168.1.5:8000/auth/me/profile-photo', // or /auth/me/cover-photo
data: formData,
options: Options(headers: {'Authorization': 'Bearer $token'}),
);
print('Photo uploaded: ${response.data}');
// The response contains the new URL, e.g., "profile_photo": "/static/uploads/..."
// Construct full URL: http://192.168.1.5:8000/static/uploads/...
} catch (e) {
print('Error uploading photo: $e');
}- Endpoint:
POST /krishi/chat - Body:
{"message": "How to grow wheat?", "history": []} - Response:
{"response": "To grow wheat..."}
Your IoT device will send sensor data to the backend.
- URL:
http://<YOUR_IP>:8000/iot/update - Method:
POST - Content-Type:
application/json
{
"user_id": 1,
"moisture": 45.5,
"nitrogen": 120,
"phosphorus": 40,
"potassium": 60,
"temperature": 28.5,
"humidity": 65.0
}Note: user_id should be hardcoded or configured on the device for the specific farmer.
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
// ======================= USER CONFIGURATION =======================
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// Updated URL: Ensure this matches your FastAPI endpoint
const char* serverUrl = "http://192.168.1.5:8000/iot/update";
#define MOISTURE_PIN 34 // Analog Pin (AO)
#define MOISTURE_POWER 15 // Digital Pin (VCC)
// Calibration for Resistive Sensor
const int dryValue = 4095;
const int wetValue = 1728;
void setup() {
Serial.begin(115200);
pinMode(MOISTURE_POWER, OUTPUT);
digitalWrite(MOISTURE_POWER, LOW);
WiFi.begin(ssid, password);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWi-Fi Connected!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}
int readSoilMoisture() {
digitalWrite(MOISTURE_POWER, HIGH);
delay(100);
int val = analogRead(MOISTURE_PIN);
digitalWrite(MOISTURE_POWER, LOW);
return val;
}
void loop() {
// 1. Read Moisture
int rawMoisture = readSoilMoisture();
int moisturePct = map(rawMoisture, dryValue, wetValue, 0, 100);
moisturePct = constrain(moisturePct, 0, 100);
Serial.print("Moisture: "); Serial.print(moisturePct); Serial.println("%");
// 2. Simulate NPK Values (Randomized for Demo)
float nitrogen = random(20, 50) + (random(0, 100) / 10.0);
float phosphorus = random(10, 40) + (random(0, 100) / 10.0);
float potassium = random(30, 60) + (random(0, 100) / 10.0);
// 3. Send Data via HTTP POST
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(serverUrl);
http.addHeader("Content-Type", "application/json");
// JSON Payload
StaticJsonDocument<200> doc;
// ⚠️ IMPORTANT: Use the unique Device ID (e.g., MAC Address)
// This must match the 'device_id' registered for the user in the database
doc["device_id"] = "A1:B2:C3:D4:E5:F6";
doc["moisture"] = moisturePct;
doc["nitrogen"] = nitrogen;
doc["phosphorus"] = phosphorus;
doc["potassium"] = potassium;
String requestBody;
serializeJson(doc, requestBody);
int httpResponseCode = http.POST(requestBody);
if (httpResponseCode > 0) {
String response = http.getString();
Serial.println("Server Response: " + response);
} else {
Serial.print("HTTP Error: "); Serial.println(httpResponseCode);
}
http.end();
} else {
Serial.println("Wi-Fi Disconnected!");
}
delay(5000); // Send every 5 seconds
}To see the data sent by the IoT device, the frontend must request it using the authenticated user's token.
- Endpoint:
GET /iot/my-logs - Headers:
Authorization: Bearer <token> - Response: List of sensor readings for the logged-in user.
- Endpoint:
GET /iot/latest - Headers:
Authorization: Bearer <token> - Response: The single most recent reading.
The backend broadcasts alerts as JSON. The frontend must parse this JSON and check if the user_id matches the logged-in user.
- URL:
ws://<YOUR_IP>:8000/ws/alerts
import 'dart:convert';
import 'package:web_socket_channel/web_socket_channel.dart';
void listenToAlerts(int myUserId) {
final channel = WebSocketChannel.connect(
Uri.parse('ws://192.168.1.5:8000/ws/alerts'),
);
channel.stream.listen((message) {
try {
final data = jsonDecode(message);
// Check if the alert is for THIS user
if (data['user_id'] == myUserId) {
List<dynamic> alerts = data['messages'];
String alertText = alerts.join("\n");
print("New Alert for Me: $alertText");
// Show Notification
}
} catch (e) {
print("Error parsing alert: $e");
}
});
}The backend automatically checks the sensor data every time it receives it (every 5 seconds). If moisture is low (< 30) or temperature is high (> 40), it broadcasts an alert immediately.
- URL:
ws://<YOUR_IP>:8000/ws/alerts
Add web_socket_channel: ^2.4.0 to pubspec.yaml.
import 'package:web_socket_channel/web_socket_channel.dart';
void listenToAlerts() {
// Replace with your PC's IP
final channel = WebSocketChannel.connect(
Uri.parse('ws://192.168.1.5:8000/ws/alerts'),
);
channel.stream.listen((message) {
print("New Alert Received: $message");
// Show a local notification or a SnackBar here
// Example: ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
}, onError: (error) {
print("WebSocket Error: $error");
});
}