forked from external-repos/ESP32-CAM-ONVIF
Compare commits
21 Commits
John-Vargh
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64e4f44aed | ||
|
|
53d1e6be94 | ||
|
|
effbd83d93 | ||
|
|
e654da3e25 | ||
|
|
5e16f21407 | ||
|
|
ae4c9c6392 | ||
|
|
478cc59d9c | ||
|
|
b04e4cb381 | ||
|
|
605b55fce9 | ||
|
|
8a19dcb0b6 | ||
|
|
9d6b6d90ff | ||
|
|
567f01ca65 | ||
|
|
27c5d944c0 | ||
|
|
3af64c8499 | ||
|
|
3b270b5bec | ||
|
|
c104f055ee | ||
|
|
35e529e7e9 | ||
|
|
778cf4aaaf | ||
|
|
2ca9c5a84e | ||
|
|
af8d85e41d | ||
|
|
9294ddb4a9 |
15
.github/FUNDING.yml
vendored
Normal file
15
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: John-Varghese-EH # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: CyberTrinity # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
|
polar: # Replace with a single Polar username
|
||||||
|
buy_me_a_coffee: CyberTrinity # Replace with a single Buy Me a Coffee username
|
||||||
|
thanks_dev: # Replace with a single thanks.dev username
|
||||||
|
custom: 'johnvarghese.netlify.app/support' # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
johnvarghese.work@gmail.com.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
73
CONTRIBUTING.md
Normal file
73
CONTRIBUTING.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Contributing to ESP32-CAM-ONVIF
|
||||||
|
|
||||||
|
Thank you for your interest in contributing to **ESP32-CAM-ONVIF**!
|
||||||
|
Your help is essential to make this project robust, feature-rich, and easy to use for everyone.
|
||||||
|
We welcome contributions of all kinds: code, documentation, bug reports, feature requests, and ideas.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Contribute
|
||||||
|
|
||||||
|
### 1. Fork & Clone
|
||||||
|
|
||||||
|
- Fork the repository to your own GitHub account.
|
||||||
|
- Clone your fork:
|
||||||
|
|
||||||
|
- Open a Pull Request (PR) against the `main` branch of this repository.
|
||||||
|
- In your PR description, include:
|
||||||
|
- A summary of your changes
|
||||||
|
- Any relevant issue numbers (e.g., `Closes #123`)
|
||||||
|
- Screenshots or logs if applicable
|
||||||
|
|
||||||
|
### 6. Respond to Feedback
|
||||||
|
|
||||||
|
- Be ready to answer questions or make revisions.
|
||||||
|
- We may suggest changes or request further testing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Can You Contribute?
|
||||||
|
|
||||||
|
- **Features**: Add new capabilities (e.g., web UI, SD recording, motion detection)
|
||||||
|
- **Bug Fixes**: Find and squash bugs
|
||||||
|
- **Documentation**: Improve guides, add examples, or clarify instructions
|
||||||
|
- **Testing**: Test on different hardware/NVRs and report results
|
||||||
|
- **Ideas**: Suggest improvements or new directions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
|
||||||
|
- **Follow best practices**: Comment your code and keep it readable.
|
||||||
|
- **Respect the project structure**: Organize new files logically.
|
||||||
|
- **Document new features**: Update README.md and add usage notes.
|
||||||
|
- **Stay compatible**: Ensure your changes work with both PlatformIO and Arduino IDE.
|
||||||
|
- **Be respectful**: All interactions should be friendly and constructive.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reporting Issues & Feature Requests
|
||||||
|
|
||||||
|
- Use [GitHub Issues](https://github.com/John-Varghese-EH/ESP32-CAM-ONVIF/issues) for:
|
||||||
|
- Bug reports (include logs, hardware details, steps to reproduce)
|
||||||
|
- Feature requests (describe your idea and use case)
|
||||||
|
- General questions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
This project was first forked by [BitVenturesUSA/ESP32-CAM-ONVIF](). We appreciate their interest and contributions to the ESP32-CAM ONVIF community.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
We are committed to providing a welcoming and inclusive environment for all.
|
||||||
|
Please read and follow our [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Thank you for your contributions!
|
||||||
|
Together, we can make ESP32-CAM-ONVIF the best open-source ONVIF camera firmware for ESP32.
|
||||||
|
|
||||||
BIN
ESP32-CAM-ONVIF.jpg
Normal file
BIN
ESP32-CAM-ONVIF.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 194 KiB |
10
ESP32CAM-ONVIF/MyStreamer.cpp
Normal file
10
ESP32CAM-ONVIF/MyStreamer.cpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// MyStreamer.cpp
|
||||||
|
#include "MyStreamer.h"
|
||||||
|
|
||||||
|
void MyStreamer::streamImage(uint32_t curMsec) {
|
||||||
|
uint8_t *image = m_cam.getfb();
|
||||||
|
uint32_t size = m_cam.getSize();
|
||||||
|
if (image && size) {
|
||||||
|
streamFrame(image, size, curMsec);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
ESP32CAM-ONVIF/MyStreamer.h
Normal file
13
ESP32CAM-ONVIF/MyStreamer.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// MyStreamer.h
|
||||||
|
#pragma once
|
||||||
|
#include "OV2640.h"
|
||||||
|
#include "CStreamer.h"
|
||||||
|
|
||||||
|
class MyStreamer : public CStreamer {
|
||||||
|
public:
|
||||||
|
MyStreamer(OV2640 &cam) : CStreamer(cam.getWidth(), cam.getHeight()), m_cam(cam) {}
|
||||||
|
virtual ~MyStreamer() {}
|
||||||
|
virtual void streamImage(uint32_t curMsec) override;
|
||||||
|
private:
|
||||||
|
OV2640 &m_cam;
|
||||||
|
};
|
||||||
@@ -4,12 +4,12 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>J0X-ESP-CAM Control Panel</title>
|
<title>J0X-ESP-CAM Control Panel</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="/style.css">
|
<link rel="stylesheet" href="./style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="login-overlay" class="overlay">
|
<div id="login-overlay" class="overlay">
|
||||||
<div class="login-box">
|
<div class="login-box">
|
||||||
<img src="/logo.png" alt="Logo" class="logo-large">
|
<img src="./logo.png" alt="Logo" class="logo-large">
|
||||||
<h2>J0X-ESP-CAM</h2>
|
<h2>J0X-ESP-CAM</h2>
|
||||||
<form id="login-form" onsubmit="return login(event)">
|
<form id="login-form" onsubmit="return login(event)">
|
||||||
<input type="text" id="username" placeholder="Username" autocomplete="username" required>
|
<input type="text" id="username" placeholder="Username" autocomplete="username" required>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<header>
|
<header>
|
||||||
<div class="header-bar">
|
<div class="header-bar">
|
||||||
<img src="/logo.png" alt="Logo" class="logo-small">
|
<img src="./logo.png" alt="Logo" class="logo-small">
|
||||||
<span class="brand">J0X-ESP-CAM</span>
|
<span class="brand">J0X-ESP-CAM</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="status" id="status">Connecting...</div>
|
<div class="status" id="status">Connecting...</div>
|
||||||
@@ -129,6 +129,6 @@
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
Made with <span style="color:#e25555;">❤️</span> by <a herf="https://github.com/John-Varghese-EH/">J0X</a>
|
Made with <span style="color:#e25555;">❤️</span> by <a herf="https://github.com/John-Varghese-EH/">J0X</a>
|
||||||
</div>
|
</div>
|
||||||
<script src="/app.js"></script>
|
<script src="./app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ main { max-width: 700px; margin: 2em auto; background: #232323; border-radius: 8
|
|||||||
.section { margin-bottom: 2em; }
|
.section { margin-bottom: 2em; }
|
||||||
label { display: block; margin: 0.5em 0 0.2em; }
|
label { display: block; margin: 0.5em 0 0.2em; }
|
||||||
input, select, button { padding: 0.5em; border-radius: 4px; border: none; margin-bottom: 0.5em; }
|
input, select, button { padding: 0.5em; border-radius: 4px; border: none; margin-bottom: 0.5em; }
|
||||||
.stream-container { width: 90vw; max-width: 100vw; height: 60vw; /* Or adjust as needed */ max-height: 80vh; overflow: hidden; margin: 0 auto; background: #111; touch-action: none; /* Required for custom gestures */ position: relative; }
|
.stream-container { width: 95%; max-width: 100%; height: 60%; /* Or adjust as needed */ max-height: 80%; overflow: hidden; margin: 0 auto; background: #111; touch-action: none; /* Required for custom gestures */ position: relative; }
|
||||||
.video-preview { width: 100%; height: 100%; object-fit: contain; user-select: none; cursor: grab; transition: transform 0.05s linear; will-change: transform; }
|
.video-preview { width: 100%; height: 100%; object-fit: contain; user-select: none; cursor: grab; transition: transform 0.05s linear; will-change: transform; }
|
||||||
.btn { background: #08f; color: #fff; cursor: pointer; transition: background 0.2s; }
|
.btn { background: #08f; color: #fff; cursor: pointer; transition: background 0.2s; }
|
||||||
.btn:active { background: #06c; }
|
.btn:active { background: #06c; }
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
#include "motion_detection.h"
|
#include "motion_detection.h"
|
||||||
#include "esp_camera.h"
|
#include "esp_camera.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
#include "onvif_server.h"
|
#include "onvif_server.h"
|
||||||
#include "rtsp_server.h"
|
#include "rtsp_server.h"
|
||||||
#include <WiFiUdp.h>
|
#include <WiFiUdp.h>
|
||||||
#include <WebServer.h>
|
#include <WebServer.h>
|
||||||
#include "utils.h"
|
#include "config.h"
|
||||||
|
|
||||||
WebServer onvifServer(8000);
|
WebServer onvifServer(8000);
|
||||||
WiFiUDP onvifUDP;
|
WiFiUDP onvifUDP;
|
||||||
@@ -42,6 +43,25 @@ String getDeviceInfoResponse() {
|
|||||||
"</SOAP-ENV:Envelope>";
|
"</SOAP-ENV:Envelope>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getStreamUriResponse() {
|
||||||
|
String ip = WiFi.localIP().toString();
|
||||||
|
return
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||||
|
"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://www.w3.org/2003/05/soap-envelope\" "
|
||||||
|
"xmlns:trt=\"http://www.onvif.org/ver10/media/wsdl\">"
|
||||||
|
"<SOAP-ENV:Body>"
|
||||||
|
"<trt:GetStreamUriResponse>"
|
||||||
|
"<trt:MediaUri>"
|
||||||
|
"<tt:Uri>rtsp://" + ip + ":554/mjpeg/1</tt:Uri>"
|
||||||
|
"<tt:InvalidAfterConnect>false</tt:InvalidAfterConnect>"
|
||||||
|
"<tt:InvalidAfterReboot>false</tt:InvalidAfterReboot>"
|
||||||
|
"<tt:Timeout>PT0S</tt:Timeout>"
|
||||||
|
"</trt:MediaUri>"
|
||||||
|
"</trt:GetStreamUriResponse>"
|
||||||
|
"</SOAP-ENV:Body>"
|
||||||
|
"</SOAP-ENV:Envelope>";
|
||||||
|
}
|
||||||
|
|
||||||
void handle_onvif_soap() {
|
void handle_onvif_soap() {
|
||||||
String req = onvifServer.arg(0);
|
String req = onvifServer.arg(0);
|
||||||
if (req.indexOf("GetCapabilities") > 0) {
|
if (req.indexOf("GetCapabilities") > 0) {
|
||||||
@@ -92,7 +112,7 @@ void handle_onvif_discovery() {
|
|||||||
void onvif_server_start() {
|
void onvif_server_start() {
|
||||||
onvifServer.on("/onvif/device_service", HTTP_POST, handle_onvif_soap);
|
onvifServer.on("/onvif/device_service", HTTP_POST, handle_onvif_soap);
|
||||||
onvifServer.begin();
|
onvifServer.begin();
|
||||||
onvifUDP.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 3702);
|
onvifUDP.beginMulticast(IPAddress(239,255,255,250), 3702); // Fixed: use only 2 args
|
||||||
Serial.println("[INFO] ONVIF server started.");
|
Serial.println("[INFO] ONVIF server started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#include <WebServer.h>
|
||||||
|
|
||||||
void onvif_server_start();
|
void onvif_server_start();
|
||||||
void onvif_server_loop();
|
void onvif_server_loop();
|
||||||
|
|||||||
@@ -1,28 +1,70 @@
|
|||||||
#include "rtsp_server.h"
|
#include "rtsp_server.h"
|
||||||
#include <WiFi.h>
|
|
||||||
#include "OV2640.h"
|
|
||||||
#include "CRtspSession.h"
|
|
||||||
|
|
||||||
WiFiServer rtspServer(554);
|
WiFiServer rtspServer(554);
|
||||||
OV2640 cam;
|
OV2640 cam;
|
||||||
|
MyStreamer *streamer = nullptr;
|
||||||
|
|
||||||
String getRTSPUrl() {
|
String getRTSPUrl() {
|
||||||
return "rtsp://" + WiFi.localIP().toString() + ":554/mjpeg/1";
|
return "rtsp://" + WiFi.localIP().toString() + ":554/mjpeg/1";
|
||||||
}
|
}
|
||||||
|
|
||||||
void rtsp_server_start() {
|
void rtsp_server_start() {
|
||||||
|
// Fill in your ESP32-CAM pin assignments
|
||||||
|
camera_config_t config;
|
||||||
|
config.pin_pwdn = -1;
|
||||||
|
config.pin_reset = -1;
|
||||||
|
config.pin_xclk = 4;
|
||||||
|
config.pin_sscb_sda = 18;
|
||||||
|
config.pin_sscb_scl = 23;
|
||||||
|
config.pin_d7 = 36;
|
||||||
|
config.pin_d6 = 37;
|
||||||
|
config.pin_d5 = 38;
|
||||||
|
config.pin_d4 = 39;
|
||||||
|
config.pin_d3 = 35;
|
||||||
|
config.pin_d2 = 14;
|
||||||
|
config.pin_d1 = 13;
|
||||||
|
config.pin_d0 = 34;
|
||||||
|
config.pin_vsync = 5;
|
||||||
|
config.pin_href = 27;
|
||||||
|
config.pin_pclk = 25;
|
||||||
|
config.xclk_freq_hz = 20000000;
|
||||||
|
config.ledc_timer = LEDC_TIMER_0;
|
||||||
|
config.ledc_channel = LEDC_CHANNEL_0;
|
||||||
|
config.pixel_format = PIXFORMAT_JPEG;
|
||||||
|
config.frame_size = FRAMESIZE_VGA;
|
||||||
|
config.jpeg_quality = 12;
|
||||||
|
config.fb_count = 1;
|
||||||
|
|
||||||
|
cam.init(config);
|
||||||
|
streamer = new MyStreamer(cam);
|
||||||
rtspServer.begin();
|
rtspServer.begin();
|
||||||
cam.init(esp_camera_sensor_get());
|
|
||||||
Serial.println("[INFO] RTSP server started at " + getRTSPUrl());
|
Serial.println("[INFO] RTSP server started at " + getRTSPUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
void rtsp_server_loop() {
|
void rtsp_server_loop() {
|
||||||
WiFiClient client = rtspServer.available();
|
WiFiClient client = rtspServer.available();
|
||||||
if (client) {
|
if (client) {
|
||||||
CRtspSession session(client, cam);
|
// Micro-RTSP expects a SOCKET (int) and a CStreamer*
|
||||||
|
// On ESP32, WiFiClient.fd() is not available, so we use a hack:
|
||||||
|
// Pass the client object as a void* and cast it back in the library.
|
||||||
|
// This requires a custom build of Micro-RTSP or using the ESP32-compatible fork.
|
||||||
|
// For simplicity, this example assumes you have modified Micro-RTSP to accept WiFiClient*.
|
||||||
|
// If not, use the standard Micro-RTSP example and adapt as needed.
|
||||||
|
// For now, this is a placeholder:
|
||||||
|
// CRtspSession session(client, streamer); // Won't work out of the box!
|
||||||
|
// Instead, use the following workaround (requires library modification):
|
||||||
|
// CRtspSession session((void*)&client, streamer);
|
||||||
|
// Or use the standard Micro-RTSP example code.
|
||||||
|
|
||||||
|
// IMPORTANT: The standard Micro-RTSP library does not support WiFiClient directly.
|
||||||
|
// You must either:
|
||||||
|
// 1. Use the standard Micro-RTSP example with a custom streamer, or
|
||||||
|
// 2. Modify the library to accept WiFiClient* (advanced).
|
||||||
|
|
||||||
|
// For now, here is a placeholder. See notes below for a real solution.
|
||||||
|
Serial.println("Client connected, but RTSP session handling is not implemented.");
|
||||||
while (client.connected()) {
|
while (client.connected()) {
|
||||||
session.handleRequests(0);
|
// Handle client here if you modify the library
|
||||||
session.broadcastCurrentFrame(0);
|
|
||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
client.stop();
|
client.stop();
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include "OV2640.h"
|
||||||
|
#include "CRtspSession.h"
|
||||||
|
#include "MyStreamer.h"
|
||||||
|
|
||||||
|
extern WiFiServer rtspServer;
|
||||||
|
extern OV2640 cam;
|
||||||
|
extern MyStreamer *streamer;
|
||||||
|
|
||||||
|
String getRTSPUrl();
|
||||||
void rtsp_server_start();
|
void rtsp_server_start();
|
||||||
void rtsp_server_loop();
|
void rtsp_server_loop();
|
||||||
String getRTSPUrl();
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#include "web_config.h"
|
#include "web_config.h"
|
||||||
#include <WebServer.h>
|
#include <WebServer.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include "rtsp_server.h" // For getRTSPUrl()
|
#include "rtsp_server.h"
|
||||||
#include "onvif_server.h" // For ONVIF URL if needed
|
#include "onvif_server.h"
|
||||||
#include "motion_detection.h" // For motion_detected()
|
#include "motion_detection.h"
|
||||||
#include <FS.h>
|
#include <FS.h>
|
||||||
#include <SPIFFS.h>
|
#include <SPIFFS.h>
|
||||||
#include <SD_MMC.h>
|
#include <SD_MMC.h>
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
WebServer webConfigServer(80);
|
WebServer webConfigServer(80);
|
||||||
|
|
||||||
// --- Helper: HTTP Basic Auth ---
|
|
||||||
const char* WEB_USER = "admin";
|
const char* WEB_USER = "admin";
|
||||||
const char* WEB_PASS = "esp123";
|
const char* WEB_PASS = "esp123";
|
||||||
|
|
||||||
@@ -25,15 +24,36 @@ bool isAuthenticated(WebServer &server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void web_config_start() {
|
void web_config_start() {
|
||||||
// Mount SPIFFS for static files
|
|
||||||
if (!SPIFFS.begin(true)) {
|
if (!SPIFFS.begin(true)) {
|
||||||
Serial.println("SPIFFS/LittleFS Mount Failed");
|
Serial.println("SPIFFS/LittleFS Mount Failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Protect static files with authentication
|
|
||||||
webConfigServer.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html").setAuthentication(WEB_USER, WEB_PASS);
|
|
||||||
|
|
||||||
// === API ENDPOINTS ===
|
// All route definitions go here, inside this function!
|
||||||
|
webConfigServer.on("/", []() {
|
||||||
|
File file = SPIFFS.open("/index.html", "r");
|
||||||
|
if (!file) {
|
||||||
|
webConfigServer.send(404, "text/plain", "File not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
webConfigServer.streamFile(file, "text/html");
|
||||||
|
file.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
webConfigServer.on("/", HTTP_GET, []() {
|
||||||
|
if (!webConfigServer.authenticate(WEB_USER, WEB_PASS)) {
|
||||||
|
return webConfigServer.requestAuthentication();
|
||||||
|
}
|
||||||
|
File file = SPIFFS.open("/index.html", "r");
|
||||||
|
if (!file) {
|
||||||
|
webConfigServer.send(404, "text/plain", "File not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
webConfigServer.streamFile(file, "text/html");
|
||||||
|
file.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- API ENDPOINTS ---
|
||||||
webConfigServer.on("/api/status", HTTP_GET, []() {
|
webConfigServer.on("/api/status", HTTP_GET, []() {
|
||||||
if (!isAuthenticated(webConfigServer)) return;
|
if (!isAuthenticated(webConfigServer)) return;
|
||||||
String json = "{";
|
String json = "{";
|
||||||
@@ -77,10 +97,10 @@ void web_config_start() {
|
|||||||
if (doc.containsKey("awb")) s->set_whitebal(s, doc["awb"]);
|
if (doc.containsKey("awb")) s->set_whitebal(s, doc["awb"]);
|
||||||
if (doc.containsKey("awb_gain")) s->set_awb_gain(s, doc["awb_gain"]);
|
if (doc.containsKey("awb_gain")) s->set_awb_gain(s, doc["awb_gain"]);
|
||||||
if (doc.containsKey("wb_mode")) s->set_wb_mode(s, doc["wb_mode"]);
|
if (doc.containsKey("wb_mode")) s->set_wb_mode(s, doc["wb_mode"]);
|
||||||
if (doc.containsKey("aec")) s->set_aec(s, doc["aec"]);
|
if (doc.containsKey("aec")) s->set_aec2(s, doc["aec"]);
|
||||||
if (doc.containsKey("aec2")) s->set_aec2(s, doc["aec2"]);
|
if (doc.containsKey("aec2")) s->set_aec2(s, doc["aec2"]);
|
||||||
if (doc.containsKey("ae_level")) s->set_ae_level(s, doc["ae_level"]);
|
if (doc.containsKey("ae_level")) s->set_ae_level(s, doc["ae_level"]);
|
||||||
if (doc.containsKey("agc")) s->set_agc(s, doc["agc"]);
|
if (doc.containsKey("agc")) s->set_gain_ctrl(s, doc["agc"]);
|
||||||
if (doc.containsKey("gainceiling")) s->set_gainceiling(s, doc["gainceiling"]);
|
if (doc.containsKey("gainceiling")) s->set_gainceiling(s, doc["gainceiling"]);
|
||||||
if (doc.containsKey("bpc")) s->set_bpc(s, doc["bpc"]);
|
if (doc.containsKey("bpc")) s->set_bpc(s, doc["bpc"]);
|
||||||
if (doc.containsKey("wpc")) s->set_wpc(s, doc["wpc"]);
|
if (doc.containsKey("wpc")) s->set_wpc(s, doc["wpc"]);
|
||||||
@@ -198,9 +218,9 @@ void web_config_start() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
webConfigServer.begin();
|
webConfigServer.begin();
|
||||||
Serial.println("[INFO] Web config server started.");
|
Serial.println("[INFO] Web config server started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void web_config_loop() {
|
void web_config_loop() {
|
||||||
webConfigServer.handleClient();
|
webConfigServer.handleClient();
|
||||||
}
|
}
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 John Varghese (J0X)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
180
README.md
180
README.md
@@ -1,60 +1,171 @@
|
|||||||
# ESP32-CAM | ONVIF | DVR/NVR Stream and Recording | Ultimate Feature Packed Firmware
|
# ESP32-CAM ONVIF RTSP Camera
|
||||||
## ESP32-CAM ONVIF RTSP Camera
|
|
||||||
|
**Professional, Feature-Rich, and Network Camera Firmware for ESP32-CAM**
|
||||||
|
|
||||||
[](https://www.espressif.com/en/products/socs/esp32)
|
[](https://www.espressif.com/en/products/socs/esp32)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
## Overview
|

|
||||||
|
|
||||||
**ESP32-CAM ONVIF RTSP Camera** is an open-source firmware project that transforms your affordable ESP32-CAM module into a network camera compatible with professional NVR/DVR systems, including Hikvision, Dahua, and other ONVIF-compliant solutions.
|
Transform your affordable ESP32-CAM module into a powerful ONVIF-compatible network camera, ready for integration with professional NVR/DVR systems (Hikvision, Dahua, and more). This firmware provides RTSP streaming, ONVIF discovery, and a roadmap for advanced features like web configuration, SD card recording, and motion detection.
|
||||||
|
|
||||||
This project brings together:
|
> [!NOTE]
|
||||||
- **RTSP streaming** (MJPEG) for real-time video
|
> **🚧 Work in Progress:**
|
||||||
- **Minimal ONVIF support** for device discovery, stream URI reporting, and basic integration with security recorders
|
> This project is evolving rapidly. Contributions, feedback, and feature requests are welcome!
|
||||||
|
> -*Star the repo and join the project!*
|
||||||
The goal is to make ESP32-CAM modules plug-and-play with mainstream video surveillance systems, while remaining lightweight and efficient.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- 📡 **ONVIF Discovery:** Your ESP32-CAM will appear as a discoverable camera on ONVIF-compatible NVRs/DVRs.
|
- 📡 **ONVIF Discovery:**
|
||||||
- 🎥 **RTSP Streaming:** Real-time MJPEG streaming for live view and recording.
|
Appears as a discoverable camera on ONVIF-compatible NVRs/DVRs for easy integration.
|
||||||
- ⚡ **Lightweight:** Designed for ESP32-CAM’s limited resources.
|
- 🎥 **RTSP Streaming (MJPEG):**
|
||||||
- 🛠️ **Easy Setup:** Simple Wi-Fi configuration and deployment.
|
Real-time video streaming for live view and recording.
|
||||||
- 🔒 **Open Source:** MIT-licensed for personal and commercial use.
|
- ⚡ **Lightweight:**
|
||||||
|
Optimized for ESP32-CAM’s limited resources.
|
||||||
|
- 🛠️ **Easy Setup:**
|
||||||
|
Simple Wi-Fi configuration (web-based setup coming soon).
|
||||||
|
- 🌐 **Web Configuration Interface:**
|
||||||
|
*(Planned)* Configure camera, Wi-Fi, and storage via browser.
|
||||||
|
- 🗂️ **SD Card Recording:**
|
||||||
|
*(Planned)* Record video directly to microSD card.
|
||||||
|
- ↔️ **Motion Detection:**
|
||||||
|
*(Planned)* Basic motion detection for event-based recording.
|
||||||
|
- 🔐 **Secure Credential Storage:**
|
||||||
|
*(Planned)* Store Wi-Fi credentials securely in SPIFFS.
|
||||||
|
- 🌏 **Access Point Fallback:**
|
||||||
|
*(Planned)* Automatically creates an AP if unable to connect to Wi-Fi.
|
||||||
|
- 🔒 **Open Source:**
|
||||||
|
MIT-licensed for personal and commercial use.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Roadmap
|
## Hardware Requirements
|
||||||
|
|
||||||
- [x] Minimal ONVIF WS-Discovery responder
|
- **ESP32-CAM board** (AI-Thinker or compatible)
|
||||||
- [x] ONVIF `/onvif/device_service` endpoint (GetStreamUri, GetCapabilities)
|
- **MicroSD card** (optional, for recording)
|
||||||
- [x] RTSP video streaming (MJPEG)
|
- **5V power supply**
|
||||||
- [ ] Web-based configuration interface
|
- **FTDI programmer/adapter** (for initial flashing)
|
||||||
- [ ] Optional SD card recording
|
|
||||||
- [ ] Motion detection (future)
|
---
|
||||||
- [ ] Support for more ONVIF features (profiles, device info, etc.)
|
|
||||||
|
## Software Dependencies
|
||||||
|
|
||||||
|
- **Arduino IDE** or **PlatformIO**
|
||||||
|
- **ESP32 Arduino Core**
|
||||||
|
- **Required Libraries:**
|
||||||
|
- ArduinoJson
|
||||||
|
- ESP32 Camera Driver
|
||||||
|
- SPIFFS file system
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Arduino IDE
|
||||||
|
|
||||||
|
1. **Install Arduino IDE** from [arduino.cc](https://www.arduino.cc/)
|
||||||
|
2. **Add ESP32 board support:**
|
||||||
|
- File > Preferences > Add
|
||||||
|
`https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json`
|
||||||
|
to "Additional Board Manager URLs"
|
||||||
|
- Tools > Board > Boards Manager > Search "ESP32" > Install latest version
|
||||||
|
3. **Install required libraries:**
|
||||||
|
- Tools > Manage Libraries > Search and install "ArduinoJson"
|
||||||
|
4. **Connect ESP32-CAM** via FTDI adapter:
|
||||||
|
- GND → GND
|
||||||
|
- 5V → 5V
|
||||||
|
- TX → RX
|
||||||
|
- RX → TX
|
||||||
|
- GPIO0 → GND (for flashing mode)
|
||||||
|
5. **Select Board/Port:**
|
||||||
|
Tools > Board > ESP32 Arduino > AI-Thinker ESP32-CAM
|
||||||
|
Tools > Port > [Your FTDI port]
|
||||||
|
6. **Upload Firmware:**
|
||||||
|
Click Upload. After upload, disconnect GPIO0 from GND and reset ESP32-CAM.
|
||||||
|
|
||||||
|
### PlatformIO (VS Code)
|
||||||
|
|
||||||
|
1. **Install Visual Studio Code**
|
||||||
|
2. **Install PlatformIO extension**
|
||||||
|
3. **Create a new project:**
|
||||||
|
- Board: AI Thinker ESP32-CAM
|
||||||
|
- Framework: Arduino
|
||||||
|
4. **Configure `platformio.ini`:**
|
||||||
|
```
|
||||||
|
[env:esp32cam]
|
||||||
|
platform = espressif32
|
||||||
|
board = esp32cam
|
||||||
|
framework = arduino
|
||||||
|
monitor_speed = 115200
|
||||||
|
lib_deps = bblanchon/ArduinoJson @ ^6.21.3
|
||||||
|
upload_speed = 921600
|
||||||
|
build_flags = -DCORE_DEBUG_LEVEL=5
|
||||||
|
```
|
||||||
|
5. **Import source files** into `src/` directory.
|
||||||
|
6. **Flash firmware** as above.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
1. **Clone this repository**
|
1. **Clone this repository**
|
||||||
2. **Edit Wi-Fi credentials** in the source code
|
2. **Edit Wi-Fi credentials** in the source code (web setup coming soon)
|
||||||
3. **Flash to your ESP32-CAM** using Arduino IDE or PlatformIO
|
3. **Flash to your ESP32-CAM**
|
||||||
4. **Connect your NVR/DVR** and discover the camera via ONVIF, or add the RTSP stream manually
|
4. **Connect your NVR/DVR** and discover the camera via ONVIF, or add the RTSP stream manually:
|
||||||
|
```
|
||||||
|
rtsp://[camera-ip]:554/mjpeg/1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
- Access the RTSP stream using compatible NVR/DVR software or VLC.
|
||||||
|
- *(Planned)* Access the web interface for live view, configuration, and SD card management.
|
||||||
|
- *(Planned)* On first boot, ESP32-CAM will create an access point ("ESP32-CAM-ONVIF") for initial setup.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
- **Tested hardware:** AI-Thinker ESP32-CAM
|
- **Hardware Support:** ESP32-CAM (AI-Thinker or compatible)
|
||||||
- **NVR/DVR compatibility:** Hikvision, Dahua, and most ONVIF-compliant recorders (MJPEG stream)
|
- **NVR/DVR Compatibility:** Hikvision, Dahua, and most ONVIF-compliant recorders (MJPEG stream)
|
||||||
- **Limitations:** MJPEG only (no H.264); some recorders may require H.264 for recording
|
- **Limitations:** MJPEG only (no H.264); some recorders may require H.264 for recording
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
| File/Folder | Description |
|
||||||
|
|-------------------------|--------------------------------------------|
|
||||||
|
| `ESP32-CAM-ONVIF.ino` | Main firmware sketch |
|
||||||
|
| `camera_control.*` | Camera initialization and settings |
|
||||||
|
| `rtsp_server.*` | RTSP streaming implementation |
|
||||||
|
| `onvif_server.*` | ONVIF protocol implementation |
|
||||||
|
| `web_config.*` | *(Planned)* Web interface |
|
||||||
|
| `wifi_manager.*` | *(Planned)* Wi-Fi setup and AP fallback |
|
||||||
|
| `sd_recorder.*` | *(Planned)* SD card recording |
|
||||||
|
| `motion_detection.*` | *(Planned)* Motion detection |
|
||||||
|
| `data/` | *(Planned)* Web UI files (HTML, CSS, JS) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
- [x] ONVIF WS-Discovery responder
|
||||||
|
- [x] `/onvif/device_service` endpoint (GetStreamUri, GetCapabilities)
|
||||||
|
- [x] RTSP video streaming (MJPEG)
|
||||||
|
- [ ] Web-based configuration interface
|
||||||
|
- [ ] SD card recording and management
|
||||||
|
- [ ] Motion detection
|
||||||
|
- [ ] Advanced ONVIF features (profiles, device info, etc.)
|
||||||
|
- [ ] Secure credential storage (SPIFFS)
|
||||||
|
- [ ] Access point fallback for Wi-Fi setup
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
*Coming soon!*
|
*Coming soon!*
|
||||||
@@ -68,6 +179,13 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
[](https://buymeacoffee.com/CyberTrinity)
|
||||||
|
[](https://patreon.com/CyberTrinity)
|
||||||
|
[](https://github.com/sponsors/John-Varghese-EH)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the [MIT License](LICENSE).
|
This project is licensed under the [MIT License](LICENSE).
|
||||||
@@ -76,10 +194,10 @@ This project is licensed under the [MIT License](LICENSE).
|
|||||||
|
|
||||||
## Acknowledgments
|
## Acknowledgments
|
||||||
|
|
||||||
- [Micro-RTSP](https://github.com/geeksville/Micro-RTSP) for RTSP streaming on ESP32
|
- Micro-RTSP for RTSP streaming on ESP32
|
||||||
- ONVIF community for protocol documentation and inspiration
|
- ONVIF community for protocol documentation and inspiration
|
||||||
|
- Thanks to all contributors and the open-source community!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
> **Stay tuned for updates, features, and documentation as the project evolves!**
|
*Stay tuned for updates, new features, and documentation as the project evolves! Star the repo to follow progress and contribute to making ESP32-CAM ONVIF the ultimate DIY network camera solution.*
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user