Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

Commit

Permalink
Add ESP32 camera support (#544)
Browse files Browse the repository at this point in the history
* Add ESP32 Camera

* Add methods

* Lint

* Fixes

* clang-format

* clang-tidy

* Update total_daily_energy.cpp

* Updates

* ESP8266 override pin mode INPUT_PULLUP with INPUT for GPIO16

Fixes esphome/issues#118

* Add servo

* Run clang-format

* Better error message
  • Loading branch information
OttoWinter authored Mar 13, 2019
1 parent 750f1a5 commit f3292eb
Show file tree
Hide file tree
Showing 23 changed files with 816 additions and 81 deletions.
17 changes: 17 additions & 0 deletions src/esphome/api/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,20 @@ message ExecuteServiceRequest {
fixed32 key = 1;
repeated ExecuteServiceArgument args = 2;
}

message ListEntitiesCameraResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
}

message CameraImageResponse {
fixed32 key = 1;
bytes data = 2;
bool done = 3;
}
message CameraImageRequest {
bool single = 1;
bool stream = 2;
}
3 changes: 3 additions & 0 deletions src/esphome/api/api_message.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ enum class APIMessageType {
LIST_ENTITIES_SWITCH_RESPONSE = 17,
LIST_ENTITIES_TEXT_SENSOR_RESPONSE = 18,
LIST_ENTITIES_SERVICE_RESPONSE = 41,
LIST_ENTITIES_CAMERA_RESPONSE = 43,
LIST_ENTITIES_DONE_RESPONSE = 19,

SUBSCRIBE_STATES_REQUEST = 20,
Expand All @@ -43,6 +44,7 @@ enum class APIMessageType {
SENSOR_STATE_RESPONSE = 25,
SWITCH_STATE_RESPONSE = 26,
TEXT_SENSOR_STATE_RESPONSE = 27,
CAMERA_IMAGE_RESPONSE = 44,

SUBSCRIBE_LOGS_REQUEST = 28,
SUBSCRIBE_LOGS_RESPONSE = 29,
Expand All @@ -51,6 +53,7 @@ enum class APIMessageType {
FAN_COMMAND_REQUEST = 31,
LIGHT_COMMAND_REQUEST = 32,
SWITCH_COMMAND_REQUEST = 33,
CAMERA_IMAGE_REQUEST = 45,

SUBSCRIBE_SERVICE_CALLS_REQUEST = 34,
SERVICE_CALL_RESPONSE = 35,
Expand Down
70 changes: 70 additions & 0 deletions src/esphome/api/api_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ void APIServer::setup() {
});

this->last_connected_ = millis();

#ifdef USE_ESP32_CAMERA
if (global_esp32_camera != nullptr) {
global_esp32_camera->add_image_callback([this](std::shared_ptr<CameraImage> image) {
for (auto *c : this->clients_)
if (!c->remove_)
c->send_camera_state(image);
});
}
#endif
}
void APIServer::loop() {
// Partition clients into remove and active
Expand Down Expand Up @@ -385,6 +395,7 @@ void APIConnection::read_message_(uint32_t size, uint32_t type, uint8_t *msg) {
case APIMessageType::LIST_ENTITIES_SWITCH_RESPONSE:
case APIMessageType::LIST_ENTITIES_TEXT_SENSOR_RESPONSE:
case APIMessageType::LIST_ENTITIES_SERVICE_RESPONSE:
case APIMessageType::LIST_ENTITIES_CAMERA_RESPONSE:
case APIMessageType::LIST_ENTITIES_DONE_RESPONSE:
// Invalid
break;
Expand All @@ -401,6 +412,7 @@ void APIConnection::read_message_(uint32_t size, uint32_t type, uint8_t *msg) {
case APIMessageType::SENSOR_STATE_RESPONSE:
case APIMessageType::SWITCH_STATE_RESPONSE:
case APIMessageType::TEXT_SENSOR_STATE_RESPONSE:
case APIMessageType::CAMERA_IMAGE_RESPONSE:
// Invalid
break;
case APIMessageType::SUBSCRIBE_LOGS_REQUEST: {
Expand Down Expand Up @@ -484,6 +496,14 @@ void APIConnection::read_message_(uint32_t size, uint32_t type, uint8_t *msg) {
this->on_execute_service_(req);
break;
}
case APIMessageType::CAMERA_IMAGE_REQUEST: {
#ifdef USE_ESP32_CAMERA
CameraImageRequest req;
req.decode(msg, size);
this->on_camera_image_request_(req);
#endif
break;
}
}
}
void APIConnection::on_hello_request_(const HelloRequest &req) {
Expand Down Expand Up @@ -714,6 +734,31 @@ void APIConnection::loop() {
this->sent_ping_ = true;
this->send_ping_request();
}

#ifdef USE_ESP32_CAMERA
if (this->image_reader_.available()) {
uint32_t space = this->client_->space();
// reserve 15 bytes for metadata, and at least 64 bytes of data
if (space >= 15 + 64) {
uint32_t to_send = std::min(space - 15, this->image_reader_.available());
auto buffer = this->get_buffer();
// fixed32 key = 1;
buffer.encode_fixed32(1, global_esp32_camera->get_object_id_hash());
// bytes data = 2;
buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
// bool done = 3;
bool done = this->image_reader_.available() == to_send;
buffer.encode_bool(3, done);
bool success = this->send_buffer(APIMessageType::CAMERA_IMAGE_RESPONSE);
if (success) {
this->image_reader_.consume_data(to_send);
}
if (success && done) {
this->image_reader_.return_image();
}
}
}
#endif
}

#ifdef USE_BINARY_SENSOR
Expand Down Expand Up @@ -1014,6 +1059,31 @@ APIBuffer APIConnection::get_buffer() {
void APIConnection::send_time_request() { this->send_empty_message(APIMessageType::GET_TIME_REQUEST); }
#endif

#ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<CameraImage> image) {
if (!this->state_subscription_)
return;
if (this->image_reader_.available())
return;
this->image_reader_.set_image(image);
}
#endif

#ifdef USE_ESP32_CAMERA
void APIConnection::on_camera_image_request_(const CameraImageRequest &req) {
if (global_esp32_camera == nullptr)
return;

ESP_LOGV(TAG, "on_camera_image_request_ stream=%s single=%s", YESNO(req.get_stream()), YESNO(req.get_single()));
if (req.get_single()) {
global_esp32_camera->request_image();
}
if (req.get_stream()) {
global_esp32_camera->request_stream();
}
}
#endif

} // namespace api

ESPHOME_NAMESPACE_END
Expand Down
9 changes: 9 additions & 0 deletions src/esphome/api/api_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class APIConnection {
#endif
#ifdef USE_TEXT_SENSOR
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state);
#endif
#ifdef USE_ESP32_CAMERA
void send_camera_state(std::shared_ptr<CameraImage> image);
#endif
bool send_log_message(int level, const char *tag, const char *line);
bool send_disconnect_request(const char *reason);
Expand Down Expand Up @@ -111,6 +114,9 @@ class APIConnection {
void on_subscribe_home_assistant_states_request_(const SubscribeHomeAssistantStatesRequest &req);
void on_home_assistant_state_response_(const HomeAssistantStateResponse &req);
void on_execute_service_(const ExecuteServiceRequest &req);
#ifdef USE_ESP32_CAMERA
void on_camera_image_request_(const CameraImageRequest &req);
#endif

enum class ConnectionState {
WAITING_FOR_HELLO,
Expand All @@ -128,6 +134,9 @@ class APIConnection {
std::string client_info_;
ListEntitiesIterator list_entities_iterator_;
InitialStateIterator initial_state_iterator_;
#ifdef USE_ESP32_CAMERA
CameraImageReader image_reader_;
#endif

bool state_subscription_{false};
int log_subscription_{ESPHOME_LOG_LEVEL_NONE};
Expand Down
20 changes: 20 additions & 0 deletions src/esphome/api/command_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,26 @@ uint32_t SwitchCommandRequest::get_key() const { return this->key_; }
bool SwitchCommandRequest::get_state() const { return this->state_; }
#endif

#ifdef USE_ESP32_CAMERA
bool CameraImageRequest::get_single() const { return this->single_; }
bool CameraImageRequest::get_stream() const { return this->stream_; }
bool CameraImageRequest::decode_varint(uint32_t field_id, uint32_t value) {
switch (field_id) {
case 1:
// bool single = 1;
this->single_ = value;
return true;
case 2:
// bool stream = 2;
this->stream_ = value;
return true;
default:
return false;
}
}
APIMessageType CameraImageRequest::message_type() const { return APIMessageType::CAMERA_IMAGE_REQUEST; }
#endif

} // namespace api

ESPHOME_NAMESPACE_END
Expand Down
14 changes: 14 additions & 0 deletions src/esphome/api/command_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,20 @@ class SwitchCommandRequest : public APIMessage {
};
#endif

#ifdef USE_ESP32_CAMERA
class CameraImageRequest : public APIMessage {
public:
bool decode_varint(uint32_t field_id, uint32_t value) override;
bool get_single() const;
bool get_stream() const;
APIMessageType message_type() const override;

protected:
bool single_{false};
bool stream_{false};
};
#endif

} // namespace api

ESPHOME_NAMESPACE_END
Expand Down
10 changes: 10 additions & 0 deletions src/esphome/api/list_entities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_SERVICE_RESPONSE);
}

#ifdef USE_ESP32_CAMERA
bool ListEntitiesIterator::on_camera(ESP32Camera *camera) {
auto buffer = this->client_->get_buffer();
buffer.encode_nameable(camera);
// string unique_id = 4;
buffer.encode_string(4, get_default_unique_id("camera", camera));
return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_CAMERA_RESPONSE);
}
#endif

APIMessageType ListEntitiesRequest::message_type() const { return APIMessageType::LIST_ENTITIES_REQUEST; }

} // namespace api
Expand Down
3 changes: 3 additions & 0 deletions src/esphome/api/list_entities.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class ListEntitiesIterator : public ComponentIterator {
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
#endif
bool on_service(UserServiceDescriptor *service) override;
#ifdef USE_ESP32_CAMERA
bool on_camera(ESP32Camera *camera) override;
#endif
bool on_end() override;

protected:
Expand Down
20 changes: 20 additions & 0 deletions src/esphome/api/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ void APIBuffer::encode_bool(uint32_t field, bool value) {
void APIBuffer::encode_string(uint32_t field, const std::string &value) {
this->encode_string(field, value.data(), value.size());
}
void APIBuffer::encode_bytes(uint32_t field, const uint8_t *data, size_t len) {
this->encode_string(field, reinterpret_cast<const char *>(data), len);
}
void APIBuffer::encode_string(uint32_t field, const char *string, size_t len) {
if (len == 0)
return;
Expand Down Expand Up @@ -299,6 +302,20 @@ void ComponentIterator::advance() {
success = this->on_service(service);
}
break;
#ifdef USE_ESP32_CAMERA
case IteratorState::CAMERA:
if (global_esp32_camera == nullptr) {
advance_platform = true;
} else {
if (global_esp32_camera->is_internal()) {
advance_platform = success = true;
break;
} else {
advance_platform = success = this->on_camera(global_esp32_camera);
}
}
break;
#endif
case IteratorState::MAX:
if (this->on_end()) {
this->state_ = IteratorState::NONE;
Expand All @@ -316,6 +333,9 @@ void ComponentIterator::advance() {
bool ComponentIterator::on_end() { return true; }
bool ComponentIterator::on_begin() { return true; }
bool ComponentIterator::on_service(UserServiceDescriptor *service) { return true; }
#ifdef USE_ESP32_CAMERA
bool ComponentIterator::on_camera(ESP32Camera *camera) { return true; }
#endif

} // namespace api

Expand Down
8 changes: 8 additions & 0 deletions src/esphome/api/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "esphome/helpers.h"
#include "esphome/component.h"
#include "esphome/controller.h"
#include "esphome/esp32_camera.h"

ESPHOME_NAMESPACE_BEGIN

Expand All @@ -26,6 +27,7 @@ class APIBuffer {
void encode_bool(uint32_t field, bool value);
void encode_string(uint32_t field, const std::string &value);
void encode_string(uint32_t field, const char *string, size_t len);
void encode_bytes(uint32_t field, const uint8_t *data, size_t len);
void encode_fixed32(uint32_t field, uint32_t value);
void encode_float(uint32_t field, float value);
void encode_nameable(Nameable *nameable);
Expand Down Expand Up @@ -78,6 +80,9 @@ class ComponentIterator {
virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0;
#endif
virtual bool on_service(UserServiceDescriptor *service);
#ifdef USE_ESP32_CAMERA
virtual bool on_camera(ESP32Camera *camera);
#endif
virtual bool on_end();

protected:
Expand Down Expand Up @@ -106,6 +111,9 @@ class ComponentIterator {
TEXT_SENSOR,
#endif
SERVICE,
#ifdef USE_ESP32_CAMERA
CAMERA,
#endif
MAX,
} state_{IteratorState::NONE};
size_t at_{0};
Expand Down
1 change: 1 addition & 0 deletions src/esphome/application.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "esphome/log_component.h"
#include "esphome/ota_component.h"
#include "esphome/power_supply_component.h"
#include "esphome/servo.h"
#include "esphome/spi_component.h"
#include "esphome/status_led.h"
#include "esphome/uart_component.h"
Expand Down
2 changes: 2 additions & 0 deletions src/esphome/defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
#define USE_TEMPLATE_COVER
#ifdef ARDUINO_ARCH_ESP32
#define USE_ESP32_HALL_SENSOR
#define USE_ESP32_CAMERA
#endif
#define USE_DUTY_CYCLE_SENSOR
#define USE_STATUS_LED
Expand Down Expand Up @@ -138,6 +139,7 @@
#define USE_MQTT
#define USE_COPY_OUTPUT
#define USE_WIFI_INFO_TEXT_SENSOR
#define USE_SERVO
#endif

#ifdef USE_REMOTE_RECEIVER
Expand Down
Loading

0 comments on commit f3292eb

Please sign in to comment.