/*********

  Sends HMC5883 readings to a Web Server on the local WiFi net - Updates every 2 s
  (see unsigned long timerDelay = 2000;)
  Uses the ESP8266 ESP-12 board
  See https://televideo.ws/index.php/wifi-magnetic-compass-2 for the schematics

  The HMC5883 is a magnetic sensor reading the Earth's magnetic field (so it's a compass)
  The model used here is from Olimex. Clones could need different libraries
  
  March 2025
  
  Adapted by Giovanni Carboni IZ5PQT from
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp8266-nodemcu-web-server-sent-events-sse/
  
  See also 
  https://televideo.ws/index.php/wifi-magnetic-compass
  for a device based on the MKR WIFI 1010 board
    
*********/

#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>

char text0[3];
// Replace with your network credentials
const char* ssid = "xxxxxx";
const char* password = "yyyyyyyyyy";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

// Create an Event Source on /events
AsyncEventSource events("/events");

/* Assign a unique ID to this sensor at the same time */
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(99999);

// Timer variables
unsigned long lastTime = 0;  
unsigned long timerDelay = 2000;

 
float heading;
/*
 * 
 float temperature;
float humidity;
float pressure;

*/
void displaySensorDetails(void)
{
  sensor_t sensor;
  mag.getSensor(&sensor);
  Serial.println("------------------------------------");
  Serial.print  ("Sensor:       "); Serial.println(sensor.name);
  Serial.print  ("Driver Ver:   "); Serial.println(sensor.version);
  Serial.print  ("Unique ID:    "); Serial.println(sensor.sensor_id);
  Serial.print  ("Max Value:    "); Serial.print(sensor.max_value); Serial.println(" uT");
  Serial.print  ("Min Value:    "); Serial.print(sensor.min_value); Serial.println(" uT");
  Serial.print  ("Resolution:   "); Serial.print(sensor.resolution); Serial.println(" uT");
  Serial.println("------------------------------------");
  Serial.println("");
  delay(500);
}

// Initialize WiFi
void initWiFi() {
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    Serial.print("Connecting to WiFi ..");
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print('.');
        delay(1000);
    }
    Serial.println(WiFi.localIP());
}
float getheading() {
  /* Get a new sensor event */
  sensors_event_t event;
  mag.getEvent(&event);

  /* Display the results (magnetic vector values are in micro-Tesla (uT)) */
  // Serial.print("X: "); Serial.print(event.magnetic.x); Serial.print("  ");
  // Serial.print("Y: "); Serial.print(event.magnetic.y); Serial.print("  ");
  // Serial.print("Z: "); Serial.print(event.magnetic.z); Serial.print("  ");Serial.println("uT");

  // Hold the module so that Z is pointing 'up' and you can measure the heading with x&y
  // Calculate heading when the magnetometer is level, then correct for signs of axis.
  float heading = atan2(event.magnetic.y, event.magnetic.x);

  // Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.
  // Find yours here: http://www.magnetic-declination.com/
  // Mine is: -13* 2' W, which is ~13 Degrees, or (which we need) 0.22 radians
  // If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
  float declinationAngle = 0.22;
  heading += declinationAngle;

  // Correct for when signs are reversed.
  if (heading < 0)
    heading += 2 * PI;

  // Check for wrap due to addition of declination.
  if (heading > 2 * PI)
    heading -= 2 * PI;

  // Convert radians to degrees for readability.
  float headingDegrees = heading * 180 / M_PI;
  float result = headingDegrees;
  return result;
}
String processor(const String& var){
//  getSensorReadings();
// !! float heading = getheading();
    float zz = getheading();
    
  sprintf(text0, "%3d", (int)zz);
  //Serial.println(var);
  if(var == "HEADING"){
// !!   return String(heading);
  return String(text0);
  }
  
  return String();
}

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    p { font-size: 1.2rem;}
    body {  margin: 0;}
    .topnav { overflow: hidden; background-color: #6B8E23; color: white; font-size: 1rem; }
    .content { padding: 20px; }
    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }
    .cards { max-width: 400px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
    .reading { font-size: 1.4rem; }
  </style>
</head>
<body>
  <div class="topnav">
    <h1>WiFi COMPASS by IZ5PQT</h1>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card">
        <p><i class="fas fa-thermometer-half" style="color:#059e8a;"></i> HEADING</p><p><span class="reading"><span id="temp">%HEADING%</span> &deg;</span></p>
      <p><img src="https://televideo.ws/images/compass.png" width="128" alt="Girl in a jacket" ></p>
      </div>
    </div>
  </div>
<script>
if (!!window.EventSource) {
 var source = new EventSource('/events');
 
 source.addEventListener('open', function(e) {
  console.log("Events Connected");
 }, false);
 source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
 }, false);
 
 source.addEventListener('message', function(e) {
  console.log("message", e.data);
 }, false);

 source.addEventListener('compass', function(e) { 
  console.log("compass", e.data);
  document.getElementById("temp").innerHTML = e.data;
 }, false);
}
</script>
</body>
</html>)rawliteral";

void setup() {
  Serial.begin(115200);
  initWiFi();
  Serial.println("HMC5883 Magnetometer Test with Web Server"); Serial.println("");
  // Initialize the I2C
  // SDA to GP0, SCL to GP2
  // SDA white SCL yellow
/* Initialise the sensor */
// Initialize the I2C
  // for ESP-01 SDA to GP0, SCL to GP2 (needs initialize)
  // for ESP-12 SDA to GPIO4 (D2), SCL to GPI05 (D1) (default)
  if (!mag.begin())
  {
    /* There was a problem detecting the HMC5883 ... check your connections */
    Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
    while (1);
  }

// Display some basic information on this sensor 
  displaySensorDetails(); 
   
// Handle Web Server
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

// Handle Web Server Events
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
    }
    // send event with message "hello!", id current millis
    // and set reconnect delay to 1 second
    client->send("hello!", NULL, millis(), 10000);
  });
  server.addHandler(&events);
  server.begin();
}

void loop() {
  if ((millis() - lastTime) > timerDelay) {
    heading = getheading();
    
    Serial.printf("Heading = %.2f º \n", heading);
    Serial.println();
// round heading to nearest integer (we don't need decimal resolution)
    sprintf(text0, "%3d", (int)(heading+0.5));
      
    // Send Events to the Web Server with the Sensor Readings
    events.send("ping",NULL,millis());
//!!    events.send(String(heading).c_str(),"compass",millis());
//!!!    events.send(String(text0).c_str(),"compass",millis()); 
    events.send(text0,"compass",millis()); 
    lastTime = millis();
  }
}
