Skocz do zawartości

Skaner numerów dla niecyfrowej biblioteki (worklog)


Szern

Pomocna odpowiedź

Dzień dobry,

Pilny projekt, który wydawał się prosty, ale mam z nim problemy.

Tania implementacja aparatu, który wykonuje zdjęcia ręcznie wpisanych numerów katalogowych w książkach, wysyła je na serwer, gdzie są one traktowane OCR-em i przesyłane w formie tekstowej do aparatu w celu zatwierdzenia. Po zatwierdzeniu w aparacie, kod na serwerze dodawany jest do arkusza kalkulacyjnego.

Ponieważ spieszę się, nie zrobiłem jeszcze schematu (jestem głupim początkującym). Urządzenie wygląda tak:

biblioskaner01.thumb.png.e44a430a6ac468491d7011c46fc3b15c.pngbiblioskaner02.thumb.png.6779edc7e74b7f630afae446fa67006b.png

Sercem jest ESP32-CAM, cztery przyciski (miało być sześć, ale głupio zapomniałem ile dostępnych pinów ma ESP32-CAM), dwa mikroprzełączniki i miniaturowy ekran OLED.

Program w ESP:

#include <Arduino.h>
#include <esp_system.h>
#include <esp_camera.h>
#include <soc/soc.h>            // Disable brownour problems
#include <soc/rtc_cntl_reg.h>   // Disable brownour problems

#include <WiFiClientSecure.h>
#include <MQTTPubSubClient.h>
#include <base64.h>
#include <Wire.h>
#include <SSD1306Wire.h>
#include "images.h"
// #include <OLEDDisplayUi.h>

// GPIO 0 - camera using
// GPIO 1 pin_switch
// GPIO 2 pin_button_bottom
// GPIO 3 pin_button_top
// GPIO 4 - pin_flash
// GPIO 12 pin_button_green
// GPIO 13 pin_button_yellow
// GPIO 14 pin_SLC
// GPIO 15 pin_SDA
// GPIO 16 - PSRAM using
// GPIO 33 - red internal LED

const byte pin_flash = GPIO_NUM_4;
const byte pin_SDA = GPIO_NUM_15;
const byte pin_SLC = GPIO_NUM_14;
const byte pin_button_green = GPIO_NUM_12;
const byte pin_button_yellow = GPIO_NUM_13;
const byte pin_button_top = GPIO_NUM_3;
const byte pin_button_bottom = GPIO_NUM_2;
const byte pin_switch = GPIO_NUM_1;
const byte pin_power = GPIO_NUM_32;
const byte pin_led_red = GPIO_NUM_33;

const char* ssid = "*********"; // Replace with your network name
const char* password = "*********"; // Replace with your password to WiFi

const char* mqttServer = "*********"; // Replace with your webserver adress or webserver IP
const long mqttPortS = 8883;
const long mqttPortU = 8884;
const char* mqttUser = "*********"; // Replace with your MQTT brooker user name
const char* mqttPass = "*********"; // Replace with your MQTT brooker users password
const char* mqttId = "bibliotekarz";
const char* mqttTopicOut = "bib/out"; // Replace with your webserver topic name to send images from camera to server
const char* mqttTopicDebug = "bib/debug";
const char* mqttTopicComm = "bib/comm";
const char * mqtt_lamp_on = "fon";
const char * mqtt_lamp_off = "foff";

long rssi;
int security = 1;
int mqtt_initialised = 0;
int mqtt_return = 0;
char * mqtt_liczba;
int treshold = 200;
int flashPower = 0;

// chain.pem
static const char * host_ca = "-----BEGIN CERTIFICATE-----\n"
"MIIFBjCCAu6gAwIBAgIRAIp9PhPWLzDvI4a9KQdrNPgwDQYJKoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\n"
"WhcNMjcwMzEyMjM1OTU5WjAzMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n"
"RW5jcnlwdDEMMAoGA1UEAxMDUjExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"
"CgKCAQEAuoe8XBsAOcvKCs3UZxD5ATylTqVhyybKUvsVAbe5KPUoHu0nsyQYOWcJ\n"
"DAjs4DqwO3cOvfPlOVRBDE6uQdaZdN5R2+97/1i9qLcT9t4x1fJyyXJqC4N0lZxG\n"
"AGQUmfOx2SLZzaiSqhwmej/+71gFewiVgdtxD4774zEJuwm+UE1fj5F2PVqdnoPy\n"
"6cRms+EGZkNIGIBloDcYmpuEMpexsr3E+BUAnSeI++JjF5ZsmydnS8TbKF5pwnnw\n"
"SVzgJFDhxLyhBax7QG0AtMJBP6dYuC/FXJuluwme8f7rsIU5/agK70XEeOtlKsLP\n"
"Xzze41xNG/cLJyuqC0J3U095ah2H2QIDAQABo4H4MIH1MA4GA1UdDwEB/wQEAwIB\n"
"hjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgwBgEB\n"
"/wIBADAdBgNVHQ4EFgQUxc9GpOr0w8B6bJXELbBeki8m47kwHwYDVR0jBBgwFoAU\n"
"ebRZ5nu25eQBc4AIiMgaWPbpm24wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAC\n"
"hhZodHRwOi8veDEuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcG\n"
"A1UdHwQgMB4wHKAaoBiGFmh0dHA6Ly94MS5jLmxlbmNyLm9yZy8wDQYJKoZIhvcN\n"
"AQELBQADggIBAE7iiV0KAxyQOND1H/lxXPjDj7I3iHpvsCUf7b632IYGjukJhM1y\n"
"v4Hz/MrPU0jtvfZpQtSlET41yBOykh0FX+ou1Nj4ScOt9ZmWnO8m2OG0JAtIIE38\n"
"01S0qcYhyOE2G/93ZCkXufBL713qzXnQv5C/viOykNpKqUgxdKlEC+Hi9i2DcaR1\n"
"e9KUwQUZRhy5j/PEdEglKg3l9dtD4tuTm7kZtB8v32oOjzHTYw+7KdzdZiw/sBtn\n"
"UfhBPORNuay4pJxmY/WrhSMdzFO2q3Gu3MUBcdo27goYKjL9CTF8j/Zz55yctUoV\n"
"aneCWs/ajUX+HypkBTA+c8LGDLnWO2NKq0YD/pnARkAnYGPfUDoHR9gVSp/qRx+Z\n"
"WghiDLZsMwhN1zjtSC0uBWiugF3vTNzYIEFfaPG7Ws3jDrAMMYebQ95JQ+HIBD/R\n"
"PBuHRTBpqKlyDnkSHDHYPiNX3adPoPAcgdF3H2/W0rmoswMWgTlLn1Wu0mrks7/q\n"
"pdWfS6PJ1jty80r2VKsM/Dj3YIDfbjXKdaFU5C+8bhfJGqU3taKauuz0wHVGT3eo\n"
"6FlWkWYtbt4pgdamlwVeZEW+LM7qZEJEsMNPrfC03APKmZsJgpWCDWOKZvkZcvjV\n"
"uYkQ4omYCTX5ohy+knMjdOmdH9c7SpqEWBDC86fiNex+O0XOMEZSa8DA\n"
"-----END CERTIFICATE-----\n";

WiFiClientSecure client;
MQTTPubSubClient mqtt;

SSD1306Wire display(0x3c, 15, 14, GEOMETRY_128_64, I2C_ONE);  // ADDRESS, SDA, SCL

void memory_status();
void connect();
const char* wl_status_to_string(wl_status_t status);
void wyswietl(const String tekst1, const String tekst2);
void konwerter(uint8_t* bufor);

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

  pinMode(pin_flash, OUTPUT);
  digitalWrite(pin_flash, LOW);

  pinMode(pin_led_red, OUTPUT);
  digitalWrite(pin_led_red, HIGH);

  Serial.begin(115200);
  Serial.println("");

  display.init();
  display.normalDisplay();
  display.setFont(ArialMT_Plain_10); // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24
  display.setContrast(100); 
//  display.setContrast(200,241,64); // normal brightness & contrast:  100,241,64, / really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0
  display.setBrightness(10);

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

  // define pin & power up the camera
  pinMode(pin_power, OUTPUT);
  digitalWrite(pin_power, LOW);
  pinMode(pin_button_green, INPUT);
  pinMode(pin_button_yellow, INPUT);
  pinMode(pin_button_top, INPUT);
  pinMode(pin_button_bottom, INPUT);
  pinMode(pin_switch, INPUT);

  // Use PWM channel 7 to control the white on-board LED (flash) connected to GPIO 4
  ledcSetup(7, 5000, 8);
  ledcAttachPin(4, 7);
  // Turn the LED on at specified power
  ledcWrite(7, flashPower);

// Pin definition for CAMERA_MODEL_AI_THINKER
  static camera_config_t camera_config = {
    .pin_pwdn  = 32,
    .pin_reset = -1, //software reset will be performed
    .pin_xclk = 0,
    .pin_sccb_sda = 26,
    .pin_sccb_scl = 27,
    .pin_d7 = 35,
    .pin_d6 = 34,
    .pin_d5 = 39,
    .pin_d4 = 36,
    .pin_d3 = 21,
    .pin_d2 = 19,
    .pin_d1 = 18,
    .pin_d0 = 5,
    .pin_vsync = 25,
    .pin_href = 23,
    .pin_pclk = 22,

    // PCLK = sensor->xclk_freq_hz * (freqdoubler?2:1) / 4
    .xclk_freq_hz = 16000000, //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_GRAYSCALE,//YUV422 (2BPP/YUV422),GRAYSCALE (1BPP/GRAYSCALE),RGB565 (2BPP/RGB565),JPEG (JPEG/COMPRESSED)// PIXFORMAT_JPEG
    .frame_size = FRAMESIZE_QVGA, // framebuffer size must be greater than or equal to the frame size; FRAMESIZE_ + QVGA 320x240 || CIF 352x288 || VGA 640x480 || SVGA 800x600 || XGA 1024x768 || SXGA 1280x1024 || UXGA 1600x1200; 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 = 10, // 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.
    .fb_location = CAMERA_FB_IN_DRAM, // CAMERA_FB_IN_DRAM (.frame_size: QVGA or CIF and .pixel_format: jpeg )|| CAMERA_FB_IN_PSRAM;
    .grab_mode = CAMERA_GRAB_WHEN_EMPTY // CAMERA_GRAB_WHEN_EMPTY - more control over the system, but results in longer time to get the frame // CAMERA_GRAB_LATEST Sets when buffers should be filled (.fb_count > 1), This approach puts more strain on the CPU/Memory, but allows for double the frame rate. Please use only with JPEG.
  };

  esp_err_t err = esp_camera_init(&camera_config); // initialize the camera
  delay(1000);
  int i = 0;
  while ( err != ESP_OK ) {
    i++;
    pinMode(pin_power, OUTPUT); // After the camera uses the GPIO, the pinMode is reset. You need to set the pinMode again after the camera is turned off.
    digitalWrite(pin_power, HIGH);
    delay(1000);
    pinMode(pin_power, OUTPUT);
    digitalWrite(pin_power, LOW);
    if ( i > 4 ) {
      Serial.println("Nie mogę aktywować kamery, restartuję...");
      wyswietl((char*)"Błąd aktywacji", (char*)"kamery: restart");
      pinMode(pin_power, OUTPUT);
      digitalWrite(pin_power, HIGH);
      WiFi.disconnect();
      delay(1000);
      ESP.restart();
    }
  }

  int jakosc = 10, jasnosc = -2, kontrast = 2, nasycenie = 0, efekty = 2, naswietlanie = 0, kontrolanaswietlania = 1, iso = 0, balansbieli = 0, obraz_kontrolny = 0, lampa = 0;
  sensor_t * s = esp_camera_sensor_get();
    // Gain
  s->set_gain_ctrl(s, 1);      // Auto-Gain Control 0 = disable , 1 = enable
  s->set_agc_gain(s, 10);       // Manual Gain 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)4);  // 0 to 6  The maximum gain allowed, when agc_mode is set to auto. This parameter seems act as “ISO” setting.
  // Exposure
  s->set_exposure_ctrl(s, 1);  // Auto-Exposure Control 0 = disable , 1 = enable  Set exposure control
  s->set_aec_value(s, 300);    // Manual Exposure 0 to 1200 (300)  The Exposure value to apply to the picture (when aec_mode is set to manual), from 0 to 1200.
  // Exposure Correction
  s->set_aec2(s, 1);           // Automatic Exposure Correction 0 = disable , 1 = enable  Auto Exposure Control
  s->set_ae_level(s, 2);       // Manual Exposure Correction -2 to 2  The auto exposure level to apply to the picture (when aec_mode is set to auto),
  // White Balance
  s->set_awb_gain(s, 1);       // Auto White Balance 0 = disable , 1 = enable  Set white balance gain
  s->set_wb_mode(s, 3);        // White Balance Mode 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_whitebal(s, 0);       // White Balance 0 = disable , 1 = enable  Set white balance
  s->set_bpc(s, 1);            // Black Pixel Correction 0 = disable , 1 = enable
  s->set_wpc(s, 1);            // White Pixel Correction 0 = disable , 1 = enable
  s->set_brightness(s, -2);     // Brightness -2 to 2
  s->set_contrast(s, 0);       // Contrast -2 to 2
  s->set_saturation(s, 2);     // Saturation -2 to 2
  s->set_special_effect(s, 2); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
  // Additional settings
  s->set_lenc(s, 0);           // Lens correction 0 = disable , 1 = enable
  s->set_hmirror(s, 0);        // Horizontal flip image 0 = disable , 1 = enable
  s->set_vflip(s, 0);          // Vertical flip image 0 = disable , 1 = enable
  s->set_colorbar(s, 0);       // Colour Testbar For tests purposes, it’s possible to replace picture get from sensor by a test color pattern.
  s->set_raw_gma(s, 0);        // 0 = disable , 1 = enable
  s->set_dcw(s, 0);            // 0 = disable , 1 = enable

  s->set_framesize(s, FRAMESIZE_QVGA);
  s->set_quality(s, 10); // 10-63, for OV series camera sensors, lower number means higher quality

  camera_fb_t *fb = NULL;

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  client.setCACert(host_ca);
  mqtt.begin(client); // initialize mqtt client

  connect();

  Serial.println(" END setup");
  wyswietl("END setup", "Push button to start");
  memory_status();

  while ( digitalRead(pin_button_green) || digitalRead(pin_button_yellow) ) {
    delay(50);
  }

}

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

  if ( digitalRead(pin_switch) ) {
    if ( !digitalRead(pin_button_top) ) {
      if ( flashPower <= 244 ) {
        flashPower = flashPower + 10;
        ledcWrite(7, flashPower);
      }
    } else if ( !digitalRead(pin_button_bottom) ) {
      if ( flashPower >= 10 ) {
        flashPower = flashPower - 10;
        ledcWrite(7, flashPower);
      }
    }
  } else if ( !digitalRead(pin_switch) ) {
    if ( !digitalRead(pin_button_top) ) {
      if ( treshold <= 244 ) {
        treshold = treshold + 10;
      }
    } else if ( !digitalRead(pin_button_bottom) ) {
      if ( treshold >= 10 ) {
        treshold = treshold - 10;
      }
    }
  }

  if ( !( (WiFi.status() == WL_CONNECTED) && mqtt.isConnected() && client.connected() ) ) {
    digitalWrite(pin_led_red, LOW);
    wyswietl("Restart w kamerze", "JESZCZE RAZ!");
    WiFi.disconnect();
    delay(1000);
    ESP.restart();
  }
  mqtt.update(); // should be called
  delay(100);
  camera_fb_t * fb = esp_camera_fb_get(); // acquire a frame
  konwerter(fb->buf);
  if ( !digitalRead(pin_button_green) ) {
    String imgDataB64 = base64::encode(fb->buf, fb->len);
    mqtt.publish(mqttTopicOut, imgDataB64, false, 0); // send image data (mqttTopicOut, imgDataB64, false, 0)
    wyswietl("wyslalem zdjecie", "czekaj");
    while ( ( mqtt_return == 0 ) && ( digitalRead(pin_button_top) == HIGH ) ) {
      mqtt.update();
      delay(50);
    }
    if ( mqtt_return == 2 ) {
      wyswietl(mqtt_liczba, "zatwierdz?");
      mqtt_return = 0;
      while ( !digitalRead(pin_button_green) || !digitalRead(pin_button_yellow) ) { // rozpisać na dwa przyciski OK i nieOK
        delay(50);
      }
      if ( !digitalRead(pin_button_green) ) {
        mqtt.publish(mqttTopicComm, "OK", false, 0);
      } else if ( !digitalRead(pin_button_yellow) ) {
        mqtt.publish(mqttTopicComm, "DELETE", false, 0);
      }
    }
  }
  esp_camera_fb_return(fb); // return the frame buffer back to the driver for reuse

} // ###################################################################################################################

void memory_status() {
  Serial.print("PSRAM found: ");
  Serial.println((String)psramFound());
  Serial.print("Total heap: ");
  Serial.println((String)ESP.getHeapSize());
  Serial.print("Free heap: ");
  Serial.println((String)ESP.getFreeHeap());
  Serial.print("Total PSRAM: ");
  Serial.println((String)ESP.getPsramSize());
  Serial.print("Free PSRAM: ");
  Serial.println((String)ESP.getFreePsram());
}

const char* wl_status_to_string(wl_status_t status) {
  switch (status) {
    case WL_NO_SHIELD: return "WL_NO_SHIELD";
    case WL_IDLE_STATUS: return "WL_IDLE_STATUS";
    case WL_NO_SSID_AVAIL: return "WL_NO_SSID_AVAIL";
    case WL_SCAN_COMPLETED: return "WL_SCAN_COMPLETED";
    case WL_CONNECTED: return "WL_CONNECTED";
    case WL_CONNECT_FAILED: return "WL_CONNECT_FAILED";
    case WL_CONNECTION_LOST: return "WL_CONNECTION_LOST";
    case WL_DISCONNECTED: return "WL_DISCONNECTED";
    default: return "ERROR";
  }
}

void connect() { // wifi, brooker mqtt ################################################################################

long mqttPort;

connect_to_wifi:
  if (WiFi.status() != WL_CONNECTED) {
    Serial.print("connecting to WiFi...");
    while (WiFi.status() != WL_CONNECTED) {
      Serial.print(".");
      wyswietl("connecting to WiFi...", wl_status_to_string(WiFi.status()));
      display.clear();
      display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
      display.display();
      delay(3000);
    }
    Serial.println(" connected!");
    rssi = WiFi.RSSI();
    char signal[4];
    sprintf(signal, "%d", rssi);
    Serial.print("RSSI: ");
    Serial.println(signal);
    wyswietl((char*)"RSSI: ", signal);
  }

connect_to_host:
  Serial.print("connecting to MQTT host...");
  wyswietl("connecting to MQTT host...", "");
  client.stop();
  if (security) {
    client.setCACert(host_ca);
    mqttPort = mqttPortS;
  } else {
    client.setInsecure(); // skip verification
    mqttPort = mqttPortU;
  }
  while (!client.connect(mqttServer, mqttPort)) {
    Serial.print(".");
    delay(1000);
    if (WiFi.status() != WL_CONNECTED) {
      Serial.println("WiFi disconnected");
      wyswietl("WiFi disconnected", "");
      WiFi.disconnect();
      delay(2000);
      ESP.restart();
    }
  }
  Serial.println(" connected!");
  wyswietl("connected!", "");

connect_to_brooker:
  Serial.print("connecting to MQTT broker...");
  wyswietl("connecting to MQTT broker...", "");
  mqtt.disconnect();
  while (!mqtt.connect(mqttId, mqttUser, mqttPass)) {
    Serial.print(".");
    delay(1000);
    if (WiFi.status() != WL_CONNECTED) {
      Serial.println("WiFi disconnected");
      wyswietl("WiFi disconnected", "");
      WiFi.disconnect();
      delay(2000);
      ESP.restart();
    }
    if (!client.connected()) {
      Serial.println("MQTT host disconnected");
      wyswietl("MQTT disconnected", "");
      goto connect_to_host;
    }
  }
  mqtt_initialised = 1;
  Serial.println("connected to MQTT brooker!");
  wyswietl("connected to MQTT brooker!", "");

  // #### subscribe topic
  mqtt.subscribe(mqttTopicComm, [](const char * payload, const size_t size) {
    if ( strcmp(payload, "") == 0 ) {
      mqtt_return = 1;
    } else {
      mqtt_liczba = strdup(payload);
      mqtt_return = 2;
    }
  });

}

void konwerter(uint8_t* bufor) { // #############################################################################

// bufor: 76800, piksele: 8192, tablica 1024

// wykadrować i odwrócić środek 128x64 z 320x240

  int startX = 96;
  int endX = 224;
  int startY = 88;
  int endY = 152;

  uint8_t * kadr = (uint8_t *) ps_malloc (8192 * sizeof (uint8_t));

  int i = 8191;
  for (int y = startY; y < endY; y++) {
    for (int x = startX; x < endX; x++) {
      if ( bufor[x+y*320] > treshold ) {
        kadr[i] = 0;
      } else {
        kadr[i] = 1;
      }
      i--;
    }
  }

  // przerobić bity na bajty (xbm)

  uint8_t * xbm = (uint8_t *) ps_malloc (1024 * sizeof (uint8_t));

  int z = 0;
  for ( int i = 0; i < 1024; i++ ) {
    xbm[i] = 0;
    for ( int j = 7; j >= 0; j-- ) {
      xbm[i] += kadr[z] * pow(2,j); 
      z++;
    }
  }

  free(kadr);

  display.clear();
  display.drawXbm(0, 0, 128, 64, xbm);
  display.display();
  display.clear();

  free(xbm);

}

void wyswietl(const String tekst1, const String tekst2) {

    display.clear();
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.setFont(ArialMT_Plain_10);
    display.drawStringMaxWidth(0, 0, 128, tekst1);
    if ( tekst2 != "") {
      display.drawStringMaxWidth(0, 10, 128, tekst2);
    }
    display.display();
    delay(500);

}

A do tego zawartość platformio.ini:

; 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

[env:esp32cam]
platform = espressif32@6.7.0
framework = arduino
board = esp32cam
build_flags =
    -DBOARD_HAS_PSRAM
    -mfix-esp32-psram-cache-issue
board_build.partitions = huge_app.csv
; board_build.partitions = min_spiffs.csv  ; needed for OTA
; board_build.f_cpu = 240000000L
; board_build.f_flash = 80000000L

monitor_rts = 0
monitor_dtr = 0

upload_protocol = esptool

platform_packages =
  ; use GCC AVR 5.0+
  toolchain-gccarmnoneeabi@>1.50000.0

; Verbose
; build_flags = -DCORE_DEBUG_LEVEL=5
; board_flags = -DARDUINO_USB_CDC_ON_BOOT=1

; upload_port = /dev/ttyUSB0
upload_speed = 921600

; monitor_port = /dev/ttyUSB0
monitor_speed = 115200

lib_deps =
  esp32-camera
  hideakitai/MQTTPubSubClient@^0.3.2
  thingpulse/ESP8266 and ESP32 OLED driver for SSD1306 displays@^4.6.1

W zasadzie działa.

Wyświetlacz przyjmuje dane w formacie XBM czyli: szerokość obrazka, długość obrazka i dane w formie ciągu bajtów (każdy bajt zawiera osiem pixeli-bitów w odwróconej kolejności (tzn., że pixel (0,0) zapisany jest w najmniej znaczącym bicie pierwszego bajtu). To jest proste.

Z ciągu znaków wygenerowanego przez kamerę (obrazek 320x240) wycinam środkowe 128x64. Przekształcam odcienie szarości na czarnobiały obrazek poprzez prosty podział na zera i jedynki, a granicę tego przedziału reguluję dwoma przyciskami. (kod powyżej void konwerter(uint8_t* bufor) ) To znaczy, że np. dla granicy 128, wszystkie bajty o wartości większej niż 128 mają wartość 0, a mniejszej niż 128 mają wartość 1. W teorii powinienem móc uzyskać czarnobiały obraz znaków na papierze (w zależności od przyjętej granicy). W praktyce nie potrafię uzyskać takiego obrazu na wyświetlaczu. Potrzebuję jedynie zarys tekstu - wyświetlacz ma działać jako wizjer. Czy ktoś ma jakiś pomysł na rozwiązanie?

Niejako równolegle wysyłam obrazek w skali szarości (320x240) na serwer przy pomocy protokołu Mqtt, gdzie próbuję go przeskanować przy pomocy wrappera do silnika OCR. Nad tym kodem jeszcze pracuję, pokażę go, jak będzie jako tako działał.

Czy ktoś ma pomysł, jak z obrazka w skali szarości 128x64 zrobić przyzwoicie wyglądający obrazek czarnobiały, tak, aby tekst albo druk były widoczne na ekranie (niekoniecznie czytelne)? Wszystko w C, na ESP32-CAM, więc moc i pamięć są pewnym ograniczeniem, chociaż dla tej wielkości obrazka niezbyt uciążliwym. Mile widziane pomysły, algorytmy, przykłady, cokolwiek - trochę dotarłem do ściany.

W najbliższych dniach dołożę kody na serwer i schemat.

Link do komentarza
Share on other sites

No tak. Są dwa pytania których nie zadam bo odpowiedzi nie oczekuję, ale mogą być interesujące.
 

1 godzinę temu, Szern napisał:

cztery przyciski (miało być sześć, ale głupio zapomniałem ile dostępnych pinów ma ESP32-CAM)

Dlaczego więc nie użyłeś ekspandera? W najprostszym przypadku (bez bawienia się w matryce) masz 8 przycisków bez wykorzystywania jakichkolwiek dodatkowych pinów w ESP32 (łączysz do tych samych pinów co OLED).

1 godzinę temu, Szern napisał:

A czemu nie TFT? Nawet te 5 bitów skali szarości to 32x więcej niż jeden...

Dobra, dość pytań.

Wrzuć parę przykładowych obrazków z kamery (tzn. dokładnie tego co dostaje konwerter, mile widziane również to co wysyłasz na serwer). Bez tego możemy sobie teoretyzować, a mając konkretne przykłady można na szybko przetestować algorytmy.

 

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

6 godzin temu, ethanak napisał:

Dlaczego więc nie użyłeś ekspandera? W najprostszym przypadku (bez bawienia się w matryce) masz 8 przycisków bez wykorzystywania jakichkolwiek dodatkowych pinów w ESP32 (łączysz do tych samych pinów co OLED).

A czemu nie TFT? Nawet te 5 bitów skali szarości to 32x więcej niż jeden..

Na temat ekspanderów nie mam zielonego pojęcia, nawet nie wiedziałem i ich istnieniu. Dzięki za temat.

TFT? O tym akurat myślałem, ale WYDAWAŁO MI SIĘ, że 1) SPI potrzebuje trochę więcej pinów, 2) pobiera więcej prądu, 3) a głównie nie wiedziałem czy w ogóle będę potrafił wyświetlić grafikę strumieniowo na takim wyświetlaczu i czy ESP32 poradzi sobie z taka ilością danych (w tej chwili obrazek wysyłany na wyświetlacz ma 1 KB, dla TFT będzie to co najmniej 24 razy więcej), więc na próbę zamówiłem najprostszy.

Bardzo dziękuję za pomoc! Nie ma to jak dobrze zadane pytanie, często jest więcej warte od odpowiedzi. Właśnie kupuję wyświetlacz i ekspander. Na razie nie będę robił zdjęć, pójdę inną ścieżką, zmienię mocno urządzenie i pokażę jak poszło, chyba, że znów się zablokuję i będę tu jęczał o pomoc.

  • 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

29 minut temu, ethanak napisał:

@Szern co będzie szybciej: 1 kB po 400 kHz czy 16 kB po 26 MHz? I zgadnij dlaczego 16 a nie 24.

Raczej 16 niż 26 MHz jeśli chcemy mieć eDMA, ale to w dalszym ciągu sporo szybciej. Z tym, że nie zależało mi na szybkości, tylko docelowo nie chciałem korzystać z PSRAM-u (trochę bez sensu).

Bo R i B mają tylko po pięć bitów, a G niewiele więcej, bo sześć. 😄 Dzięki, że o tym wspomniałeś.

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

Dobra, mam już zapięty i działający ekspander, ale teraz dopiero zajarzyłem, że nie wiem czy można zapiąć taki wyświetlacz (komunikujący się przez SPI) przez taki ekspander (również komunikujący się przez SPI). Można? Przepraszam za głupie pytanie, jeśli można, to szczegóły rozpracuję, ale nie wiem czy SPI przez SPI zadziała, czy raczej muszę pokombinować z obydwoma SPI zapiętymi do ESP32-CAM. Wydaje mi się, że jednak nie można (nie bardzo wyobrażam sobie linię zegarową przez SPI).

 

Link do komentarza
Share on other sites

48 minut temu, Szern napisał:

Można? Przepraszam za głupie pytanie, jeśli można, to szczegóły rozpracuję, ale nie wiem czy SPI przez SPI zadziała, czy raczej muszę pokombinować z obydwoma SPI zapiętymi do ESP32-CAM. Wydaje mi się, że jednak nie można (nie bardzo wyobrażam sobie linię zegarową przez SPI).

Jak chcesz to wszystko można, tylko będzie beznadziejnie działać 😄 Ekspanderem sterujesz przez I2C, które ma przepustowość ok. 100kbps, max 1Mbps). Sygnał zegarowy SPI to zwykle 10Mbps (zdarza się nawet 133Mbps)... Więc licząc straty wynikające z overhead'a I2C zdecydowanie wolisz tego nie robić.

Oczywiście wszędzie mowa o jednobitowych szynach danych (a nie QSPI czy OSPI).

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

(edytowany)
15 godzin temu, H1M4W4R1 napisał:

Jak chcesz to wszystko można, tylko będzie beznadziejnie działać 😄 

Tylko widzisz, ja to robię na ESP32-CAM, który ma tylko jedno SPI. Mam dwa urządzenia: ekran na SPI i ekspander na I2C. Na jednej szynie tego chyba nie zapnę, prawda? Po prostu nie wiedziałem, że są ekspandery na I2C i SPI. Teraz chyba po prostu kupię ekspander na SPI i zapnę to razem na jednej szynie. Czy dobrze myślę?

Przepraszam, za infantylne pytania, ale z konieczności borykam się z tematem szyn, ekranów i ekspanderów w przyspieszonym tempie.

Poza tym, jak się zorientowałem, jest SPI, HSPI, VSPI, QSPI itd.

Edytowano przez Szern
Link do komentarza
Share on other sites

1 godzinę temu, Szern napisał:

Tylko widzisz, ja to robię na ESP32-CAM, który ma tylko jedno SPI. Mam dwa urządzenia: ekran na SPI i ekspander na I2C. Na jednej szynie tego chyba nie zapnę, prawda? Po prostu nie wiedziałem, że są ekspandery na I2C i SPI. Teraz chyba po prostu kupię ekspander na SPI i zapnę to razem na jednej szynie. Czy dobrze myślę?

I2C ma adresowanie, SPI ma wybór komponentu z którym się komunikujesz (NSS, CS, CE lub temu podobne). Kiedy CS jest w stanie zablokowania to urządzenie nie odbiera danych po SPI, w ten sposób możesz wybrać do którego urządzenia na magistrali SPI wysyłasz żądania. Żądania zawsze wysyła główny mikrokontroler. SPI jest dwukierunkowe i równoległe - na każdy wysłany bit jest odbierany jeden. W ten sposób gdy mikrokontroler wyśle co chce otrzymać to wysyła puste dane by odebrać to co urządzenie ma do przekazania. Aka. tak możesz podpiąć kilka urządzeń SPI do jednej magistrali.

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

Powoli dobijam do końca projektu. Po drodze zrobiłem drugi prototyp, ale jego przeznaczeniem były głównie testy, bardzo dużo się dzięki niemu nauczyłem. Postaram się w najbliższych dniach wrzucić jakiś filmik.

Drugi prototyp również jest na ESP32-CAM. Ma ma ten sam ekran na I2C co pierwszy prototyp, ale udało mi się usunąć błędy (głównie programowe), tak, że ekran działa poprawnie. Pomimo tego ten ekran okazał się niewystarczający na potrzeby mojego skanera.

Ponieważ ESP32-CAM ma bardzo mało pinów, które mógłbym używać jako GPIO, to drugi prototyp dostał ekspander portów na PCF8575, podłączony na I2C. Spisuje się bardzo dobrze, dzięki niemu mogłem zrobić prostą, sześcioprzyciskową klawiaturę - do regulacji jasności diody flash i kontrastu (treshold - granica między poziomem czerni i bieli), a także obsługi skanera. Ustawienia zapisywane są w nieulotnym EPROMIE.

Zdjęcia przesyłane są na serwer, gdzie powinny być obrabiane OCR-em.

Dla ciekawych, kod dla ESP32-CAM:

#include <Arduino.h>
#include <esp_system.h>
#include <esp_camera.h>
#include <soc/soc.h>            // Disable brownour problems
#include <soc/rtc_cntl_reg.h>   // Disable brownour problems

#include <WiFiClientSecure.h>
#include <MQTTPubSubClient.h>
#include <base64.h>
#include <Wire.h>
#include <SSD1306Wire.h>
#include "images.h"
#include <Preferences.h>

// GPIO 0 - camera using
// GPIO 1 pin_switch
// GPIO 2 pin_button_bottom
// GPIO 3 pin_button_top
// GPIO 4 - pin_flash
// GPIO 12 pin_button_green
// GPIO 13 pin_button_yellow
// GPIO 14 pin_SCL
// GPIO 15 pin_SDA
// GPIO 16 - PSRAM using
// GPIO 33 - red internal LED

const byte pin_flash = GPIO_NUM_4;
const byte pin_SDA = GPIO_NUM_15;
const byte pin_SLC = GPIO_NUM_14;
const byte pin_button_green = GPIO_NUM_12;
const byte pin_button_yellow = GPIO_NUM_13;
const byte pin_button_top = GPIO_NUM_3;
const byte pin_button_bottom = GPIO_NUM_2;
const byte pin_switch = GPIO_NUM_1;
const byte pin_power = GPIO_NUM_32;
const byte pin_led_red = GPIO_NUM_33;

const char* ssid = "..."; // Replace with your network name
const char* password = "..."; // Replace with your password to WiFi

const char* mqttServer = "..."; // Replace with your webserver adress or webserver IP
const long mqttPortS = 8883;
const long mqttPortU = 8884;
const char* mqttUser = "..."; // Replace with your MQTT brooker user name
const char* mqttPass = "..."; // Replace with your MQTT brooker users password
const char* mqttId = "bibliotekarz";
const char* mqttTopicOut = "bib/out"; // Replace with your webserver topic name to send images from camera to server
const char* mqttTopicDebug = "bib/debug";
const char* mqttTopicComm = "bib/comm";
const char * mqtt_lamp_on = "fon";
const char * mqtt_lamp_off = "foff";

long rssi;
int security = 1;
int mqtt_initialised = 0;
int mqtt_return = 0;
char * mqtt_liczba;
int treshold = 200;
int flashPower = 0;

// chain.pem
static const char * host_ca = "-----BEGIN CERTIFICATE-----\n"
"MIIFBjCCAu6gAwIBAgIRAIp9PhPWLzDvI4a9KQdrNPgwDQYJKoZIhvcNAQELBQAw\n"
"TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
"cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\n"
"WhcNMjcwMzEyMjM1OTU5WjAzMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n"
"RW5jcnlwdDEMMAoGA1UEAxMDUjExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"
"CgKCAQEAuoe8XBsAOcvKCs3UZxD5ATylTqVhyybKUvsVAbe5KPUoHu0nsyQYOWcJ\n"
"DAjs4DqwO3cOvfPlOVRBDE6uQdaZdN5R2+97/1i9qLcT9t4x1fJyyXJqC4N0lZxG\n"
"AGQUmfOx2SLZzaiSqhwmej/+71gFewiVgdtxD4774zEJuwm+UE1fj5F2PVqdnoPy\n"
"6cRms+EGZkNIGIBloDcYmpuEMpexsr3E+BUAnSeI++JjF5ZsmydnS8TbKF5pwnnw\n"
"SVzgJFDhxLyhBax7QG0AtMJBP6dYuC/FXJuluwme8f7rsIU5/agK70XEeOtlKsLP\n"
"Xzze41xNG/cLJyuqC0J3U095ah2H2QIDAQABo4H4MIH1MA4GA1UdDwEB/wQEAwIB\n"
"hjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgwBgEB\n"
"/wIBADAdBgNVHQ4EFgQUxc9GpOr0w8B6bJXELbBeki8m47kwHwYDVR0jBBgwFoAU\n"
"ebRZ5nu25eQBc4AIiMgaWPbpm24wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAC\n"
"hhZodHRwOi8veDEuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcG\n"
"A1UdHwQgMB4wHKAaoBiGFmh0dHA6Ly94MS5jLmxlbmNyLm9yZy8wDQYJKoZIhvcN\n"
"AQELBQADggIBAE7iiV0KAxyQOND1H/lxXPjDj7I3iHpvsCUf7b632IYGjukJhM1y\n"
"v4Hz/MrPU0jtvfZpQtSlET41yBOykh0FX+ou1Nj4ScOt9ZmWnO8m2OG0JAtIIE38\n"
"01S0qcYhyOE2G/93ZCkXufBL713qzXnQv5C/viOykNpKqUgxdKlEC+Hi9i2DcaR1\n"
"e9KUwQUZRhy5j/PEdEglKg3l9dtD4tuTm7kZtB8v32oOjzHTYw+7KdzdZiw/sBtn\n"
"UfhBPORNuay4pJxmY/WrhSMdzFO2q3Gu3MUBcdo27goYKjL9CTF8j/Zz55yctUoV\n"
"aneCWs/ajUX+HypkBTA+c8LGDLnWO2NKq0YD/pnARkAnYGPfUDoHR9gVSp/qRx+Z\n"
"WghiDLZsMwhN1zjtSC0uBWiugF3vTNzYIEFfaPG7Ws3jDrAMMYebQ95JQ+HIBD/R\n"
"PBuHRTBpqKlyDnkSHDHYPiNX3adPoPAcgdF3H2/W0rmoswMWgTlLn1Wu0mrks7/q\n"
"pdWfS6PJ1jty80r2VKsM/Dj3YIDfbjXKdaFU5C+8bhfJGqU3taKauuz0wHVGT3eo\n"
"6FlWkWYtbt4pgdamlwVeZEW+LM7qZEJEsMNPrfC03APKmZsJgpWCDWOKZvkZcvjV\n"
"uYkQ4omYCTX5ohy+knMjdOmdH9c7SpqEWBDC86fiNex+O0XOMEZSa8DA\n"
"-----END CERTIFICATE-----\n";

WiFiClientSecure client;
MQTTPubSubClient mqtt;

SSD1306Wire display(0x3c, 15, 14, GEOMETRY_128_64, I2C_ONE);  // ADDRESS, SDA, SCL
Preferences preferences;

void memory_status();
void connect();
const char* wl_status_to_string(wl_status_t status);
void wyswietl(const String tekst1, const String tekst2);
void konwerter(uint8_t* bufor);
void regulacje();

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

  pinMode(pin_flash, OUTPUT);
  digitalWrite(pin_flash, LOW);

  pinMode(pin_led_red, OUTPUT);
  digitalWrite(pin_led_red, HIGH);

  preferences.begin("ustawienia", false);
  flashPower  = preferences.getUInt("flashPower", 0);
  treshold = preferences.getUInt("treshold", 200);

  Serial.begin(115200);
  Serial.println("");

  display.init();
  display.normalDisplay();
  display.setFont(ArialMT_Plain_10); // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24
  display.setContrast(100); 
//  display.setContrast(200,241,64); // normal brightness & contrast:  100,241,64, / really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0
  display.setBrightness(10);

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

  // define pin & power up the camera
  pinMode(pin_power, OUTPUT);
  digitalWrite(pin_power, LOW);
  pinMode(pin_button_green, INPUT);
  pinMode(pin_button_yellow, INPUT);
  pinMode(pin_button_top, INPUT);
  pinMode(pin_button_bottom, INPUT);
  pinMode(pin_switch, INPUT);

  // Use PWM channel 7 to control the white on-board LED (flash) connected to GPIO 4
  ledcSetup(7, 5000, 8);
  ledcAttachPin(4, 7);
  // Turn the LED on at specified power
  ledcWrite(7, flashPower);

// Pin definition for CAMERA_MODEL_AI_THINKER
  static camera_config_t camera_config = {
    .pin_pwdn  = 32,
    .pin_reset = -1, //software reset will be performed
    .pin_xclk = 0,
    .pin_sccb_sda = 26,
    .pin_sccb_scl = 27,
    .pin_d7 = 35,
    .pin_d6 = 34,
    .pin_d5 = 39,
    .pin_d4 = 36,
    .pin_d3 = 21,
    .pin_d2 = 19,
    .pin_d1 = 18,
    .pin_d0 = 5,
    .pin_vsync = 25,
    .pin_href = 23,
    .pin_pclk = 22,

    // PCLK = sensor->xclk_freq_hz * (freqdoubler?2:1) / 4
    .xclk_freq_hz = 16000000, //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_GRAYSCALE,//YUV422 (2BPP/YUV422),GRAYSCALE (1BPP/GRAYSCALE),RGB565 (2BPP/RGB565),JPEG (JPEG/COMPRESSED)// PIXFORMAT_JPEG
    .frame_size = FRAMESIZE_QVGA, // framebuffer size must be greater than or equal to the frame size; FRAMESIZE_ + QVGA 320x240 || CIF 352x288 || VGA 640x480 || SVGA 800x600 || XGA 1024x768 || SXGA 1280x1024 || UXGA 1600x1200; 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 = 10, // 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.
    .fb_location = CAMERA_FB_IN_DRAM, // CAMERA_FB_IN_DRAM (.frame_size: QVGA or CIF and .pixel_format: jpeg )|| CAMERA_FB_IN_PSRAM;
    .grab_mode = CAMERA_GRAB_WHEN_EMPTY // CAMERA_GRAB_WHEN_EMPTY - more control over the system, but results in longer time to get the frame // CAMERA_GRAB_LATEST Sets when buffers should be filled (.fb_count > 1), This approach puts more strain on the CPU/Memory, but allows for double the frame rate. Please use only with JPEG.
  };

  esp_err_t err = esp_camera_init(&camera_config); // initialize the camera
  delay(1000);
  int i = 0;
  while ( err != ESP_OK ) {
    i++;
    pinMode(pin_power, OUTPUT); // After the camera uses the GPIO, the pinMode is reset. You need to set the pinMode again after the camera is turned off.
    digitalWrite(pin_power, HIGH);
    delay(1000);
    pinMode(pin_power, OUTPUT);
    digitalWrite(pin_power, LOW);
    if ( i > 4 ) {
      Serial.println("Nie mogę aktywować kamery, restartuję...");
      wyswietl((char*)"Błąd aktywacji", (char*)"kamery: restart");
      pinMode(pin_power, OUTPUT);
      digitalWrite(pin_power, HIGH);
      WiFi.disconnect();
      delay(1000);
      ESP.restart();
    }
  }

  int jakosc = 10, jasnosc = -2, kontrast = 2, nasycenie = 0, efekty = 2, naswietlanie = 0, kontrolanaswietlania = 1, iso = 0, balansbieli = 0, obraz_kontrolny = 0, lampa = 0;
  sensor_t * s = esp_camera_sensor_get();
    // Gain
  s->set_gain_ctrl(s, 1);      // Auto-Gain Control 0 = disable , 1 = enable
  s->set_agc_gain(s, 10);       // Manual Gain 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)4);  // 0 to 6  The maximum gain allowed, when agc_mode is set to auto. This parameter seems act as “ISO” setting.
  // Exposure
  s->set_exposure_ctrl(s, 1);  // Auto-Exposure Control 0 = disable , 1 = enable  Set exposure control
  s->set_aec_value(s, 300);    // Manual Exposure 0 to 1200 (300)  The Exposure value to apply to the picture (when aec_mode is set to manual), from 0 to 1200.
  // Exposure Correction
  s->set_aec2(s, 1);           // Automatic Exposure Correction 0 = disable , 1 = enable  Auto Exposure Control
  s->set_ae_level(s, 2);       // Manual Exposure Correction -2 to 2  The auto exposure level to apply to the picture (when aec_mode is set to auto),
  // White Balance
  s->set_awb_gain(s, 1);       // Auto White Balance 0 = disable , 1 = enable  Set white balance gain
  s->set_wb_mode(s, 3);        // White Balance Mode 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_whitebal(s, 0);       // White Balance 0 = disable , 1 = enable  Set white balance
  s->set_bpc(s, 1);            // Black Pixel Correction 0 = disable , 1 = enable
  s->set_wpc(s, 1);            // White Pixel Correction 0 = disable , 1 = enable
  s->set_brightness(s, -2);     // Brightness -2 to 2
  s->set_contrast(s, 0);       // Contrast -2 to 2
  s->set_saturation(s, 2);     // Saturation -2 to 2
  s->set_special_effect(s, 2); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
  // Additional settings
  s->set_lenc(s, 0);           // Lens correction 0 = disable , 1 = enable
  s->set_hmirror(s, 0);        // Horizontal flip image 0 = disable , 1 = enable
  s->set_vflip(s, 0);          // Vertical flip image 0 = disable , 1 = enable
  s->set_colorbar(s, 0);       // Colour Testbar For tests purposes, it’s possible to replace picture get from sensor by a test color pattern.
  s->set_raw_gma(s, 0);        // 0 = disable , 1 = enable
  s->set_dcw(s, 0);            // 0 = disable , 1 = enable

  s->set_framesize(s, FRAMESIZE_QVGA);
  s->set_quality(s, 10); // 10-63, for OV series camera sensors, lower number means higher quality

  camera_fb_t *fb = NULL;

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  client.setCACert(host_ca);
  mqtt.begin(client); // initialize mqtt client

  connect();

  Serial.println(" END setup");
  wyswietl("END setup", "Push button to start");
  memory_status();

  while ( digitalRead(pin_button_green) || digitalRead(pin_button_yellow) ) {
    delay(50);
  }

}

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

  if ( !digitalRead(pin_button_top) || !digitalRead(pin_button_bottom) ) {
    regulacje();
  }

  if ( !( (WiFi.status() == WL_CONNECTED) && mqtt.isConnected() && client.connected() ) ) {
    digitalWrite(pin_led_red, LOW);
    wyswietl("Restart w kamerze", "JESZCZE RAZ!");
    WiFi.disconnect();
    delay(1000);
    ESP.restart();
  }
  mqtt.update(); // should be called
  delay(100);
  camera_fb_t * fb = esp_camera_fb_get(); // acquire a frame
  konwerter(fb->buf);
  if ( !digitalRead(pin_button_green) ) {
    String imgDataB64 = base64::encode(fb->buf, fb->len);
    mqtt.publish(mqttTopicOut, imgDataB64, false, 0); // send image data (mqttTopicOut, imgDataB64, false, 0)
    wyswietl("wyslalem zdjecie", "czekaj");
    while ( ( mqtt_return == 0 ) && ( digitalRead(pin_button_top) == HIGH ) ) {
      mqtt.update();
      delay(50);
    }
    if ( mqtt_return == 2 ) {
      wyswietl(mqtt_liczba, "zatwierdz?");
      mqtt_return = 0;
      while ( !digitalRead(pin_button_green) || !digitalRead(pin_button_yellow) ) { // rozpisać na dwa przyciski OK i nieOK
        delay(50);
      }
      if ( !digitalRead(pin_button_green) ) {
        mqtt.publish(mqttTopicComm, "OK", false, 0);
      } else if ( !digitalRead(pin_button_yellow) ) {
        mqtt.publish(mqttTopicComm, "DELETE", false, 0);
      }
    }
  }
  esp_camera_fb_return(fb); // return the frame buffer back to the driver for reuse

} // ###################################################################################################################

void memory_status() {
  Serial.print("PSRAM found: ");
  Serial.println((String)psramFound());
  Serial.print("Total heap: ");
  Serial.println((String)ESP.getHeapSize());
  Serial.print("Free heap: ");
  Serial.println((String)ESP.getFreeHeap());
  Serial.print("Total PSRAM: ");
  Serial.println((String)ESP.getPsramSize());
  Serial.print("Free PSRAM: ");
  Serial.println((String)ESP.getFreePsram());
}

const char* wl_status_to_string(wl_status_t status) {
  switch (status) {
    case WL_NO_SHIELD: return "WL_NO_SHIELD";
    case WL_IDLE_STATUS: return "WL_IDLE_STATUS";
    case WL_NO_SSID_AVAIL: return "WL_NO_SSID_AVAIL";
    case WL_SCAN_COMPLETED: return "WL_SCAN_COMPLETED";
    case WL_CONNECTED: return "WL_CONNECTED";
    case WL_CONNECT_FAILED: return "WL_CONNECT_FAILED";
    case WL_CONNECTION_LOST: return "WL_CONNECTION_LOST";
    case WL_DISCONNECTED: return "WL_DISCONNECTED";
    default: return "ERROR";
  }
}

void connect() { // wifi, brooker mqtt ################################################################################

long mqttPort;

connect_to_wifi:
  if (WiFi.status() != WL_CONNECTED) {
    Serial.print("connecting to WiFi...");
    while (WiFi.status() != WL_CONNECTED) {
      Serial.print(".");
      wyswietl("connecting to WiFi...", wl_status_to_string(WiFi.status()));
      display.clear();
      display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
      display.display();
      delay(3000);
    }
    Serial.println(" connected!");
    rssi = WiFi.RSSI();
    char signal[4];
    sprintf(signal, "%d", rssi);
    Serial.print("RSSI: ");
    Serial.println(signal);
    wyswietl((char*)"RSSI: ", signal);
  }

connect_to_host:
  Serial.print("connecting to MQTT host...");
  wyswietl("connecting to MQTT host...", "");
  client.stop();
  if (security) {
    client.setCACert(host_ca);
    mqttPort = mqttPortS;
  } else {
    client.setInsecure(); // skip verification
    mqttPort = mqttPortU;
  }
  while (!client.connect(mqttServer, mqttPort)) {
    Serial.print(".");
    delay(1000);
    if (WiFi.status() != WL_CONNECTED) {
      Serial.println("WiFi disconnected");
      wyswietl("WiFi disconnected", "");
      WiFi.disconnect();
      delay(2000);
      ESP.restart();
    }
  }
  Serial.println(" connected!");
  wyswietl("connected!", "");

connect_to_brooker:
  Serial.print("connecting to MQTT broker...");
  wyswietl("connecting to MQTT broker...", "");
  mqtt.disconnect();
  while (!mqtt.connect(mqttId, mqttUser, mqttPass)) {
    Serial.print(".");
    delay(1000);
    if (WiFi.status() != WL_CONNECTED) {
      Serial.println("WiFi disconnected");
      wyswietl("WiFi disconnected", "");
      WiFi.disconnect();
      delay(2000);
      ESP.restart();
    }
    if (!client.connected()) {
      Serial.println("MQTT host disconnected");
      wyswietl("MQTT disconnected", "");
      goto connect_to_host;
    }
  }
  mqtt_initialised = 1;
  Serial.println("connected to MQTT brooker!");
  wyswietl("connected to MQTT brooker!", "");

  // #### subscribe topic
  mqtt.subscribe(mqttTopicComm, [](const char * payload, const size_t size) {
    if ( strcmp(payload, "") == 0 ) {
      mqtt_return = 1;
    } else {
      mqtt_liczba = strdup(payload);
      mqtt_return = 2;
    }
  });

}

void konwerter(uint8_t* bufor) { // #############################################################################

// bufor: 76800, piksele: 8192, tablica 1024

// wykadrować i odwrócić środek 128x64 z 320x240

  int startX = 96;
  int endX = 224;
  int startY = 88;
  int endY = 152;

  uint8_t * kadr = (uint8_t *) ps_malloc (8192 * sizeof (uint8_t));

  int i = 8191;
  for (int y = startY; y < endY; y++) {
    for (int x = startX; x < endX; x++) {
      if ( bufor[x+y*320] > treshold ) {
        kadr[i] = 0;
      } else {
        kadr[i] = 1;
      }
      i--;
    }
  }

  // przerobić bity na bajty (xbm)

  uint8_t * xbm = (uint8_t *) ps_malloc (1024 * sizeof (uint8_t));

  int z = 0;
  for ( int i = 0; i < 1024; i++ ) {
    xbm[i] = 0;
    for ( int j = 7; j >= 0; j-- ) {
      xbm[i] += kadr[z] * pow(2,j); 
      z++;
    }
  }

  free(kadr);

  display.clear();
  display.drawXbm(0, 0, 128, 64, xbm);
  display.display();
  display.clear();

  free(xbm);

}

void wyswietl(const String tekst1, const String tekst2) {

    display.clear();
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.setFont(ArialMT_Plain_10);
    display.drawStringMaxWidth(0, 0, 128, tekst1);
    if ( tekst2 != "") {
      display.drawStringMaxWidth(0, 10, 128, tekst2);
    }
    display.display();
    delay(500);

}

void regulacje() {

bool change_flash = 0;
bool change_treshold = 0;

  while ( digitalRead(pin_button_green) || digitalRead(pin_button_yellow) ) {
    if ( digitalRead(pin_switch) ) {
      if ( !digitalRead(pin_button_top) ) {
        if ( flashPower <= 244 ) {
          flashPower = flashPower + 10;
          ledcWrite(7, flashPower);
        }
      } else if ( !digitalRead(pin_button_bottom) ) {
        if ( flashPower >= 10 ) {
          flashPower = flashPower - 10;
          ledcWrite(7, flashPower);
        }
      }
      change_flash = 1;
    } else if ( !digitalRead(pin_switch) ) {
      if ( !digitalRead(pin_button_top) ) {
        if ( treshold <= 244 ) {
          treshold = treshold + 10;
        }
      } else if ( !digitalRead(pin_button_bottom) ) {
        if ( treshold >= 10 ) {
          treshold = treshold - 10;
        }
      }
      change_treshold = 1;
    }
    camera_fb_t * fb = esp_camera_fb_get(); // acquire a frame
    konwerter(fb->buf);
    esp_camera_fb_return(fb);
  }

  if (digitalRead(pin_button_green) ) {
    if (change_flash) {
      preferences.putUInt("flashPower", flashPower);
    }
    if (change_treshold) {
      preferences.putUInt("treshold", treshold);
    }
    if (change_flash || change_treshold) {
      preferences.end();
    }
  }

}

Trzeci prototyp zacząłem testować dziś - jest już w pełni funkcjonalny, opiszę go w najbliższych dniach, wraz ze schematem i kompletem kodów dla serwera. Być może jeszcze zrobię czwarty prototyp, ponieważ poległem przy podłączeniu ekspandera MCP23S08-E/P.

  • 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.