diff --git a/app_httpd.cpp b/app_httpd.cpp index a75ebc5..7c2689f 100644 --- a/app_httpd.cpp +++ b/app_httpd.cpp @@ -1,16 +1,3 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. #include "esp_http_server.h" #include "esp_timer.h" #include "esp_camera.h" diff --git a/esp32-cam-webserver.cpp b/esp32-cam-webserver.cpp index 810d9e6..fd04b9e 100644 --- a/esp32-cam-webserver.cpp +++ b/esp32-cam-webserver.cpp @@ -5,30 +5,6 @@ #define APP_CPU 1 #define PRO_CPU 0 -/* This sketch is a extension/expansion/reork of the 'official' ESP32 Camera example - * sketch from Expressif: - * https://github.com/espressif/arduino-esp32/tree/master/libraries/ESP32/examples/Camera/CameraWebServer - * - * It is modified to allow control of Illumination LED Lamps's (present on some modules), - * greater feedback via a status LED, and the HTML contents are present in plain text - * for easy modification. - * - * A camera name can now be configured, and wifi details can be stored in an optional - * header file to allow easier updated of the repo. - * - * The web UI has had changes to add the lamp control, rotation, a standalone viewer, - * more feeedback, new controls and other tweaks and changes, - * note: Make sure that you have either selected ESP32 AI Thinker, - * or another board which has PSRAM enabled to use high resolution camera modes - */ - -/* - * FOR NETWORK AND HARDWARE SETTINGS COPY OR RENAME 'myconfig.sample.h' TO 'myconfig.h' AND EDIT THAT. - * - * By default this sketch will assume an AI-THINKER ESP-CAM and create - * an accesspoint called "ESP32-CAM-CONNECT" (password: "InsecurePassword") - * - */ // Primary config, or defaults. #if __has_include("myconfig.h") @@ -73,6 +49,8 @@ IPAddress gw; extern void startCameraServer(); // Declare external function from esp32-streamserver.cpp extern void startStreamServer(); +// Declare external function from esp32-streamserver.cpp +void startMQTTClient(const char *, int, const char *, const char *, const void *); // A Name for the Camera. (set in myconfig.h) #if defined(CAM_NAME) @@ -471,10 +449,10 @@ void setup() delay(10000); ESP.restart(); } - sensor_t *s = esp_camera_sensor_get(); + sensor_t *espCamSensor = esp_camera_sensor_get(); // Dump camera module, warn for unsupported modules. - switch (s->id.PID) + switch (espCamSensor->id.PID) { case OV9650_PID: Serial.println("WARNING: OV9650 camera module is not properly supported, will fallback to OV2640 operation"); @@ -493,11 +471,11 @@ void setup() } // OV3660 initial sensors are flipped vertically and colors are a bit saturated - if (s->id.PID == OV3660_PID) + if (espCamSensor->id.PID == OV3660_PID) { - s->set_vflip(s, 1); //flip it back - s->set_brightness(s, 1); //up the blightness just a bit - s->set_saturation(s, -2); //lower the saturation + espCamSensor->set_vflip(espCamSensor, 1); //flip it back + espCamSensor->set_brightness(espCamSensor, 1); //up the blightness just a bit + espCamSensor->set_saturation(espCamSensor, -2); //lower the saturation } // M5 Stack Wide has special needs @@ -516,7 +494,7 @@ void setup() // set initial frame rate #if defined(DEFAULT_RESOLUTION) - s->set_framesize(s, DEFAULT_RESOLUTION); + espCamSensor->set_framesize(espCamSensor, DEFAULT_RESOLUTION); #else s->set_framesize(s, FRAMESIZE_SVGA); #endif @@ -596,7 +574,7 @@ void setup() startStreamServer(); // Construct the app and stream URLs - sprintf(httpURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], HTTP_PORT); + sprintf(httpURL, "http://%d.%d.%d.%d:%d/", ip[0], ip[1], ip[2], ip[3], HTTP_PORT); Serial.printf("\nCamera Ready!\nUse '%s' to connect\n", httpURL); @@ -605,6 +583,14 @@ void setup() else Serial.println("Camera debug data is disabled (send any char to enable)"); + /* + * Main camera streams running, now enable MQTT + * */ + +#if defined(MQTT_HOST) + startMQTTClient(MQTT_HOST, MQTT_PORT, MQTT_TOPIC, myName, MQTT_CERT); +#endif + // Used when dumping status; these are slow functions, so just do them once during startup sketchSize = ESP.getSketchSize(); sketchSpace = ESP.getFreeSketchSpace(); diff --git a/mqtt_client.cpp b/mqtt_client.cpp new file mode 100644 index 0000000..36e4a50 --- /dev/null +++ b/mqtt_client.cpp @@ -0,0 +1,149 @@ +#include "Arduino.h" +#include +#include "PubSubClient.h" +#include +#include + +extern UBaseType_t activeClients; + +WiFiClient wifiClient; +PubSubClient mqttClient(wifiClient); +uint16_t mqtt_port = 1883; +char *mqtt_host = new char[64]; +char *mqtt_pub_topic = new char[256]; +char *mqtt_client_id = new char[64]; +const char *MQTT_JSON = "{\"ClientId\":\"%s\",\"ActiveClients\":%d}"; + +void startMQTTClient(char *host, char *topic, char *cert); +void mqttconnect(); +void mqttCB(void *pvParameters); + +TaskHandle_t tMQTT; // handles client connection to the MQTT Server +void mqttCB(void *pvParameters) +{ + TickType_t xLastWakeTime = xTaskGetTickCount(); + TickType_t xFrequency = pdMS_TO_TICKS(60000); //Every minute + + while (true) + { + /* if client was disconnected then try to reconnect again */ + if (!mqttClient.connected()) + { + mqttconnect(); + } + + Serial.printf("ActiveClients %d\n", activeClients); + + char msg[64] = ""; + snprintf(msg, 64, MQTT_JSON, mqtt_client_id, activeClients); + Serial.printf("Publishing message %s\n", msg); + /* publish the message */ + mqttClient.publish(mqtt_pub_topic, msg); + + // Let other tasks run after serving every client + taskYIELD(); + vTaskDelayUntil(&xLastWakeTime, xFrequency); + } +} + +void mqttconnect() +{ + u32_t defaultIp = 0; + IPAddress noConnIP = IPAddress(defaultIp); + IPAddress serverIp = IPAddress(defaultIp); + unsigned long period = 5000; + unsigned long lastRun = 0; + + /* Loop until reconnected */ + while (!mqttClient.connected()) + { + + if (millis() - lastRun < period) + { + yield(); + continue; + } + + lastRun = millis(); + + /* Configure the MQTT server with IPaddress and port. */ + while (noConnIP == serverIp) + { + Serial.printf("Querying '%s:%d'...\n", mqtt_host, mqtt_port); + + serverIp = MDNS.queryHost(mqtt_host); + + Serial.printf("IP address of server: %s\n", serverIp.toString().c_str()); + + if (noConnIP == serverIp) + { + WiFi.hostByName(mqtt_host, serverIp); + Serial.printf("IP address of server: %s\n", serverIp.toString().c_str()); + if ((uint32_t)serverIp == 0) + { + serverIp = noConnIP; + } + } + + if (noConnIP != serverIp) + { + break; + } + yield(); + } + + mqttClient.setServer(serverIp, mqtt_port); + + /* connect now */ + char clientId[100] = {}; + char random[32] = {'-'}; + + srand((unsigned int)(time(NULL))); + int index = 0; + + char char1[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + for (index = 1; index < 31; index++) + { + random[index] = char1[rand() % (sizeof char1 - 1)]; + } + Serial.println(random); + sprintf(clientId, "%s", mqtt_client_id); + strcat(clientId, random); + + Serial.printf("MQTT connecting as '%s'...\n", clientId); + if (mqttClient.connect(clientId, "testUser", "1234")) //, "security/healthcheck", 20, true, "WillMessage", true)) + { + Serial.println("connected"); + } + else + { + mqttClient.disconnect(); + Serial.print("failed, status code ="); + Serial.print(mqttClient.state()); + Serial.printf("try again in %d seconds/n", 5); + } + } +} + +void startMQTTClient(const char *host, int port, const char *topic, const char *client_id, const void *cert) +{ + mqtt_port = port; + strcpy(mqtt_pub_topic, topic); + strcpy(mqtt_host, host); + strcpy(mqtt_client_id, client_id); + + Serial.printf("Starting MQTT Client '%s'\n", client_id); + + /* Set SSL/TLS certificate */ + // wifiClient.setCACert(ca_cert); + + /* Start main task. */ + xTaskCreatePinnedToCore( + mqttCB, + "mqtt", + 4096, + NULL, + 2, + &tMQTT, + 1); +} diff --git a/platformio.ini b/platformio.ini index 94d3d36..6e59252 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,13 +1,13 @@ ; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html -; The esp32-cam-webserver project is intended to be easily compilable -; with the stock Arduino IDE. -; - Maintaining compatibility with other development environments -; is important, but I wont accept changes to the PlatformIO build that -; break compatibilty with the stock IDE. Eg by using non-standard -; partition schemes or overriding Arduino defined limits, etc. - [platformio] src_dir = ./ @@ -16,7 +16,7 @@ platform = espressif32 board = esp32cam framework = arduino monitor_speed = 115200 -build_flags = - -DBOARD_HAS_PSRAM - -mfix-esp32-psram-cache-issue - \ No newline at end of file +build_flags = + -DBOARD_HAS_PSRAM + -mfix-esp32-psram-cache-issue +lib_deps = knolleary/PubSubClient@^2.8 diff --git a/stream_httpd.cpp b/stream_httpd.cpp index 75b3175..5caf8a8 100644 --- a/stream_httpd.cpp +++ b/stream_httpd.cpp @@ -1,25 +1,3 @@ -/* - - This is a simple MJPEG streaming webserver implemented for AI-Thinker ESP32-CAM - and ESP-EYE modules. - This is tested to work with VLC and Blynk video widget and can support up to 10 - simultaneously connected streaming clients. - Simultaneous streaming is implemented with FreeRTOS tasks. - - Inspired by and based on this Instructable: $9 RTSP Video Streamer Using the ESP32-CAM Board - (https://www.instructables.com/id/9-RTSP-Video-Streamer-Using-the-ESP32-CAM-Board/) - - Board: AI-Thinker ESP32-CAM or ESP-EYE - Compile as: - ESP32 Dev Module - CPU Freq: 240 - Flash Freq: 80 - Flash mode: QIO - Flash Size: 4Mb - Patrition: Minimal SPIFFS - PSRAM: Enabled -*/ - // ESP32 has two cores: APPlication core and PROcess core (the one that runs ESP32 SDK stack) #define APP_CPU 1 #define PRO_CPU 0 @@ -29,7 +7,6 @@ #include #include #include - #include #include #include @@ -74,6 +51,7 @@ SemaphoreHandle_t frameSync = NULL; // Queue stores currently connected clients to whom we are streaming QueueHandle_t streamingClients; +UBaseType_t activeClients; // We will try to achieve 25 FPS frame rate const int FPS = 14; @@ -91,6 +69,7 @@ void handleJPG(void); void handleNotFound(); void startStreamServer(int sPort); + // ==== Memory allocator that takes advantage of PSRAM if present ======================= char *allocateMemory(char *aPtr, size_t aSize) { @@ -142,6 +121,7 @@ void mjpegCB(void *pvParameters) // Creating a queue to track all connected clients streamingClients = xQueueCreate(10, sizeof(WiFiClient *)); + activeClients = 0; //=== setup section ================== @@ -358,15 +338,13 @@ void streamCB(void *pvParameters) xFrequency = pdMS_TO_TICKS(1000 / FPS); // Only bother to send anything if there is someone watching - UBaseType_t activeClients = uxQueueMessagesWaiting(streamingClients); + activeClients = uxQueueMessagesWaiting(streamingClients); if (activeClients) { // Adjust the period to the number of connected clients xFrequency /= activeClients; - Serial.printf("ActiveClients %d\n", activeClients); - // Since we are sending the same frame to everyone, // pop a client from the the front of the queue WiFiClient *client;