Skip to content

11. Embedded Networking and Communications#

1.groupwork#

For more details, please see here.

https://fabacademy.org/2025/labs/kannai/Weekly_Group_Assignment/week11/

2.individual work#

Note

  • Individual assignment:
    • design, build and connect wired or wireless node(s) with network or bus addresses and a local input and/or output devices

The program was implemented in Week12,13. For more details, please refer to the following pages.

3. excerpts from the network communication part conducted in week12#

Camera Embedded Programming#

I created a program to photograph a karesansui (Zen rock garden) and upload the captured image to the cloud via Wi-Fi.

Environment Used:#

  • PlatformIO
    PlatformIO is an open-source IoT development ecosystem that supports multiple embedded boards and frameworks, providing cross-platform build, library management, debugging, testing, and IDE integration. It specializes in programming in C++.
    For this project, I used PlatformIO to write the code. Therefore, I will also explain how to prepare and install PlatformIO.
  • Seeed Studio XIAO ESP32S3 Sense
    The Seeed Studio XIAO ESP32S3 features a dual-core ESP32-S3 chip with both Wi-Fi and BLE connectivity, and supports battery charging. It includes a built-in camera sensor and digital microphone, 8 MB of PSRAM, 8 MB of flash memory, and an external SD card slot.

1. PlatformIO Setup#

  1. In VS Code (or a VS Code fork), open the Extensions view, search for “PlatformIO,” and install it.

2. Creating the Project Files#

  1. Open PlatformIO Home and select New Project.

  2. Enter the project name, select the board and language, then click Finish. For this project, I chose the XIAO ESP32S3 board and the Arduino framework.

  3. The environment is now ready. In the video below, I create a new sample project so you can see how it behaves.

3. Configuring the platformio.ini File#

The Arduino IDE uses a “one sketch = one project” model, automatically retaining board selection and build settings via GUI and internal settings files (~/.arduinoIDE/settings.json / arduino-cli.yaml), so you don’t need a per-project config file.
However, PlatformIO declaratively manages multiple platforms (MCUs), frameworks, build options, and library dependencies on a per-project basis to reproduce the same source across different environments. Therefore, you need a platformio.ini file:

ini files
[env:seeed_xiao_esp32s3]
platform = espressif32
board = seeed_xiao_esp32s3
framework = arduino
upload_speed = 115200
monitor_speed = 115200
upload_port = /your/devices/ports
board_build.partitions = huge_app.csv
build_flags =
  -DARDUINO_USB_MODE=1
  -DARDUINO_USB_CDC_ON_BOOT=1
lib_deps =
  WiFi
  HTTPClient
  base64
  bblanchon/ArduinoJson @ ^6.21.2

4. Task Breakdown and Sequencing#

To achieve a complex goal, planning is necessary.
First, I defined the goal of this work and then broke down the tasks by working backwards from it.

Next, I considered the sequence of tasks. The important thing is to maintain a working state regardless of when each task is completed.

5. Serial Monitor Test#

It’s advisable to test whether it displays correctly on the serial monitor in PlatformIO as well.

!!!
- In PlatformIO, you must explicitly include <Arduino.h>.
- You can display the serial monitor by selecting the plug icon at the bottom of the screen.

Serial monitor test
#include <Arduino.h>

void setup()
{
  Serial.begin(115200);
  Serial.println("Hello from ESP32");
}

void loop()
{
  Serial.println("Looping...");
  delay(1000);
}

6. Communicating over Wi-Fi#

To connect via Wi-Fi, you need to configure the config file.
Create the following config.h file in the include folder.

!!!
- Wi-Fi credentials are personal information. Please configure your .gitignore in advance so you don’t upload them to GitLab or similar.
- You can check the port information by running pio device list in the terminal.

wifi config setting#

config.h
#ifndef CONFIG_H
#define CONFIG_H

// ====== Wi-Fi setting ======
#define WIFI_SSID     "YourWiFiSSID"
#define WIFI_PASSWORD "YourWiFiPassword"

// ====== Web API setting ======
#define API_URL       "https://machinical-memory-landscapes.cloud/api/upload-image"
#define API_HEADER_CONTENT_TYPE "multipart/form-data"
#define API_IMAGE_FIELD_NAME    "image"
#define API_FILENAME            "image.jpg"

#endif

!!!
- You can build & upload to the microcontroller by running pio run -t upload in the terminal.

Wifi connecting program#

main.cpp
#include <WiFi.h>             // ESP32 Wi-Fi library
#include <HTTPClient.h>       // HTTP client for ESP32
#include <WiFiClientSecure.h> // HTTPS support for ESP32
#include "config.h"

// Wi-Fi connect
void connectWiFi()
{
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    Serial.print("Connecting to WiFi");
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(3000);
        Serial.print(".");
    }
    Serial.println("\nConnected!");
}

// call connecting Wi-Fi
void setup()
{
    Serial.begin(115200);
    delay(3000);
    connectWiFi();
}

Wifi connecting test

7.Camera Shooting#

#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include "esp_camera.h"
#include "config.h"

void setupCamera()
{
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = 15;
  config.pin_d1 = 17;
  config.pin_d2 = 18;
  config.pin_d3 = 16;
  config.pin_d4 = 14;
  config.pin_d5 = 12;
  config.pin_d6 = 11;
  config.pin_d7 = 48;
  config.pin_xclk = 10;
  config.pin_pclk = 13;
  config.pin_vsync = 38;
  config.pin_href = 47;
  config.pin_sccb_sda = 40;
  config.pin_sccb_scl = 39;
  config.pin_pwdn = -1;
  config.pin_reset = -1;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_QVGA;
  config.jpeg_quality = 10;
  config.fb_count = 1;

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK)
  {
    Serial.printf("Camera init failed: 0x%x\n", err);
    while (true)
      delay(1000);
  }
}

void setup()
{
  Serial.begin(115200);
  delay(3000);
  Serial.println("Booting...");
  setupCamera();
  Serial.println("Camera ready!");
}

void loop()
{
  digitalWrite(LED_BUILTIN, LOW);
  delay(50);
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb)
  {
    Serial.println("Capture failed");
  }
  else
  {
    Serial.println("Image captured successfully.");
    Serial.print("Image size: ");
    Serial.print(fb->len);
    Serial.println(" bytes");
  }
  esp_camera_fb_return(fb);
  delay(3000);
}
The following video shows that it is possible to obtain the size of the image taken.

8.Send images captured by camera to webapi via wifi#

Since it is more convenient to test whether the webapi can actually be transmitted by taking pictures with a camera and actually POSTing them over Wi-Fi, we conducted an integrated operation test instead of testing the webapi as a stand-alone unit.

Send images captured by camera to webapi via wifi
#include <WiFi.h>             // ESP32 Wi-Fi library
#include <HTTPClient.h>       // HTTP client for ESP32
#include <WiFiClientSecure.h> // HTTPS support for ESP32
#include <ArduinoJson.h>      // JSON handling library

#include "esp_camera.h" // Camera support for ESP32
#include "config.h"     // Contains WIFI_SSID, WIFI_PASSWORD, API_URL, etc.

void setupCamera();              // Declare function to setup the camera
void postImage(camera_fb_t *fb); // Declare function to send image to server
void connectWiFi();              // Declare function to connect WiFi

void connectWiFi()
{
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(3000);
    Serial.print(".");
  }
  Serial.println("\nConnected!");
}

void setupCamera()
{
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = 15;
  config.pin_d1 = 17;
  config.pin_d2 = 18;
  config.pin_d3 = 16;
  config.pin_d4 = 14;
  config.pin_d5 = 12;
  config.pin_d6 = 11;
  config.pin_d7 = 48;
  config.pin_xclk = 10;
  config.pin_pclk = 13;
  config.pin_vsync = 38;
  config.pin_href = 47;
  config.pin_sccb_sda = 40;
  config.pin_sccb_scl = 39;
  config.pin_pwdn = -1;
  config.pin_reset = -1;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_QVGA;
  config.jpeg_quality = 10;
  config.fb_count = 1;

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK)
  {
    Serial.printf("Camera init failed: 0x%x\n", err);
    while (true)
      delay(1000);
  }
}

void postImage(camera_fb_t *fb)
{
  digitalWrite(LED_BUILTIN, LOW);
  Serial.println("Starting postImage function");
  WiFiClientSecure client;
  client.setInsecure();

  if (!client.connect("machinical-memory-landscapes.cloud", 443))
  {
    Serial.println("Connection failed!");
    return;
  }

  String boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";
  String contentType = "multipart/form-data; boundary=" + boundary;
  String start = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"image\"; filename=\"upload.jpg\"\r\n" + "Content-Type: image/jpeg\r\n\r\n";
  String end = "\r\n--" + boundary + "--\r\n";

  int contentLength = start.length() + fb->len + end.length();

  client.println("POST /api/karesansui-generator HTTP/1.1");
  client.println("Host: machinical-memory-landscapes.cloud");
  client.println("User-Agent: ESP32");
  client.println("Content-Type: " + contentType);
  client.println("Content-Length: " + String(contentLength));
  client.println("Connection: close");
  client.println();

  client.print(start);
  client.write(fb->buf, fb->len);
  client.print(end);

  while (client.connected())
  {
    String line = client.readStringUntil('\n');
    Serial.println(line);
  }

  client.stop();
  Serial.println("Image POST completed");
}

void setup()
{
  Serial.begin(115200);
  delay(3000);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  connectWiFi();
  setupCamera();
  Serial.println("Setup complete");
}

void loop()
{

  digitalWrite(LED_BUILTIN, LOW);
  for (int i = 0; i < 3; i++)
  {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    delay(200);
  }
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb)
  {
    Serial.println("Capture failed");
    digitalWrite(LED_BUILTIN, LOW);
    delay(5000);
    return;
  }
  digitalWrite(LED_BUILTIN, LOW);
  Serial.printf("Image captured: %zu bytes\n", fb->len);
  postImage(fb);
  esp_camera_fb_return(fb);
  delay(15000);
}

9. When the trigger is pressed, the internal LED lights up.#

limitswitch_on
#include <Arduino.h>

const int limitSwitchPin = 6; 
const int ledPin = 21; 

void setup()
{
  pinMode(limitSwitchPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);            
  Serial.begin(115200);              
  Serial.println("System startup: monitoring limit switches...");
  digitalWrite(LED_BUILTIN, LOW); 
}

void loop()
{
  int switchState = digitalRead(limitSwitchPin);

  if (switchState == LOW)
  {                            
    digitalWrite(ledPin, HIGH);
  }
  else
  {
    digitalWrite(ledPin, LOW);
  }

  delay(200); 
}

10. When the trigger is pressed, the image captured by the camera is sent to webapi via wifi.#

Integrate all previous content and verify that it works. If it works, then the minimum functionality can be provided.

When the trigger is pressed, the image captured by the camera is sent to webapi via wifi.
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include "esp_camera.h"
#include "config.h"

#define LED_BUILTIN 21
const int limitSwitchPin = 6; // D6 = GPIO6

void setupCamera();
void postImage(camera_fb_t *fb);
void connectWiFi();

void connectWiFi()
{
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(3000);
    Serial.print(".");
  }
  Serial.println("\nConnected!");
}

void setupCamera()
{
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = 15;
  config.pin_d1 = 17;
  config.pin_d2 = 18;
  config.pin_d3 = 16;
  config.pin_d4 = 14;
  config.pin_d5 = 12;
  config.pin_d6 = 11;
  config.pin_d7 = 48;
  config.pin_xclk = 10;
  config.pin_pclk = 13;
  config.pin_vsync = 38;
  config.pin_href = 47;
  config.pin_sccb_sda = 40;
  config.pin_sccb_scl = 39;
  config.pin_pwdn = -1;
  config.pin_reset = -1;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_QVGA;
  config.jpeg_quality = 10;
  config.fb_count = 1;

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK)
  {
    Serial.printf("Camera init failed: 0x%x\n", err);
    while (true)
      delay(1000);
  }
}

void postImage(camera_fb_t *fb)
{
  digitalWrite(LED_BUILTIN, LOW);
  Serial.println("Starting postImage function");
  WiFiClientSecure client;
  client.setInsecure();

  if (!client.connect("machinical-memory-landscapes.cloud", 443))
  {
    Serial.println("Connection failed!");
    return;
  }

  String boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";
  String contentType = "multipart/form-data; boundary=" + boundary;
  String start = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"image\"; filename=\"upload.jpg\"\r\n" + "Content-Type: image/jpeg\r\n\r\n";
  String end = "\r\n--" + boundary + "--\r\n";

  int contentLength = start.length() + fb->len + end.length();

  client.println("POST /api/karesansui-generator HTTP/1.1");
  client.println("Host: machinical-memory-landscapes.cloud");
  client.println("User-Agent: ESP32");
  client.println("Content-Type: " + contentType);
  client.println("Content-Length: " + String(contentLength));
  client.println("Connection: close");
  client.println();

  client.print(start);
  client.write(fb->buf, fb->len);
  client.print(end);

  while (client.connected() || client.available())
  {
    String line = client.readStringUntil('\n');
    Serial.println(line);
  }

  client.stop();
  Serial.println("Image POST completed");
}

void setup()
{
  Serial.begin(115200);
  delay(3000);
  pinMode(limitSwitchPin, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  connectWiFi();
  setupCamera();
  Serial.println("Setup complete");
}

void loop()
{
  int switchState = digitalRead(limitSwitchPin);

  if (switchState == LOW)
  {
    digitalWrite(LED_BUILTIN, LOW);
    for (int i = 0; i < 3; i++)
    {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(200);
    }

    camera_fb_t *fb = esp_camera_fb_get();
    if (!fb)
    {
      Serial.println("Capture failed");
      digitalWrite(LED_BUILTIN, LOW);
      delay(5000);
      return;
    }

    Serial.printf("Image captured: %zu bytes\n", fb->len);
    postImage(fb);
    esp_camera_fb_return(fb);
    delay(15000);
  }

  delay(200);
}

11. Improve camera image quality#

The effort will now be to improve functionality. As the quality of the camera improves, the image size will also increase, which will naturally affect the transmission speed. In addition, if an external storage medium is not used, the microcontroller’s RAM buffer capacity will be limited.

The image quality can be improved by changing the following variables.

config.frame_size | Value | Resolution (px) | Raw Buffer Size (bytes) | | — | — | — | | QQVGA | 160 × 120 | 160×120×2=38400 | | QCIF | 176 × 144 | 176×144×2=50688 | | HQVGA | 240 × 176 | 240×176×2=84480 | | QVGA | 320 × 240 | 320×240×2=153600 | | CIF | 400 × 296 | 400×296×2=236800 | | HVGA | 480 × 320 | 480×320×2=307200 | | VGA | 640 × 480 | 640×480×2=614400 | | SVGA | 800 × 600 | 800×600×2=960000 | | XGA | 1024 × 768 | 1024×768×2=1572864 | | HD | 1280 × 720 | 1280×720×2=1843200 | | SXGA | 1280 × 1024 | 1280×1024×2=2621440 | | UXGA | 1600 × 1200 | 1600×1200×2=3840000 |

Initially, we were thinking of verifying whether it would be possible to transmit images without using an external storage medium when image quality was improved, but since saving to an SD card is not that difficult, we decided to do that and then conduct an actual transmission test.

12. Save captured images to SD card#

In order to improve image quality, data needs to be saved to an external medium; code needs to be created to allow saving using an SD card and tested to see if it can actually be saved. This was also integrated and tested since it is used in a series of steps such as “camera shot > photo saved to SD card > POST to Web API”.

Save captured images to SD card
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include "esp_camera.h"
#include "config.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <SD.h>

// #define LED_BUILTIN 21  // commented out to avoid redefinition warning
const int limitSwitchPin = 6; // D6 = GPIO6

void setupCamera();
void postImageFromSD(const String &path);
void connectWiFi();

void connectWiFi()
{
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("\nConnected!");
}

void setupCamera()
{
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = 15;
  config.pin_d1 = 17;
  config.pin_d2 = 18;
  config.pin_d3 = 16;
  config.pin_d4 = 14;
  config.pin_d5 = 12;
  config.pin_d6 = 11;
  config.pin_d7 = 48;
  config.pin_xclk = 10;
  config.pin_pclk = 13;
  config.pin_vsync = 38;
  config.pin_href = 47;
  config.pin_sccb_sda = 40;
  config.pin_sccb_scl = 39;
  config.pin_pwdn = -1;
  config.pin_reset = -1;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = FRAMESIZE_QVGA; // High quality (1600x1200)
  config.jpeg_quality = 10;           // High quality JPEG output
  config.fb_count = 1;

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK)
  {
    Serial.printf("Camera init failed: 0x%x\n", err);
    while (true)
      delay(1000);
  }
}

void postImageFromSD(const String &path)
{
  WiFiClientSecure client;
  client.setInsecure();

  Serial.println("Starting POST...");

  if (!client.connect("machinical-memory-landscapes.cloud", 443))
  {
    Serial.println("Connection failed!");
    return;
  }

  File file = SD.open(path.c_str(), FILE_READ);
  if (!file)
  {
    Serial.println("Failed to open file for reading");
    return;
  }

  String boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";
  String contentType = "multipart/form-data; boundary=" + boundary;
  String start = "--" + boundary + "\r\n"
                                   "Content-Disposition: form-data; name=\"image\"; filename=\"upload.jpg\"\r\n"
                                   "Content-Type: image/jpeg\r\n\r\n";
  String end = "\r\n--" + boundary + "--\r\n";

  int contentLength = start.length() + file.size() + end.length();

  client.println("POST /api/upload-image HTTP/1.1");
  client.println("Host: machinical-memory-landscapes.cloud");
  client.println("User-Agent: ESP32");
  client.println("Content-Type: " + contentType);
  client.println("Content-Length: " + String(contentLength));
  client.println("Connection: close");
  client.println();

  Serial.println("Posting image data...");
  client.print(start);
  while (file.available())
  {
    client.write(file.read());
  }
  client.print(end);
  file.close();

  while (client.connected() || client.available())
  {
    String line = client.readStringUntil('\n');
    Serial.println(line);
  }

  client.stop();
  Serial.println("Image POST completed");
}

void setup()
{
  Serial.begin(115200);
  delay(3000);
  Serial.println("Serial monitor initialized. Starting setup...");
  pinMode(limitSwitchPin, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  connectWiFi();
  setupCamera();
  SPI.begin(7, 8, 9, 21); // SCK, MISO, MOSI, CS  // CLK, CMD, D0(仮定ピン)
  if (!SD.begin(21))
  {
    Serial.println("SD_MMC mount failed");
    while (true)
      delay(1000);
  }

  uint8_t cardType = SD.cardType();
  if (cardType == CARD_NONE)
  {
    Serial.println("Error: No SD card attached or card not recognized.");
    while (true)
      delay(1000);
  }
  else
  {
    Serial.println("SD card detected and ready.");
  }
  Serial.print("SD Card Type: ");
  if (cardType == CARD_MMC)
  {
    Serial.println("MMC");
  }
  else if (cardType == CARD_SD)
  {
    Serial.println("SDSC");
  }
  else if (cardType == CARD_SDHC)
  {
    Serial.println("SDHC");
  }
  else
  {
    Serial.println("Unknown");
  }

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.print("SD Card Size: ");
  Serial.print(cardSize);
  Serial.println("MB");

  Serial.println("SD Card initialized.");

  Serial.println("Setup complete");
}

void loop()
{
  // Serial.println("Loop running...");
  int switchState = digitalRead(limitSwitchPin);

  if (switchState == LOW)
  {
    digitalWrite(LED_BUILTIN, LOW);

    camera_fb_t *fb = esp_camera_fb_get();
    if (!fb)
    {
      Serial.println("Capture failed");
      delay(100);
      return;
    }
    Serial.println("Image captured successfully.");

    Serial.println("Saving image to SD card...");
    String filename = "/photo_" + String(millis()) + ".jpg";
    File file = SD.open(filename.c_str(), FILE_WRITE);
    if (!file)
    {
      Serial.println("Failed to open file for writing");
      esp_camera_fb_return(fb);
      return;
    }

    file.write(fb->buf, fb->len);
    file.close();
    Serial.println("Image saved to SD card: " + filename);
    esp_camera_fb_return(fb);

    postImageFromSD(filename);
  }

  delay(200);
}

13. Save camera images to SD card and send those images via Wifi with Webapi#

RAM buffer capacity of the microcontroller, which was one of the constraints for improving image quality, was solved by the introduction of SD cards. However, the POST time increases as the transmission volume increases. It is necessary to test the image quality that can complete transmission in session time.

The results of the experiment are as follows.

Value size transmittable
96×96 18432 OK
QQVGA 38400 OK
QCIF 50688 OK
HQVGA 84480 OK
QVGA 153600 OK
CIF 236800 OK
HVGA 307200 OK
VGA 614400 OK
SVGA 960000 OK
XGA 1572864 OK
SXGA 2621440 NG

送信はできたが送信にかかる時間は長くなってしまうため、システム統合テストの際には”QVGA”で実施した。

14.When the trigger is pressed, the camera takes a picture, saves the camera image to the SD card, and sends the image via Wifi with Webapi.#

Integrate the code so far.

#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include "esp_camera.h"
#include "config.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <SD.h>

// #define LED_BUILTIN 21  // commented out to avoid redefinition warning
const int limitSwitchPin = 8; // D8 = GPIO8

void setupCamera();
void postImageFromSD(const String &path);
void connectWiFi();
void connectWiFi()
{
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("Connected!");

  configTime(0, 0, "pool.ntp.org", "time.nist.gov");
  struct tm timeinfo;
  while (!getLocalTime(&timeinfo))
  {
    Serial.println("Waiting for NTP time...");
    delay(1000);
  }
}

void setupCamera()
{
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = 15;
  config.pin_d1 = 17;
  config.pin_d2 = 18;
  config.pin_d3 = 16;
  config.pin_d4 = 14;
  config.pin_d5 = 12;
  config.pin_d6 = 11;
  config.pin_d7 = 48;
  config.pin_xclk = 10;
  config.pin_pclk = 13;
  config.pin_vsync = 38;
  config.pin_href = 47;
  config.pin_sccb_sda = 40;
  config.pin_sccb_scl = 39;
  config.pin_pwdn = -1;
  config.pin_reset = -1;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  // config.frame_size = FRAMESIZE_XGA; // High quality (1600x1200)
  // config.jpeg_quality = 8;           // High quality JPEG output
  config.frame_size = FRAMESIZE_QVGA; 
  config.jpeg_quality = 10;   

  config.fb_count = 1;

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK)
  {
    Serial.printf("Camera init failed: 0x%x\n", err);
    while (true)
      delay(1000);
  }
}

void postImageFromSD(const String &path)
{
  WiFiClientSecure client;
  client.setInsecure();

  Serial.println("Starting POST...");

  if (!client.connect("machinical-memory-landscapes.cloud", 443))
  {
    Serial.println("Connection failed!");
    return;
  }

  File file = SD.open(path.c_str(), FILE_READ);
  if (!file)
  {
    Serial.println("Failed to open file for reading");
    return;
  }

  String boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";
  String contentType = "multipart/form-data; boundary=" + boundary;
  String start = "--" + boundary + "\r\n"
                                   "Content-Disposition: form-data; name=\"image\"; filename=\"upload.jpg\"\r\n"
                                   "Content-Type: image/jpeg\r\n\r\n";
  String end = "\r\n--" + boundary + "--\r\n";

  int contentLength = start.length() + file.size() + end.length();

  client.println("POST /api/upload-image HTTP/1.1");
  client.println("Host: machinical-memory-landscapes.cloud");
  client.println("User-Agent: ESP32");
  client.println("Content-Type: " + contentType);
  client.println("Content-Length: " + String(contentLength));
  client.println("Connection: close");
  client.println();

  Serial.println("Posting image data...");
  client.print(start);
  while (file.available())
  {
    client.write(file.read());
  }
  client.print(end);
  file.close();

  while (client.connected() || client.available())
  {
    String line = client.readStringUntil('\n');
    Serial.println(line);
  }

  client.stop();
  Serial.println("Image POST completed");
}

void setup()
{
  Serial.begin(115200);
  delay(3000);
  Serial.println("Serial monitor initialized. Starting setup...");
  pinMode(limitSwitchPin, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  connectWiFi();
  setupCamera();
  SPI.begin(7, 8, 9, 21); 
  if (!SD.begin(21))
  {
    Serial.println("SD_MMC mount failed");
    while (true)
      delay(1000);
  }

  uint8_t cardType = SD.cardType();
  if (cardType == CARD_NONE)
  {
    Serial.println("Error: No SD card attached or card not recognized.");
    while (true)
      delay(1000);
  }
  else
  {
    Serial.println("SD card detected and ready.");
  }
  Serial.print("SD Card Type: ");
  if (cardType == CARD_MMC)
  {
    Serial.println("MMC");
  }
  else if (cardType == CARD_SD)
  {
    Serial.println("SDSC");
  }
  else if (cardType == CARD_SDHC)
  {
    Serial.println("SDHC");
  }
  else
  {
    Serial.println("Unknown");
  }

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.print("SD Card Size: ");
  Serial.print(cardSize);
  Serial.println("MB");

  Serial.println("SD Card initialized.");

  Serial.println("Setup complete");
}

void loop()
{

  int switchState = digitalRead(limitSwitchPin);

  if (switchState == LOW)
  {
    digitalWrite(LED_BUILTIN, LOW);

    camera_fb_t *fb = esp_camera_fb_get();
    if (!fb)
    {
      Serial.println("Capture failed");
      delay(100);
      return;
    }
    Serial.println("Image captured successfully.");
    Serial.print("Image size: ");
    Serial.print(fb->len);
    Serial.println(" bytes");

    Serial.println("Saving image to SD card...");
    struct tm now;
    getLocalTime(&now);
    char filename[32];
    strftime(filename, sizeof(filename), "/%Y%m%d%H%M%S.jpg", &now);
    File file = SD.open(filename, FILE_WRITE);
    if (!file)
    {
      Serial.println("Failed to open file for writing");
      esp_camera_fb_return(fb);
      return;
    }

    file.write(fb->buf, fb->len);
    file.close();
    Serial.print("Image saved to SD card: ");
    Serial.println(filename);
    esp_camera_fb_return(fb);

    postImageFromSD(filename);
  }

  delay(200);
}

In the following video, you can see that by pressing the button on the homebrew board, camera shooting is started, the size of the captured image is displayed on the serial monitor, and then posted to karesansui-WebAPI and its response is displayed.