There is more to see...
Description
Photolvoltaic System Monitor with real time updates made available on a custom device based on ESP32
Concept

Features
Server
- Process raw data
- Gather weather information to give to clients
- Provide websocket connection with updates every 10s
- Dockerized and hosted on a Raspberry Pi
Client
- Displays Weather forecast
- Displays current photovoltaic system values
- Displays total daily photo voltaic system values
- Displays sunlight information
- Fully custom Design
- Custom 3d printed case
- Power saving e-ink display
Technologies
- Rust for the Esp32
- Golang for the server
Parts
- Waveshare EPD 2.9inch E-Ink display
- Arduino Nano Esp32
- Battery Controller ( TP4056 )
- Small Lipo battery
Images
Description
Pictures
Solar system yield prediction
Machine learning model trained to predict photovolataic system yield based on weather data
Goals
Improve energy efficiency by providing accurate energy availability predictions that can be used to improve energy decisions therefore reducing costs and environmental footprint.
Model architecture

The model consists of 2 LSTM cells. The first one is fed the weather data for the previous 4 hours in combination with the forcast for the next 2 hours.
The first LSTM cell's output is combined with past yield data ( 4 hours ) are fed into the second LSTM model.
The last block is just constituted of a dense layer with an output size of 12 ( the next 12 datapoints which represent one hour of prediction )
To achieve full day prediction you invoke the model recurrently 24 times in a row, while feeding it it's own previous output as input.
The first LSTM cell's output is combined with past yield data ( 4 hours ) are fed into the second LSTM model.
The last block is just constituted of a dense layer with an output size of 12 ( the next 12 datapoints which represent one hour of prediction )
To achieve full day prediction you invoke the model recurrently 24 times in a row, while feeding it it's own previous output as input.
Data source and preparation
Data source
The data was obtained from a senec dashboard and combined with daily weather data from open-meteo.
Data preparation

The yield data was preprocessed by smoothing the output values with a rolling window of 12 ( so 1 hour ) .
This was done to match the label data timesteps (which previously where values every 5 minutes) to the weather data timesteps (which were hourly)
Furthermore the weather data was normalized and distilled down to 12 features. One other feature was added that contained the day of season. All datapoints were normalized to improve accuracy. The training data was split into the 4 season to allow the model to learn seasonal patterns. For training the lstm based model which acted on strict hours the data was carefully grouped by hours and matched with the weather information for that hour.
Furthermore the weather data was normalized and distilled down to 12 features. One other feature was added that contained the day of season. All datapoints were normalized to improve accuracy. The training data was split into the 4 season to allow the model to learn seasonal patterns. For training the lstm based model which acted on strict hours the data was carefully grouped by hours and matched with the weather information for that hour.
Results
The model was able to achieve an average accuracy of 90% on test sets across seasons.

Features
Handlers with state
#[derive(Clone, Debug, Default)]
pub struct AppState{
pub hello_page: String,
}
fn test_handler(
_req: Request,
state: AppState,<--------
) -> HandlerResponse<'static>{
Box::pin(async move{
respond(Html(state.hello_page))
})
}
Easy json response
fn test_handler(
_req: Request,
) -> HandlerResponse<'static>{
Box::pin(async move{
respond(Json(YourJsonStruct{...})
})
}
Handlers with path-extracts
let router = Router::new()
.add_handler('/user/:id/ts/:time',router::Handler::WithState(test_handler))
.unwrap()
.add_state(AppState {...});
Easy Status response
fn test_handler(
_req: Request,
) -> HandlerResponse<'static>{
Box::pin(async move{
respond(StatusCode::Ok)
})
}
Easy json deserialization
fn test_handler(
_req: Request,
) -> HandlerResponse<'static>{
Box::pin(async move{
let data: JsonTest = req.from_json_to_struct().unwrap();
{...}
})
}
Description
Features
Interested in what I am up to now ?
Check it out right
here !