Enhiker | a Portable Smart Weather Decision Maker
by Mukesh_Sankhla in Workshop > 3D Printing
7997 Views, 88 Favorites, 0 Comments
Enhiker | a Portable Smart Weather Decision Maker
Enhiker is a compact, 3D-printed device designed to assist outdoor enthusiasts in making informed decisions about their environment. Powered by Unihiker from DFRobot, it integrates environmental and GNSS sensors to assess real-time weather conditions and location data. Enhiker provides users with a clear recommendation on whether it's safe to set up camp or continue outdoor activities based on health and safety parameters.
The device features a touchscreen display that shows a comprehensive set of weather and environmental metrics, including Current date and time in UTC, Temperature, Humidity, Ultraviolet Intensity, Luminous Intensity, Atmospheric Pressure, Elevation, Latitude & Longitude, Altitude, Heat Index, Satellites. When connected to WiFi, Enhiker accesses additional data from openweathermap API, displaying: Air Quality, Wind Speed, Wind Direction, Sunrise & Sunset, Cloud Coverage.
Beyond its weather-monitoring capabilities, Enhiker functions as an emergency power bank and can be recharged via solar panel, making it an essential tool for any outdoor adventure. Additionally, it logs all collected data with timestamps and location coordinates into a CSV file, making it valuable for meteorological and agricultural research.
Enhiker is a versatile and reliable companion for anyone exploring the great outdoors, ensuring safety and preparedness in any environment.
Sponsored By NextPCB
This project was made possible thanks to the support from NextPCB, a trusted leader in the PCB manufacturing industry for over 15 years. Their engineers work diligently to produce durable, high-performing PCBs that meet the highest quality standards. If you're looking for top-notch PCBs at an affordable price, don't miss NextPCB!
NextPCB’s HQDFM software services help improve your designs, ensuring a smooth production process. Personally, I prefer avoiding delays from waiting on DFM reports, and HQDFM is the best and quickest way to self-check your designs before moving forward.
Explore their DFM free online PCB Gerber viewer for instant design verification:
Supplies
1x Unihiker
1x GNSS GPS BeiDou Receiver Module
1x 2 Cell 18650 Battery Holder
2x Type-C L-Shaped Male to Female Extension Cable
Tools:
Software's:
CAD & 3D Printing
To begin, I designed the Enhiker using Fusion 360. You can view the design directly in your browser and download it to open in Fusion 360 for any modifications or creative additions. Feel free to customize and enhance the design as you like!
For 3D printing, you can directly download the STL files listed below:
- 1x Housing.stl
- 1x Cover.stl
- 1x Board_Plate.stl
- 1x Antenna_Cover.stl
- 3x Button1.stl
- 1x Button2.stl
- 4x Spacer.stl
I used my Anycubic Kobra printer with matte black PLA filament for the printing process.
Power Connection
To power the Enhiker, we are using a "2 Cell 18650 Battery Manager". Since the Unihiker can be powered via USB Type-C, we’re utilizing a Type-C extension cable, which has been modified to serve two purposes: providing power from the batteries and connecting to a PC for accessing and programming the Unihiker.
To modify the extension cable, follow these steps:
- Take two male-to-female jumper wires and cut them in half.
- Remove the heat shrink from the female side of the extension cable.
- Solder the male jumper wires as shown in the diagram above.
- Once soldered, cover the exposed parts with heat shrink tubing to ensure safety.
Antenna Assembly
Position the antenna in its designated spot, ensuring that the antenna wire is routed inside the housing.
Place the 3D-printed antenna cover over the antenna.
Secure the cover in place using a screw.
Board Plate Assembly
Next, let's prepare the board plate, which serves as the skeleton of the project and the mounting base for all components.
Take the 4 spacers that were 3D-printed.
Secure each spacer to the bottom side of the board plate using M3 screws.
Sensors Assembly
In this step, we’ll mount the Environmental and GNSS sensors onto the base plate.
Position the GNSS sensor in the center of the base plate towards the bottom side.
Align the holes on the sensor with those on the base plate.
Secure the GNSS sensor using 4 M3 screws.
Place the Environmental sensor onto the designated bracket on the base plate and secure it in place using 2 M3 screws.
Unihiker Assembly
Now it's time to mount the Unihiker onto the base plate.
Before mounting, plug in the Type-C extension cable that was prepared earlier.
Position the Unihiker on the base plate, aligning it with the three designated holes.
Route the cable through the designed slot on the base plate.
Secure the Unihiker in place using M3 screws.
Sensors Connection
Connecting the sensors to the Unihiker is simple and hassle-free, thanks to Unihiker’s plug-and-play connectors, which make it easy to connect DFRobot sensors without worrying about wire alignment.
Use two 4-pin connectors that came with the Unihiker to plug both the Environmental and GNSS sensors into the Unihiker as shown in the diagram.
These sensors will communicate with the Unihiker via the I2C protocol.
Ensure that the small switch on each sensor is positioned towards the "I2C" setting.
Housing Assembly
Now it's time to assemble everything into the housing.
Begin by placing the 3D-printed buttons into their designated holes in the housing.
Use masking tape to temporarily hold the buttons in place, as they might fall out during the assembly process.
Carefully position the base plate assembly inside the housing, aligning the buttons and routing the antenna wire.
Secure the base plate to the housing using 4 M3 screws.
Once the base plate is secured, remove the masking tape holding the buttons.
Finally, connect the antenna wire to the GNSS sensor.
Battery Assembly
Solder the two female jumper wires (previously cut) to the GND(Brown/Black) and 5V(Red) terminals of the battery maintain the color code of the wire.
Plug the Type-C extension cable into the battery. This cable will be used for charging the batteries.
Position the Unihiker's Type-C female connector into the middle slot of the housing body and secure the connector using hot glue.
Insert the Button 3D printed into its designated slot in the housing.
Place the battery manager board onto the spacers, by aligning the USB ports with its corresponding opening in the housing and secure the board with 4 M3 screws.
Connect the female jumper wires to the male jumper wires of the Unihiker. (Please double check the Unihiker and battery connection before connecting as the wrong connection might permanently damage the components)
Insert the battery’s Type-C cable into its designated slot in the housing and secure the cable using hot glue to keep it in place.
Make sure that the switch on battery management board is positioned at Hold.
Final Assembly
Before proceeding with the final assembly, power on the system to ensure everything is functioning correctly.
If the Unihiker board powers up, it indicates that all connections are properly made.
If the board does not power up, double-check all connections to identify and correct any issues.
Carefully tuck all the wires into the housing, making sure they are neatly arranged and not pinched and snap the cover onto the housing, ensuring it fits securely.
And that's it! The assembly of your Enhiker is now complete.
Setup and Programming
Now that's everything is assembled and working, lets program the board:
Download and install Mind+ from here.
Connect your Unihiker (or "Enhiker"!) to your PC using a USB cable.
Open a browser (any browser except Internet Explorer) and navigate to http://10.1.2.3 to access the local web page menu on the Unihiker.
Go to the "Network Settings" on the web page and connect the Unihiker to your Wi-Fi network by entering your network credentials.
Open the Mind+ software.
Click on "Extensions" located at the bottom left side of the screen.
Select "Unihiker" and then click "Back" to return to the main menu.
Click on "Code" in Mind+ and then connect the Remote Terminal.
Once connected, you will see the Unihiker's file system on the right side of the screen.
Download the code from this GitHub repository and unzip the files.
Drag and drop the entire Enhiker folder into the Unihiker’s file system. This will copy the folder to the Unihiker.
Open the main.py file in the Unihiker's file system.
Go to OpenWeatherMap and log in to your account.
Navigate to "My API Keys" and copy your API key.
Paste the API key in the main.py file
Run the main.py file.
The program should execute without issues, as the packages used are the default ones that come pre-installed on the Unihiker.
If any issues arise, check the folder structure and the installed packages.
On Boot Setup
Enable Auto Boot:
Press the "Home" button on the Unihiker.
Navigate to 3-Service Toggle -> Auto Boot and enable it.
Set the Program to Run on Boot:
Navigate to 2-Run Program -> root/ -> Enhiker/ -> main.py.
Press the "Home" button to execute the program.
The Auto Boot feature will automatically run the last program that was executed.
Now, whenever the Enhiker is powered on, it will automatically run main.py by default.
Main.py
setup() Function:
The function tries to start the sensor. If the sensor doesn't start, it keeps trying until it succeeds.
check_wifi() Function:
This function sends a simple command to test the internet connection. If it gets a response, it returns True (indicating the Wi-Fi is connected), otherwise, it returns False.
loop() Function:
First, the program checks how many satellites the GNSS sensor can connect to(satellite count is sufficient). If it finds enough satellites, it proceeds to gather data.
# Wait until the required number of satellites is found
while True:
num_satellites = GNSS.get_num_sta_used()
if num_satellites > MINIMUM_SATELLITES:
break
print(f"Searching... Satellites found: {num_satellites}")
display_loading_screen()
time.sleep(1)
The sensors measure various environmental parameters like temperature, humidity, UV intensity, light intensity, and atmospheric pressure. The GNSS sensor provides location data (latitude, longitude, altitude) and current date and time.
# Read data from sensors
temperature = SEN0501.get_temperature(TEMP_C)
humidity = SEN0501.get_humidity()
uv_intensity = SEN0501.get_ultraviolet_intensity()
light_intensity = SEN0501.get_luminousintensity()
pressure = SEN0501.get_atmosphere_pressure(HPA)
elevation = SEN0501.get_elevation()
heat_index = calculate_heat_index(temperature, humidity)
# Get GNSS data if the satellite count is sufficient
latitude, longitude, altitude = None, None, None
current_date, current_time = None, None
if num_satellites > MINIMUM_SATELLITES:
lat_data = GNSS.get_lat()
lon_data = GNSS.get_lon()
alt_data = GNSS.get_alt()
latitude = f"{lat_data[0]:.6f}° {lat_data[1]}"
longitude = f"{lon_data[0]:.6f}° {lon_data[1]}"
altitude = f"{alt_data:.2f} m"
# Get the date and time from GNSS
current_date = GNSS.get_date()
current_time = GNSS.get_time()
If the device is connected to Wi-Fi, it retrieves additional weather data like air quality, wind speed, wind direction, sunrise, sunset, and cloud coverage from the internet.
wifi_connected = check_wifi()
air_quality, wind_speed, wind_direction, sunrise, sunset, clouds = None, None, None, None, None, None
if wifi_connected:
air_quality, wind_speed, wind_direction, sunrise, sunset, clouds = get_weather_data(latitude, longitude, API_Key)
The program then evaluates the environmental conditions and assigns a health rating based on the collected data.
if wifi_connected:
# Evaluate environmental advance conditions based on sensor and internet data
rating, message = evaluate_advance_conditions(current_time, temperature, humidity, uv_intensity, light_intensity, pressure, elevation, heat_index, air_quality, wind_speed, wind_direction, clouds)
else:
# Evaluate environmental conditions based on sensor data
rating, message = evaluate_conditions(temperature, humidity, uv_intensity, light_intensity, pressure, elevation, heat_index)
Store the data into data_log.csv file.
# Log data into csv
with open('data_log.csv', mode='a', newline='') as file:
writer = csv.writer(file)
writer.writerow([wifi_connected, current_date, current_time, num_satellites, temperature, humidity, heat_index, light_intensity, uv_intensity, pressure, elevation, latitude, longitude, rating, message, air_quality, wind_speed, wind_direction, sunrise, sunset, clouds])
Finally, it displays all this information on the screen and logs it into a CSV file for later reference.
# Display data
display_data(wifi_connected, current_date, current_time, num_satellites, temperature, humidity, heat_index, light_intensity, uv_intensity, pressure, elevation, latitude, longitude, rating, message, air_quality, wind_speed, wind_direction, sunrise, sunset, clouds)
Main Execution Block (if __name__ == "__main__":):
When the program is run, it first calls the setup() function to get the sensors ready. Then, it enters an infinite loop (while True:) where it keeps reading data and updating the display with the latest environmental information.
if __name__ == "__main__":
setup() # Initialize the sensors
while True:
loop() # Continuously read and display sensor data
Heat_index.py
The calculate_heat_index function estimates the "feels-like" temperature, which is how hot it feels to humans, considering both the actual temperature and the humidity. Here's a simple breakdown:
Understanding Heat Index:
The heat index is a measure of how hot it feels when humidity is factored in with the actual air temperature. High humidity can make it feel much hotter than the actual temperature because it makes it harder for sweat to evaporate, which is how the body cools itself.
The Formula:
This function uses a specific formula known as the Rothfusz regression to calculate the heat index. The formula is a complex combination of temperature and humidity, adjusted by several constants (c1 to c9).
Steps in the Function:
Temperature (T) and Humidity (R) are the main inputs. Temperature is measured in degrees Celsius, and humidity is the relative humidity percentage.
The formula is a polynomial, which means it combines the temperature and humidity in different ways (like multiplying them together or squaring them) to estimate how hot it actually feels.
The function calculates the heat index using these inputs and the constants provided, which have been determined through research to best estimate the "feels-like" temperature.
Final Output:
After calculating the heat index, the function rounds the result to two decimal places for simplicity and returns this value.
# Return the heat index value rounded to two decimal places.
return round(heat_index, 2)
Internet_data.py
The get_weather_data function retrieves weather and air quality information from the internet based on your location (latitude and longitude). Here's a simple breakdown of how it works:
Inputs:
lat and lon: These are the latitude and longitude coordinates of your location.
api_key: This is a unique key you get from OpenWeatherMap, which allows you to access their data.
API Endpoints:
The function uses two API endpoints:
Weather Data URL: Fetches current weather conditions like wind speed, wind direction, sunrise, sunset, and cloud coverage.
# Define the API endpoint for current weather data
weather_url = f"http://api.openweathermap.org/data/2.5/weather?lat={lat_des}&lon={lon_des}&appid={api_key}&units=metric"
Air Quality Data URL: Fetches information about the air quality index (AQI) at your location.
# Define the API endpoint for air quality data
air_quality_url = f"http://api.openweathermap.org/data/2.5/air_pollution?lat={lat_des}&lon={lon_des}&appid={api_key}"
Making API Requests:
The function tries to connect to these URLs and get the weather and air quality data.
Error Handling: If there's any problem during the request (like no internet, a timeout, or a wrong URL), the function catches the error and prints a message explaining what went wrong. It returns None in such cases, meaning no data is retrieved.
Extracting Data:
Once the data is fetched, the function tries to pull out specific pieces of information:
Wind Speed and Direction: How fast the wind is blowing and from which direction.
Sunrise and Sunset: The times when the sun rises and sets at your location, converted into a readable format.
Cloud Coverage: How much of the sky is covered by clouds, given as a percentage.
Air Quality Index (AQI): A number that indicates how clean or polluted the air is.
Error Handling: If any of these pieces of information are missing in the data (for example, if the API didn't send it), the function catches this and provides a message stating "Data not available" instead.
Output:
The function returns a set of six values: air quality index, wind speed, wind direction, sunrise time, sunset time, and cloud coverage. If any of these pieces of data couldn't be retrieved, the function returns "Data not available" for that item.
# Return the extracted data
return (air_quality_index, wind_speed, wind_direction, sunrise, sunset, clouds)
Decision_maker.py
This function, evaluate_conditions, is designed to determine whether it's safe to stay in a particular location based on various weather conditions. Here's a simple explanation of how it works:
Best Case: If the weather conditions are ideal (like comfortable temperature, moderate humidity, and low UV exposure), the function will suggest that the location is suitable and ideal for staying.
Dangerous Conditions:
Extreme UV Radiation: If the UV index is very high, it warns that it's unsafe to be outdoors.
High Heat and Humidity: If it's very hot and humid, it suggests leaving immediately due to the risk of heat stroke.
Extreme Temperatures and Pressures: If it's too hot or too cold combined with unusual atmospheric pressure, it warns against staying because of potential health risks like heat exhaustion or hypothermia.
High Elevation Issues: If you are at a high elevation with other harsh conditions (like low oxygen levels), it advises against staying due to risks like altitude sickness.
Moderate Conditions:
If the conditions are somewhat uncomfortable but not immediately dangerous (like high UV exposure or mild heat), the function will give a "Moderate" rating and advise caution. This means you might stay, but you should be careful.
Final Output: The function ultimately returns a rating (like "Suitable: Best," "Moderate," or "Can't stay") and a message that explains the safety of the location based on the current conditions.
This way, the function helps you make informed decisions about whether it's safe to stay outdoors in specific weather conditions.
def evaluate_conditions(temperature, humidity, uv_intensity, light_intensity, pressure, elevation, heat_index):
rating = "Suitable: Best"
message = "The location is ideal for camping or staying."
# Very High UV Intensity
if uv_intensity > 11:
rating = "Can't stay"
message = "Extreme UV intensity makes it unsafe to stay outdoors."
# High Heat Index and High Humidity
elif heat_index > 45 and humidity > 70:
rating = "Leave the place immediately"
message = "Dangerously high heat index and humidity; staying here can lead to heat stroke."
.
.
.
.
.
.
.
.
# Final Message
#print(f"Rating: {rating}")
#print(f"Message: {message}")
return rating, message
Display.py
This code uses Pygame to create a display for visualizing various environmental and sensor data on a 240x320 pixel screen.
Pygame Initialization
pygame.init(): Initializes the Pygame modules.
Display Setup: The display dimensions are set to 240x320 pixels using pygame.display.set_mode.
pygame.init()
# Set the display dimensions
screen_width = 240
screen_height = 320
screen = pygame.display.set_mode((screen_width, screen_height))
Loading Icons
Icons representing various data types (e.g., satellite, Wi-Fi, temperature) are loaded from image files and scaled to the appropriate sizes.
SatelliteIcon = pygame.image.load("Icons/satellite.png")
SatelliteIcon = pygame.transform.scale(SatelliteIcon, (18, 18))
WifiIcon = pygame.image.load("Icons/wifi.png")
WifiIcon = pygame.transform.scale(WifiIcon, (18, 18))
.
.
.
.
.
Display Functions:
display_loading_screen():
Shows a loading screen while searching for satellites. It displays a loading icon centered on the screen.
def display_loading_screen():
frame_surface = pygame.Surface((screen_width, screen_height))
frame_surface.fill((0, 0, 0))
screen.blit(frame_surface, (0, 0))
# Display loading icon
screen.blit(LoadingIcon, (50, 90))
pygame.display.update()
display_data():
This function takes multiple parameters like Wi-Fi status, date, time, sensor data (temperature, humidity, light intensity, etc.), and updates the display accordingly.
The screen is cleared and filled with a black background at the start of the function.
The function draws various icons and corresponding data values (e.g., temperature, humidity) on the screen at specified positions.
Environmental data is displayed in two columns.
Additional data like air quality, wind speed, direction, and cloud coverage are shown if the Wi-Fi is connected.
A final section displays a rating and a message, with the message text wrapped to fit within the screen width.
Conclusion
Enhiker may not be a groundbreaking innovation in the rapidly evolving world of smartphones and advanced technology, but it serves a unique purpose by offering valuable lessons in IoT, communication, and offline functionality—features that many modern devices often overlook. This project stands out for its ability to operate independently of the internet, making it a reliable companion for those who venture into the great outdoors where connectivity is limited.
Whether you're an outdoor enthusiast, an engineer, or a professional in meteorology or agriculture, Enhiker is a practical tool worth considering. It bridges the gap between technology and nature, providing essential data and insights in environments where conventional tech falls short.
If you enjoyed this project, don't forget to hit the like button and leave a comment below. And if you've created your own Enhiker, be sure to share your experience in the "I Made It" section.
Thank you! See you next time ;)