Thermometer & Hygrometer on ATMEGA 328P-MU - Raising the Arduino Development Level

    Today I want to share one of my Arduino projects. Once upon a time, not very long ago, somewhere on the Internet, I found out about Arduino. I joined this business quite quickly, the level of entry there is not high. After some time, having already collected a bunch of sensors, sensors for a smart home began to catch myself thinking that somehow it was all out of mind. Modules, large plain boxes, a bunch of wires and hot glue :). Looking at my box with a temperature sensor and, for example, at the temperature sensor of the same Xiaomi, I realized that I would like it to look like Xiaomi, but at the same time that you could reprogram it like mine in a box measuring 10cm by 6cm with wires and hotmelt adhesive. And probably then the beginning of my DIY arduino projects on PCB boards was laid.

    In today's article, we will talk about a temperature and humidity sensor based on the atmega328p-mu processor. This is a “smaller” version (an absolute analogue) of the atmega328p-au processor (Arduino Uno, Pro Mini, Nano) known to all arduino developers. If someone has read my articles before, they know that I prefer Mysensors. What is it? This is a very simple and well-designed and, what is important, well-described library for Arduino IDE (and not only) for creating IOT radio networks at frequencies of 2.4 GHz, 915, 868, 433 MHz, as well as wired networks on the 485 interface, maybe not all mentioned The protocol is constantly evolving, something is being added all the time.

    The first thing that was done was the sensor itself on the PCB board. I did it without looking at the case, according to the principle, the main thing is to make a sensor, and I’ll print the case somehow ... yeah, don’t do that :). In fact, the sensor itself is the same Arduinka Pro Mini, the nRF24l01 radio module, the SHT20 temperature and humidity sensor, only without wires and hot glue. Of the “bells and whistles”, this is an external SPI flash drive for flashing through the air (the DualOptibut bootloader is required for operation, later I stopped putting them (flash drives) on the circuit packs, as there are no firmware over the air and half the battery) and the “crypto mic” ATSHA204A, so to speak, for full hardware kit (in Mysensors, to activate signatures, encryption, etc., you just need to specify the necessary #def at the beginning of the sketch).



    The board itself was made in the Diptrace program, after watching the video tutorials on YouTube, at first it seemed like something "hellish", but in reality it turned out to be not so difficult. I ordered boards in China on jlcpcb.com, 2 bucks, any color, and after 2 weeks you already get 10 pieces of “your own creation” on hand :).





    The next step was the development of the body. Oh, this turned out to be the same problem. In general, I did not look for easy ways, I decided to master the Solid Works. As it turned out, this is not at all like with Deeptrace. Nevertheless, I recommend this editor for study. The learning process lasted for a month of leisurely watching video lessons on YouTube and repeating the lesson in the editor. In the process of developing the case, it became clear that making a device board without taking into account the parameters of the future case is a bad decision, from the series we insert the sticks into our own wheels. According to the results of the board versions, three came out, taking into account the installation of the board in the case, and I think this is not the last option.

    At the beginning of the development of the building, the idea was to print it on an FDM 3D printer, but the further into the forest the more it became clear that it was not able to reproduce all my Wishlist. By the time this understanding came I already learned about another 3D printing technology - SLA. Without thinking twice and impressed with the print quality, the Wishlist on Ali - ANYCUBIC Photon was issued . (Link ana Ali, not advertising, not affiliate, ... just a link).

    I’ll write right away, now, based on my experience at the time of this writing, ... oh, this cool thing is a cool thing !!! The case which was designed in the editor of course and was printed not the first time and there were a lot of improvements. Well, it’s probably not the other way. As a result, I got the result that I wanted. Quite a miniature device, a good DIY case with very precise details, buttons, fonts, everything as represented in the head. A magnet was added to the back cover, now it can be easily mounted on iron surfaces.

















    These are attempts to print the same model on an FDM printer:





    Since the device turned out to be small, but it is still an arduinka, I was puzzled by the output of miniature connectors for programming. And accordingly, a small adapter was made for the connectors for convenient connection with the programmer and TTL converter.



    Everything was purchased on Ali (yes, there are not only full arduino modules there)

    SMD tantalum capacitor 4.7uF - 4.7uF | 10v | 10% - C1
    SMD ceramic capacitor 100nF | Y5V - 100nF | 50v | + 80-20% - C2, C3, C4, C5, C6, C7
    LED - LED SIDE - D1
    Pin Header Female - 2x3P | 6pin | 1.27mm - J1, J2
    SMD resistor 20K Ohm - 20K | 5% - R1
    SMD resistor 4.7K Ohm - 4.7K | 5% - R2, R3, R4
    SMD resistor 470K Ohm - 470 | 1% - R5
    SMD resistor 1M Ohm - 1M | 1% - R6
    SMD resistor 18K Ohm - 18K | 5% - R7
    SMD resistor 10K Ohm - 10K | 5% - R8
    4-pin SMD side button - SW1, SW2
    512-Kbit, 1.65V SPI Serial Flash Memory - AT25DF512C-SSHN-B - U1
    Mini NRF24L01 + 2.4GHz 1.27MM RF - nRF24l01 1.27 SMD - U2
    ATMEGA328P-MU QFN32 - U3
    CRYPTO AUTHENTICATION, 1 WIRE - ATSHA204A-STUCZ-T - U4
    Humidity and Temperature Sensor IC - SHT20 - U5
    BATTERY HOLDER FOR CR2477-1 - L-KLS5-CR2477-1 - U6

    The program code is quite simple. An example of the DFRobot library was used to work with the SHT20 sensor. In principle, any sketch, for any sensor can be turned into a sketch for work in the Mysensors network in 5 minutes.

    Code listing
    #include 
    #include "DFRobot_SHT20.h"
    DFRobot_SHT20    sht20; // https://github.com/DFRobot/DFRobot_SHT20
    #define MY_DEBUG
    //#define MY_DISABLED_SERIAL
    #define MY_RADIO_RF24
    #define MY_PASSIVE_NODE
    #define MY_NODE_ID 200
    #define MY_PARENT_NODE_ID 0
    #define MY_PARENT_NODE_IS_STATIC
    #define MY_TRANSPORT_UPLINK_CHECK_DISABLED
    //#define MY_OTA_FIRMWARE_FEATURE
    //#define MY_SIGNING_ATSHA204
    //#define MY_SIGNING_ATSHA204_PIN A3
    //#define MY_SIGNING_REQUEST_SIGNATURES
    #define TEMP_SENS_ID 1
    #define HUM_SENS_ID 2
    #define SETTING_LED_SENS_ID 100
    #define DELAY_TIME_SENS_ID 101
    #define BATTARY_SEND_SENS_ID 102
    #define BATTARY_DATA_SENS_ID 103
    #define BAT_COOF 3.04
    #define BAT_MIN 195
    #define BAT_MAX 295
    #define ON 1
    #define OFF 0
    float humd;
    float temp;
    float oldhumd;
    float oldtemp;
    float tempThreshold = 0.5;
    float humThreshold = 10.0;
    static uint32_t lightMillis;
    static uint32_t previousMillis;
    uint32_t send_batteryTime;
    uint32_t w_battetyTime = 0;
    static uint8_t led_pin = 4;
    static uint8_t mode_pin = 2; // interrupt
    uint32_t delayTime;
    int8_t battery;
    int8_t old_battery;
    uint8_t set_led;
    boolean sleep_mode;
    boolean configMode = 0;
    int8_t timer_status = 0;
    bool flag_mode_button = 0;
    bool sleep_flag = 0;
    bool listen_flag = 0;
    #include 
    MyMessage msg_temp(TEMP_SENS_ID, V_TEMP);
    MyMessage msg_hum(HUM_SENS_ID, V_HUM);
    MyMessage msg_setting_led(SETTING_LED_SENS_ID, V_VAR1);
    MyMessage msg_delay_time(DELAY_TIME_SENS_ID, V_VAR1);
    MyMessage msg_battary_send(BATTARY_SEND_SENS_ID, V_VAR1);
    MyMessage powerMsg(BATTARY_DATA_SENS_ID, V_VAR1);
    void preHwInit()
    {
      pinMode(led_pin, OUTPUT);
      digitalWrite(led_pin, OFF);
      pinMode(mode_pin, INPUT_PULLUP);
    }
    void before()
    {
      set_led = loadState(100);
      if (set_led > 1) {
        set_led = 1;
        saveState(100, set_led);
      }
      delayTime = loadState(101);
      if (delayTime > 60) {
        delayTime = 3;
        saveState(101, delayTime);
      }
      send_batteryTime = loadState(102);
      if (send_batteryTime > 48) {
        send_batteryTime = 6;
        saveState(102, send_batteryTime);
      }
      digitalWrite(led_pin, ON);
    }
    void presentation()
    {
      sendSketchInfo("Temp & Hum Sensor CR2477", "1.0");
      wait(100);
      present(TEMP_SENS_ID, S_TEMP, "TEMPERATURE DATA");
      wait(100);
      present(HUM_SENS_ID, S_HUM, "HUMIDITY DATA");
      wait(100);
      present(SETTING_LED_SENS_ID, S_CUSTOM, "LED MODE");
      wait(100);
      present(DELAY_TIME_SENS_ID, S_CUSTOM, "DELAY TIME/MIN");
      wait(100);
      present(BATTARY_SEND_SENS_ID, S_CUSTOM, "BATTERY SEND TIME/H");
      wait(100);
      present(BATTARY_DATA_SENS_ID, S_CUSTOM, "BATTERY DATA");
    }
    void setup()
    {
      //attachInterrupt(0, configListener, RISING);
      digitalWrite(led_pin, OFF);
      wait(500);
      digitalWrite(led_pin, ON);
      wait(75);
      digitalWrite(led_pin, OFF);
      wait(50);
      digitalWrite(led_pin, ON);
      wait(75);
      digitalWrite(led_pin, OFF);
      wait(50);
      digitalWrite(led_pin, ON);
      wait(75);
      digitalWrite(led_pin, OFF);
      TRANSPORT_DEBUG(PSTR("MyS: OPERATING MODE\n"));
      wait(100);
      readBatLev();
      wait(100);
      sht20.initSHT20();
      wait(100);
      send_data();
      wait(100);
      send(msg_delay_time.set(delayTime));
      wait(100);
      send(msg_setting_led.set(set_led));
      wait(100);
      send(msg_battary_send.set(send_batteryTime));
    }
    void loop()
    {
      if (configMode == 0) {
        if (sleep_flag == 0) {
          timer_status = sleep(digitalPinToInterrupt(mode_pin), FALLING, delayTime * 60 * 1000, false);
          //timer_status = sleep(digitalPinToInterrupt(mode_pin), RISING, delayTime * 60 * 1000, false);
          sleep_flag = 1;
        }
        if (timer_status == -1) {
          w_battetyTime = w_battetyTime + (delayTime * 60 * 1000);
          if (w_battetyTime >= send_batteryTime * 60 * 60 * 1000) {
            readBatLev();
            w_battetyTime = 0;
          }
          send_data();
          sleep_flag = 0;
        }
        if (timer_status == 0) {
          if (digitalRead(2) == LOW && flag_mode_button == 0) //если кнопка нажата
          {
            flag_mode_button = 1;
            previousMillis = millis();
            wait(50);
          }
          if (digitalRead(2) == LOW && flag_mode_button == 1) {
            if ((millis() - previousMillis > 0) && (millis() - previousMillis <= 2000)) {
              if (millis() - lightMillis > 50)    {
                lightMillis = millis();
                digitalWrite(led_pin, !digitalRead(led_pin));
              }
            }
            if ((millis() - previousMillis > 2000) && (millis() - previousMillis <= 2500)) {
              digitalWrite(led_pin, OFF);
            }
            if ((millis() - previousMillis > 2500) && (millis() - previousMillis <= 4500)) {
              if (millis() - lightMillis > 25)    {
                lightMillis = millis();
                digitalWrite(led_pin, !digitalRead(led_pin));
              }
            }
            if (millis() - previousMillis > 4500) {
              digitalWrite(led_pin, OFF);
            }
          }
          if (digitalRead(2) == HIGH && flag_mode_button == 1) //если кнопка НЕ нажата
          {
            if ((millis() - previousMillis > 0) && (millis() - previousMillis <= 2000)) {
              configMode = !configMode;
              flag_mode_button = 0;
              TRANSPORT_DEBUG(PSTR("MyS: CONFIGURATION MODE\n"));
              sleep_flag = 0;
              digitalWrite(led_pin, OFF);
            }
            if ((millis() - previousMillis > 2000) && (millis() - previousMillis <= 2500)) {
              flag_mode_button = 0;
              sleep_flag = 0;
            }
            if ((millis() - previousMillis > 2500) && (millis() - previousMillis <= 4500))
            {
              flag_mode_button = 0;
              sleep_flag = 0;
              digitalWrite(led_pin, OFF);
            }
            if (millis() - previousMillis > 4500) {
              flag_mode_button = 0;
              sleep_flag = 0;
              wait(50);
            }
          }
        }
      } else {
        if (listen_flag == 0) {
          RF24_startListening();
          listen_flag = 1;
        }
        if (millis() - lightMillis > 1000) {
          lightMillis = millis();
          digitalWrite(led_pin, !digitalRead(led_pin));
        }
        if (digitalRead(2) == LOW && flag_mode_button == 0) //если кнопка нажата
        {
          flag_mode_button = 1;
          //previousMillis = millis();
          wait(50);
        }
        if (digitalRead(2) == LOW && flag_mode_button == 1) {
        }
        if (digitalRead(2) == HIGH && flag_mode_button == 1) //если кнопка НЕ нажата
        {
          configMode = !configMode;
          listen_flag = 0;
          flag_mode_button = 0;
          TRANSPORT_DEBUG(PSTR("MyS: OPERATING MODE\n"));
          digitalWrite(led_pin, OFF);
          wait(50);
        }
      }
    }
    void receive(const MyMessage & message)
    {
      if (message.sensor == SETTING_LED_SENS_ID) {
        if (message.type == V_VAR1) {
          if (message.getByte() <= 1) {
            set_led = message.getBool();
            saveState(100, set_led);
            send(msg_setting_led.set(set_led));
            if (set_led == 0) {
              TRANSPORT_DEBUG(PSTR("MyS: STATUS LED: OFF\n"));
            }
            if (set_led == 1) {
              TRANSPORT_DEBUG(PSTR("MyS: STATUS LED: ON\n"));
              if (set_led == 1) {
                digitalWrite(led_pin, ON);
                wait(50);
                digitalWrite(led_pin, OFF);
              }
            }
          }
        }
      }
      if (message.sensor == DELAY_TIME_SENS_ID) {
        if (message.type == V_VAR1) {
          if (message.getULong() <= 60 && message.getULong() != 0) {
            delayTime = message.getULong();
            saveState(101, delayTime);
            send(msg_delay_time.set(delayTime));
            TRANSPORT_DEBUG(PSTR("MyS: THE NEW INTERVAL TEMP&HUM SEND VALUE IS SET: %d MIN.\n"), delayTime);
            if (set_led == 1) {
              digitalWrite(led_pin, ON);
              wait(50);
              digitalWrite(led_pin, OFF);
            }
          } else if (message.getULong() > 60) {
            delayTime = 60;
            saveState(101, delayTime);
            send(msg_delay_time.set(delayTime));
            TRANSPORT_DEBUG(PSTR("MyS: THE NEW INTERVAL TEMP&HUM SEND VALUE IS SET: %d MIN.\n"), delayTime);
            if (set_led == 1) {
              digitalWrite(led_pin, ON);
              wait(50);
              digitalWrite(led_pin, OFF);
            }
          } else if (message.getULong() == 0) {
            delayTime = 1;
            saveState(101, delayTime);
            send(msg_delay_time.set(delayTime));
            TRANSPORT_DEBUG(PSTR("MyS: THE NEW INTERVAL TEMP&HUM SEND VALUE IS SET: %d MIN.\n"), delayTime);
            if (set_led == 1) {
              digitalWrite(led_pin, ON);
              wait(50);
              digitalWrite(led_pin, OFF);
            }
          }
        }
      }
      if (message.sensor == BATTARY_SEND_SENS_ID) {
        if (message.type == V_VAR1) {
          if (message.getULong() <= 168) {
            send_batteryTime = message.getULong();
            saveState(102, send_batteryTime);
            send(msg_battary_send.set(send_batteryTime));
            TRANSPORT_DEBUG(PSTR("MyS: THE NEW INTERVAL BATTERY SEND IS SET: %d HOUR\n"), send_batteryTime);
            if (set_led == 1) {
              digitalWrite(led_pin, ON);
              wait(50);
              digitalWrite(led_pin, OFF);
            }
          }
        }
      }
    }
    void send_data()
    {
      humd = sht20.readHumidity();
      temp = sht20.readTemperature();
      int t_humd = (int)humd;
      int t_temp = (int)temp;
      if (abs(temp - oldtemp) >= tempThreshold) {
        send(msg_temp.set(temp, 1));
        oldtemp = temp;
        if (set_led == 1) {
          digitalWrite(led_pin, ON);
          wait(50);
          digitalWrite(led_pin, OFF);
        }
      }
      wait(100);
      if (abs(humd - oldhumd) >= humThreshold) {
        send(msg_hum.set(humd, 1));
        oldhumd = humd;
        if (set_led == 1) {
          digitalWrite(led_pin, ON);
          wait(50);
          digitalWrite(led_pin, OFF);
        }
      }
      TRANSPORT_DEBUG(PSTR("MyS: DATA - TEMPERATURE: %d, HUMIDITY %d\n"), t_temp, t_humd);
    }
    void readBatLev() {
      ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX0);
      wait(100);
      RF24_startListening();
      wait(200);
      ADCSRA |= _BV(ADSC);
      while (bit_is_set(ADCSRA, ADSC));
      uint8_t low  = ADCL;
      uint8_t high = ADCH;
      long temp = (high << 8) | low;
      float vcc = temp * 1.1 / 1023 * BAT_COOF * 100;
      battery = map((int)vcc, BAT_MIN, BAT_MAX, 0, 100);
      if (battery < 0) {
        battery = 0;
      }
      if (battery > 100) {
        battery = 100;
      }
      TRANSPORT_DEBUG(PSTR("MyS: BATTERY LEVEL: %d, PREVIUS BATTERY LEVEL: %d\n"), battery, old_battery);
      TRANSPORT_DEBUG(PSTR("MyS: BATTERY LEVEL ADC: %d\n"), temp);
      /*
        if (old_battery != battery) {
          if (battery < old_battery) {
            old_battery = battery;
            wait(100);
            sendBatteryLevel(battery);
            wait(100);
            send(powerMsg.set(temp));
            TRANSPORT_DEBUG(PSTR("MyS: SEND BATTERY LEVEL\n"));
          } else {
            battery = old_battery;
          }
        }
      */
      wait(100);
      sendBatteryLevel(battery);
      wait(100);
      send(powerMsg.set(temp));
      TRANSPORT_DEBUG(PSTR("MyS: SEND BATTERY LEVEL\n"));
    }
    


    The video shows a test of the sensor:


    Sensor operation with the Majordomo:


    The controller of the smart home is Majordomo (I think many people know the system), the Mysensors module is written (perhaps one of the best implementations of the Mysensors protocol support in controllers)



    The project is naturally open and recommended for repetition. All development details, board files, firmware, 3d models of the case are available on the website www.openhardware.io . For questions about this development, about the difficulties in your development, the Arduino and Mysensors will always come to the rescue in our telegram chat - t.me/mysensors_rus .

    Also popular now: