Electronics

ESP32 family reference manuals & datasheets (direct links)

ESP8xxx series

ESP32 series

ESP32S series

ESP32C series

ESP32H series

Source: Espressif document download page

Posted by Uli Köhler in ESP8266/ESP32

LumenPnP 16mm feeder 3D-print STL

Opulo, the company behind LumenPnP, published a FreeCAD file of the LumenPnP 16mm manual strip feeder on Github. However, the file is hard to use as the part is not visible in the default setting. TechOverflow provides a modified FreeCAD file with the only change being that the part is visible, and a STL file directly exported from FreeCAD.

Download the modified 16mm feeder FreeCAD file

Download the 16mm feeder STL file for direct 3D printing

Posted by Uli Köhler in LumenPnP

How to add compiler flags to PlatformIO project

In order to add compiler flags to your PlatformIO project, add the following line to your build config(s) in platformio.ini:

build_flags = -std=gnu++17

You can also use multiple lines:

build_flags =
    -std=gnu++17
    -ffast-math

Full platformio.ini example:

[env:d1_mini]
platform = espressif8266
board = d1_mini
framework = arduino
monitor_speed = 115200
build_flags = -std=gnu++17

 

Posted by Uli Köhler in PlatformIO

How to parse query URLs using ESP-IDF webserver on ESP32

This utility class allows you to easily parse query URLs. In order to use it, you must add -std=gnu++17 or later to your compiler flags.

class QueryURLParser {
public:
    QueryURLParser(httpd_req_t *req) {
        size_t queryURLLen = httpd_req_get_url_query_len(req) + 1;

        if(queryURLLen > 1) {
            // Allocate temporary buffer to store the parameter
            char buf[queryURLLen] = {0};
            if (httpd_req_get_url_query_str(req, buf, queryURLLen) != ESP_OK) {
                ESP_LOGE("Query URL parser", "Failed to extract query URL");
                // Set string to empty string => "not found"
                this->queryURLString = "";
            } else {
                // Copy into a std::string
                this->queryURLString = std::string(buf, queryURLLen);
            }
            delete[] buf;
        }
    }


    std::string GetParameter(const char* key) {
        // Allocate enough space to store the parameter.
        // NOTE: The parameter can only be as long as the query URL itself.
        // Therefore, allocating "too much" space upfront will
        // avoid unneccessary copying
        size_t bufSize = queryURLString.size();
        char* buf = (char*)malloc(bufSize);

        /* Get value of expected key from query string */
        esp_err_t err = httpd_query_key_value(queryURLString.c_str(), key, buf, bufSize);
        if(err != ESP_OK) {
          ESP_LOGE("Query URL parser", "parsing URL");
          Serial.println(esp_err_to_name(err));
          free(buf);
          return "";
        }
        // Convert to std::string
        std::string param(buf);
        free(buf);
        // Shrink param so it fits.
        return param;
    }

private:
    std::string queryURLString;
};

Usage example:

static const httpd_uri_t setMQTTURLHandler = {
    .uri       = "/api/set-mqtt-url",
    .method    = HTTP_GET,
    .handler   = [](httpd_req_t *req) {
        QueryURLParser parser(req);
        std::string url = parser.GetParameter("url");
        if(url.empty()) {
            return SendStatusError(req, "Query parameter 'url' missing");
        }
        // TODO Do something with [url]
        return SendStatusOK(req);
    }
};

SendStatusError() and SendStatusOK() are from our blogpost ESP-IDF webserver: How to respond with JSON success or error message.

Posted by Uli Köhler in C/C++, ESP8266/ESP32

ESP-IDF webserver: How to respond with JSON success or error message

These utility functions allow you to easily respond with a {"status": "ok"} or {"status": "error", "error": "..."}.

Note that the error message is not being escaped but sent as-is, so some error messages containing quotes (") or other special characters might break the JSON.

esp_err_t SendStatusOK(httpd_req_t *request) {
    httpd_resp_set_type(request, "application/json");
    httpd_resp_sendstr(request, "{\"status\":\"ok\"}");
    return ESP_OK;
}

esp_err_t SendStatusError(httpd_req_t *request, const char* description) {
    httpd_resp_set_type(request, "application/json");
    httpd_resp_send_chunk(request, "{\"status\":\"error\", \"error\": \"", HTTPD_RESP_USE_STRLEN);
    // NOTE: We silently assume that description does not have any special characters
    httpd_resp_send_chunk(request, description, HTTPD_RESP_USE_STRLEN);
    httpd_resp_send_chunk(request, "\"}", HTTPD_RESP_USE_STRLEN);
    httpd_resp_send_chunk(request, nullptr, 0); // Finished
    return ESP_OK;
}

 

Posted by Uli Köhler in C/C++, ESP8266/ESP32

Easy-to-use ESP32 IDF webserver C++ wrapper class

#include <esp_http_server.h>

/**
 * HTTP server.
 * Works with ESP-IDF library internally.
 * 
 * Usage:
 * First call http.StartServer()
 * Then call http.RegisterHandler(...) for all handlers
 */
class HTTPServer {
public:
    HTTPServer(): conf(HTTPD_DEFAULT_CONFIG()) {
    }

    void StartServer() {
        if (httpd_start(&server, &conf) != ESP_OK) {
            ESP_LOGE("HTTP server", "Error starting server!");
        }
    }

    void RegisterHandler(const httpd_uri_t *uri_handler) {
        httpd_register_uri_handler(this->server, uri_handler);
    }

    httpd_handle_t server = nullptr;
    httpd_config_t conf;
};

Usage example:

// Declare server globally
HTTPServer http;

// Example handler
static const httpd_uri_t rebootHandler = {
    .uri       = "/api/reboot",
    .method    = HTTP_GET,
    .handler   = [](httpd_req_t *req) {
        SendStatusOK(req);
        delay(20);
        ESP.restart();
        return ESP_OK;
    }
};

void setup() {
    // TODO setup wifi or Ethernet
    http.StartServer();
    http.RegisterHandler(&myHandler);
}

 

Posted by Uli Köhler in C/C++, Embedded, ESP8266/ESP32

MAX31855: How to skip invalid values

Using the MAX31855 as a thermocouple amplifier & digitizer sometimes leads to invalid values, be it due to noise on the analog (thermocouple) side or due to digital signal integrity issues such as GND noise.

This code modified from our post on ESP32 MAX31855 thermocouple LCD minimal example using PlatformIO provides an example of how to skip & ignore those values, but report an error if more than 10 (configurable) consecutive values are invalid.

Depending on your application, you might need to choose a more suitable temperature range in IsValidReading(). Choosing a valid range as narrow as possible will avoid more bad values being sent to downstream code.

SPIClass vspi(VSPI);
Adafruit_MAX31855 thermocouple1(Pin_MAX31855_A_CS, &vspi);

bool IsValidReading(double reading) {
  return !isnan(reading) && reading > -40.0 && reading < 1000.0;
}

void ReadTemperature() {
  double celsius = thermocouple1.readCelsius();
  if(!IsValidReading(celsius)) {
    consecutiveNaNReads++;
    if(consecutiveNaNReads >= maxConsecutiveNaNReads) {
      // Thermocouple is disconnected
      Serial.println("Thermocouple error");
    }
  } else {
    consecutiveNaNReads = 0;
    Serial.println(celsius);
  }
}

 

Posted by Uli Köhler in C/C++, Electronics

ESP32 MAX31855 thermocouple LCD minimal example using PlatformIO

Important: In newer versions of PlatformIO, most ESP32 boards will crash with this example unless you create a custom lv_conf.h config file specifying to use internal memory instead of PSRAM. See How to fix ESP32 LVGL crash / reboot in lv_tlsf_create() / lv_mem_init() for instructions how to fix that.

In our previous example Adafruit ST7735R display minimal LVGL example for PlatformIO we showed how to use LVGL to display a simple text on a ST7735R-based SPI display.

In this post, we’ll extend this example by

See Minimal ESP32 PlatformIO TFT display example using Adafruit ST7735 for the recommended hardware configuration for the display. The code supports two MAX31855s but only one is used for this example. The pinout for the MAX31855 is:

constexpr int8_t Pin_MAX31855_CLK = 21;
constexpr int8_t Pin_MAX31855_MISO = 5;
constexpr int8_t Pin_MAX31855_A_CS = 18;

Using an ESP32 with a 128x160px ST7735R display, this code achieves a ~5Hz update rate. Note that this example can easily be modified to work with other LCD controllers or displays.

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_LvGL_Glue.h>
#include <Adafruit_MAX31855.h>
#include <string>
#include <sstream>

constexpr int8_t Pin_LCD_CS = 27;
constexpr int8_t Pin_LCD_DC = 23;
constexpr int8_t Pin_LCD_RST = 22;
constexpr int8_t Pin_LCD_SCLK = 14;
constexpr int8_t Pin_LCD_MISO = 12;
constexpr int8_t Pin_LCD_MOSI = 13;

constexpr int8_t Pin_MAX31855_CLK = 21;
constexpr int8_t Pin_MAX31855_MISO = 5;
// Two different Chip selects for two different MAX31855
constexpr int8_t Pin_MAX31855_A_CS = 18;
constexpr int8_t Pin_MAX31855_B_CS = 19;

SPIClass vspi(VSPI);
Adafruit_MAX31855 thermocouple1(Pin_MAX31855_A_CS, &vspi);
Adafruit_MAX31855 thermocouple2(Pin_MAX31855_B_CS, &vspi);

Adafruit_ST7735 lcd(Pin_LCD_CS, Pin_LCD_DC, Pin_LCD_MOSI, Pin_LCD_SCLK,
                                 Pin_LCD_RST);
Adafruit_LvGL_Glue glue;



// Number of consecutive NaN reads before we assume the thermocouple is actually disconnected
uint32_t consecutiveNaNReads = 0;
constexpr uint32_t maxConsecutiveNaNReads = 10;
std::string labelText = "";
lv_obj_t *label;


void lvgl_setup(void) {
  // Create simple label centered on screen
  label = lv_label_create(lv_scr_act());
  lv_label_set_text(label, "Initializing..");
  lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}


template<typename T>
std::string celsiusToString(const T a_value, const int decimals = 1)
{
    std::ostringstream out;
    out.precision(decimals);
    out << std::fixed << a_value << " °C";
    return std::move(out).str();
}

bool IsValidReading(double reading) {
  return !isnan(reading) && reading > -40.0 && reading < 1000.0;
}

void ReadTemperature() {
  double celsius = thermocouple1.readCelsius();
  if(!IsValidReading(celsius)) {
    consecutiveNaNReads++;
    if(consecutiveNaNReads >= maxConsecutiveNaNReads) {
      // Thermocouple is disconnected
      lv_label_set_text(label, "Error");
    }
  } else {
    consecutiveNaNReads = 0;
    labelText = "T: " + celsiusToString(celsius);
    lv_label_set_text(label, labelText.c_str());
  }
}

void setup() {
  Serial.begin(115200);
  // Start MAX31855 SPI
  vspi.begin(Pin_MAX31855_CLK, Pin_MAX31855_MISO, Pin_MAX31855_A_CS);

  lcd.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab
  lcd.setRotation(1);

  LvGLStatus status = glue.begin(&lcd);
  if(status != LVGL_OK) {
    Serial.printf("Glue error %d\r\n", (int)status);
    ESP.restart();
  }

  lvgl_setup(); // Call UI-building function above
}

void loop() {
  ReadTemperature();
  lv_task_handler(); // Call LittleVGL task handler periodically
  delay(5);
}
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
    adafruit/Adafruit GFX Library@^1.11.5
    adafruit/Adafruit ST7735 and ST7789 Library@^1.10.0
    adafruit/Adafruit LittlevGL Glue Library@^2.1.4
    adafruit/SdFat - Adafruit Fork@^2.2.1
    lvgl/lvgl@^8.3.7
    adafruit/Adafruit MAX31855 library@^1.4.0

 

 

Posted by Uli Köhler in Arduino, LVGL, PlatformIO

ST7735R LVGL live update chart example for PlatformIO

Important: In newer versions of PlatformIO, most ESP32 boards will crash with this example unless you create a custom lv_conf.h config file specifying to use internal memory instead of PSRAM. See How to fix ESP32 LVGL crash / reboot in lv_tlsf_create() / lv_mem_init() for instructions how to fix that.

In our previous example ST7735R minimal LVGL chart example for PlatformIO

See Minimal ESP32 PlatformIO TFT display example using Adafruit ST7735 for the recommended hardware configuration.

Using an ESP32 with a 128x160px ST7735R display, this code achieves a ~3Hz update rate. Note that this example can easily be modified to work with other LCD controllers or displays.

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_LvGL_Glue.h>
#include <lvgl.h>
#include <string>

constexpr int Pin_LCD_CS = 27;
constexpr int Pin_LCD_DC = 23;
constexpr int Pin_LCD_RST = 22;
constexpr int Pin_LCD_SCLK = 14;
constexpr int Pin_LCD_MISO = 12;
constexpr int Pin_LCD_MOSI = 13;

Adafruit_ST7735 lcd(Pin_LCD_CS, Pin_LCD_DC, Pin_LCD_MOSI, Pin_LCD_SCLK,
                                 Pin_LCD_RST);
Adafruit_LvGL_Glue glue;

lv_obj_t* chart;
lv_chart_series_t * ser1;

void lvgl_chart_setup() {
    /*Create a chart*/
    chart = lv_chart_create(lv_scr_act());
    lv_obj_set_size(chart, 160, 100);
    lv_obj_align(chart, LV_ALIGN_BOTTOM_MID, 0, -3);
    lv_chart_set_type(chart, LV_CHART_TYPE_LINE);   /*Show lines and points too*/

    /*Add two data series*/
    ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);

    /*Set the next points on 'ser1'*/
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 30);
    lv_chart_set_next_value(chart, ser1, 70);
    lv_chart_set_next_value(chart, ser1, 90);

    lv_chart_refresh(chart);
}

void lvgl_setup(void) {
  // Create simple label centered on screen
  lv_obj_t *label = lv_label_create(lv_scr_act());
  lv_label_set_text(label, "Temperature");
  lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 2);

  lvgl_chart_setup();
}

void setup() {
  Serial.begin(115200);
  lcd.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab
  lcd.setRotation(1);

  LvGLStatus status = glue.begin(&lcd);
  if(status != LVGL_OK) {
    Serial.printf("Glue error %d\r\n", (int)status);
    ESP.restart();
  }

  lvgl_setup(); // Call UI-building function above
}

void UpdateChart() {
  // Fill chart data with sine wave data
  float sineOffset = millis() / 1000.0;
  int16_t maxValue = 50;
  size_t count = lv_chart_get_point_count(chart);

  for (size_t i = 0; i < count; i++) {
    lv_chart_set_value_by_id(chart, ser1,  i, maxValue * (1 + sin((sineOffset + i) * 0.7)));
  }

  lv_chart_refresh(chart);
}

void loop() {
  // Update display
  UpdateChart();
  lv_task_handler();
  delay(5);
}
[env:esp32dev]
platform = espressif32 
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
    adafruit/Adafruit GFX Library@^1.11.5
    adafruit/Adafruit ST7735 and ST7789 Library@^1.10.0
    adafruit/Adafruit LittlevGL Glue Library@^2.1.4
    adafruit/SdFat - Adafruit Fork@^2.2.1
    lvgl/lvgl@^8.3.7

 

 

Posted by Uli Köhler in Arduino, LVGL, PlatformIO

ST7735R minimal LVGL chart example for PlatformIO

Important: In newer versions of PlatformIO, most ESP32 boards will crash with this example unless you create a custom lv_conf.h config file specifying to use internal memory instead of PSRAM. See How to fix ESP32 LVGL crash / reboot in lv_tlsf_create() / lv_mem_init() for instructions how to fix that.

This example builds on our previous post Adafruit ST7735R display minimal LVGL example for PlatformIO

See Minimal ESP32 PlatformIO TFT display example using Adafruit ST7735 for the recommended hardware configuration.

Note that this example can easily be modified to work with other LCD controllers or displays.

 

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_LvGL_Glue.h>
#include <lvgl.h>
#include <string>

constexpr int Pin_LCD_CS = 27;
constexpr int Pin_LCD_DC = 23;
constexpr int Pin_LCD_RST = 22;
constexpr int Pin_LCD_SCLK = 14;
constexpr int Pin_LCD_MISO = 12;
constexpr int Pin_LCD_MOSI = 13;

Adafruit_ST7735 lcd(Pin_LCD_CS, Pin_LCD_DC, Pin_LCD_MOSI, Pin_LCD_SCLK,
                                 Pin_LCD_RST);
Adafruit_LvGL_Glue glue;

void lvgl_chart_setup() {
    /*Create a chart*/
    lv_obj_t * chart;
    chart = lv_chart_create(lv_scr_act());
    lv_obj_set_size(chart, 160, 100);
    lv_obj_align(chart, LV_ALIGN_BOTTOM_MID, 0, -3);
    lv_chart_set_type(chart, LV_CHART_TYPE_LINE);   /*Show lines and points too*/

    /*Add two data series*/
    lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
    lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_SECONDARY_Y);

    /*Set the next points on 'ser1'*/
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 10);
    lv_chart_set_next_value(chart, ser1, 30);
    lv_chart_set_next_value(chart, ser1, 70);
    lv_chart_set_next_value(chart, ser1, 90);

    /*Directly set points on 'ser2'*/
    ser2->y_points[0] = 90;
    ser2->y_points[1] = 70;
    ser2->y_points[2] = 65;
    ser2->y_points[3] = 65;
    ser2->y_points[4] = 65;
    ser2->y_points[5] = 65;
    ser2->y_points[6] = 65;
    ser2->y_points[7] = 65;
    ser2->y_points[8] = 65;
    ser2->y_points[9] = 65;

    lv_chart_refresh(chart); /*Required after direct set*/
}

void lvgl_setup(void) {
  // Create simple label centered on screen
  lv_obj_t *label = lv_label_create(lv_scr_act());
  lv_label_set_text(label, "Temperature");
  lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 2);

  lvgl_chart_setup();
}

void setup() {
  lcd.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab
  lcd.setRotation(1);

  LvGLStatus status = glue.begin(&lcd);
  if(status != LVGL_OK) {
    Serial.printf("Glue error %d\r\n", (int)status);
    ESP.restart();
  }

  lvgl_setup(); // Call UI-building function above
}

void loop() {
  lv_task_handler(); // Call LittleVGL task handler periodically
  delay(5);
}

 

[env:esp32dev]
platform = espressif32 
board = esp32dev
framework = arduino
lib_deps =
    adafruit/Adafruit GFX Library@^1.11.5
    adafruit/Adafruit ST7735 and ST7789 Library@^1.10.0
    adafruit/Adafruit LittlevGL Glue Library@^2.1.4
    adafruit/SdFat - Adafruit Fork@^2.2.1
    lvgl/lvgl@^8.3.7

 

 

Posted by Uli Köhler in Arduino, LVGL, PlatformIO

Adafruit ST7735R display minimal LVGL example for PlatformIO

Important: In newer versions of PlatformIO, most ESP32 boards will crash with this example unless you create a custom lv_conf.h config file specifying to use internal memory instead of PSRAM. See How to fix ESP32 LVGL crash / reboot in lv_tlsf_create() / lv_mem_init() for instructions how to fix that.

This example builds on the hardware & software setup outlined in Minimal ESP32 PlatformIO TFT display example using Adafruit ST7735 but uses the LVGL library to provide more advanced rendering possibilities.

See Minimal ESP32 PlatformIO TFT display example using Adafruit ST7735 for the hardware configuration.

Note that it’s just a minimal template to get started with LVGL, besides centered text rendering, no advanced LVGL techniques are being used.

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_LvGL_Glue.h>
#include <string>

constexpr int Pin_LCD_CS = 27;
constexpr int Pin_LCD_DC = 23;
constexpr int Pin_LCD_RST = 22;
constexpr int Pin_LCD_SCLK = 14;
constexpr int Pin_LCD_MISO = 12;
constexpr int Pin_LCD_MOSI = 13;

Adafruit_ST7735 lcd(Pin_LCD_CS, Pin_LCD_DC, Pin_LCD_MOSI, Pin_LCD_SCLK,
                                 Pin_LCD_RST);
Adafruit_LvGL_Glue glue;

void lvgl_setup(void) {
  // Create simple label centered on screen
  lv_obj_t *label = lv_label_create(lv_scr_act());
  lv_label_set_text(label, "Hello LVGL!");
  lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}

void setup() {
  lcd.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab
  lcd.setRotation(1);

  LvGLStatus status = glue.begin(&lcd);
  if(status != LVGL_OK) {
    Serial.printf("Glue error %d\r\n", (int)status);
    ESP.restart();
  }

  lvgl_setup(); // Call UI-building function above
}

void loop() {
  lv_task_handler(); // Call LittleVGL task handler periodically
  delay(5);
}
[env:esp32dev]
platform = espressif32 
board = esp32dev
framework = arduino
lib_deps =
    adafruit/Adafruit GFX Library@^1.11.5
    adafruit/Adafruit ST7735 and ST7789 Library@^1.10.0
    adafruit/Adafruit LittlevGL Glue Library@^2.1.4
    adafruit/SdFat - Adafruit Fork@^2.2.1
    lvgl/lvgl@^8.3.7

 

 

Posted by Uli Köhler in Arduino, LVGL, PlatformIO

Adafruit ST7735R TFT display minimal text example for PlatformIO

This example builds on the hardware & software setup outlined in Minimal ESP32 PlatformIO TFT display example using Adafruit ST7735. See there for the PlatformIO example & hardware setup.

Our code displays a counter on the display that is updated every second. Only the rectangle from the last text draw is cleared, facilitating much faster screen updates than if clearing the entire screen. Also, this technique allows you to flexibly update only part of the screen instead of having to re-draw all other segments on update.

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <string>

constexpr int Pin_LCD_CS = 27;
constexpr int Pin_LCD_DC = 23;
constexpr int Pin_LCD_RST = 22;
constexpr int Pin_LCD_SCLK = 14;
constexpr int Pin_LCD_MISO = 12;
constexpr int Pin_LCD_MOSI = 13;

Adafruit_ST7735 lcd(Pin_LCD_CS, Pin_LCD_DC, Pin_LCD_MOSI, Pin_LCD_SCLK,
                                 Pin_LCD_RST);

uint16_t backgroundColor = ST7735_WHITE;
uint16_t textColor = ST7735_RED;

void setup() {
  lcd.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab
  lcd.enableDisplay(true);        // Enable display
  // Fill screen with background color
  lcd.fillScreen(backgroundColor);
}

uint32_t counter = 0;

/**
 * The rectangle to clear the previous text
 * This is computed
 * 
 * @return int 
 */
struct Rect {
  int16_t x1;
  int16_t y1;
  uint16_t w;
  uint16_t h;
};
Rect lastTextRect;

void loop() {
  // The text we'll draw
  std::string text = "#" + std::to_string(counter);

  // Clear previous text
  if(!lastTextRect.w == 0 || !lastTextRect.h == 0) {
    lcd.fillRect(
      lastTextRect.x1,
      lastTextRect.y1,
      lastTextRect.w,
      lastTextRect.h,
      backgroundColor
    );
  }

  // Set position & parameters
  lcd.setCursor(0, 0);
  lcd.setTextColor(textColor);
  lcd.setTextWrap(true);
  lcd.setTextSize(5);

  // Compute bounding box of text text we'll draw
  // (so we can clear it later)
  lcd.getTextBounds(
    text.c_str(),
    lcd.getCursorX(),
    lcd.getCursorY(),
    &lastTextRect.x1,
    &lastTextRect.y1,
    &lastTextRect.w,
    &lastTextRect.h
  );
  
  lcd.print(text.c_str());
  delay(1000);
  counter++;
}

 

Posted by Uli Köhler in Arduino, PlatformIO

Minimal ESP32 PlatformIO TFT display example using Adafruit ST7735

This example code is for the KMR-1.8 SPI display (128x160px) and provides a minimal example using the Adafruit-ST7735 library that toggles the screen from black to white repeatedly. You can use this as a check if your hardware works correctly.

Hardware connection

Note that you can use any pin number on the ESP32 ; if you use other pins, ensure to change them in the code

  • Connect A0 or DC (whichever one exists on your board) to D23 on the ESP32 board.
  • Connect SCL (which is actually SPI SCK – the pin labeled SCK is just connected to the SD card!) to D14 on the ESP32 board.
  • Connect SDA (which is actually MOSI – the pin labeled MOSI is just connected to the SD card!) to D12 on the ESP32 board.
  • Connect CS to D27 on the ESP32 board.
  • ConnectRESET to D22 on the ESP32 board.
  • Connect LED- to GND on the ESP32 board.
  • Connect VCC to 3V3 on the ESP32 board.
  • Connect LED+ to 3V3 on the ESP32 board.
  • Connect Vcc to 3V3 on the ESP32 board.

Do not connect any pin to VIn or 5V. This can easily damage your display!

PlatformIO source code

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>

constexpr int Pin_LCD_CS = 27;
constexpr int Pin_LCD_DC = 23;
constexpr int Pin_LCD_RST = 22;
constexpr int Pin_LCD_SCLK = 14;
constexpr int Pin_LCD_MISO = 12;
constexpr int Pin_LCD_MOSI = 13;

Adafruit_ST7735 lcd(Pin_LCD_CS, Pin_LCD_DC, Pin_LCD_MOSI, Pin_LCD_SCLK,
                                 Pin_LCD_RST);

void setup() {
  lcd.initR();      // Init ST7735S chip, black tab
  lcd.enableDisplay(true);        // Enable display
}

void loop() {
  delay(500);
  lcd.fillScreen(ST7735_BLACK);
  delay(500);
  lcd.fillScreen(ST7735_WHITE);
}
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
    adafruit/Adafruit GFX Library@^1.11.5
    adafruit/Adafruit ST7735 and ST7789 Library@^1.10.0

 

Posted by Uli Köhler in Arduino, PlatformIO

JLCPCB Which Via plating thickness?

JLCPCB’s standard via plating thickness is 18μm

Source: My support request on 2023-05-23

Posted by Uli Köhler in Electronics

What are the part numbers for JST VH crimp contacts?

The part number for JST VH crimp contacts is SVH-21T-P1.1.

You can source them, for example, at LCSC.

Posted by Uli Köhler in Electronics

How I export KiCAD pick&place CSVs using kicad-cli

Since KiCAD 7.0, you can use the kicad-cli command line tool to export a pick & place position CSV file from a KiCAD project. You can also export an ASCII format, depending on what software you’ll use to process the data.

My standard export command uses the drill origin as a pick&place origin since you can place it anywhere you like:

kicad-cli pcb export pos MyPCB.kicad_pcb --format csv --use-drill-file-origin --units mm -o MyPCB.positions.csv

 

 

Posted by Uli Köhler in KiCAD

How to read KiCAD XML BOM using Python

This script reads an XML BOM (also called python-bom in kicad-cli) using beautifulsoup4 and – as an usage example, generates a RefDes to footprint name dictionary:

from bs4 import BeautifulSoup

def extract_footprints_by_ref_from_xml(filename):
    footprints_by_ref = {} # e.g. "C3" => "Capacitor_SMD:C_0603_1608Metric"
    
    with open(filename, "r") as infile:
        soup = BeautifulSoup(infile, features="xml")
        
    for component in soup.export.components.findChildren("comp"):
        refdes = component.attrs["ref"]
        footprints_by_ref[refdes] = component.footprint.text
    
    return footprints_by_ref

 

Posted by Uli Köhler in KiCAD, Python

How to export KiCAD XML BOM using kicad-cli

kicad-cli sch export python-bom MyBoard.kicad_sch -o BOM.xml

 

Posted by Uli Köhler in KiCAD

How to export KiCAD PCB as PDF using kicad-cli

This command will create a PDF printout of the PCB. The following example prints the front side in front of the back side:

kicad-cli pcb export pdf MyPCB.kicad_pcb -l "F.Cu,F.Mask,F.Silkscreen,F.Courtyard,Edge.Cuts,B.Cu,B.Mask,B.Silkscreen,B.Courtyard" -o MyPCB.front.pdf

This is the corresponding command to print just the back side, so that it isn’t occluded by the front side.

kicad-cli pcb export pdf MyPCB.kicad_pcb -l "B.Cu,B.Mask,B.Silkscreen,B.Courtyard,Edge.Cuts" -o MyPCB.back.pdf

 

Posted by Uli Köhler in KiCAD