Tuesday, December 2, 2025

MUD Three Mode operation Manual Automatic GPS

 Code for three mode operation:
/*

  3-Mode Headlight Controller
  - Manual mode (driver uses a toggle to pick high/low)
  - Auto mode (LDR switches beams automatically)
  - GPS mode (if inside geofence, force LOW beam)
  - If more than one mode is selected -> show "MULTIPLE MODES" and set LOW beam

  Libraries required:
   - TinyGPSPlus
   - LiquidCrystal_I2C
*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <TinyGPSPlus.h>
#include <SoftwareSerial.h>

// -------------------- USER CONFIG --------------------
// Set the geofence center (replace with your target coordinates)
const double GEOFENCE_LAT = 28.6139;   // example: New Delhi lat (replace)
const double GEOFENCE_LON = 77.2090;   // example: New Delhi lon (replace)
const double GEOFENCE_RADIUS_METERS = 2000.0; // radius in meters

// I2C address of LCD - change if your module uses different address (0x27 or 0x3F)
// 16 cols x 2 rows LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);

// GPS (SoftwareSerial)
static const uint8_t GPS_RX_PIN = 10; // connect to GPS TX
static const uint8_t GPS_TX_PIN = 11; // connect to GPS RX
SoftwareSerial gpsSerial(GPS_RX_PIN, GPS_TX_PIN);
TinyGPSPlus gps;

// Pin assignments
const int pinLDR = 2;          // LDR DO -> digital read
const int pinManualBeam = 3;   // Manual beam toggle (HIGH/LOW) - used only when Manual mode active
const int pinModeManual = 4;   // Mode toggle: Manual mode (SPST toggle)
const int pinModeAuto = 5;     // Mode toggle: Auto (LDR)
const int pinModeGPS = 6;      // Mode toggle: GPS mode

const int relayHighPin = 8;    // Relay IN1 - HIGH beam
const int relayLowPin  = 9;    // Relay IN2 - LOW beam

// Timing
unsigned long lastGpsCheck = 0;
const unsigned long GPS_CHECK_INTERVAL = 2000; // ms

// -------------------- SETUP --------------------
void setup() {
  Serial.begin(115200);
  gpsSerial.begin(9600); // many GPS modules use 9600
  lcd.init();
  lcd.backlight();

  // Pin modes
  pinMode(pinLDR, INPUT); // LDR module DO
  pinMode(pinManualBeam, INPUT_PULLUP); // using internal pull-up; switch to GND when ON

  // mode switches - using internal pullups (switch to GND when ON)
  pinMode(pinModeManual, INPUT_PULLUP);
  pinMode(pinModeAuto, INPUT_PULLUP);
  pinMode(pinModeGPS, INPUT_PULLUP);

  pinMode(relayHighPin, OUTPUT);
  pinMode(relayLowPin, OUTPUT);

  // Default: LOW beam ON
  digitalWrite(relayHighPin, LOW);
  digitalWrite(relayLowPin, HIGH);

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Headlight System");
  lcd.setCursor(0,1);
  lcd.print("Initializing...");
  delay(1200);
}

// -------------------- MAIN LOOP --------------------
void loop() {
  // read incoming GPS serial into TinyGPS parser
  while (gpsSerial.available()) {
    gps.encode(gpsSerial.read());
  }

  // read mode toggles (active LOW because of INPUT_PULLUP)
  bool modeManual = (digitalRead(pinModeManual) == LOW);
  bool modeAuto   = (digitalRead(pinModeAuto)   == LOW);
  bool modeGPS    = (digitalRead(pinModeGPS)    == LOW);

  int modesOn = (modeManual ? 1 : 0) + (modeAuto ? 1 : 0) + (modeGPS ? 1 : 0);

  // If more than one mode selected -> error state, force LOW beam
  if (modesOn > 1) {
    setLowBeam();
    lcdMultipleModes();
    return; // quickly go back to start of loop
  }

  // No mode selected -> default safe LOW beam
  if (modesOn == 0) {
    setLowBeam();
    lcdShow("MODE: NONE", "Beam: LOW");
    return;
  }

  // Exactly one mode selected
  if (modeManual) {
    handleManualMode();
  } else if (modeAuto) {
    handleAutoMode();
  } else if (modeGPS) {
    handleGPSMode();
  }
}

// -------------------- MODE HANDLERS --------------------
void handleManualMode() {
  // manualBeam toggle: using INPUT_PULLUP -> LOW = switched ON for "HIGH" maybe; choose mapping:
  // Assume: manualBeam switch ON = HIGH beam, OFF = LOW beam. Adjust as you prefer.
  bool manualHigh = (digitalRead(pinManualBeam) == LOW); // switch closed -> LOW -> treat as HIGH beam
  if (manualHigh) {
    setHighBeam();
    lcdShow("MODE: MANUAL", "Beam: HIGH");
  } else {
    setLowBeam();
    lcdShow("MODE: MANUAL", "Beam: LOW");
  }
}

void handleAutoMode() {
  // LDR DO: DO == LOW when bright light detected (incoming headlight)
  int ldrState = digitalRead(pinLDR);
  if (ldrState == LOW) {
    // bright light -> switch to LOW beam
    setLowBeam();
    lcdShow("MODE: AUTO(LDR)", "Beam: LOW");
  } else {
    // dark road -> HIGH beam
    setHighBeam();
    lcdShow("MODE: AUTO(LDR)", "Beam: HIGH");
  }
}

void handleGPSMode() {
  // Check GPS fix & position (do not block; check periodically)
  unsigned long now = millis();
  if (now - lastGpsCheck < GPS_CHECK_INTERVAL) {
    // avoid checking too fast; but still show previous status
    // We'll show last known position/beam state by calling evaluateGPS once per interval in next run
    // For simplicity, run evaluation now anyway if data available
  }
  lastGpsCheck = now;

  if (!gps.location.isValid()) {
    // No fix yet
    setLowBeam(); // safe default
    lcdShow("MODE: GPS", "No GPS Fix - LOW");
    return;
  }

  double lat = gps.location.lat();
  double lon = gps.location.lng();
  double dist = distanceMeters(lat, lon, GEOFENCE_LAT, GEOFENCE_LON);

  if (dist <= GEOFENCE_RADIUS_METERS) {
    // inside geofence -> force LOW beam
    setLowBeam();
    char line2[17];
    snprintf(line2, 17, "LOW in zone %dm", (int)dist);
    lcdShow("MODE: GPS", line2);
  } else {
    // outside geofence -> allow HIGH beam
    setHighBeam();
    char line2[17];
    snprintf(line2, 17, "Outside zone %dm", (int)dist);
    lcdShow("MODE: GPS", line2);
  }
}

// -------------------- RELAY CONTROL HELPERS --------------------
void setHighBeam() {
  // HIGH beam ON -> activate relayHighPin, deactivate relayLowPin
  digitalWrite(relayLowPin, LOW);
  digitalWrite(relayHighPin, HIGH);
}

void setLowBeam() {
  // LOW beam ON -> activate relayLowPin, deactivate relayHighPin
  digitalWrite(relayHighPin, LOW);
  digitalWrite(relayLowPin, HIGH);
}

// -------------------- LCD helpers --------------------
void lcdShow(const char* line1, const char* line2) {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(line1);
  lcd.setCursor(0,1);
  lcd.print(line2);
}

void lcdMultipleModes() {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("MULTIPLE MODES!");
  lcd.setCursor(0,1);
  lcd.print("Beam: LOW (safe)");
}

// -------------------- Utility: haversine-like distance (meters) --------------------
// double radians(double deg) {
//   return deg * 3.14159265358979323846 / 180.0;
// }

// double distanceMeters(double lat1, double lon1, double lat2, double lon2) {
//   // approximate haversine formula
//   const double R = 6371000.0; // Earth radius in meters
//   double dLat = radians(lat2 - lat1);
//   double dLon = radians(lon2 - lon1);
//   double a = sin(dLat/2.0)*sin(dLat/2.0) + cos(radians(lat1)) * cos(radians(lat2)) * sin(dLon/2.0)*sin(dLon/2.0);
//   double c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a));
//   return R * c;
// }

// -------------------- Utility: haversine-like distance (meters) --------------------
double toRadians(double deg) {
  return deg * 3.14159265358979323846 / 180.0;
}

double distanceMeters(double lat1, double lon1, double lat2, double lon2) {
  // approximate haversine formula
  const double R = 6371000.0; // Earth radius in meters
  double dLat = toRadians(lat2 - lat1);
  double dLon = toRadians(lon2 - lon1);

  double a = sin(dLat/2.0)*sin(dLat/2.0)
           + cos(toRadians(lat1)) * cos(toRadians(lat2))
           * sin(dLon/2.0)*sin(dLon/2.0);

  double c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a));
  return R * c;
}

Below I’ve given a complete, ready-to-upload Arduino sketch plus a detailed, line-by-line hardware wiring and pin-description section. The code implements:

  • 3 mode toggles (Manual / Auto (LDR) / GPS).

  • Manual mode: driver selects High/Low beam with a separate manual-beam toggle.

  • Auto (LDR) mode: LDR digital output switches beams automatically.

  • GPS mode: when inside a user-configured geofence (lat/lon + radius) beams force LOW.

  • If more than one mode toggle is ON at the same time → LCD shows "MULTIPLE MODES" and beam stays LOW.

I explain each connection and give alternatives (pull-ups/pull-downs), power notes, and how to set the GPS geofence.


1) Summary of pins (reference for wiring)

(Using Arduino UNO)

  • D2 — LDR digital output (DO) (from LDR module)

  • D3 — Manual Beam toggle switch input (driver chooses high/low)

  • D4 — Mode toggle: MANUAL (ON = manual mode selected)

  • D5 — Mode toggle: AUTO_LDR (ON = automatic LDR mode selected)

  • D6 — Mode toggle: GPS_MODE (ON = GPS mode selected)

  • D8 — Relay control for HIGH beam (Relay IN1)

  • D9 — Relay control for LOW beam (Relay IN2)

  • A4 — I2C SDA (LCD)

  • A5 — I2C SCL (LCD)

  • D10 — GPS RX (connect to GPS module TX)

  • D11 — GPS TX (connect to GPS module RX) — used by SoftwareSerial

Power / common:

  • 5V and GND — supply to Arduino, LDR module VCC, relay VCC (if relay module is 5V)

  • Bike 12V — to relay COM terminals (not to Arduino). Use a fuse on the +12V feed.


2) Wiring — step-by-step (textual diagram)

Important safety: disconnect battery while wiring. Use appropriate inline fuse on the bike +12V line (e.g., 1–2 A for headlight feed, check headlight rating). Always common ground the Arduino and the relay module (and GPS/LDR modules) to the bike ground if using a buck converter.

Modules & components

  • Arduino UNO / Nano

  • 2-channel relay module (5V)

  • LDR module (3-pin: VCC, GND, DO)

  • I2C LCD (e.g., 16x2 with PCF8574 backpack) — SDA/A4, SCL/A5

  • GPS module (e.g., NEO-6M) — TX/RX pins

  • 4 toggle switches:

    • 3 mode selection toggles: ManualMode, AutoMode (LDR), GPSMode.

    • 1 manual-beam toggle (High/Low) used when ManualMode is active.

  • Wiring, common ground, buck converter (12V→5V) if powering Arduino from bike 12V.

Wiring map (detailed)

Arduino ←→ LDR module

  • LDR VCC → Arduino 5V

  • LDR GND → Arduino GND

  • LDR DO → Arduino D2

Arduino ←→ Relay module

  • Relay VCC → Arduino 5V (or 5V regulator output)

  • Relay GND → Arduino GND

  • Relay IN1 → Arduino D8 (controls HIGH beam relay)

  • Relay IN2 → Arduino D9 (controls LOW beam relay)

Relay ←→ Bike headlight

  • Relay1 COM → +12V battery (through fuse)

  • Relay1 NO → Headlight HIGH beam wire (e.g., white). When relay ON, COM→NO supplies +12V to HIGH beam.

  • Relay2 COM → +12V battery (through fuse)

  • Relay2 NO → Headlight LOW beam wire (e.g., blue/yellow).

  • Headlight ground wire → Bike chassis ground (do not run headlight ground through relays).

Arduino ←→ I2C LCD

  • SDA → A4

  • SCL → A5

  • LCD VCC → 5V

  • LCD GND → GND

Arduino ←→ GPS module (SoftwareSerial)

  • GPS TX → Arduino D10 (GPS TX → SoftwareSerial RX)

  • GPS RX → Arduino D11 (GPS RX ← SoftwareSerial TX)

  • GPS VCC → 5V (or module-specific voltage)

  • GPS GND → GND

Mode toggle switches wiring (recommended using internal pull-ups)

  • One side of each toggle switch → Arduino input pin (D4, D5, D6 for mode toggles; D3 for manual-beam toggle).

  • Other side of each toggle → Arduino GND.

  • In code we use INPUT_PULLUP so the pin reads HIGH when switch is open (OFF), and LOW when switch is closed (ON). That simplifies wiring (no external resistors).

So wiring summary for toggles:

  • ManualMode toggle: one end → D4, other end → GND

  • Auto(LDR) toggle: one end → D5, other → GND

  • GPSMode toggle: one end → D6, other → GND

  • Manual Beam toggle (High/Low): one end → D3, other → GND

(If you prefer active HIGH toggles, wire to 5V and use external pull-down resistors or enable INPUT + externals — but INPUT_PULLUP is easiest.)


3) Behavior details & rules (clarify logic)

  • Mutual exclusivity rule: If more than one mode toggle is ON at the same time → the system shows "MULTIPLE MODES" on the LCD and sets beam to LOW (safe default).

  • Manual Mode ON alone: use Manual Beam toggle (D3) to set HIGH or LOW.

  • Auto LDR Mode ON alone: read LDR DO — when DO == LOW (bright incoming light) => set LOW beam; when DO == HIGH => set HIGH beam (same as your original logic).

  • GPS Mode ON alone: if GPS position is valid AND within user-configured radius of the defined geofence center => force LOW beam; else allow HIGH beam. (This lets you force low beam in certain areas like city limits; set lat/lon/radius in code.)

  • No mode selected (all OFF): default safe behavior — LOW beam ON.

  • LCD shows current mode and beam state and error messages (GPS no fix, multiple modes, etc).

No comments:

Post a Comment

MUD Three Mode operation Manual Automatic GPS

 Code for three mode operation: /*   3-Mode Headlight Controller   - Manual mode (driver uses a toggle to pick high/low)   - Auto mode (LDR...