UV sensor via wireless and data upload to the Internet

Hello.

I am looking to create a project that measures the UV Index.

The unit will sit outside in a project box.

If I have a clear project box, will this still work? or will the plastic block the sensor too much?

I have a Arduino Nano and was looking at getting something like a Adafruit UV Sensor (SI1145).

I have DC power going to where the unit will be installed but not a cabled network connection, hence the need for wireless.

What is the best wireless card to use?

So: UV Sensor > Arduino Nano (via wireless) > upload every 5 minutes to the Internet.

Will Plotly be OK to upload the data to?

I'm a total beginner to this, so getting this all going and coding would be great!

Many thanks.

otronics:
I am looking to create a project that measures the UV Index.
What is the best wireless card to use?
I'm a total beginner to this, so getting this all going and coding would be great!

I would probably use a WeMos D1 mini, or a bare ESP8266-12 module.
Arduino, WiFi and flash storage all in one.
But this project could be too advanced for a beginner.
Read this guide. The last part has a temp logger that could be converted to a UV logger.
Leo..

If I used the instructions here: GitHub - plotly/arduino-api: Arduino library for real-time logging and streaming data to online plotly graphs

would I be able to modify it so it could upload the data via wireless?

otronics:
If I used the instructions here: GitHub - plotly/arduino-api: Arduino library for real-time logging and streaming data to online plotly graphs

would I be able to modify it so it could upload the data via wireless?

I'd rather use the one that works for wifi.
That will be your biggest challenge not the getting data from the uv sensor.

Indeed - I've just spotted there is a guide on there for wi-fi setup.

I'll get the parts and let you know how I get on.

Also, have a look at ESP8266 | Martyn Currey

I find Martyn's tutorial very very helpful.

Fab - thank you.

will the plastic block the sensor too much?

Yes. Most plastics absorb strongly in the UV. Even glass is a problem, depending on what UV wavelength is of interest.

My UV sensor is in a weatherproof box with a clear plastic top. Or at least it was when I installed it 7~8 months ago. Now the clear plastic has gone milky and the screws have corroded! Must find a better quality box...

That picture above is what I am aiming for.

jremington:
Yes. Most plastics absorb strongly in the UV. Even glass is a problem, depending on what UV wavelength is of interest.

What is the best way to get around this?

How do the pro UV sensors do it?

Use materials that do not absorb the UV of interest. Quartz is one. Do your research!

I am getting around to setting this up.

I have the following error when compiling:

Arduino: 1.8.5 (Windows 10), Board: "NodeMCU 1.0 (ESP-12E Module), 80 MHz, 4M (1M SPIFFS), v2 Prebuilt (MSS=536), Disabled, None, 115200"

UV_Sensor:16: error: 'A1' was not declared in this scope

int REF_3V3 = A1;

^

exit status 1
'A1' was not declared in this scope

What needs to be done to fix this?

Thanks.

A NodeMCU has AFAIK only one analogue input (A0).

Try const byte REF_3V3 = A0;

Leo..

Indeed, only one analog input on the NodeMCU/ESP8266, and indeed it's called A0 (the markings at the pin you connect your sensor to should have acted as strong suggestion to what pin number to use in your code!).

const byte REF_3V3 = A0; has fixed that.

I am now getting the error:

C:\Users\OTronics\Documents\Arduino\libraries\plotly_streaming_wifi\plotly_streaming_wifi.cpp:5:25: fatal error: avr/dtostrf.h: No such file or directory

#include <avr/dtostrf.h>

"avr" is a trade mark of the manufacturer Atmel who make the chips in most Arduino. But they did not make the esp8266. So that library may not be available/suitable for esp. You need to find an equivalent library that is suitable.

Why not post your code so we can suggest how to fix it? Use code tags.

PaulRB:
"avr" is a trade mark of the manufacturer Atmel who make the chips in most Arduino. But they did not make the esp8266. So that library may not be available/suitable for esp. You need to find an equivalent library that is suitable.

Why not post your code so we can suggest how to fix it? Use code tags.

OK - that explains it.

Here is my project code, followed by the .h and .cpp wifi files. I've not added individual settings to my code (ie/ wifi passwords etc) - just trying to get it to compile first.

PROJECT CODE:

#include <WiFi.h>
#include "plotly_streaming_wifi.h"

// Sign up to plotly here: https://plot.ly
// View your API key and streamtokens here: https://plot.ly/settings
#define nTraces 3
// View your tokens here: https://plot.ly/settings
// Supply as many tokens as data traces
// e.g. if you want to ploty A0 and A1 vs time, supply two tokens
char *tokens[nTraces] = {"25tm9197rz", "unbi52ww8a", "ibsfyg7qd8"};
// arguments: username, api key, streaming token, filename
plotly graph = plotly("workshop", "v6w5xlbx9j", tokens, "your_filename", nTraces);

// Setup UV Sensor (ML8511) Pins
int UVOUT = A0;
const byte REF_3V3 = A0;

int status = WL_IDLE_STATUS;     // the Wifi radio's status
char ssid[] = "wifi_network_name"; //  your network SSID (name)
char pass[] = "wifi_network_password"; // // your network password

void wifi_connect(){
    // attempt to connect using WPA2 encryption:
    Serial.println("... Attempting to connect to WPA network...");
    status = WiFi.begin(ssid, pass);
    // if you're not connected, stop here:
    if ( status != WL_CONNECTED) {
      Serial.println("... Couldn't get a WiFi connection, trying again");
      wifi_connect();
    }
    // if you are connected, print out info about the connection:
    else {
      Serial.println("... Connected to network");
    }
}

void setup() {

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  // Initialize pinMode for UV Sensor
  pinMode(UVOUT, INPUT);
  pinMode(REF_3V3, INPUT);

  wifi_connect();

  graph.fileopt="overwrite"; // See the "Usage" section in https://github.com/plotly/arduino-api for details
  bool success;
  success = graph.init();
  if(!success){while(true){}}
  graph.openStream();
}

void loop() {

  int uvLevel = averageAnalogRead(UVOUT);
  int refLevel = averageAnalogRead(REF_3V3);
  float outputVoltage = 3.3 / refLevel * uvLevel;
  float uvIntensity = mapfloat(outputVoltage, 0.99, 2.9, 0.0, 15.0);

  Serial.print("MP8511 output: ");
  Serial.print(uvLevel);
  graph.plot(millis(), uvLevel, tokens[0]);

  Serial.print("MP8511 voltage: ");
  Serial.print(outputVoltage);
  graph.plot(millis(), outputVoltage, tokens[1]);

  Serial.print("UV Intensity (mW/cm^2): ");
  Serial.print(uvIntensity);
  graph.plot(millis(), uvIntensity, tokens[2]);

  delay(200);

}

//Takes an average of readings on a given pin
//Returns the average
int averageAnalogRead(int pinToRead)
{
  byte numberOfReadings = 8;
  unsigned int runningValue = 0;

  for(int x = 0 ; x < numberOfReadings ; x++)
    runningValue += analogRead(pinToRead);
  runningValue /= numberOfReadings;

  return(runningValue);
}

//The Arduino Map function but for floats
//From: http://forum.arduino.cc/index.php?topic=3922.0
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

.h file:

#ifndef plotly_streaming_wifi_h
#define plotly_streaming_wifi_h

#include "Arduino.h"
#include <avr/pgmspace.h>

#include <WiFi.h>

class plotly
{
    public:
        plotly(char *username, char *api_key, char* stream_tokens[], char *filename, int nTraces);
        WiFiClient client;
        bool init();
        void openStream();
        void closeStream();
        void reconnectStream();
        void jsonStart(int i);
        void jsonMiddle();
        void jsonEnd(char *token);

        void plot(unsigned long x, int y, char *token);
        void plot(unsigned long x, float y, char *token);

        int log_level;
        bool dry_run;
        int maxpoints;
        bool world_readable;
        bool convertTimestamp;
        char *timezone;
        char *fileopt;

    private:
        void print_(int d);
        void print_(unsigned long d);
        void print_(float d);
        void print_(char *d);
        void print_(const __FlashStringHelper* d);

        int len_(int i);
        int len_(unsigned long i);
        int len_(char *i);

        unsigned long fibonacci_;
        char *username_;
        char *api_key_;
        char** stream_tokens_;
        char *filename_;
        int nTraces_;

};
#endif

.cpp file:

PART 1 (had to split due to char limit)

#include "Arduino.h"
#include <WiFi.h>
#include "plotly_streaming_wifi.h"

#include <avr/dtostrf.h>
#include <avr/pgmspace.h>


plotly::plotly(char *username, char *api_key, char* stream_tokens[], char *filename, int nTraces)
  {
    log_level = 2;  // 0 = Debugging, 1 = Informational, 2 = Status, 3 = Errors, 4 = Quiet (// Serial Off)
    dry_run = false;
    username_ = username;
    api_key_ = api_key;
    stream_tokens_ = stream_tokens;
    filename_ = filename;
    nTraces_ = nTraces;
    maxpoints = 30;
    fibonacci_ = 1;
    world_readable = true;
    convertTimestamp = true;
    timezone = "America/Montreal";
    fileopt = "overwrite";
}

bool plotly::init(){
    //
    //  Validate a stream with a REST post to plotly
    //
    if(dry_run && log_level < 3){
        Serial.println(F("... This is a dry run, we are not connecting to plotly's servers..."));
    }
    else if(log_level < 3) {
        Serial.println(F("... Attempting to connect to plotly's REST servers"));
    }
    while ( !client.connect("plot.ly", 80) ) {
        if(log_level < 4){
            Serial.println(F("... Couldn\'t connect to plotly's REST servers... trying again!"));
        }
        fibonacci_ += fibonacci_;
        delay(min(fibonacci_, 60000));
    }
    fibonacci_ = 1;
    if(log_level < 3){} Serial.println(F("... Connected to plotly's REST servers"));
    if(log_level < 3){} Serial.println(F("... Sending HTTP Post to plotly"));
    print_(F("POST /clientresp HTTP/1.1\r\n"));
    print_(F("Host: plot.ly:80\r\n"));
    print_(F("User-Agent: Arduino/0.6.0\r\n"));

    print_(F("Content-Length: "));
    int contentLength = 126 + len_(username_) + len_(fileopt) + nTraces_*(87+len_(maxpoints)) + (nTraces_-1)*2 + len_(filename_);
    if(world_readable){
        contentLength += 4;
    } else{
        contentLength += 5;
    }
    print_(contentLength);
    // contentLength =
    //   44  // first part of querystring below
    // + len_(username)  // upper bound on username length
    // + 5   // &key=
    // + 10  // api_key length
    // + 7  // &args=[...
    // + nTraces*(87+len(maxpoints)) // len({\"y\": [], \"x\": [], \"type\": \"scatter\", \"stream\": {\"token\": \") + 10 + len(\", "maxpoints": )+len(maxpoints)+len(}})
    // + (nTraces-1)*2 // ", " in between trace objects
    // + 22  // ]&kwargs={\"fileopt\": \"
    // + len_(fileopt)
    // + 16  // \", \"filename\": \"
    // + len_(filename)
    // + 21 // ", "world_readable":
    // + 4 if world_readable, 5 otherwise
    // + 1   // closing }
    //------
    // 126 + len_(username) + len_(fileopt) + nTraces*(86+len(maxpoints)) + (nTraces-1)*2 + len_(filename)
    //
    // Terminate headers with new lines
    print_(F("\r\n\r\n"));

    // Start printing querystring body
    print_(F("version=2.3&origin=plot&platform=arduino&un="));
    print_(username_);
    print_(F("&key="));
    print_(api_key_);
    print_(F("&args=["));
    // print a trace for each token supplied
    for(int i=0; i<nTraces_; i++){
        print_(F("{\"y\": [], \"x\": [], \"type\": \"scatter\", \"stream\": {\"token\": \""));
        print_(stream_tokens_[i]);
        print_(F("\", \"maxpoints\": "));
        print_(maxpoints);
        print_(F("}}"));
        if(nTraces_ > 1 && i != nTraces_-1){
            print_(F(", "));
        }
    }
    print_(F("]&kwargs={\"fileopt\": \""));
    print_(fileopt);
    print_(F("\", \"filename\": \""));
    print_(filename_);
    print_(F("\", \"world_readable\": "));
    if(world_readable){
        print_("true");
    } else{
        print_("false");
    }
    print_(F("}"));
    // final newline to terminate the POST
    print_(F("\r\n"));

    //
    // Wait for a response
    // Parse the response for the "All Streams Go!" and proceed to streaming
    // if we find it
    //
    char allStreamsGo[] = "All Streams Go!";
    int asgCnt = 0; // asg stands for All Streams Go
    char url[] = "\"url\": \"http://plot.ly/~";
    char fid[4];
    int fidCnt = 0;
    int urlCnt = 0;
    int usernameCnt = 0;
    int urlLower = 0;
    int urlUpper = 0;
    bool proceed = false;
    bool fidMatched = false;

    if(log_level < 2){
        Serial.println(F("... Sent message, waiting for plotly's response..."));
    }
    if(!dry_run){
        char c;
        while(client.connected()){
            if(client.available()){
                c = client.read();

                if(log_level < 2) Serial.print(c);
                //
                // Attempt to read the "All streams go" msg if it exists
                // by comparing characters as they roll in
                //

                if(asgCnt == len_(allStreamsGo) && !proceed){
                    proceed = true;
                }
                else if(allStreamsGo[asgCnt]==c){
                    asgCnt += 1;
                } else if(asgCnt > 0){
                    // reset counter
                    asgCnt = 0;
                }

                //
                // Extract the last bit of the URL from the response
                // The url is in the form http://plot.ly/~USERNAME/FID
                // We'll character-count up through char url[] and through username_, then start
                // filling in characters into fid
                //

                if(log_level < 3){
                    if(url[urlCnt]==c && urlCnt < len_(url)){
                        urlCnt += 1;
                    } else if(urlCnt > 0 && urlCnt < len_(url)){
                        // Reset counter
                        urlCnt = 0;
                    }
                    if(urlCnt == len_(url) && fidCnt < 4 && !fidMatched){
                        // We've counted through the url, start counting through the username
                        if(usernameCnt < len_(username_)+2){
                            usernameCnt += 1;
                        } else {
                            // the url ends with "
                            if(c != '"'){
                                fid[fidCnt] = c;
                                fidCnt += 1;
                            } else if(fidCnt>0){
                                fidMatched = true;
                            }
                        }
                    }
                }
            }
        }
        client.stop();
    }
    if(!dry_run && !proceed && log_level < 4){
        Serial.println(F("... Error initializing stream, aborting. Try again or get in touch with Chris at chris@plot.ly"));
    }

    if(!dry_run && proceed && log_level < 3){
        Serial.println(F("... A-ok from plotly, All Streams Go!"));
        if(fidMatched){
            Serial.print(F("... View your streaming plot here: https://plot.ly/~"));
            Serial.print(username_);
            Serial.print(F("/"));
            for(int i=0; i<fidCnt; i++){
                Serial.print(fid[i]);
            }
            Serial.println(F(""));
        }
    }
    return proceed;

}
void plotly::openStream() {
    //
    // Start request to stream servers
    //
    if(log_level < 3){} Serial.println(F("... Connecting to plotly's streaming servers..."));
    char server[] = "arduino.plot.ly";
    int port = 80;
    while ( !client.connect(server, port) ) {
        if(log_level < 4) Serial.println(F("... Couldn\'t connect to servers... trying again!"));
        fibonacci_ += fibonacci_;
        delay(min(fibonacci_, 60000));
    }
    fibonacci_ = 1;
    if(log_level < 3){} Serial.println(F("... Connected to plotly's streaming servers\n... Initializing stream"));

    print_(F("POST / HTTP/1.1\r\n"));
    print_(F("Host: arduino.plot.ly\r\n"));
    print_(F("User-Agent: Arduino\r\n"));
    print_(F("Transfer-Encoding: chunked\r\n"));
    print_(F("Connection: close\r\n"));
    if(convertTimestamp){
        print_(F("plotly-convertTimestamp: \""));
        print_(timezone);
        print_(F("\"\r\n"));
    }
    print_(F("\r\n"));

    if(log_level < 3){} Serial.println(F("... Done initializing, ready to stream!"));
}

void plotly::closeStream(){
    print_(F("0\r\n\r\n"));
    client.stop();
}
void plotly::reconnectStream(){
    while(!client.connected()){
        if(log_level<4) Serial.println(F("... Disconnected from streaming servers"));
        closeStream();
        openStream();
    }
}

.cpp file:

PART 2

void plotly::jsonStart(int i){
    // Print the length of the message in hex:
    // 15 char for the json that wraps the data: {"x": , "y": }\n
    // + 23 char for the token: , "token": "abcdefghij"
    // = 38
    if(log_level<2) Serial.print(i+44, HEX);
    if(!dry_run) client.print(i+44, HEX);
    print_("\r\n{\"x\": ");
}
void plotly::jsonMiddle(){
    print_(", \"y\": ");
}
void plotly::jsonEnd(char *token){
    print_(", \"streamtoken\": \"");
    print_(token);
    print_("\"}\n\r\n");
}

int plotly::len_(int i){
    // int range: -32,768 to 32,767
    if(i > 9999) return 5;
    else if(i > 999) return 4;
    else if(i > 99) return 3;
    else if(i > 9) return 2;
    else if(i > -1) return 1;
    else if(i > -10) return 2;
    else if(i > -100) return 3;
    else if(i > -1000) return 4;
    else if(i > -10000) return 5;
    else return 6;
}
int plotly::len_(unsigned long i){
    // max length of unsigned long: 4294967295
    if(i > 999999999) return 10;
    else if(i > 99999999) return 9;
    else if(i > 9999999) return 8;
    else if(i > 999999) return 7;
    else if(i > 99999) return 6;
    else if(i > 9999) return 5;
    else if(i > 999) return 4;
    else if(i > 99) return 3;
    else if(i > 9) return 2;
    else return 1;
}
int plotly::len_(char *i){
    return strlen(i);
}
void plotly::plot(unsigned long x, int y, char *token){
    reconnectStream();
    jsonStart(len_(x)+len_(y));
    print_(x);
    jsonMiddle();
    print_(y);
    jsonEnd(token);
}
void plotly::plot(unsigned long x, float y, char *token){
    reconnectStream();

    char s_[15];
    dtostrf(y,2,3,s_);

    jsonStart(len_(x)+len_(s_)-1);
    print_(x);
    jsonMiddle();
    print_(y);
    jsonEnd(token);
}
void plotly::print_(int d){
    if(log_level < 2) Serial.print(d);
    if(!dry_run) client.print(d);
}
void plotly::print_(unsigned long d){
    if(log_level < 2) Serial.print(d);
    if(!dry_run) client.print(d);
}
void plotly::print_(float d){
    if(log_level < 2) Serial.print(d);
    if(!dry_run) client.print(d);
}
void plotly::print_(char *d){
    if(log_level < 2) Serial.print(d);
    if(!dry_run) client.print(d);
}
void plotly::print_(const __FlashStringHelper* d){
    if(log_level < 2) Serial.print(d);
    if(!dry_run) client.print(d);
}