Weather Station Project Signal name: ANO wind direction, ANO wind Speed Team Anonymous Mohammad Ahsan Nazmul MD Mahmadul Hasan Technical document April 2023 Course name : Embedded System Software Engineering Program 2023 Table of contens 1 Hardware ........................................................................................................... 3 2 Software............................................................................................................. 4 2.1 Code Explanation ....................................................................................... 4 2.2 Verification ................................................................................................ 6 Whole Code ............................................................................................................. 8 1 Hardware The purpose of this project is to gain foundational knowledge in embedded systems and create a functional weather station, with the aim of successfully completing the course. we believe that this project will add value to our portfolio and enhance our understanding of the subject matter. The primary objective is to measure wind direction and wind speed using the weather station. To create a weather station project that can measure wind direction and speed, the following hardware components were used: o Breadboard - It provides a platform to connect different components of the project together without the need for soldering. It is a convenient way to create circuits and prototypes. o LCD Display 16x2 - It displays the measured values of wind direction and speed. It is a standard display module. o Arduino Mega - It is a microcontroller board that provides the processing power for the project. It receives data from the sensors and uses the data to calculate the wind direction and speed. It then displays the measured values on the LCD screen. o Keypad 4x4 - It is used to input values. We used it to display different values on the display. It is a standard keypad with four rows and four columns. o Ethernet module - It enables the weather station to connect to the internet and share the measured data. It is a network module that allows the weather station to communicate with the broker (Raspberry Pi ) and send message to database. o Wires , Connectors, Resistors – Besides We used wires, 1 resistor and different connector cables to make the connection. 2 Software We have used mainly Arduino IDE and it’s Libraries to do the coding. Besides this we have also practiced with www.tinkercad.com website at the beginning to get an idea of the circuit board. We have used C++ Programming Language for the project. o Arduino IDE - This is a software development environment used to write and upload code to the Arduino microcontroller. It is the main software used to program the microcontroller. It can be downloaded from the official Arduino website and is available for free. o Arduino libraries - These are pre-written code modules that provide a set of functions to make programming the microcontroller easier. There are many libraries available for Arduino that can be downloaded and included in the code. The following libraries are used for this project: 1. LiquidCrystal.h - This library provides functions to control the LCD display. 2. Keypad.h - This library provides functions to read inputs from the keypad. 3. Ethernet.h - This library used to enable the Arduino Mega to connect to the internet and communicate with other devices using the W5100 Ethernet module 4. ArduinoJson.h - The ArduinoJson library is used to manipulate JSON (JavaScript Object Notation) data, which is a lightweight data format often used for web application 5. TimerOne.h – This is used to set up and manage timers on the microcontroller. 6. PubSubClient.h – This is used to implement the MQTT (Message Queuing Telemetry Transport) protocol. This library allows the Arduino Mega to publish and subscribe to topics on an MQTT broker. 2.1 Code Explanation Part 1: At beginning the code imports libraries and declares variables for various components used in the project, including Ethernet, MQTT, LCD display, keypad, and ArduinoJson. It also defines the pin connections for the LCD display and keypad, sets up keypad button press initial conditions, and initializes variables for wind speed and direction. In addition, it sets up the MQTT client object with the required parameters, such as the IP address of the MQTT server, port number, client ID, username, and password. Finally, it defines the input and output MQTT topic names for receiving and sending data. Part 2: In the Setup() section we used fetch_IP() function to initialize Ethernet connection. Connect_MQTT_server() function is called to connect to the MQTT server. The LCD is initialized with 16 columns and 2 rows. Pin A2 is set as an input. The variables related to wind speed calculation are initialized to 0. The pulseCountFunction() function is attached as an interrupt service routine to the signal input pin (pin 2) with a trigger condition of a rising edge. The timerInterruptFunction() function is attached as an interrupt service routine to the Timer1 interrupt, which is set to trigger every 1 second. Using this two interrupt we calculated the windFrequency and the calculated the Wind speed using formula as windSpeed = -0.24 + windFrequency * 0.699; Part 3: The main loop of the program reads the keypad input (1,2 or 3) to determine whether to display wind direction, speed, or both on the LCD. It then reads the wind direction and map it in 360 degree values and shows the direction using a range of respective degree values. wind speed is calculated based on the number of pulses received from Arduino mega pin 2 within 1 second and using timer interrupt function . Then we calculated the frequency and speed as follows: void timerInterruptFunction() { timeCount++; if (timeCount >= 19) { // Perform wind speed calculation every 5 seconds windFrequency = pulseCount / 10; // Multiply pulse count by 2 to get frequency in Hz pulseCount = 0; // Reset pulse count windSpeed = -0.24 + windFrequency * 0.699; // Calculate wind speed using equation timeCount = 0; // Reset time counter } } If the MQTT client is connected to the broker, it sends the wind direction and speed data to the broker using the send_MQTT_message() function. If the client is not connected, it attempts to reconnect to the broker. The send_MQTT_message() function is responsible for formatting and sending the wind direction and speed data to the broker using MQTT protocol. Callback function is used for receiving MQTT messages and printing them to the Serial Monitor and LCD 2.2 Pic:1 Pic-2 Verification Picture 1 and 2 showing wind direction and wind speed sent to the server. Pic:3 Picture of the Weather station board Whole Code #include <Ethernet.h> #include <PubSubClient.h> #include <TimerOne.h> // incluide Ethernet library W5100 // include MQTT library //timer function #include <Keypad.h> #include <LiquidCrystal.h> #include <ArduinoJson.h> //-----------------------------------------------------------------LiquidCrystal lcd(12, 11, 35, 34, 33, 32);// LCD display pin connection //--------keypad settings---------const byte ROWS = 4; const byte COLS = 4; char keys[ROWS][COLS] = { {'1','2','3','A'}, {'4','5','6','B'}, {'7','8','9','C'}, {'*','0','#','D'} }; byte rowPins[ROWS] = { 38, 39, 40, 41 }; byte colPins[COLS] = { 42, 43, 44, 45 }; Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); //-------button press conditions---------------volatile boolean buttonPressed1 = false; volatile boolean buttonPressed2 = false; volatile boolean buttonPressed3 = false; // Define variables String windDirection; bool displayDirection; bool displaySpeed; bool displayBoth; int degree_Value; volatile unsigned long pulseCount; volatile unsigned long windFrequency; float windSpeed; unsigned long timeCount; // Define pin numbers const int signalInputPin = 2; EthernetClient ethClient;// Ethernet object variable static uint8_t mymac[6] = { 0x44,0x76,0x58,0x10,0x00,0x62 }; // -------- MQTT settings-----------unsigned int Port = 1883; byte server[] = { 10,6,0,21 }; char* deviceId char* clientId // MQTT port number // TAMK IP = "esdevice23";// * set your device id (will be the MQTT client username) = "escg223"; // * set a random string (max 23 chars, will be the MQTT client id) char* deviceSecret = "tamk"; // * set your device secret (will be the MQTT client password) // MQTT Server settings void callback(char* topic, byte* payload, unsigned int length); // subscription callback for received MQTTT messages PubSubClient client(server, Port, callback, ethClient); // mqtt client // MQTT topic names #define inTopic "ICT4_in_2020" // * MQTT channel where data are received #define outTopic "ICT4_out_2020" // * MQTT channel where data is send // ---------------- Main SETUP section-----------------------------void setup() { Serial.begin(9600); // Serial monitor baudrate = 9600 Serial.println("Start 20.1.2023"); // print to serial monitor delay(500); fetch_IP(); // initialize Ethernet connection Connect_MQTT_server(); // connect to MQTT server lcd.begin(16, 2); pinMode(A2, INPUT); //wind speed void setup pulseCount = 0; windFrequency = 0; windSpeed = 0.0; timeCount = 0; // Attach interrupt function to signal input pin attachInterrupt(digitalPinToInterrupt(signalInputPin), pulseCountFunction, RISING);//counting pulse on rising // Set up timer for 1 second intervals Timer1.initialize(1000000); Timer1.attachInterrupt(timerInterruptFunction); //These two intterupt helps to calculate the frequency } void loop(){ // Check if 1,2 or 3 button is pressed to switch between wind direction, speed display or both char key = keypad.getKey(); if(key == '1'){ buttonPressed1 = true; buttonPressed2 = false; buttonPressed3 = false; } else if(key == '2'){ buttonPressed1= false; buttonPressed2 = true; buttonPressed3 = false; } else if(key == '3'){ buttonPressed1= false; buttonPressed2 = false; buttonPressed3 = true; } else { buttonPressed1= false; buttonPressed2 = false; buttonPressed3 = false; } //-------------------------------------// if(buttonPressed1 == true){ displayDirection = true; displaySpeed = false; displayBoth = false; } else if(buttonPressed2 == true){ displayDirection = false; displaySpeed = true; displayBoth = false; } else if(buttonPressed3 == true){ displayDirection = false; displaySpeed = false; displayBoth = true; } //---- Read wind direction and speed from sensors and display on LCD--lcd.setCursor(20, 1); lcd.print("1-Dir 2-Spd 3-Both"); float analog_Value = analogRead(A2); float voltage_Value = 5 * analog_Value / 1023; if (voltage_Value < 1.2) { degree_Value = 0; } else{ degree_Value = ((voltage_Value - 1.2) / 3.8) * 360; } if (displayDirection == true) { lcd.setCursor(0, 0); if (voltage_Value <= 1.19) { windDirection = "Loading...."; } else if (degree_Value >= 337.5 || (degree_Value > -1 && degree_Value <= 22.4)) { windDirection = "North"; } else if (degree_Value > 22.4 && degree_Value <= 67.5) { windDirection = "North East"; } else if (degree_Value > 67.5 && degree_Value <= 112.5) { windDirection = "East"; } else if (degree_Value > 112.5 && degree_Value <= 157.5) { windDirection = "South East"; } else if (degree_Value > 157.5 && degree_Value <= 202.5) { windDirection = "South"; } else if (degree_Value > 202.5 && degree_Value <= 247.5) { windDirection = "South West"; } else if (degree_Value > 247.5 && degree_Value <= 292.5) { windDirection = "West"; } else { windDirection = "North West"; } lcd.clear(); // clear the display before printing new data lcd.setCursor(0, 0); lcd.print("Dir : "); lcd.print(windDirection); lcd.setCursor(0, 1); lcd.print(degree_Value); lcd.print(" degrees"); lcd.setCursor(20, 1); lcd.print("1-Dir 2-Spd 3-Both"); } else if (displaySpeed == true){ lcd.clear(); // clear the display before printing new data lcd.setCursor(0, 0); lcd.print("Wind Speed: "); lcd.setCursor(0, 1); lcd.print("Freq: "); lcd.setCursor(7, 1); lcd.print(windFrequency); lcd.setCursor(13, 1); lcd.print("Hz"); lcd.setCursor(13, 0); lcd.print(windSpeed); lcd.setCursor(20, 1); lcd.print("1-Dir 2-Spd 3-Both"); } else if (displayBoth == true){ lcd.clear(); // clear the display before printing new data lcd.setCursor(0, 0); lcd.print("Dir : "); lcd.print(degree_Value); lcd.setCursor(0, 1); lcd.print("Wind Speed: "); lcd.setCursor(13, 1); lcd.print(windSpeed); lcd.setCursor(20, 1); lcd.print("1-Dir 2-Spd 3-Both"); } // ----------------------Send MQTT message to broker----------------------------------------------if (client.connected()){ const char* windDirections = windDirection.c_str(); send_MQTT_message(degree_Value,windSpeed);// call MQTT routine function } else { client.connect(clientId, deviceId, deviceSecret);// Reconnect if connection is lost Serial.println("No, re-connecting" ); } // Delay before checking again delay(1000); } // -------GET IP number from DHCP server-----------void fetch_IP(void){ byte rev=1; rev=Ethernet.begin(mymac);// get IP number Serial.print( F("\nW5100 Revision ") ); if (rev == 0){ Serial.println( F( "Failed to access Ethernet controller" ) ); } Serial.println( F( "Setting up DHCP" )); Serial.print("Connected with IP: "); Serial.println(Ethernet.localIP()); } // ----------MQTT Routines--------------------void send_MQTT_message(int dir, float spd){ // Send MQTT message char bufa[50]; char fufa[50]; char sufa[50]; int timeDelay = 1000; // This is for adjust the time delay of MQTT + JSON transferring if (displayDirection == true){ const size_t bufferSize = JSON_OBJECT_SIZE(2) + 30; StaticJsonDocument<bufferSize> jsonDoc; jsonDoc["S_name1"] = "ANO Wind Direction"; jsonDoc["S_value1"] = dir; String jsonStr; serializeJson(jsonDoc, jsonStr); char message[jsonStr.length() + 10]; sprintf(message, "IOTJS=%s", jsonStr.c_str()); Serial.println( message ); client.publish(outTopic,message); delay(timeDelay); } else if (displaySpeed == true){ const size_t bufferSize = JSON_OBJECT_SIZE(2) + 30; StaticJsonDocument<bufferSize> jsonDoc; jsonDoc["S_name1"] = "ANO Wind Speed"; jsonDoc["S_value1"] = spd; String jsonStr; serializeJson(jsonDoc, jsonStr); char message[jsonStr.length() + 10]; sprintf(message, "IOTJS=%s", jsonStr.c_str()); // create message with header and data Serial.println( message ); client.publish(outTopic,message); delay(timeDelay); } else if (displayBoth == true) { // --------Adding wind direction here---------------const size_t bufferSize = JSON_OBJECT_SIZE(2) + 30; StaticJsonDocument<bufferSize> jsonDoc; jsonDoc["S_name1"] = "ANO Wind Direction"; jsonDoc["S_value1"] = dir; String jsonStr; serializeJson(jsonDoc, jsonStr); char message[jsonStr.length() + 10]; sprintf(message, "IOTJS=%s", jsonStr.c_str()); // create message with header and data Serial.println(message); client.publish(outTopic, message); //--------------- Adding wind speed here----------------jsonDoc.clear(); // Clear the previous data from the JSON document delay(500); StaticJsonDocument<bufferSize> jsonDoc2; jsonDoc2["S_name1"] = "ANO Wind Speed"; jsonDoc2["S_value1"] = spd; String jsonStr2; serializeJson(jsonDoc2, jsonStr2); char message2[jsonStr2.length() + 10]; sprintf(message2, "IOTJS=%s", jsonStr2.c_str()); // create message with header and data Serial.println( message2 ); client.publish(outTopic,message2); delay(timeDelay); } } // -------------------MQTT server connection -------------------------------void Connect_MQTT_server(){ Serial.println(" Connecting to MQTT" ); Serial.print(server[0]); Serial.print("."); // Print MQTT server IP number to Serial monitor Serial.print(server[1]); Serial.print("."); Serial.print(server[2]); Serial.print("."); Serial.println(server[3]); // delay(500); if (!client.connected()){ // check if allready connected if (client.connect(clientId, deviceId, deviceSecret)){ // connection to MQTT server Serial.println(" Connected OK " ); client.subscribe(inTopic); // subscript to in topic } else{ Serial.println(client.state()); } } } // ------------------Receive incoming MQTT message ----------------------------void callback(char* topic, byte* payload, unsigned int length){ char* receiv_string; // copy the payload content into a char* receiv_string = (char*) malloc(length + 1); memcpy(receiv_string, payload, length); // copy received message to receiv_string receiv_string[length] = '\0'; Serial.println( receiv_string ); free(receiv_string); } //------------------- Interrupt function to count pulses--------------------------void pulseCountFunction() { pulseCount++; } //----------- Interrupt function to calculate wind frequency on every seconds-----void timerInterruptFunction() { timeCount++; if (timeCount >= 10) { // when 10 seconds is elapsed we calculate the frequency windFrequency = pulseCount / 10; // frequency is calculated from (total number of pulse)/ elasped time(every 10 sec) pulseCount = 0; // Reset pulse count windSpeed = -0.24 + windFrequency * 0.699; // Calculate wind speed using equation timeCount = 0; // Reset time counter } }