Final Project

Navigation
  1. Final Project
  2. Laser Cutting
  3. 3D Printing
  4. Printed circuit board
  5. Arduino IDE
  6. Conclusion
  7. Downloadfiles

Final Project - Weather Station with an integrated Lamp

Introduction

My final project is a weather station with an LCD Display, which has additionally neopixel to represent the chosen values.
It detects humidity, temperature and brightness with a photocell and a humidity and temperature sensor. The values are shown on the display.
Furthermore one can choose between which value should be displayed with the lights. For example the temperature goes from white to blue to a red color depending on how hot it is.
A humidity more than 65% is a risk for mold. That is why I made a transition from yellow to red, when the humidity is above 65%. Below should be green because everything is fine. So technically it prevents cancer. :)

Because we have a woodstove it often gets pretty warm and I would like to know the temperature just by looking at the lamp. However I can also know the exact degree when looking at the display. Same goes for the humidity. I want to reduce the risk and I can quickly see when the percentage is too high. The photocell changes the brightness of the lights because I do not want the lamp to be super bright when it is dark. In contrast I want it to be bright when it is bright to see the colors.
Moreover I have simply a nice, decorative lamp which is also very meaningful. In addition I can change the lampshade easily into whatever I like.


License

I am using the Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) license because I want everyone to feel free to rebuilt or remix the project for fun and non-commercial purposes.


Price
Picture Name Price
ATmega1284P-AU 4,80 €
DHT11 Humidity & Temperature Sensor 2,00 €
Photocell 1,70 €
Display graphic 12864 10,00 €
Neopixel 15,00 €
5V Voltage Regulator 1,00 €
Button 1,50 €
Switch 1,00 €
Total
~ 40+ €


Laser Cutting

Designing

→ Go to: 2D and 3D Design

To test the size of the box I made a prototype out of cardboard.




Based on this prototype I designed the final box in LibreCAD.

To make sure everything fits I extruded the sketch again in Fusion360 and assembled it.
It looks perfect.

Cutting

→ Go to: Laser Cutting

I am using 5 mm wood and the Epilog Laser Cutter with the following settings:
    - 100% Power
    - 20% Speed
    - 4000 Hz

Engraving:
    - 100% Speed
    - 100% Speed

Kerf (in Fusion):
    - 0.35 mm

The settings were pretty good. It fits without glue but maybe you could go even more with the kerf and/ or keep the top and bottom plate original without kerf in Fusion.
I cut the new top part raw and it fit even better without the kerf calculation of Fusion. So I would recommend to let Fusion calculate the kerf just on the sides and the top and bottom part cut raw. It will fit very very good.

You can also easily remove the brown/ orange parts with a wet cloth.

After 3d printing I had to design a new top plate:


Everything fits good.

3D Printing

Designing

→ Go to: 2D and 3D Design
→ Go to: 3D Printing

I used for everything in this chapter the same techniques explained in the upper chapters.

Because it is a weather station and I like the 3D printed low poly look from my first 3D print, I tried to make a low poly cloud by myself in blender.
The file is exported as .obj and opened in Fusion 360 for further modeling.

But I cannot put the cloud like this on top of the weather box.
For that I drew in Fusion 360 the exact measurements I need for the lampshade and put the object on top of it.

The sketch is 155 mm wide. I extruded the circle and made a stand for the object.

The object can be saved as .stl now and opened in Cura to be 3D printed.

Later on I adjusted some support for the base.

Cura

Because there was no white material left I had to use the last white material of the Delta 4070 Pro Wasp printer.
Go to add printer and choose the DeltaBot under "others".

Now adjust the settings. We used 0.7 even though the nozzle is 0.4 because we had problems printing because of the uneven bed.



Quality settings:



Shell settings:



Infill settings:



Material settings:



Speed settings:



Support settings:



Brim settings:

If you have problems with keeping the print on the bed you can also try to use paper. It helped a lot.

It actually turned out pretty good for a hollow print.
There were just some parts that needed support.

Also personally I really do not like the base as a big shell. I chose another method to put it in the top part of the box. It has a 1 cm smaller circle that will be set in the wood and the rest will be placed over it.
And I made the top triangles higher for a smaller angle for the 3d printer.

After a lot of try and error, we came up with these final settings:

The support now:

After spending four days, this is the final result:
It is still not perfect but I think it is really hard to be printed perfectly because of its shape and hollowness.

Printed Circuit Board

→ Go to: Electronics Design
I am still using the board from "Electronics Design" for my final project.

Arduino IDE

→ Go to: Input Devices
→ Go to: Output Devices

I based my code on my previous projects, the test files from the arduino libraries and documentations.
I will post the plain code and describe it in the comments of the code.

These are the notes I made to visualize the color schemes.

One problem was that I wanted the color to change immediately but I had to press the button until it changed because of the delays for the neopixel and the display.
I googled a bit and found that interrupt could solve my problem.
It stops the code immediately and uses my button function changeMode() when the button is pressed. Unfortunately it still did not work how I wanted it to work. I programmed myself a delay with a counter, to not have a delay that stops my program and therefore is not able to detect a button press. I also used debounce to check if the button is pressed and released and that the modes do not change again because the loop is already going through next time.

                    
    #include <Adafruit_NeoPixel.h> // neopixel
    #include "DHT.h" // DHT11 temp sensor
    #include <SoftwareSerial.h> // photocell
    #include "U8glib.h" // display
    
    //PINs connected to the display
    U8GLIB_ST7920_128X64 u8g(A0, A1, A2, A3);
    
    // TEMP SENSOR
    #define DHTPIN PB1
    #define DHTTYPE DHT11
    
    // LEDs
    // #define LED_PIN_ONE 11
    #define LED_PIN_TWO PB2
    
    // PHOTOCELL
    #define PHOTOCELL A4
    
    // NEOPIXEL
    #define PIN 3
    // The number of pixels you want to be on
    #define NUMPIXELS 20
    
    // BUTTON
    #define BUTTON_PIN 11
    
    Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
    DHT dht(DHTPIN, DHTTYPE);
    
    int photocellReading;
    float h = 0;
    float t = 0;
    int brightness = 0;
    int neoBrightness = 0;
    int mode = 0;
    int count = 1000;
    
    
    int red = 0;
    int green = 0;
    int blue = 0;
    
    
    // DEBOUNCE from: https://www.arduino.cc/en/tutorial/debounce
    int buttonState;             // the current reading from the input pin
    int lastButtonState = LOW;   // the previous reading from the input pin
    
    // the following variables are unsigned longs because the time, measured in
    // milliseconds, will quickly become a bigger number than can be stored in an int.
    unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
    unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers
    
    void setup() {
        // Value also has to be set in serial monitor
        Serial.begin(115200);
    
        // Test to see values on the serial monitor
        Serial.println("DHT11 started!");
    
        // Photocell
        pinMode(PHOTOCELL, INPUT);
    
        // LEDs on the boards
        // pinMode(LED_PIN_ONE,OUTPUT);
        pinMode(LED_PIN_TWO, OUTPUT);
    
        // Resistors
        // digitalWrite(LED_PIN_ONE, HIGH);
        digitalWrite(LED_PIN_TWO, HIGH);
    
        // Temp sensor and neopixel
        dht.begin();
        pixels.begin();
    
        // Button
        pinMode(BUTTON_PIN, INPUT );
        digitalWrite(BUTTON_PIN, HIGH); // Comment if interrupt
    
        // If you want to use interrupt comment the section "BUTTON SECTION" at the beginning of the loop and use the attachInterrupt function below
        // https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
        //attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), changeMode, CHANGE);
    
        // DISPLAY
        // assign default color value
        if ( u8g.getMode() == U8G_MODE_R3G3B2 )
        u8g.setColorIndex(255); // white
        else if ( u8g.getMode() == U8G_MODE_GRAY2BIT )
        u8g.setColorIndex(3); // max intensity
        else if ( u8g.getMode() == U8G_MODE_BW )
        u8g.setColorIndex(1); // pixel on
    }
    
    
    void loop() {
        // DEBOUNCE - BUTTON SECTION
        // read the state of the switch into a local variable:
        int reading = digitalRead(BUTTON_PIN);
    
        // check to see if you just pressed the button
        // (i.e. the input went from LOW to HIGH), and you've waited long enough
        // since the last press to ignore any noise:
    
        // If the switch changed, due to noise or pressing:
        if (reading != lastButtonState) {
        // reset the debouncing timer
        lastDebounceTime = millis();
        }
    
        if ((millis() - lastDebounceTime) > debounceDelay) {
        // whatever the reading is at, it's been there for longer than the debounce
        // delay, so take it as the actual current state:
    
        // if the button state has changed:
        if (reading != buttonState) {
            buttonState = reading;
    
            // CODE for pressing button
            if (buttonState == HIGH) {
            mode++;
            // Choose how many modes you want to have
            // It starts with 0
            if (mode > 1) {
                mode = 0;
                count = 1000;
            }
            }
        }
        }
    
        // save the reading. Next time through the loop, it'll be the lastButtonState:
        lastButtonState = reading;
        
        // BUTTON SECTION - OLD
        /*
        if (digitalRead(BUTTON_PIN)) {
    
        mode++;
        // Choose how many modes you want to have
        // It starts with 0
        if (mode > 1) {
            mode = 0;
            count = 1000;
        }
    
        delay(500);
        }
        // -------------------------
        */
    
        
        Serial.println(count);
        // Delay is like a counter but without stopping the program
        if (count >= 1000) {
        count = 0;
    
        //Activate resistor
        digitalWrite(PHOTOCELL, HIGH);
    
        // Value of photocell
        photocellReading = analogRead(PHOTOCELL);
    
        //Map values from 0 to 100; 0 is dark
        brightness = map(photocellReading, 0, 1023, 100, 0);
    
        // For the serial monitor
        //Serial.print("Analog Photocell: ");
        //Serial.println(brightness);
    
        // Reading temperature or humidity takes about 250 milliseconds!
        // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
        h = dht.readHumidity();
        t = dht.readTemperature();
    
        // Check if any reads failed and exit early (to try again).
        if (isnan(h) || isnan(t)) {
            //Serial.println("Failed to read from DHT sensor!");
            return;
        }
    
        // Print to serial monitor
        /*Serial.print("Humidity: ");
            Serial.print(h);
            Serial.println(" %\t");
            Serial.print("Temperature: ");
            Serial.print(t);
            Serial.println(" *C \n");*/
    
        // Change the color values of the pixels
        // Define the modes here
        for (int i = 0 ; i < NUMPIXELS; i++) {
            if (mode == 0) {
            // Temperature
            red = 0;
            green = 0;
            blue = 0;
    
            if ( t >= 15 ) {
                // Temperature above 15 *C are 255 red and blue values
                // RED - VIOLET
                // MAP temp between 15 and 35 and put it in a new value between 255 and 0
                // Choose whatever values you want at your borders
                red = 255;
                green = 0;
                blue = map(t, 15, 35, 255, 0);
            } else if ( t < 15 && t >= 0) {
                // VIOLET - BLUE
                red = map(t, 15, 0, 255, 0);
                green = 0;
                blue = 255;
            } else if ( t < 0 ) {
                // WHITE because it is cold
                red = 255;
                green = 255;
                blue = 255;
            }
            } else if (mode == 1) {
            // Humidity
            if ( h >= 60 ) {
                // Humidity above 65% should be yellow and go to red by 100% because of mold risk
                red = 255;
                green = map(h, 60, 100, 255, 0);
                blue = 0;
            } else if ( h < 65) {
                // Humidity is ok and green
                red = map(h, 65, 0, 255, 0);
                green = 255;
                blue = 0;
            }
            }
    
            // Set pixel brightness
            // If it is completely dark I do not want the neopixels to be off, I chose 20 as a minimum value
            neoBrightness = map(photocellReading, 0, 1023, 255, 20);
            pixels.setBrightness(neoBrightness);
    
            // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
            pixels.setPixelColor(i, pixels.Color(red, green, blue));
            pixels.show(); // This sends the updated pixel color to the hardware.
    
            //delay(250); // Delay for a period of time (in milliseconds).
        }
    
        // picture loop for the display
        u8g.firstPage();
        do {
            draw();
        } while ( u8g.nextPage() );
    
        // delay(10);
        }
        count++;
    }
    
    void draw(void) {
        // graphic commands to redraw the complete screen should be placed here
        u8g.setFont(u8g_font_6x12);
    
        // Position of the text: x , y
        u8g.drawStr( 12, 12, "Temperature:");
    
        // Cast numbers to text
        char temp[5];
        sprintf (temp, "%d", (int)t);
    
        // Positioning of the numbers to be right aligned
        if (t >= 10 || t <= (-10)) {
        u8g.drawStr( 90, 12, temp);
        } else {
        u8g.drawStr( 96, 12, temp);
        }
        u8g.drawStr( 108, 12, "C");
    
        u8g.drawStr( 12, 24, "Humidity:");
        char hum[5];
        sprintf (hum, "%d", (int)h);
        if (h >= 10) {
        u8g.drawStr( 90, 24, hum);
        } else {
        u8g.drawStr( 96, 24, hum);
        }
        u8g.drawStr( 108, 24, "%");
    
        u8g.drawStr( 12, 36, "Brightness:");
        char dark[5];
        sprintf (dark, "%d", (int)brightness);
    
        //check how long brightness is
        if (brightness >= 10 && brightness < 100) {
        u8g.drawStr( 90, 36, dark);
        } else if (brightness < 10) {
        u8g.drawStr( 96, 36, dark);
        } else {
        u8g.drawStr( 84, 36, dark);
        }
        u8g.drawStr( 108, 36, "%");
    
        u8g.drawStr( 12, 56, "Mode:");
        if (mode == 0) {
        u8g.drawStr( 44, 56, "Temperature");
        } else if (mode == 1) {
        u8g.drawStr( 44, 56, "Humidity");
        }
    }
    
    
    // INTERRUPT
    void changeMode() {
        //delay(500);
        mode++;
        // Choose how many modes you want to have
        // It starts with 0
        if (mode > 1) {
        mode = 0;
        }
        delay(500);
    }
                    
                

Conclusion

First the video of my final project:

Unfortunately I did not know that the neopixel in combination with the display need a lot of power. I could not use a battery because the power was not stable enough. I would have to make another board to guarantee a stable power supply with a battery. But there was not enough time to make a new board. I connected it now with an ftdi cable and a powerbank which is also a nice method because I can recharge the powerbank or connect it directly to a plug. Also the temperature sensor is not that accurate. As you can see in the video it jumps a long time until it reaches the right temperature.
I would also make a much smaller box and an easier printable design.
And by time I will add a few more modes because I would like to have more colors. I really like the neopixel.
But all in all it works.




Downloadfiles

  1. weather_station_lamp.zip