#include <WiFi.h>
#include <WiFiClient.h>
#include <PubSubClient.h>
//---------------- Wifi configuration -----------------------------
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
// Defining the WiFi channel speeds up the connection:
#define WIFI_CHANNEL 6
int status = WL_IDLE_STATUS;     // the Wifi radio's status
//------ Stepper Motor Pins with A4988 and ESP32 Driver ----------- 
#define DIR_PIN  12
#define STEP_PIN 13
#define DELAY 2000
//----------- LDR configuration ---------------------------------
#define LDR_PIN 35  // sets the photoresistor read pin LDR
// Constants that correspond to the "gamma" and "rl10" attributes of the photoresistor.
const float GAMMA = 0.7;
const float RL10 = 33;
float lux = 0;
//----------------- Leds configuration --------------------------
const int ledCount = 10;  // Set number of LEDs in the bar graph
// Array of pins for LEDs in the bar graph (in ordem)
int ledPins[] = {
  15, 2, 4, 5, 18, 19, 21, 22, 23, 34
};
// Map the result to a range from lux to the number of LEDs
  int ledLevel = map(lux, 0, 10000, 0, ledCount);
//----------- MQTT configuration --------------------------------
const char* mqtt_server = "test.mosquitto.org";// MQTT broker
char* my_topic_SUB = "analogLux";// the chosen topic
char* my_topic_PUB = "analogLux";// the chosen topic
//---------- Wifi and MQTT configuration ------------------------
WiFiClient espClient;
PubSubClient client(espClient);
//------------------ Reading time setting -----------------------
int contador = 1;
char msg[30];
unsigned long lastMillis = 0;
//---------------- Stepper Motor angles ------------------------
const int theta0 = 34.0; // ~61.2º  // Each step = 1.8 degrees.
const int theta1 = 17.0; // ~30.6º  // Adds clockwise angle.
//---------------------------------------------------------------
void setup() {
  // Setup code, to run once:
  Serial.begin(115200);
  Serial.println("Hello, ESP32!");
  conectawifi();
  client.setServer(mqtt_server, 1883); // Set the MQTT broker server and port
  client.setCallback(callback); // Set the callback function for incoming MQTT messages
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  delay(DELAY);
  // Loop over the LEDs pins and set all to output
  for (int thisLed = 0; thisLed < ledCount; thisLed++) {
    pinMode(ledPins[thisLed], OUTPUT);
  }
}  // end setup()
void loop() {
  float sunlight =  photoresistor();
  leds(); // Update lights leds
  // Check MQTT connection and process incoming messages
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  delay(DELAY); // Add a delay to avoid flooding the MQTT broker with messages
  // Wait 15 seconds to send a new message
  if (millis() - lastMillis > 15000) {
    lastMillis = millis();
    sprintf(msg, "%.2f", sunlight);
    Serial.print("Message sent: ");
    Serial.println(msg);
    // Send the message to the broker
    client.publish(my_topic_PUB, msg);
    Serial.print(contador);
    Serial.println(" - Message sent successfully...");
    contador++; // Increment the counter
    leds(); // Update lights leds
  }
  leds(); // Update lights leds
  //-------------------- Stepper Motor Rotation ------------------------
  // Defines rotation direction (clockwise)
  digitalWrite(DIR_PIN, HIGH);
  // Turns angle theta0 (clockwise)
  for (int i = 0; i < theta0; i++) {
    // Take a step
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(DELAY);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(DELAY);
  }  // end for i
  Serial.println("At 6 am - 8 am");
  leds(); // Update lights leds
  delay(DELAY);
  // Changes the direction of rotation of angle theta_1 (counterclockwise)
  digitalWrite(DIR_PIN, LOW);
   for (int j = 0; j < 4; j++) { // j: 0-3 Nº de ângulos no sentido anti-horário
    for (int i = 0; i < theta1; i++) {
      // Take a step
      digitalWrite(STEP_PIN, HIGH);
      delayMicroseconds(DELAY);
      digitalWrite(STEP_PIN, LOW);
      delayMicroseconds(DELAY);
    }  // end for i
    if (j==0) Serial.println("At 8 am - 10 am");
    if (j==1) Serial.println("At 10 am - 1 pm");
    if (j==2) Serial.println("At 1 pm - 3 pm");
    if (j==3) Serial.println("At 3 pm - 5 pm");
    leds(); // Update lights leds
    delay(DELAY);
  }  // end for j
  //------------------- Return the panel to Azimuth ------------------------------------
  // Defines direction of rotation (clockwise).
  digitalWrite(DIR_PIN, HIGH);
  // Performs the rotation of 2 theta1 angles (clockwise).
  for (int j = 0; j < 2; j++) { // j: 0-1 Nº de ângulos no sentido horário
   for (int i = 0; i < theta1; i++) {
     // Take a step
     digitalWrite(STEP_PIN, HIGH);
     delayMicroseconds(DELAY);
     digitalWrite(STEP_PIN, LOW);
     delayMicroseconds(DELAY);
   }  // end for i
  if (j==0 or j==1) Serial.println("Returning the panel to the east...");
  leds(); // Update lights leds
  delay(DELAY);
  }  // end for j
} // end loop()
void conectawifi() {
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL);
  Serial.print("Conectando ao WiFi ");
  Serial.print(WIFI_SSID);
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
    //WiFi.begin(WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL);
  }
  Serial.println(" Connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}  // end conectawifi()
void leds() {
// Map the result to a range from lux to the number of LEDs
  ledLevel = map(lux, 0, 10000, 0, ledCount);
  // Loop over the LEDs pins and set element
  for (int thisLed = 0; thisLed < ledCount; thisLed++) {
    // If the array element's index is less than ledLevel
    if (thisLed < ledLevel) {
      // Turn the pin for this element on
      digitalWrite(ledPins[thisLed], HIGH);
    } else {
      // Turn off all pins higher than the ledLevel
      digitalWrite(ledPins[thisLed], LOW);
    }
  }
} // end leds
float  photoresistor() {
  int analogLux = analogRead(LDR_PIN);
  Serial.print("analogLux value: ");
  Serial.println(analogLux);
  // Only reads if there is a change in value
  // if (analogLux != valorAnterior) {
  // Code provided by WOKWI to convert the return value from analogRead() 
  // into an illuminance level value (in lux). 
  // Read more: [https://docs.wokwi.com/parts/wokwi-photoresistor-sensor]
    float voltage = analogLux / 4096. * 3.3;
    float resistance = 2000 * voltage / (1 - voltage / 3.3);
    lux = pow(RL10 * 1e3 * pow(10, GAMMA) / resistance, (1 / GAMMA));
// Prints message about sunlight intensity
  if (analogLux <= 39) 
    Serial.println("Excelent!     ");
  else if (analogLux <= 54 and analogLux > 39)
   Serial.println("Good          ");
  else if (analogLux <= 86 and analogLux > 54)
   Serial.println("Moderate      ");
  else if (analogLux <= 169 and analogLux > 86)
   Serial.println("Weak          ");
  else
  Serial.println("Inappropriate ");
  Serial.print("Luminosidade: ");
  Serial.println(lux);
  return lux;
}
// Process incoming MQTT message and control the servo motor
void callback(char* topic, byte* payload, unsigned int length) {
  String string;
  Serial.print("The message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    string += ((char)payload[i]);
  } // for
  Serial.print(string);
} // end callback()
// Attempt to reconnect to the MQTT broker if the connection is lost
void reconnect() {
  while (!client.connected()) {
    Serial.print("Tentando conectar ao MQTT ...");
    if (client.connect("ESPClient")) {
      Serial.println("Conectado");
      client.subscribe(my_topic_SUB);
    } else {
      Serial.print("falhou, rc=");
      Serial.print(client.state());
      Serial.println(" Tente novamente em 5 segundos");
    } // else
  } // while
} // end reconnect()