Skocz do zawartości

Aktywny System Monitorujący


Pomocna odpowiedź

Dziś o kamerce ESP32-CAM. Bardzo przypadł mi do gustu ten mikrokontroler.

U mnie jego zasilaniem steruje Pico. Po włączeniu zasilania kamerka łączy się z moją siecią domową, a następnie pobiera z mojego serwera FTP pliki z konfiguracją dla kamery. W ten sposób mogę zmienić kilka ustawień kamery zdalnie )rozdzielczość, jasność itp.). Synchronizuje czas z serwera czasu (w kolejnej wersji asm może dołożę zegar czasu rzeczywistego). Robi zdjęcie i wysyła je na FTP-a nazwę nadając na podstawie timestampa. Na koniec umieszcza na serwerze pusty plik, który jest informacją dla serwera, że pojawił się nowy plik i co ważniejsze, że jest poprawnie (w całości) przesłany. I tak w kółko. Jeśli chcę zmienić ustawienia muszę zapisać nowe ustawienia w plikach konfiguracyjnych na serwerze i przy pomocy SMS-a za pośrednictwem Pico wyłączyć i włączyć zasilanie dla ESP32.

Mam dwa problemy, których nie rozwiązałem, ale je ominąłem. Spróbuję je rozwiązać w kolejnych wersjach asm.

Nie potrafię skonfigurować ESP32 do pracy z sftp (szyfrowanie). Już wiem, że to jest możliwe, ale jeszcze nie potrafię tego zrobić i nie bardzo teraz mam na to czas. Wobec tego zainstalowałem na serwerze vsftpd i dałem do niego dostęp tylko kamerkom (dwóch użytkowników). Firewalle na serwerze ustawiłem tak, aby przepuszczały ruch na portach dla FTP-a tylko z konkretnego IP. Ale niestety mam zmienne IP.  Pico sprawdza, jakie IP ma aktualnie i po zmianie przydziału IP wysyła mi informację o tym SMS-em. Muszę wtedy ręcznie zmienić IP w dwóch warstwach firewalla (ufw i nadrzędny Oracle). To bardzo nieeleganckie rozwiązanie.

Czyli: muszę zmusić ESP32 do szyfrowania połączenia FTP i muszę zautomatyzować zmianę IP w firewallach (albo zastąpić ją lepszym rozwiązaniem).

Poniżej kod dla kamerki:

#include "esp_camera.h"
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems
#include "driver/rtc_io.h"
#include <WiFi.h>
#include "time.h"
#include <WiFiClient.h>  
#include "ESP32_FTPClient.h"

// Pin definition for CAMERA_MODEL_AI_THINKER
#define CAM_PIN_PWDN    32
#define CAM_PIN_RESET   -1 //software reset will be performed
#define CAM_PIN_XCLK     0
#define CAM_PIN_SIOD    26
#define CAM_PIN_SIOC    27
#define CAM_PIN_D7      35
#define CAM_PIN_D6      34
#define CAM_PIN_D5      39
#define CAM_PIN_D4      36
#define CAM_PIN_D3      21
#define CAM_PIN_D2      19
#define CAM_PIN_D1      18
#define CAM_PIN_D0       5
#define CAM_PIN_VSYNC   25
#define CAM_PIN_HREF    23
#define CAM_PIN_PCLK    22

char* ftp_server = "*****";
char* ftp_user = "*****";
char* ftp_pass = "*****";

const char* ssid =  "*****";
const char* password =  "*****";

const char* ntpServer = "ntp1.tp.pl";
const long  gmtOffset_sec = 7200;   //Replace with your GMT offset (seconds)
const int   daylightOffset_sec = 0;  //Replace with your daylight offset (seconds)

ESP32_FTPClient ftp (ftp_server, ftp_user, ftp_pass, 3000, 0);

void setup() {

  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector

  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    // Serial.print(".");
  }
  // Serial.print(" IP Address: ");
  // Serial.println(WiFi.localIP());

  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

  String rozdzielczosc = "", jakosc = "", jasnosc = "", kontrast = "", nasycenie = "", efekty = "";

  static camera_config_t camera_config = {
      .pin_pwdn  = CAM_PIN_PWDN,
      .pin_reset = CAM_PIN_RESET,
      .pin_xclk = CAM_PIN_XCLK,
      .pin_sccb_sda = CAM_PIN_SIOD,
      .pin_sccb_scl = CAM_PIN_SIOC,
      .pin_d7 = CAM_PIN_D7,
      .pin_d6 = CAM_PIN_D6,
      .pin_d5 = CAM_PIN_D5,
      .pin_d4 = CAM_PIN_D4,
      .pin_d3 = CAM_PIN_D3,
      .pin_d2 = CAM_PIN_D2,
      .pin_d1 = CAM_PIN_D1,
      .pin_d0 = CAM_PIN_D0,
      .pin_vsync = CAM_PIN_VSYNC,
      .pin_href = CAM_PIN_HREF,
      .pin_pclk = CAM_PIN_PCLK,

      .xclk_freq_hz = 20000000, //EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode
      .ledc_timer = LEDC_TIMER_0,
      .ledc_channel = LEDC_CHANNEL_0,
      .pixel_format = PIXFORMAT_JPEG,//YUV422,GRAYSCALE,RGB565,JPEG
      // .frame_size = FRAMESIZE_SVGA,// 1s~ 2s 2s 3s 3s 4s 7s 10s 15s (przy q=12 FRAMESIZE_ + QVGA X 320 240 |CIF X 352 288 |VGA X 640 480 |SVGA X 800 600 |XGA X 1024 768 |SXGA X 1280 1024 |UXGA X 1600 1200, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.
      //  .jpeg_quality = 12, //0-63, for OV series camera sensors, lower number means higher quality
      .fb_count = 2, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode.
      .grab_mode = CAMERA_GRAB_WHEN_EMPTY //CAMERA_GRAB_WHEN_EMPTY//CAMERA_GRAB_LATEST. Sets when buffers should be filled
  };
  
  // pobranie ustawień kamery z serwera
  while (!(ftp.isConnected())) {
    ftp.CloseConnection();
    delay(10000);
    Serial.println("*** łączenie FTP ***");
    ftp.OpenConnection();
    ftp.ChangeWorkDir("/obraz/");
    ftp.InitFile("Type A");
    ftp.DownloadString("framesize.txt", rozdzielczosc);
    ftp.CloseFile();
    ftp.InitFile("Type A");
    ftp.DownloadString("quality.txt", jakosc);
    ftp.CloseFile();
    ftp.InitFile("Type A");
    ftp.DownloadString("brightness.txt", jasnosc);
    ftp.CloseFile();
    ftp.InitFile("Type A");
    ftp.DownloadString("contrast.txt", kontrast);
    ftp.CloseFile();
    ftp.InitFile("Type A");
    ftp.DownloadString("saturation.txt", nasycenie);
    ftp.CloseFile();
    ftp.InitFile("Type A");
    ftp.DownloadString("effects.txt", efekty);
    ftp.CloseFile();
  }

  if (rozdzielczosc=="QVGA") {
    camera_config.frame_size = FRAMESIZE_QVGA;
  } else if (rozdzielczosc=="VGA") {
    camera_config.frame_size = FRAMESIZE_VGA;
  } else if (rozdzielczosc=="SVGA") {
    camera_config.frame_size = FRAMESIZE_SVGA;
  } else if (rozdzielczosc=="XGA") {
    camera_config.frame_size = FRAMESIZE_XGA;
  } else if (rozdzielczosc=="SXGA") {
    camera_config.frame_size = FRAMESIZE_SXGA;
  } else if (rozdzielczosc=="UXGA") {
    camera_config.frame_size = FRAMESIZE_UXGA;
  } else {
    camera_config.frame_size = FRAMESIZE_SVGA;
  }

  if (jakosc=="5") {
    camera_config.jpeg_quality = 5;
  } else if (jakosc=="10") {
    camera_config.jpeg_quality = 10;
  } else if (jakosc=="12") {
    camera_config.jpeg_quality = 12;
  } else if (jakosc=="15") {
    camera_config.jpeg_quality = 15;
  } else if (jakosc=="20"){
    camera_config.jpeg_quality = 20;
  } else if (jakosc=="30") {
    camera_config.jpeg_quality = 30;
  } else {
    camera_config.jpeg_quality = 12;
  }

  //power up the camera if PWDN pin is defined
  if(CAM_PIN_PWDN != -1){
      pinMode(CAM_PIN_PWDN, OUTPUT);
      digitalWrite(CAM_PIN_PWDN, LOW);
  }

  //initialize the camera
  esp_err_t err = esp_camera_init(&camera_config);

  sensor_t * s = esp_camera_sensor_get();
  
  if (jasnosc=="-2") {
    s->set_brightness(s, -2);
  } else if (jasnosc=="-1") {
    s->set_brightness(s, -1);
  } else if (jasnosc=="0") {
    s->set_brightness(s, 0);
  } else if (jasnosc=="1") {
    s->set_brightness(s, 1);
  } else if (jasnosc=="2"){
    s->set_brightness(s, 2);
  } else {
    s->set_brightness(s, 1);
  }
  if (kontrast=="-2") {
    s->set_contrast(s, -2);
  } else if (kontrast=="-1") {
    s->set_contrast(s, -1);
  } else if (kontrast=="0") {
    s->set_contrast(s, 0);
  } else if (kontrast=="1") {
    s->set_contrast(s, 1);
  } else if (kontrast=="2"){
    s->set_contrast(s, 2);
  } else {
    s->set_contrast(s, 2);
  }
  if (nasycenie=="-2") {
    s->set_saturation(s, -2);
  } else if (nasycenie=="-1") {
    s->set_saturation(s, -1);
  } else if (nasycenie=="0") {
    s->set_saturation(s, 0);
  } else if (nasycenie=="1") {
    s->set_saturation(s, 1);
  } else if (nasycenie=="2"){
    s->set_saturation(s, 2);
  } else {
    s->set_saturation(s, -2);
  }
  if (efekty=="0") {
    s->set_special_effect(s, 0);
  } else if (efekty=="1") {
    s->set_special_effect(s, 1);
  } else if (efekty=="2") {
    s->set_special_effect(s, 2);
  } else if (efekty=="3") {
    s->set_special_effect(s, 3);
  } else if (efekty=="4"){
    s->set_special_effect(s, 4);
  } else if (efekty=="5") {
    s->set_special_effect(s, 5);
  } else if (efekty=="6"){
    s->set_special_effect(s, 6);
  } else {
    s->set_special_effect(s, 0);
  }
    //  s->set_brightness(s, 1);     // -2 to 2
    //  s->set_contrast(s, 2);       // -2 to 2
    //  s->set_saturation(s, -2);     // -2 to 2
    //  s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
    s->set_whitebal(s, 1);       // 0 = disable , 1 = enable
    s->set_awb_gain(s, 1);       // 0 = disable , 1 = enable
    s->set_wb_mode(s, 0);        // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
    s->set_exposure_ctrl(s, 1);  // 0 = disable , 1 = enable
    s->set_aec2(s, 0);           // 0 = disable , 1 = enable
    s->set_ae_level(s, 0);       // -2 to 2
    s->set_aec_value(s, 300);    // 0 to 1200
    s->set_gain_ctrl(s, 1);      // 0 = disable , 1 = enable
    s->set_agc_gain(s, 0);       // 0 to 30
    s->set_gainceiling(s, (gainceiling_t)0);  // 0 to 6
    s->set_bpc(s, 0);            // 0 = disable , 1 = enable
    s->set_wpc(s, 1);            // 0 = disable , 1 = enable
    s->set_raw_gma(s, 1);        // 0 = disable , 1 = enable
    s->set_lenc(s, 1);           // 0 = disable , 1 = enable
    s->set_hmirror(s, 0);        // 0 = disable , 1 = enable
    s->set_vflip(s, 0);          // 0 = disable , 1 = enable
    s->set_dcw(s, 1);            // 0 = disable , 1 = enable
    s->set_colorbar(s, 0);
    //  s->set_framesize(s, FRAMESIZE_VGA); // VGA|CIF|QVGA|HQVGA|QQVGA ( UXGA? SXGA? XGA? SVGA? )

}

void loop() {

  // acquire a frame
  camera_fb_t * fb = esp_camera_fb_get();

  while (WiFi.status() != WL_CONNECTED) {
    delay(5000);
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    Serial.print("Reconnecting to WiFi...");
    Serial.print(".");
    Serial.println(WiFi.localIP());
  }

  // przygotowanie nazwy pliku na podstawie aktualnego czasu
  time_t now;
  time(&now);
  char czas [12];
  int ret = snprintf(czas, sizeof(czas), "%ld", now);
  char* rozszerzenie = "\.jpg";
  int len = strlen(czas) + 5;
  char* nazwa_pliku = (char*) malloc(len);
  int i=0,j=0;
  for(i=0;i<len;i++){
    if(i<strlen(czas)){
      nazwa_pliku[i]=czas[i];
    } else {
      nazwa_pliku[i]=rozszerzenie[j++];
    }
  }
  // Serial.println(nazwa_pliku);
  while (!(ftp.isConnected())) {
    ftp.CloseConnection();
    delay(15000);
    // Serial.println("*** łączenie FTP ***");
    ftp.OpenConnection();
    ftp.ChangeWorkDir("/obraz/");
  }

  // wysłanie zdjęcia na serwer
  Serial.println("wysyłam");
  ftp.InitFile("Type I");
  ftp.NewFile(nazwa_pliku);
  ftp.WriteData( fb->buf, fb->len );
  ftp.CloseFile();
  // Serial.println("wysłane ---");

  // zapisanie pustego pliku informującego serwer o pojawieniu się nowego pliku z obrazem
  ftp.InitFile("Type A");
  ftp.NewFile("nowy.txt");
  ftp.CloseFile();

  //return the frame buffer back to the driver for reuse
  esp_camera_fb_return(fb);

}

Jak zwykle: wszelkie sugestie bardzo mile widziane. Co do powyższego kodu, to biję się w piersi aż huczy, ale to moje pierwsze podejście do C.

Przy okazji, jeśli ktoś potrzebowałby bardzo silnego serwera za free, to polecam Oracle.

Teraz muszę odpowiednio skonfigurować serwer (mam tylko "brudną", jako tako działającą wersję), ale to zostawiam sobie na czas po wyjeździe, ponieważ tym mogę zajmować się z zewnątrz. Na zimę prawdopodobnie zostawię domek i ucieknę do miasta, więc przed wyjazdem muszę mieć w miarę dopracowaną i przetestowaną wersję po stronie domku. Trochę zaczynam się spieszyć.

Najprawdopodobniej pierwsze uruchomienie całości nastąpi w przyszłym tygodniu. Teraz instaluję okablowanie, lutuję połączenia i dopracowuję "system" sterujący na Pico.

Link do komentarza
Share on other sites

Taki prosty niuans poprawiający czytelność kodu zamiast makaronu elseif:

#include <stdio.h>
#include <inttypes.h>


typedef struct
{
    const char * const in;
    const int val;
}brg_t;

brg_t tab[] = 
{
    {"-1" ,-1},
    {"0",0},
    {"1",1}
    
};


int main()
{
    
      for(int i = 0 ; i < sizeof(tab)/(sizeof(brg_t)); i++)
    {
        printf("%s %d\n",tab[i].in,tab[i].val);
    }

    return 0;
}  

Do przetestowania 

Chociaż... jest funkcja atoi i można uniknąć powyższych kombinacji pod warunkiem że string zawsze będzie prawidłowy więc napisałbym:

 s->set_saturation(s, atoi(nasycenie));


 

Edytowano przez _LM_
  • Lubię! 1
Link do komentarza
Share on other sites

1 godzinę temu, _LM_ napisał:

Taki prosty niuans...
 

Dziękuję! Czy możesz polecić mi podręcznik do C? O ile po językach wysokopoziomowych to łatwo się prześlizgnąć, to te niskiego poziomu wymagają już gruntownej wiedzy.

Link do komentarza
Share on other sites

Nie jesteś całkiem początkujący to chyba to będzie dobre https://helion.pl/ksiazki/jezyk-ansi-c-programowanie-wydanie-ii-brian-w-kernighan-dennis-m-ritchie,jansvv.htm#format/d mam zawsze pod ręką w razie jakby coś się zapomniało 🙂 zaglądnij czasem na ten blog https://ucgosu.pl/ a do algorytmów mam zestaw gotowców https://helion.pl/ksiazki/algorytmy-w-c-kyle-loudon,algorc.htm#format/d Patrząc po tym w jaki sposób piszesz funkcje to pewnie bardziej będzie Ci potrzebne coś o C++ no ale na tym to się kompletnie nie znam

Edytowano przez _LM_
  • Lubię! 1
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

2 godziny temu, _LM_ napisał:

Nie jesteś całkiem początkujący (...)

W C jestem. Za książkę bardzo dziękuję, biorę. Blog bardzo się przyda. Jeśli chodzi o algorytmy, to jestem fanatycznym wyznawcą Dijkstry.

Bardzo dziękuję za polecenia!

Link do komentarza
Share on other sites

Dzień dobry,

Kolejna wersja kodu dla ESP32-CAM. Ułomna, ale taka raczej zostanie do wiosny. Położyłem nacisk na odporność WSP32 na przerwy w dostępie do internetu i serwera - tak, aby mógł się sam pozbierać po takich problemach, kiedy nie będzie mnie w domu.

ESP32 po podłączeniu zasilania łączy się moją siecią lokalną, pobiera dane z serwera czasu, inicjalizuje kamerkę (łączy się z FTP-em i pobiera z niego ustawienia rozmiaru bufora), pobiera z FTP-a ustawienia kamerki i na tej podstawie ustawia parametry zdjęcia. Później cyklicznie; sprawdza czy ma połączenie WiFi, sprawdza czy na FTP-ie nie ma nowych parametrów dla aparatu (i programu), robi zdjęcie, nadaje plikowi nazwę opartą o timestamp i wysyła na FTP-a, na koniec umieszczając plik, który sygnalizuje wgranie nowego zdjęcia.

W przypadku, kiedy rozmiar zdjęcia będzie większy niż zarezerwowany bufor - ESP32 się zresetuje. Nie potrafię zmienić wielkości bufora bez resetu.

FTP idzie bez szyfrowania, ale serwer ma: firewalla przepuszczającego jedynie ruch na portach dla FTP-a dla jednego IP. To jest słabe, bo mam sieć na LTE i IP zmienia się raz dziennie, więc muszę ręcznie zmieniać IP w firewallu sprzętowym i ufw.

Na serwerze, kiedy pojawi się nowy plik, jest kopiowany do archiwum i jednocześnie kopiowany do pliku obrazek.jpg, który jest wyświetlany. Skrypt działa w cron'ie. Strona odświeża zawartość, pokazując kolejne klatki z kamery. Tu mam problem. Zrobienie i wysłanie zdjęcia trwa ok. czterech sekund, ale nową zawartość widzę na serwerze po ok. 10 sekundach. Zabawy z serwerem zostawiam jednak na zimę.

W przyszłym roku chyba pomyślę nad MQTT - nie jestem zadowolony z obecnego rozwiązania, tyle, że działa. W tej chwili większość pomysłóœ rozbija się o mój brak wiedzy i czasu.

Chodziło mi o to, abym mógł podejrzeć obraz z kamer i zmienić zdalnie ustawienia kamery (wbrew pozorom ESP32-CAM ma bardzo duże możliwości konfiguracji.

Poniżej pliki, przy czym plik wyświetlający obraz chyba wymaga zmiany, możliwe, że opóźnienie spowodowane jest nie najlepszą konfiguracją NGINX'a, albo słabym połączeniem z siecią domową (kamerka docelowo będzie znacznie bliżej routera).

#include "esp_camera.h"
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems
#include "driver/rtc_io.h"
#include <WiFi.h>
#include "time.h"
#include <WiFiClient.h>  
#include "ESP32_FTPClient.h"

// Pin definition for CAMERA_MODEL_AI_THINKER
#define CAM_PIN_PWDN    32
#define CAM_PIN_RESET   -1 //software reset will be performed
#define CAM_PIN_XCLK     0
#define CAM_PIN_SIOD    26
#define CAM_PIN_SIOC    27
#define CAM_PIN_D7      35
#define CAM_PIN_D6      34
#define CAM_PIN_D5      39
#define CAM_PIN_D4      36
#define CAM_PIN_D3      21
#define CAM_PIN_D2      19
#define CAM_PIN_D1      18
#define CAM_PIN_D0       5
#define CAM_PIN_VSYNC   25
#define CAM_PIN_HREF    23
#define CAM_PIN_PCLK    22

const bool debagowanie = true;

char* ftp_server = "******";
char* ftp_user = "******";
char* ftp_pass = "******";

const char* ssid = "******";
const char* password = "******";

const char* ntpServer = "ntp1.tp.pl";
const long  gmtOffset_sec = 7200;   //Replace with your GMT offset (seconds)
const int   daylightOffset_sec = 0;  //Replace with your daylight offset (seconds)

ESP32_FTPClient ftp (ftp_server, ftp_user, ftp_pass, 3000, 0);

// można zmienić opóźnienia przy aktualizacji (przy starncie są ustawiane te poniżej)
int petla = 0;
int pauza_dla_ftpa = 1000;
int pauza_dla_wifi = 1000;

void kamera() { // #########################################################################################################################################################

  String rozdzielczosc = "", jakosc = "", jasnosc = "", kontrast = "", nasycenie = "", efekty = "", naswietlanie = "", kontrolanaswietlania = "", iso = "", balansbieli = "", pt = "", pw = "", pf = "";
  // String rozdzielczosc = "FRAMESIZE_SVGA", jakosc = "12", jasnosc = "1", kontrast = "2", nasycenie = "-2", efekty = "0"; // wartości domyślne

  // pobranie ustawień kamery z serwera
  while (!(ftp.isConnected())) {
    if (debagowanie) { Serial.println("Podłaczam się do FTP (kamera)"); }
    ftp.OpenConnection();
    delay(pauza_dla_ftpa);
    ftp.ChangeWorkDir("/obraz/");
  }
  ftp.InitFile("Type A");
  ftp.DownloadString("framesize.txt", rozdzielczosc); // jeśli pobierzemy większą ramkę niż zdefiniowany przy uruchomieniu kamery bufor to nastąpi restart kamery
  ftp.CloseFile();
  if (rozdzielczosc=="") { rozdzielczosc = "SVGA"; }
  ftp.InitFile("Type A");
  ftp.DownloadString("quality.txt", jakosc);
  ftp.CloseFile();
  if (jakosc=="") { jakosc = "12"; }
  ftp.InitFile("Type A");
  ftp.DownloadString("brightness.txt", jasnosc);
  ftp.CloseFile();
  if (jasnosc=="") { jasnosc = "1"; }
  ftp.InitFile("Type A");
  ftp.DownloadString("contrast.txt", kontrast);
  ftp.CloseFile();
  if (kontrast=="") { kontrast = "2"; }
  ftp.InitFile("Type A");
  ftp.DownloadString("saturation.txt", nasycenie);
  ftp.CloseFile();
  if (nasycenie=="") { nasycenie = "-2"; }
  ftp.InitFile("Type A");
  ftp.DownloadString("effects.txt", efekty);
  ftp.CloseFile();
  if (efekty=="") { efekty = "0"; }
  ftp.InitFile("Type A");
  ftp.DownloadString("exposure.txt", naswietlanie);
  ftp.CloseFile();
  if (naswietlanie=="") { naswietlanie = "300"; }
  ftp.InitFile("Type A");
  ftp.DownloadString("exposure_ctrl.txt", kontrolanaswietlania);
  ftp.CloseFile();
  if (kontrolanaswietlania=="") { kontrolanaswietlania = "1"; }
  ftp.InitFile("Type A");
  ftp.DownloadString("iso.txt", iso);
  ftp.CloseFile();
  if (iso=="") { iso = "0"; }
  ftp.InitFile("Type A");
  ftp.DownloadString("whitebalans.txt", balansbieli);
  ftp.CloseFile();
  if (balansbieli=="") { balansbieli = "0"; }
  ftp.InitFile("Type A");
  ftp.DownloadString("petla.txt", pt);
  ftp.CloseFile();
  if (pt=="") { petla = 0; } else { petla = atoi(pt.c_str()); }
  ftp.InitFile("Type A");
  ftp.DownloadString("pauza_dla_wifi.txt", pw);
  ftp.CloseFile();
  if (pw=="") { pauza_dla_wifi = 500; } else { pauza_dla_wifi = atoi(pt.c_str()); }
  ftp.InitFile("Type A");
  ftp.DownloadString("pauza_dla_ftpa.txt", pf);
  ftp.CloseFile();
  if (pf=="") { pauza_dla_ftpa = 1000; } else { pauza_dla_ftpa = atoi(pt.c_str()); }

  sensor_t * s = esp_camera_sensor_get();
  s->set_brightness(s, atoi(jasnosc.c_str()));     // -2 to 2
  s->set_contrast(s, atoi(kontrast.c_str()));       // -2 to 2
  s->set_saturation(s, atoi(nasycenie.c_str()));     // -2 to 2
  s->set_special_effect(s, atoi(efekty.c_str())); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
  s->set_whitebal(s, 1);       // 0 = disable , 1 = enable  Set white balance
  s->set_awb_gain(s, 1);       // 0 = disable , 1 = enable  Set white balance gain
  s->set_wb_mode(s, atoi(balansbieli.c_str()));        // 0 to 4 - The mode of white balace module. if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
  s->set_exposure_ctrl(s, atoi(kontrolanaswietlania.c_str()));  // 0 = disable , 1 = enable  Set exposure control
  s->set_aec2(s, 1);           // 0 = disable , 1 = enable  Auto Exposure Control
  s->set_ae_level(s, 0);       // -2 to 2  The auto exposure level to apply to the picture (when aec_mode is set to auto),
  s->set_aec_value(s, atoi(naswietlanie.c_str()));    // 0 to 1200 (300)  The Exposure value to apply to the picture (when aec_mode is set to manual), from 0 to 1200.
  s->set_gain_ctrl(s, 1);      // 0 = disable , 1 = enable
  s->set_agc_gain(s, 0);       // 0 to 30  The gain value to apply to the picture (when aec_mode is set to manual), from 0 to 30. Defaults to 0.
  s->set_gainceiling(s, (gainceiling_t)atoi(iso.c_str()));  // 0 to 6  The maximum gain allowed, when agc_mode is set to auto. This parameter seems act as “ISO” setting.
  s->set_bpc(s, 0);            // 0 = disable , 1 = enable
  s->set_wpc(s, 1);            // 0 = disable , 1 = enable
  s->set_raw_gma(s, 1);        // 0 = disable , 1 = enable
  s->set_lenc(s, 1);           // 0 = disable , 1 = enable
  s->set_hmirror(s, 0);        // 0 = disable , 1 = enable
  s->set_vflip(s, 0);          // 0 = disable , 1 = enable
  s->set_dcw(s, 1);            // 0 = disable , 1 = enable
  s->set_colorbar(s, 0);       // For tests purposes, it’s possible to replace picture get from sensor by a test color pattern.
  if (rozdzielczosc=="QVGA") {
    s->set_framesize(s, FRAMESIZE_QVGA);     // rozmiar ramki, musi się zmieścić w zadeklarowanym buforze; FRAMESIZE_ + QVGA = 5, VGA = 8, SVGA = 9, XGA = 10, SXGA = 12, UXGA = 13
  } else if (rozdzielczosc=="VGA") {
    s->set_framesize(s, FRAMESIZE_VGA);
  } else if (rozdzielczosc=="SVGA") {
    s->set_framesize(s, FRAMESIZE_SVGA);
  } else if (rozdzielczosc=="XGA") {
    s->set_framesize(s, FRAMESIZE_XGA);
  } else if (rozdzielczosc=="SXGA") {
    s->set_framesize(s, FRAMESIZE_SXGA);
  } else if (rozdzielczosc=="UXGA") {
    s->set_framesize(s, FRAMESIZE_UXGA);
  } else {
    s->set_framesize(s, FRAMESIZE_SVGA);
  }
  s->set_quality(s, atoi(jakosc.c_str())); // 10-63, for OV series camera sensors, lower number means higher quality
}

void setup() { // ##########################################################################################################################################################

  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector

  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  if (debagowanie) { Serial.print("Podłaczam się do WiFi..."); }
  while (WiFi.status() != WL_CONNECTED) {
    delay(pauza_dla_wifi);
    if (debagowanie) { Serial.print("."); }
  }
  if (debagowanie) { 
    Serial.print(" adres IP: ");
    Serial.println(WiFi.localIP());
  }

  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

  //power up the camera if PWDN pin is defined
  if (CAM_PIN_PWDN != -1) {
    pinMode(CAM_PIN_PWDN, OUTPUT);
    digitalWrite(CAM_PIN_PWDN, LOW);
  }

  static camera_config_t camera_config = {
    .pin_pwdn  = CAM_PIN_PWDN,
    .pin_reset = CAM_PIN_RESET,
    .pin_xclk = CAM_PIN_XCLK,
    .pin_sccb_sda = CAM_PIN_SIOD,
    .pin_sccb_scl = CAM_PIN_SIOC,
    .pin_d7 = CAM_PIN_D7,
    .pin_d6 = CAM_PIN_D6,
    .pin_d5 = CAM_PIN_D5,
    .pin_d4 = CAM_PIN_D4,
    .pin_d3 = CAM_PIN_D3,
    .pin_d2 = CAM_PIN_D2,
    .pin_d1 = CAM_PIN_D1,
    .pin_d0 = CAM_PIN_D0,
    .pin_vsync = CAM_PIN_VSYNC,
    .pin_href = CAM_PIN_HREF,
    .pin_pclk = CAM_PIN_PCLK,
    .xclk_freq_hz = 20000000, //EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode
    .ledc_timer = LEDC_TIMER_0,
    .ledc_channel = LEDC_CHANNEL_0,
    .pixel_format = PIXFORMAT_JPEG,//YUV422,GRAYSCALE,RGB565,JPEG
    // .frame_size = FRAMESIZE_SVGA, // rozmiar bufora, musi być więszy lub równy niż rozmiar ramki; FRAMESIZE_SVGA 1s 2s 2s 3s 3s 4s 7s 10s 15s (przy q=12 FRAMESIZE_ + QVGA X 320 240 |CIF X 352 288 |VGA X 640 480 |SVGA X 800 600 |XGA X 1024 768 |SXGA X 1280 1024 |UXGA X 1600 1200, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.
    // .jpeg_quality = 12, // 10-63, for OV series camera sensors, lower number means higher quality
    .fb_count = 1, //When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode.
    .grab_mode = CAMERA_GRAB_WHEN_EMPTY // CAMERA_GRAB_WHEN_EMPTY//CAMERA_GRAB_LATEST. Sets when buffers should be filled
  };

  String rozdzielczosc = "";

  // pobranie ustawień kamery z serwera
  while (!(ftp.isConnected())) {
    if (debagowanie) { Serial.println("Podłaczam się do FTP (setup)"); }
    ftp.OpenConnection();
    delay(pauza_dla_ftpa);
    ftp.ChangeWorkDir("/obraz/");
  }
  ftp.InitFile("Type A");
  ftp.DownloadString("framesize.txt", rozdzielczosc);
  ftp.CloseFile();
  if (rozdzielczosc=="") { rozdzielczosc = "SVGA"; }

  if (rozdzielczosc=="QVGA") {
    camera_config.frame_size = FRAMESIZE_QVGA; // 5
  } else if (rozdzielczosc=="VGA") {
    camera_config.frame_size = FRAMESIZE_VGA; // 8
  } else if (rozdzielczosc=="SVGA") {
    camera_config.frame_size = FRAMESIZE_SVGA; // 9
  } else if (rozdzielczosc=="XGA") {
    camera_config.frame_size = FRAMESIZE_XGA; // 10
  } else if (rozdzielczosc=="SXGA") {
    camera_config.frame_size = FRAMESIZE_SXGA; // 12
  } else if (rozdzielczosc=="UXGA") {
    camera_config.frame_size = FRAMESIZE_UXGA; // 13
  } else {
    camera_config.frame_size = FRAMESIZE_SVGA;
  }

  //initialize the camera
  esp_err_t err = esp_camera_init(&camera_config);

  kamera();

}

void loop() { // ###########################################################################################################################################################

  // acquire a frame
  camera_fb_t * fb = esp_camera_fb_get();

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    if (debagowanie) { Serial.print("Ponownie podłączam się do WiFi..."); }
    while (WiFi.status() != WL_CONNECTED) {
      delay(pauza_dla_wifi);
      if (debagowanie) { Serial.print("."); }
    }
    if (debagowanie) { 
      Serial.println(WiFi.localIP());
    }
  }

  String aktualizacja;
  ftp.InitFile("Type A");
  ftp.DownloadString("aktualizacja.txt", aktualizacja);
  ftp.CloseFile();
  if (aktualizacja=="tak") {
    if (debagowanie) { Serial.println("Aktualizuję"); }
    ftp.DeleteFile("aktualizacja.txt");
    ftp.InitFile("Type A");
    ftp.NewFile("aktualizacja.txt");
    ftp.CloseFile();
    kamera();
  }

  // przygotowanie nazwy pliku na podstawie aktualnego czasu
  time_t now;
  time(&now);
  char czas [12];
  int ret = snprintf(czas, sizeof(czas), "%ld", now);
  char* rozszerzenie = "\.jpg";
  int len = strlen(czas) + 5;
  char* nazwa_pliku = (char*) malloc(len);
  int i=0,j=0;
  for(i=0;i<len;i++){
    if(i<strlen(czas)){
      nazwa_pliku[i]=czas[i];
    } else {
      nazwa_pliku[i]=rozszerzenie[j++];
    }
  }
  while (!(ftp.isConnected())) {
    ftp.CloseConnection();
    delay(pauza_dla_ftpa);
    if (debagowanie) { Serial.println("Podłaczam się do FTP (loop2)"); }
    ftp.OpenConnection();
    ftp.ChangeWorkDir("/obraz/");
  }

  // wysłanie zdjęcia na serwer
  if (debagowanie) { Serial.println("wysyłam"); }
  ftp.InitFile("Type I");
  ftp.NewFile(nazwa_pliku);
  ftp.WriteData( fb->buf, fb->len );
  ftp.CloseFile();

  //return the frame buffer back to the driver for reuse
  esp_camera_fb_return(fb);

  if (debagowanie) {
    Serial.print("wysłany: ");
    Serial.println(nazwa_pliku);
  }
  // zapisanie pustego pliku informującego serwer o pojawieniu się nowego pliku z obrazem
  ftp.InitFile("Type A");
  ftp.NewFile("nowy.txt");
  ftp.CloseFile();

  delay(petla);

}

 

Link do komentarza
Share on other sites

I jeszcze pliki z serwera:

1) uruchamiany w cron co minutę:

#!/usr/bin/php

<?php

$start = microtime(true);
set_time_limit(60);
for ($i = 0; $i < 59; $i + 1) {

<------>$katalog = '/home/www/asm/kam1/pliki/obraz/';
<------>$archiwum = '/home/www/asm/archiwum/';
<------>$www = '/home/www/asm/';
<------>$regexp = '/jpg/i';
<------>$nowy = '/nowy\.txt/i';
<------>$dir = opendir($katalog);
<------>$jest = false;
<------>while(false !== ($file = readdir($dir))) {
<------><------>if(preg_match($nowy, $file)) {
<------><------><------>$jest = true;
<------><------>}
<------>}
<------>if ($jest) {
<------><------>$dir = opendir($katalog);
<------><------>while(false !== ($file = readdir($dir))) {
<------><------><------>if(preg_match($regexp, $file)) {
<------><------><------><------>$aktualny = date("YmdHis", substr($file, 0, -4)) . ".jpg";
<------><------><------><------>unlink($www . "obrazek.jpg");
<------><------><------><------>copy($katalog . $file, $www . "obrazek.jpg");
<------><------><------><------>copy($katalog . $file, $archiwum . $aktualny);
<------><------><------><------>unlink($katalog . $file);
<------><------><------>}
<------><------>}
<------><------>unlink($katalog . "nowy.txt");
<------>}
<------>time_sleep_until($start + $i + 1);
}

?>

2) i index.php:

<?php

header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("refresh: 8;");

$katalog = '/home/www/asm/';
$regexp = '/jpg/i';
$dir = opendir($katalog);
while(false !== ($file = readdir($dir)))
	if($file != '.' && $file != '..')
		if(preg_match($regexp, $file, $matches))
			$aktualny = $file;
echo '<html>
<head>
</head>
<body>

<img id="randimg" src="' . $aktualny . '" />
<br />
<br />';
echo $aktualny;
echo '

</body>
</html>';

?>

 

  • Lubię! 2
Link do komentarza
Share on other sites

21 godzin temu, Szern napisał:

W przyszłym roku chyba pomyślę nad MQTT - nie jestem zadowolony z obecnego rozwiązania, tyle, że działa.

@Szern Ciekawy pomysł, a z MQTT jaki masz pomysł? Chciałbyś wysłać po tym plik?

Link do komentarza
Share on other sites

23 godziny temu, Gieneq napisał:

@Szern Ciekawy pomysł, a z MQTT jaki masz pomysł? Chciałbyś wysłać po tym plik?

Co do MQTT zaznaczam, że po raz pierwszy przeczytałem o nim w tych dniach i nie mam jeszcze pojęcia "czym to się je". Znalazłem przykład implementacji MQTT na ESP32-CAM. Najpierw spróbuję zainstalować sobie brokera na swoim VPN-ie, a później sprawdzę jak wygląda implementacja klienta na ESP32 (i przy okazji na Pico). Jeśli mi się spodoba i będzie spełniać moje oczekiwania, to może zimą napiszę taki kod dla ESP32-CAM. Jeśli nie, to będę próbował zaimplementować klienta sftp na mikrokontroler.

Chciałbym albo przesyłać zdjęcia na serwer w sieci, albo strumieniować obraz za pośrednictwem tego serwera, a najchętniej alternatywnie jedno albo drugie do wyboru. Wąskim gardłem jest chyba moje łącze internetowe (LTE, ale o bardzo słabych parametrach).

Link do komentarza
Share on other sites

@Szern ostatnio robię trochę przy MQTT i wydaje mi się, że zdjęcie to trochę dużo. Przykładowo metryczny pakiet Azure IoT hub to 4kB. Jak robiłem wysyłanie pomiarów na ESP32 to podzieliłem na pakiety, bo tzw outbox ESP-IDF MQTT jest alokowany na stercie i łatwo o przepełnienie. Dla ESP32-C3 mini 20k sterty może być. Więc pozostaje albo podzielić dane na pakiety i zlepić po stronie serwera albo inaczej je potraktować.

Jak oglądałem przykład OTA to ze strony Huba IoT jest po MQTT wysyłane info, że zaraz nastąpi upgrade. A następnie leci binarka chyba z 300kB obrazu po HTTPS, zapisuje się w drugim sektorze pamięci i dopiero następuje próba zmiany firmwaru. Jak się powiedzie, to po MQTT zostaje odesłana informaja o pozytywnym wgraniu.

Link do komentarza
Share on other sites

(edytowany)

Tym razem trochę zdjęć, dokumentujących krótką historię powstawania systemu monitorującego.

Pierwsze testy czujek, lipiec:

IMG_20230725_170648.thumb.jpg.17da29e33402e33dfbdf4618c997bb15.jpg

Obudowy dla czujek zaprojektowałem w CorelDraw!, a wyciął mi je Dawid:

104363142_cicie.thumb.png.40644d630cf5e7d4efed174b72b3feef.png

Czujki miały zawisnąć na wysokości czterech metrów i ich kształty były naprawdę trudne do ogarnięcia:

IMG_20230921_164728.thumb.jpg.372f97af470af278194de8349b20d8da.jpg

IMG_20230921_164654.thumb.jpg.332e2f91b30d0416aa0129af20b5f33f.jpg

Sklejkę kleiłem i uszczelniałem silikonem, również silikonem mocowałem do niej elementy elektroniczne. Nie wyszło to pięknie, ale chyba wystarczająco mocno i szczelnie.

IMG_20231014_101051.thumb.jpg.a2cfaa97db2881cc4948431c9da037c2.jpg

IMG_20231014_101136.thumb.jpg.4ddea0a4ea486f8b8ac66d6af7ebd6d3.jpg

IMG_20231014_101949.thumb.jpg.357974d294cc182c4373e3fc9ef6d8a4.jpg

Montaż taki nie należał do najłatwiejszych rzeczy.

IMG_20231004_153240_hdr.thumb.jpg.54590497f6543c6cc0b2c3709dccf8e9.jpg

IMG_20231008_110435.thumb.jpg.3feb344a90ae814c289fa10b1698f1c0.jpg

W sumie jest pięć czujek *zawierających siedem czujników i dwie kamery) i dwie syreny. Wszystkie zamocowane na wysokości pięciu metrów, na czterech rogach domu. W najbliższym czasie pokażę je w akcji.

Pewnym problemem było okablowanie tego, ale ostatecznie wszystkie kable połączyły się w jednym miejscu.

IMG_20231009_174343.thumb.jpg.2b1707a8acc82d11d827d8ea8abd68b3.jpg

Pech chciał, że było to ciemne i trudno dostępne miejsce na rogu strychu.

IMG_20231011_204637.thumb.jpg.740e135ca9dae134a713342b6097044a.jpg

Ale musiałem tam dotrzeć ze sprzętem i ostatecznie obrabiam "centralkę.

IMG_20231015_190424_hdr.thumb.jpg.c8cdeeb6f1fb3c1124cc08898df4a6cf.jpg

IMG_20231015_190450.thumb.jpg.b14e152c8cfd2b237f07c5be14e6126b.jpg

W tej chwili "centralka" podpięta jest jeszcze testowo i wszystko działa! Niedługo wrzucę filmik pokazujący działanie systemu.

Mam jeden, ale za to duży problem. Centralka to Raspberry Pico W, ma program napisany w Circuitpython - to był najgłupszy możliwy pomysł. Zmienię to w przyszłości na C. 

Mój problem polega na tym, że Pico się zawiesza. Zamiera (np. po godzinie normalnej pracy) mrugając jedynie cyklicznie diodą w serii: dwa mrugnięcia i przerwa. Czy problemem może być mój program (wrzucę niedługo kody), język Python, samo Pico czy też jeszcze coś? Będę wdzięczny za sugestie, gdzie szukać problemu.

Przepraszam, że tak chaotycznie, czas mnie goni. Udostępnię pełną dokumentację (w szczególności kody dla Pico i serwera). Na razie to tylko zajawka.

 

 

Edytowano przez Szern
  • Lubię! 1
Link do komentarza
Share on other sites

9 godzin temu, Szern napisał:

Centralka to Raspberry Pico W, ma program napisany w Circuitpython

Aha. Wręcz idealny język do aplikacji, która ma chodzić bezawaryjnie przez czas rzędu miesięcy.

9 godzin temu, Szern napisał:

Czy problemem może być mój program (wrzucę niedługo kody)

Niedługo potem będziesz mógł się spodziewać odpowiedzi 😉

Aha, wrzuć od razu kody po przeróbce na C, to przynajmniej się wyeliminuje jedną możliwość stwarzania problemów.

 

Edytowano przez ethanak
Link do komentarza
Share on other sites

(edytowany)
33 minuty temu, ethanak napisał:

Aha. Wręcz idealny język do aplikacji, która ma chodzić bezawaryjnie przez czas rzędu miesięcy.

Niedługo potem będziesz mógł się spodziewać odpowiedzi 😉

Aha, wrzuć od razu kody po przeróbce na C, to przynajmniej się wyeliminuje jedną możliwość stwarzania problemów.

 

W pełni się z Tobą zgadzam, doszedłem do podobnych wniosków. Zamierzam właśnie przepisać kod na C, ale mam na to tylko dwa tygodnie.

Mam w związku z tym jedną prośbę. Doradź mi, czy mam próbować pisać w C dla Pico w środowisku Arduino IDE czy od razu w IDF? I dodatkowo: czy przyda się debugger typu Pico Probe?

Dla ESP32 próbowałem pisać w VSC, ale przerzuciłem się na Arduino IDE, gdzie to skończyłem (wrzucę na dniach na githuba).

Edytowano przez Szern
Link do komentarza
Share on other sites

Jeśli chodzi o zintegrowane środowisko programistyczne, to ten jeden z przynajmniej dwóch portów (szlacheckie nazwisko, Philhower III :D) na Arduino IDE bazuje na Pico SDK od RPi, więc jest to najbliżej "oryginału". Jest też niezależne SDK czeskiego autora, ale WiFi/BT może być tam problematyczne, jeśli w ogóle możliwe na ta chwilę. Gołe SDK może być lepsze, bo są przykłady na WiFI w C, które mogą nie być zaadaptowane w Arduino (do sprawdzenia).

  • Lubię! 1
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.