Værstasjon

IMG_2654 IMG_2664 IMG_2667

Koblingsskjema:

circuit

Kode:

/**
  Værstasjon
*/

#define DEBUG_WIFI_WEBSERVER_PORT  Serial
#define _WIFI_LOGLEVEL_            0
#define _WIFININA_LOGLEVEL_        0
#define USE_WIFI_NINA              true

#include <WiFiHttpClient.h>
#include <WiFiWebServer.h>
#include <elapsedMillis.h>
#include <ArduinoJson.h>
#include <Adafruit_MAX31865.h>
#include <Adafruit_BME280.h>
#include <Adafruit_TSL2591.h>

const uint8_t
  WIFI_RETRY      = 3,
  BUTTON_LED      = 9,
  DATA_LED        = LED_BUILTIN,
  SPI_CS_MAX31865 = 5;

uint8_t
  wifi_do                = 0,
  trigger_wifi_reconnect = 0;

uint16_t
  counterReconnect = 0;

uint32_t
  wifi_status        = WL_IDLE_STATUS,
  MILLIS_DATA_UPDATE_CURRENT,
  MILLIS_DATA_UPDATE = 60000,
  MILLIS_LED_BLINK   = 5000,
  MILLIS_WIFI_TEST   = 300000;

elapsedMillis blinkLightSystem; // Status blink
elapsedMillis getDataTest;      // Hent ny sensordata
elapsedMillis wifiTest;         // Test wifi-tilkobling

/* JSON. Hver måling er ca 250 i lengde */
StaticJsonDocument<512> json_doc;
JsonObject json_root;
char jsonLog[512];

/* Wifi */
const char WIFI_SSID[]      = "ssid";
const char WIFI_PASS[]      = "pw";
const char WIFI_TEST_ADDR[] = "192.1.1.1";
const char WIFI_PATH_TEST[] = "/";
IPAddress      LOCAL_IP(192, 1, 1, 2);
WiFiWebServer  server(80);
WiFiClient     client;
WiFiHttpClient httpClient(client, WIFI_TEST_ADDR, 80);

/**
 SENSOR MAX31865 - PT100 element, SPI, 5V, 5/3V logic safe 
 RREF=430.0 for PT100 og 4300.0 for PT1000
 RNOMINAL=0-grad motstand for sensoren. 100.0 for PT100, 1000.0 for PT1000
*/
Adafruit_MAX31865 thermo = Adafruit_MAX31865(SPI_CS_MAX31865, 2, 3, 4); // CS,DI,DO,SCK
#define RREF     430.0
#define RNOMINAL 100.0
char *maxErrors[] = {
  "RTD High Threshold",
  "RTD Low Threshold",
  "REFIN- > 0.85 x Bias",
  "REFIN- < 0.85 x Bias FORCE-open",
  "RTDIN- < 0.85 x Bias FORCE-open",
  "Under/Over voltage",
  "NULL"
};

/* SENSOR BME280 - Trykk, temperatur, fuktighet, gass - I2C, 0x77, 5V, 5/3V logic safe */
Adafruit_BME280 bme;

/**
 SENSOR TSL2591 - Lys - I2C, 0x29, 5V/3V logic safe 
 1 broadband photodiode (visible + infrared), 
 1 infrared-responding photodiode
 Gain har ingen effekt på lux
*/
Adafruit_TSL2591 tsl = Adafruit_TSL2591(2591);

void setup()
{

  Serial.begin(9600);
  delay(3000); // Vent litt så serial får med seg all output

  pinMode(DATA_LED,   OUTPUT);
  pinMode(BUTTON_LED, OUTPUT);

  digitalWrite(BUTTON_LED, HIGH);

  if(WiFi.status() == WL_NO_MODULE) {
    while(1);
  }

  begin_max();
  begin_bme();
  begin_tsl();

  getSensorData();
  setupWifi();

  digitalWrite(BUTTON_LED, LOW);

}

void loop()
{

  /* Blink system led, bare for å sjekke om hele driten har hengt seg */
  if(blinkLightSystem >= MILLIS_LED_BLINK) {
    blinkLightSystem = 0;
    digitalWrite(DATA_LED, !digitalRead(DATA_LED));
  }

  /* Hent ny sensordata */
  if(getDataTest >= MILLIS_DATA_UPDATE) {
    getDataTest = 0;
    getSensorData();
    MILLIS_DATA_UPDATE_CURRENT = millis();
  }

  /* Sjekk wifi status */
  wifi_status = WiFi.status();

  /* Koble til wifi på nytt */
  if(wifi_status != WL_CONNECTED || trigger_wifi_reconnect != 0) {
    digitalWrite(BUTTON_LED, HIGH);
    reconnectWifi();
    return;
  } else {
    digitalWrite(BUTTON_LED, LOW);
  }

  /* Test wifi tilkobling */
  if(wifiTest >= MILLIS_WIFI_TEST && wifi_do == 1) {
    wifiTest = 0;
    int werr = 0;
    werr = httpClient.get(WIFI_PATH_TEST);
    if(werr != 0) {
      trigger_wifi_reconnect = 1;
    }
    httpClient.stop();
  }

  if(wifi_do != 1) return;

  server.handleClient();

}

void begin_max()
{
  thermo.begin(MAX31865_4WIRE);
  thermo.enable50Hz(true);
}

void begin_bme()
{
  if(bme.begin()) {
    bme.setSampling(Adafruit_BME280::MODE_FORCED,
                    Adafruit_BME280::SAMPLING_X1, // temperatur
                    Adafruit_BME280::SAMPLING_X1, // trykk
                    Adafruit_BME280::SAMPLING_X1, // fuktighet
                    Adafruit_BME280::FILTER_OFF);
  }
}

void begin_tsl()
{
  if(tsl.begin()) {
    tsl.setTiming(TSL2591_INTEGRATIONTIME_500MS);
    tsl.setGain(TSL2591_GAIN_LOW);
  }
}

void getSensorData()
{

  // Lag en tom array, kaller clear internt
  json_root = json_doc.to<JsonObject>(); 

  get_max_data();
  get_bme_data();
  get_tsl_data();

  serializeJson(json_doc, jsonLog);

  Serial.println(F("JSON-data:"));
  Serial.println(jsonLog);

}

void get_max_data()
{

  float    max_ra, max_re, max_t;
  uint8_t  max_fault;
  uint16_t max_rtd;

  max_rtd   = thermo.readRTD();
  max_ra    = max_rtd;
  max_ra   /= 32768;
  max_re    = (RREF * max_ra);
  max_t     = thermo.temperature(RNOMINAL, RREF);
  max_fault = thermo.readFault();

  JsonObject json_max     = json_root.createNestedObject("MAX");
  json_max["ratio"]       = max_ra;
  json_max["resistance"]  = max_re;
  json_max["temperature"] = max_t;
  json_max["error"]       = max_fault;

  if(max_fault) {
    if(max_fault & MAX31865_FAULT_HIGHTHRESH) {
      json_max["errorText"] = maxErrors[0];
    }
    if(max_fault & MAX31865_FAULT_LOWTHRESH) {
      json_max["errorText"] = maxErrors[1];
    }
    if(max_fault & MAX31865_FAULT_REFINLOW) {
      json_max["errorText"] = maxErrors[2];
    }
    if(max_fault & MAX31865_FAULT_REFINHIGH) {
      json_max["errorText"] = maxErrors[3];
    }
    if(max_fault & MAX31865_FAULT_RTDINLOW) {
      json_max["errorText"] = maxErrors[4];
    }
    if(max_fault & MAX31865_FAULT_OVUV) {
      json_max["errorText"] = maxErrors[5];
    }
    thermo.clearFault();
  } else {
      json_max["errorText"] = maxErrors[6];
  }

}

void get_bme_data()
{

  float temperature, humidity, pressure;

  bme.takeForcedMeasurement();

  temperature = bme.readTemperature();
  humidity    = bme.readHumidity();
  pressure    = (bme.readPressure() / 100.0F); 

  JsonObject json_bme     = json_root.createNestedObject("BME");
  json_bme["temperature"] = temperature;
  json_bme["humidity"]    = humidity;
  json_bme["pressure"]    = pressure;

}

void get_tsl_data()
{

  float lux;
  uint16_t ir, full;
  uint32_t visible, lum;

  lum     = tsl.getFullLuminosity();
  ir      = lum >> 16;
  full    = lum & 0xFFFF;
  lux     = tsl.calculateLux(full, ir);
  visible = (full - ir);

  JsonObject json_tsl = json_root.createNestedObject("TSL");
  json_tsl["ir"]      = ir; // μm
  json_tsl["visible"] = visible;
  json_tsl["full"]    = full;
  json_tsl["lux"]     = lux;

}

uint16_t get_tsl_timing(tsl2591IntegrationTime_t timing)
{
  return (timing + 1) * 100;
}

uint8_t MStoMin(uint32_t ms)
{
  return (ms / 1000 / 60);
}

void setupWifi()
{

  uint8_t i = 0;
  WiFi.config(LOCAL_IP);

  while(wifi_status != WL_CONNECTED) {
    if(i >= WIFI_RETRY) {
      return;
    }
    wifi_status = WiFi.begin(WIFI_SSID, WIFI_PASS);
    delay(15000);
    i++;
  }

  wifi_do = 1;
  trigger_wifi_reconnect = 0;
  counterReconnect++;

  server.on(F("/"),                 wifiHandle_root);
  server.on(F("/getSensorData"),    wifiHandle_getSensorData);
  server.on(F("/setTslGain/low"),   wifiHandle_tslSetGainLow);
  server.on(F("/setTslGain/med"),   wifiHandle_tslSetGainMed);
  server.on(F("/setTslGain/high"),  wifiHandle_tslSetGainHigh);
  server.on(F("/setTslGain/max"),   wifiHandle_tslSetGainMax);
  server.on(F("/setTslTiming/100"), wifiHandle_tslSetTiming100);
  server.on(F("/setTslTiming/200"), wifiHandle_tslSetTiming200);
  server.on(F("/setTslTiming/300"), wifiHandle_tslSetTiming300);
  server.on(F("/setTslTiming/400"), wifiHandle_tslSetTiming400);
  server.on(F("/setTslTiming/500"), wifiHandle_tslSetTiming500);
  server.on(F("/setTslTiming/600"), wifiHandle_tslSetTiming600);
  server.on(F("/setUpdateTime/1"),  wifiHandle_updateFrequency1);
  server.on(F("/setUpdateTime/3"),  wifiHandle_updateFrequency3);
  server.on(F("/setUpdateTime/5"),  wifiHandle_updateFrequency5);

  server.onNotFound([]() {
    server.send(404, F("text/plain"), F("404: Fant ikke siden.."));
  });

  server.begin();

}

void reconnectWifi()
{
  wifi_do = 0;
  httpClient.stop();
  server.stop();
  WiFi.end();
  setupWifi();
}

void wifiHandle_root()
{

  #define BUFFER_SIZE 1500
  char ret[BUFFER_SIZE];

  snprintf(
    ret, 
    BUFFER_SIZE-1, 
    "<!DOCTYPE html>\
<html lang=\"en\">\
<head>\
<meta charset=\"utf-8\">\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
<title>Værstasjon</title>\
</head>\
<body style=\"font-family: monospace; font-size: 130\%%;\">\
<h1>Værstasjon</h1>\
<p><strong>Wi-Fi tilkobling:</strong><br>%d dBm (%s)</p>\
<p><strong>Wi-Fi reconnect teller:</strong><br>%d</p>\
<p><a href=\"/getSensorData\">Se oppdatert sensordata</a></p>\
<p><strong>Oppdateringsfrekvens:</strong><br> Hvert %d minutt</p>\
<p><strong>Siste oppdatering:</strong><br> På %d ms<br>millis() nå: %d ms</p>\
<p><strong>TSL gain og timing:</strong><br> %d og %d ms</p>\
<p>Sett TSL gain: \
<a href=\"/setTslGain/low\">LAV</a> (x0, standard), \
<a href=\"/setTslGain/med\">MEDIUM</a> (x16), \
<a href=\"/setTslGain/high\">HØY</a> (x32), \
<a href=\"/setTslGain/max\">MAKS</a> (x48)\
</p>\
<p>Sett TSL timing: \
<a href=\"/setTslTiming/100\">100 ms</a>, \ 
<a href=\"/setTslTiming/200\">200 ms</a>, \ 
<a href=\"/setTslTiming/300\">300 ms</a>, \
<a href=\"/setTslTiming/400\">400 ms</a>, \
<a href=\"/setTslTiming/500\">500 ms</a> (standard), \
<a href=\"/setTslTiming/600\">600 ms</a>\
</p>\
<p>Sett oppdateringsfrekvens: \
<a href=\"/setUpdateTime/1\">1 minutt</a> (standard), \
<a href=\"/setUpdateTime/3\">3 minutt</a>, \
<a href=\"/setUpdateTime/5\">5 minutt</a>\
</p>\
</body>\
</html>",

    WiFi.RSSI(), 
    WiFi.SSID(), 
    counterReconnect, 
    MStoMin(MILLIS_DATA_UPDATE), 
    MILLIS_DATA_UPDATE_CURRENT, 
    millis(), 
    tsl.getGain(), 
    get_tsl_timing(tsl.getTiming())

);

  server.send(200, F("text/html"), ret);

}

void wifiHandle_getSensorData()
{
  server.send(200, F("application/json"), jsonLog);
}

void wifiHandle_tslSetGainLow()
{
  tsl.setGain(TSL2591_GAIN_LOW);
  server.send(200, F("text/html"), F("Oppdaterte TSL gain (x0). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_tslSetGainMed()
{
  tsl.setGain(TSL2591_GAIN_MED);
  server.send(200, F("text/html"), F("Oppdaterte TSL gain (x16). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_tslSetGainHigh()
{
  tsl.setGain(TSL2591_GAIN_HIGH);
  server.send(200, F("text/html"), F("Oppdaterte TSL gain (x32). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_tslSetGainMax()
{
  tsl.setGain(TSL2591_GAIN_MAX);
  server.send(200, F("text/html"), F("Oppdaterte TSL gain (x48). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_tslSetTiming100()
{
  tsl.setTiming(TSL2591_INTEGRATIONTIME_100MS);
  server.send(200, F("text/html"), F("Oppdaterte TSL timing (100). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_tslSetTiming200()
{
  tsl.setTiming(TSL2591_INTEGRATIONTIME_200MS);
  server.send(200, F("text/html"), F("Oppdaterte TSL timing (200). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_tslSetTiming300()
{
  tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS);
  server.send(200, F("text/html"), F("Oppdaterte TSL timing (300). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_tslSetTiming400()
{
  tsl.setTiming(TSL2591_INTEGRATIONTIME_400MS);
  server.send(200, F("text/html"), F("Oppdaterte TSL timing (400). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_tslSetTiming500()
{
  tsl.setTiming(TSL2591_INTEGRATIONTIME_500MS);
  server.send(200, F("text/html"), F("Oppdaterte TSL timing (500). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_tslSetTiming600()
{
  tsl.setTiming(TSL2591_INTEGRATIONTIME_600MS);
  server.send(200, F("text/html"), F("Oppdaterte TSL timing (600). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_updateFrequency1()
{
  MILLIS_DATA_UPDATE = 60000;
  server.send(200, F("text/html"), F("Endret oppdateringsfrekvens (1 minutt). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_updateFrequency3()
{
  MILLIS_DATA_UPDATE = 180000;
  server.send(200, F("text/html"), F("Endret oppdateringsfrekvens (3 minutt). <a href=\"/\">Tilbake</a>"));
}

void wifiHandle_updateFrequency5()
{
  MILLIS_DATA_UPDATE = 300000;
  server.send(200, F("text/html"), F("Endret oppdateringsfrekvens (5 minutt). <a href=\"/\">Tilbake</a>"));
}