A Spring Boot application that collects temperature and humidity data from Arduino devices via HTTP polling and stores the data in PostgreSQL for historical analysis.
This system automatically polls registered Arduino devices every 15 minutes, retrieves sensor readings (temperature and humidity), and stores them in a PostgreSQL database. It provides REST APIs for retrieving current sensor data and querying historical data ranges.
- ✅ Scheduled polling of Arduino devices every 15 minutes
- ✅ Automatic handling of invalid sensor readings (NaN values)
- ✅ REST API for current and historical data retrieval
- ✅ Arduino device registration and management
- ✅ Parallel processing of multiple devices
- ✅ Error handling and detailed logging
- ✅ Database indexing for efficient queries
- Java 21 or higher
- PostgreSQL 12 or higher
- Maven 3.6+ (or use included Maven wrapper
./mvnw)
-
Install PostgreSQL (if not already installed)
-
Create the database:
CREATE DATABASE sensorsdb;
-
Create a user (optional but recommended):
CREATE USER sensor_user WITH PASSWORD 'your_password'; GRANT ALL PRIVILEGES ON DATABASE sensorsdb TO sensor_user;
Configure the database connection in src/main/resources/application.properties or use environment variables:
Option 1: Environment Variables (Recommended for Production)
export DB_URL=jdbc:postgresql://localhost:5432/sensorsdb
export DB_USERNAME=sensor_user
export DB_PASSWORD=your_passwordOption 2: Edit application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/sensorsdb
spring.datasource.username=sensor_user
spring.datasource.password=your_passwordThe application runs on port 8081 by default. Change in application.properties:
server.port=8081Build:
./mvnw clean installRun:
./mvnw spring-boot:runPackage as JAR:
./mvnw package
java -jar target/sensors-0.0.1-SNAPSHOT.jarBuild:
./mvnw package
docker build -t sensor-be .Run:
docker run -p 8081:8081 \
-e DB_URL=jdbc:postgresql://host.docker.internal:5432/sensorsdb \
-e DB_USERNAME=sensor_user \
-e DB_PASSWORD=your_password \
sensor-beGET /arduino/Response:
[
{
"id": 1,
"hostName": "bedroom.local",
"isActive": true,
"creationDate": "2025-12-01T10:00:00Z"
}
]POST /arduino/
Content-Type: application/json
{
"hostName": "living-room.local",
"isActive": true
}GET /data/current?machineName=bedroom.localResponse:
{
"machineName": "bedroom.local",
"temperature": "22.5",
"humidity": "45.2",
"creationDate": "2025-12-01T15:30:00Z"
}GET /data/historicalData?machineName=bedroom.local&startDate=2025-12-01T00:00:00Z&endDate=2025-12-01T23:59:59ZResponse:
[
{
"machineName": "bedroom.local",
"temperature": "22.5",
"humidity": "45.2",
"creationDate": "2025-12-01T15:30:00Z",
"hasError": false
},
{
"machineName": "bedroom.local",
"temperature": "nan",
"humidity": "nan",
"creationDate": "2025-12-01T15:45:00Z",
"hasError": true
}
]Query Parameters:
machineName(required): Arduino hostnamestartDate(required): ISO 8601 timestampendDate(required): ISO 8601 timestamp (must be after startDate)
Your Arduino devices must expose an HTTP endpoint that returns sensor data in JSON format:
Expected Arduino Endpoint:
http://{hostname}:80/data
Expected Response Format:
{
"temperature": "22.5",
"humidity": "45.2"
}Note: The application handles invalid readings like nan (unquoted) automatically.
- Controllers (
controllers/) - REST API endpoints - Services (
services/) - Business logic and scheduled tasks - Repositories (
repositories/) - Database access layer - Entities (
entities/) - JPA database entities - Models (
models/) - DTOs for API responses - Mappers (
mappers/) - Entity ↔ DTO conversion - REST Clients (
restClients/) - Arduino HTTP communication
The SensorScheduledServices component automatically:
- Queries all active Arduino devices from the database
- Polls each device in parallel using HTTP GET (virtual-thread-per-task executor)
- Sanitizes and validates the response data
- Stores successful readings in the database
- Logs success/failure summary
Schedule: Cron-based at wall-clock minutes 00, 15, 30, 45 of every hour (@Scheduled(cron = "0 0,15,30,45 * * * *"))
Arduino Table:
id- Primary keyhostName- DNS name or IP addressisActive- Whether to poll this devicecreationDate- Auto-generated timestamp
SensorData Table:
id- Primary keymachineName- Arduino hostnametemperature- Temperature reading (string)humidity- Humidity reading (string)creationDate- When reading was taken
Indexes:
idx_hostnameon Arduino(hostName)idx_machine_dateon SensorData(machineName, creationDate)
./mvnw testNote: The test suite is pure unit tests (Mockito) and does not require a running PostgreSQL instance.
See .claude/CLAUDE.md for detailed development guidelines and architecture documentation.
Clean build:
./mvnw clean installRun single test:
./mvnw test -Dtest=ClassName#methodNameSkip tests:
./mvnw package -DskipTestsApplication logs show polling activity:
INFO SensorScheduledServices - Starting sensor polling for 3 active Arduino(s)
ERROR SensorScheduledServices - Failed to poll Arduino 'bedroom.local': Connection timed out
INFO SensorScheduledServices - Sensor polling complete: 2/3 successful
1. Database Connection Failed
- Verify PostgreSQL is running:
sudo systemctl status postgresql - Check credentials in environment variables or
application.properties - Ensure database exists:
psql -l
2. Arduino Polling Fails
- Verify Arduino is reachable:
curl http://arduino-hostname:80/data - Check Arduino is registered and
isActive = true - Review logs for specific error messages
3. Port Already in Use
- Change port in
application.properties:server.port=8082 - Or kill process using port 8081:
lsof -ti:8081 | xargs kill
4. JSON Parsing Errors
- Application handles unquoted
nanvalues automatically - Ensure Arduino returns valid JSON structure
- Configure CORS for specific frontend origins
- Use connection pooling settings appropriate for your load
- Set up external configuration (Spring Cloud Config, etc.)
- Implement proper logging (e.g., Logback with file rotation)
- Add health check endpoint for monitoring
- Consider pagination for historical data queries with large datasets
- Set up database backups
- Use HTTPS for Arduino communication if possible
- Monitor database connection pool usage
This project is private/proprietary.
For issues or questions, please contact the development team.