Adsense HTML/JavaScript

Friday, October 29, 2021

arduino-esp32 CameraWebServer example, run on ESP32-S2-Saola-1 with ov2640

This post show how to run arduino-esp32 CameraWebServer example on ESP32-S2-Saola-1 with ov2640 camera.


For convenience, I connect ov2640 pins all in one side of ESP32-S2-Saola-1:

Connection between ESP32-S2-Saola-1 and ov2640

	ov2640
	+-----------+
3V3	|3V3	GND |	GND
IO9	|SIOC	SIOD|	IO1
IO10	|VSYNC	HREF|	IO2
IO11	|PCLK	XCLK|	IO3
IO12	|D9	D8  |	IO4
IO13	|D7	D6  |	IO5
IO14	|D5	D4  |	IO6
IO15	|D3	D2  |	IO7
IO16	|RESET	PWDN|	-
-	|D1	D0  |	-
	+-----------+
		
ESP32-S2-Saola-1
------+
 GND  |	- cam.GND
 5V   |
 IO17 |
 IO16 | - cam.RESET
 IO15 |	- cam.D3
 IO14 |	- cam.D5
 IO13 |	- cam.D7
 IO12 |	- cam.D9
 IO11 |	- cam.PCLK
 IO10 |	- cam.VSYNC
 IO9  |	- cam.SIOC
 IO8  |
 IO7  |	- cam.D2
 IO6  |	- cam.D4
 IO5  |	- cam.D6
 IO4  |	- cam.D8
 IO3  |	- cam.XCLK
 IO2  | - cam.HREF
 IO1  |	- cam.SIOD
 IO0  |
 3V3  |	- cam.3V3
------+
 
*cam.SIOC/SIOD are I2C-like control pins, pull-up resistors are needed.
 I use 2K ohm resistor for it.

To program ESP32-S2 on Arduino IDE, arduino-esp32 2.0.0 is needed. Select board of "ESP32S2 Dev Module".

Load CameraServer example:
Click on Menu : File > Examples > ESP32 > Camera > CameraWebServer

Edit camera_pins.h, update pins definition to match connection:

// for ESP32-S2-Saola-1/ov2640
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   16
#define XCLK_GPIO_NUM    3
#define SIOD_GPIO_NUM    1
#define SIOC_GPIO_NUM    9

#define Y9_GPIO_NUM      12
#define Y8_GPIO_NUM      4
#define Y7_GPIO_NUM      13
#define Y6_GPIO_NUM      5
#define Y5_GPIO_NUM      14
#define Y4_GPIO_NUM      6
#define Y3_GPIO_NUM      15
#define Y2_GPIO_NUM      7
#define VSYNC_GPIO_NUM   10
#define HREF_GPIO_NUM    2
#define PCLK_GPIO_NUM    11


Update ssid/password in your main code for your WiFi network.


Upload to board, if fail with error of "frame buffer malloc failed", Select PSRAM option to "Enabled".


If success, you can open browser (in the same WiFi network) to visit the CameraServer.







Tuesday, October 26, 2021

arduino-esp32, drive SSD1306 I2C OLED with ESP32/C3/S2 using esp8266-oled-ssd1306 library


With arduino-esp32 2.0.0 installed on Arduino IDE, this post run examples on ESP32-DevKitC V4/ESP32-S2-Saola-1/ESP32-C3-DevKitM-1 to drive SSD1306 I2C OLED, using esp8266-oled-ssd1306 library.


Open Library Manager in Arduino IDE, install "ESP8266 and ESP32 OLED driver for SSD1306 displays" by ThingPulse, currently 4.2.1. (esp8266-oled-ssd1306)


In the library examples, OLED display is initialized using pre-defined SDA and SCL based on your board's pins_arduino.h.
SSD1306Wire display(0x3c, SDA, SCL);   // ADDRESS, SDA, SCL


for ESP32 Dev Module:
SDA:  21
SCL:  22

for ESP32S2 Dev Module:
SDA:  8
SCL:  9

for ESP32C2 Dev Module:
SDA:  8
SCL:  9

* But on ESP32-C3-DevKitM-1 I used to test for ESP32C2 Dev Module, GPIO8 is connected to onboardRGB LED. (refer to the post "Drive ESP32-C3-DevKitM-1/ESP32-S2-Saola-1 on-board RGB LED (WS2812) in Arduino Framework") So I re-allocate to:
SDA:  19
SCL:  18

same GPIOs assigned in "ESP32-C3/MicroPython + SSD1306 I2C OLED".
SSD1306Wire display(0x3c, 19, 18);   // ADDRESS, SDA, SCL

Also connect ESP32 dev board's 3V3 and GND to SSD1306 VCC and GND.

Monday, October 25, 2021

arduino-esp32, list the pre-defined function pins of ESP32C3/S2 Dev Module

With arduino-esp32 2.0.0 installed on Arduino IDE, the following exercise run on ESP32-C3-DevKitM-1/ESP32-S2-Saola-1 to list the pre-defined function pins of ESP32C3/S2 Dev Module.

This video also show how to locate pins_arduino.h where it defined.


Test on ESP32-C3-DevKitM-1, ESP32-S2-Saola-1 and ESP32-DevKitC V4.

ESP32C3_pins.ino for board of ESP32C3 Dev Module, run on ESP32-C3-DevKitM-1
void setup() {
  // put your setup code here, to run once:
  delay(500);
  Serial.begin(115200);
  delay(500);
  Serial.println("\n\n================================");
  Serial.printf("Chip Model: %s\n", ESP.getChipModel());
  Serial.println("================================");

#ifdef EXTERNAL_NUM_INTERRUPTS
  Serial.printf("EXTERNAL_NUM_INTERRUPTS = %d\n", EXTERNAL_NUM_INTERRUPTS);
#endif
#ifdef NUM_DIGITAL_PINS
  Serial.printf("NUM_DIGITAL_PINS = %d\n", NUM_DIGITAL_PINS);
#endif
#ifdef NUM_ANALOG_INPUTS
  Serial.printf("NUM_ANALOG_INPUTS = %d\n", NUM_ANALOG_INPUTS);
#endif
  Serial.println();
  Serial.printf("Default TX:   %d\n", TX);
  Serial.printf("Default RX:   %d\n", RX);
  Serial.println();
  Serial.printf("Default SDA:  %d\n", SDA);
  Serial.printf("Default SCL:  %d\n", SCL);
  Serial.println();
  Serial.printf("Default SS:   %d\n", SS);
  Serial.printf("Default MOSI: %d\n", MOSI);
  Serial.printf("Default MISO: %d\n", MISO);
  Serial.printf("Default SCK:  %d\n", SCK);
  Serial.println();
  Serial.printf("Default A0:   %d\n", A0);
  Serial.printf("Default A1:   %d\n", A1);
  Serial.printf("Default A2:   %d\n", A2);
  Serial.printf("Default A3:   %d\n", A3);
  Serial.printf("Default A4:   %d\n", A4);
  Serial.printf("Default A5:   %d\n", A5);
  Serial.println("================================");
}

void loop() {
  // put your main code here, to run repeatedly:

}


output:
================================
Chip Model: ESP32-C3
================================
EXTERNAL_NUM_INTERRUPTS = 22
NUM_DIGITAL_PINS = 22
NUM_ANALOG_INPUTS = 6

Default TX:   21
Default RX:   20

Default SDA:  8
Default SCL:  9

Default SS:   7
Default MOSI: 6
Default MISO: 5
Default SCK:  4

Default A0:   0
Default A1:   1
Default A2:   2
Default A3:   3
Default A4:   4
Default A5:   5
================================

ESP32-C3-DevKitM-1 Pin Layout


ESP32S2_pins.ino for board of ESP32S2 Dev Module, run on ESP32-S2-Saola-1
void setup() {
  // put your setup code here, to run once:
  delay(500);
  Serial.begin(115200);
  delay(500);
  Serial.println("\n\n================================");
  Serial.printf("Chip Model: %s\n", ESP.getChipModel());
  Serial.println("================================");

#ifdef EXTERNAL_NUM_INTERRUPTS
  Serial.printf("EXTERNAL_NUM_INTERRUPTS = %d\n", EXTERNAL_NUM_INTERRUPTS);
#endif
#ifdef NUM_DIGITAL_PINS
  Serial.printf("NUM_DIGITAL_PINS = %d\n", NUM_DIGITAL_PINS);
#endif
#ifdef NUM_ANALOG_INPUTS
  Serial.printf("NUM_ANALOG_INPUTS = %d\n", NUM_ANALOG_INPUTS);
#endif
  Serial.println();
  Serial.printf("Default TX:   %d\n", TX);
  Serial.printf("Default RX:   %d\n", RX);
  Serial.println();
  Serial.printf("Default SDA:  %d\n", SDA);
  Serial.printf("Default SCL:  %d\n", SCL);
  Serial.println();
  Serial.printf("Default SS:   %d\n", SS);
  Serial.printf("Default MOSI: %d\n", MOSI);
  Serial.printf("Default MISO: %d\n", MISO);
  Serial.printf("Default SCK:  %d\n", SCK);

  Serial.println();
  Serial.printf("A0\tA1\tA2\tA3\tA4\tA5\tA6\tA7\tA8\tA9\n");
  Serial.printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", 
                 A0, A1, A2, A3, A4, A5, A6, A7, A8, A9);
  Serial.println();
  Serial.printf("A10\tA11\tA12\tA13\tA14\tA15\tA16\tA17\tA18\tA19\n");
  Serial.printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", 
                 A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
  Serial.println();

  Serial.printf("T1\tT2\tT3\tT4\tT5\tT6\tT7\tT8\tT9\tT10\n");
  Serial.printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", 
                 T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
  Serial.println();
  Serial.printf("T11\tT12\tT13\tT14\n");
  Serial.printf("%d\t%d\t%d\t%d\n", 
                 T11, T12, T13, T14);
  Serial.println();
  Serial.printf("Default DAC1:  %d\n", DAC1);
  Serial.printf("Default DAC2:  %d\n", DAC2);
  Serial.println("================================");
}

void loop() {
  // put your main code here, to run repeatedly:

}


output:
================================
Chip Model: ESP32-S2
================================
EXTERNAL_NUM_INTERRUPTS = 46
NUM_DIGITAL_PINS = 48
NUM_ANALOG_INPUTS = 20

Default TX:   43
Default RX:   44

Default SDA:  8
Default SCL:  9

Default SS:   34
Default MOSI: 35
Default MISO: 37
Default SCK:  36

A0	A1	A2	A3	A4	A5	A6	A7	A8	A9
1	2	3	4	5	6	7	8	9	10

A10	A11	A12	A13	A14	A15	A16	A17	A18	A19
11	12	13	14	15	16	17	18	19	20

T1	T2	T3	T4	T5	T6	T7	T8	T9	T10
1	2	3	4	5	6	7	8	9	10

T11	T12	T13	T14
11	12	13	14

Default DAC1:  17
Default DAC2:  18
================================


ESP32-S2-Saola-1 Pin Layout


ESP32_pins.ino for board of ESP32 Dev Module, run on ESP32-DevKitC V4
void setup() {
  // put your setup code here, to run once:
  delay(500);
  Serial.begin(115200);
  delay(500);
  Serial.println("\n\n================================");
  Serial.printf("Chip Model: %s\n", ESP.getChipModel());
  Serial.println("================================");

#ifdef EXTERNAL_NUM_INTERRUPTS
  Serial.printf("EXTERNAL_NUM_INTERRUPTS = %d\n", EXTERNAL_NUM_INTERRUPTS);
#endif
#ifdef NUM_DIGITAL_PINS
  Serial.printf("NUM_DIGITAL_PINS = %d\n", NUM_DIGITAL_PINS);
#endif
#ifdef NUM_ANALOG_INPUTS
  Serial.printf("NUM_ANALOG_INPUTS = %d\n", NUM_ANALOG_INPUTS);
#endif
  Serial.println();
  Serial.printf("Default TX:   %d\n", TX);
  Serial.printf("Default RX:   %d\n", RX);
  Serial.println();
  Serial.printf("Default SDA:  %d\n", SDA);
  Serial.printf("Default SCL:  %d\n", SCL);
  Serial.println();
  Serial.printf("Default SS:   %d\n", SS);
  Serial.printf("Default MOSI: %d\n", MOSI);
  Serial.printf("Default MISO: %d\n", MISO);
  Serial.printf("Default SCK:  %d\n", SCK);

  Serial.println();
  Serial.printf("A0\t\t\tA3\tA4\tA5\tA6\tA7\n");
  Serial.printf("%d\t\t\t%d\t%d\t%d\t%d\t%d\n", 
                 A0, A3, A4, A5, A6, A7);
  Serial.println();
  Serial.printf("A10\tA11\tA12\tA13\tA14\tA15\tA16\tA17\tA18\tA19\n");
  Serial.printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", 
                 A10, A11, A12, A13, A14, A15, A16, A17, A18, A19);
  Serial.println();

  Serial.printf("T0\tT1\tT2\tT3\tT4\tT5\tT6\tT7\tT8\tT9\n");
  Serial.printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", 
                 T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
  Serial.println();

  Serial.printf("Default DAC1:  %d\n", DAC1);
  Serial.printf("Default DAC2:  %d\n", DAC2);
  Serial.println("================================");
}

void loop() {
  // put your main code here, to run repeatedly:

}

output:
================================
Chip Model: ESP32-D0WDQ5
================================
EXTERNAL_NUM_INTERRUPTS = 16
NUM_DIGITAL_PINS = 40
NUM_ANALOG_INPUTS = 16

Default TX:   1
Default RX:   3

Default SDA:  21
Default SCL:  22

Default SS:   5
Default MOSI: 23
Default MISO: 19
Default SCK:  18

A0			A3	A4	A5	A6	A7
36			39	32	33	34	35

A10	A11	A12	A13	A14	A15	A16	A17	A18	A19
4	0	2	15	13	12	14	27	25	26

T0	T1	T2	T3	T4	T5	T6	T7	T8	T9
4	0	2	15	13	12	14	27	33	32

Default DAC1:  25
Default DAC2:  26
================================


ESP32-DevKitC V4 Pin Layout
source: ESP32-DevKitC V4 Getting Started Guide :Pin Layout

Remark:
Please notice that GPIO8 on ESP32-C3-DevKitM-1 and GPIO18 on ESP32-S2-Saola-1 are connected to onboard RGB LED. Ref last post "Drive ESP32-C3-DevKitM-1/ESP32-S2-Saola-1 on-board RGB LED (WS2812) in Arduino Framework".

Sunday, October 24, 2021

Drive ESP32-C3-DevKitM-1/ESP32-S2-Saola-1 on-board RGB LED (WS2812) in Arduino Framework, with Freenove WS2812 Lib for ESP32.


 According to Espressif documentation:

There is a on-board RGB LED (WS2812) on ESP32-C3-DevKitM-1, driven by GPIO8.
ref: https://docs.espressif.com/projects/esp-idf/en/v4.3.1/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html

On ESP32-S2-Saola-1, the on-board RGB LED (WS2812) is driven by GPIO18.
ref: https://docs.espressif.com/projects/esp-idf/en/v4.3.1/esp32s2/hw-reference/esp32s2/user-guide-saola-1-v1.2.html

"Freenove WS2812 Lib for ESP32" is an Arduino library for WS2812 led on ESP32. To use this library, open the Library Manager in the Arduino IDE and install it from there.


Thursday, October 21, 2021

Install arduino-esp32 2 on Arduino IDE, to program ESP32-C3/S2/S3


Currently, with development release arduino-esp32 2.0.0 installed, you can program ESP32-S2/C3 using Arduino IDE.
 

Install arduino-esp32 2.0.0:
- In Arduino IDE Menu, click File > Preferences

Add ESP32 boards:
- Open Boards Manager
- Search and install esp32 by Espressif system, currently version 2.0.0.

ESP32C3 and ESP32S2 Dev Module are now available in ESP32 Arduino board list.

ESP32C3_info.ino, get chip related info.
#include <Esp.h>

void setup() {
  delay(500);
  Serial.begin(115200);
  delay(500);
  Serial.println("\n\n================================");
  Serial.printf("Chip Model: %s\n", ESP.getChipModel());
  Serial.printf("Chip Revision: %d\n", ESP.getChipRevision());
  Serial.printf("with %d core\n", ESP.getChipCores());
  Serial.printf("Flash Chip Size : %d \n", ESP.getFlashChipSize());
  Serial.printf("Flash Chip Speed : %d \n", ESP.getFlashChipSpeed());

  esp_chip_info_t chip_info;
  esp_chip_info(&chip_info);
  Serial.printf("\nFeatures included:\n %s\n %s\n %s\n %s\n %s\n",
      (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded flash" : "",
      (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "2.4GHz WiFi" : "",
      (chip_info.features & CHIP_FEATURE_BLE) ? "Bluetooth LE" : "",
      (chip_info.features & CHIP_FEATURE_BT) ? "Bluetooth Classic" : "",
      (chip_info.features & CHIP_FEATURE_IEEE802154) ? "IEEE 802.15.4" : "");
  
  Serial.println();

  Serial.println();
  Serial.println("\n- end of setup() -");

}

void loop() {
  // put your main code here, to run repeatedly:

}



Updated@2022-02-19:

Currently, ESP32 Arduino 2.0.2 is stable. So you can enter Stable release link (https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json) into Additional Board Manager URLs field, instead of Development release link (https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json).







ref:
~ ESP32 Arduino Core’s documentation


Updated@2022-05-12

With arduino-esp32 updated 2.0.3, ESP32-S3 support added.



ESP32 Arduino 2.0.3 release notice


Wednesday, October 20, 2021

ESP32-C3/MicroPython + SSD1306 I2C OLED

With MicroPython firmware installed on ESP32-C3, It's a exercise to drive 0.96 inch 128x64 OLED with SSD1306 I2C driver.

To driver SSD1306 I2C OLED, we are going to install MicroPython ssd1306 driver:
- Visit https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py
- Copy ssd1306.py and upload to MicroPython device, '/' directory.

In my exercise, the default I2C(0) is used to drive SSD1306; SCL = 18, SDA = 19.

Connection between:
ESP32-C3    I2C SSD1306 OLED
============================
GND	    GND
3V3	    VCC
18	    SCL
19	    SDA

Exercise code

mpyESP32C3_ssd1306.py, a simplest example. Also show how to catch OSError of ENODEV if no I2C ssd1306 connected.
"""
MicroPython/ESP32C3 exercise run on ESP32-C3-DevKitM-1,
work with SSD1306 I2C OLED.
And,catch exception of ENODEV.

"""
import uos
import usys
import machine
import time
import ssd1306

print("====================================")
print(usys.implementation[0], uos.uname()[3],
      "\nrun on", uos.uname()[4])
print("====================================")

oled_i2c = machine.I2C(0)
print("Default I2C:", oled_i2c)
"""
oled_ssd1306 = ssd1306.SSD1306_I2C(128, 64, oled_i2c)
print("Default SSD1306 I2C address:",
          oled_ssd1306.addr, "/", hex(oled_ssd1306.addr))
oled_ssd1306.text('Hello, World!', 0, 0, 1)
oled_ssd1306.show()

"""
try:
    oled_ssd1306 = ssd1306.SSD1306_I2C(128, 64, oled_i2c)
    print("Default SSD1306 I2C address:",
          oled_ssd1306.addr, "/", hex(oled_ssd1306.addr))
    oled_ssd1306.text('Hello, World!', 0, 0, 1)
    oled_ssd1306.show()
except OSError as exc:
    print("OSError!", exc)
    if exc.errno == errno.ENODEV:
        print("No such device")

print("~ bye ~")

mpyESP32C3_ssd1306_hello.py
"""
MicroPython/ESP32C3 exercise run on ESP32-C3-DevKitM-1,
work with SSD1306 I2C OLED.
HelloWorld! with something.

"""
import uos
import usys
import machine
import time
import ssd1306

print("====================================")
print(usys.implementation[0], uos.uname()[3],
      "\nrun on", uos.uname()[4])
print("====================================")

oled_i2c = machine.I2C(0)
print("Default I2C:", oled_i2c)

oled_ssd1306 = ssd1306.SSD1306_I2C(128, 64, oled_i2c)
print("Default SSD1306 I2C address:",
      oled_ssd1306.addr, "/", hex(oled_ssd1306.addr))

oled_ssd1306.fill(1)    #all on
oled_ssd1306.show()
time.sleep(1)
oled_ssd1306.fill(0)    #all off
oled_ssd1306.show()
time.sleep(1)

def splitLine(src, cnt):
    return [src[i:i+cnt] for i in range(0, len(src), cnt)]

oled_ssd1306.text('Hello, World!', 0, 0, 1)
oled_ssd1306.text(str(usys.implementation[0]), 0, 20, 1)
oled_ssd1306.text(str(uos.uname()[2]), 0, 30, 1)

line_y = 40
for l in splitLine(str(uos.uname()[4]), 15):
    oled_ssd1306.text(l, 0, line_y, 1)
    line_y += 10

oled_ssd1306.show()
time.sleep(1)

oled_ssd1306.invert(1)
oled_ssd1306.show()
time.sleep(0.5)
oled_ssd1306.invert(0)
oled_ssd1306.show()
time.sleep(0.5)

oled_ssd1306.rect(15, 15, oled_ssd1306.width-30,
                  oled_ssd1306.height-30, 1)
oled_ssd1306.show()
time.sleep(1)

for x in range(oled_ssd1306.width):
    oled_ssd1306.scroll(1, 0)
    oled_ssd1306.show()
    time.sleep_ms(50)

oled_ssd1306.fill_rect(15, 15, oled_ssd1306.width-30,
                  oled_ssd1306.height-30, 1)
oled_ssd1306.show()
time.sleep(0.5)
oled_ssd1306.text('~ bye ~', 55, 55, 1)
oled_ssd1306.show()
time.sleep(0.5)

print("~ bye ~")

mpyESP32C3_ssd1306_pixel.py
"""
MicroPython/ESP32C3 exercise run on ESP32-C3-DevKitM-1,
work with SSD1306 I2C OLED.

Draw randow pixels

"""
import uos
import usys
import machine
import time
import urandom
import ssd1306

print("====================================")
print(usys.implementation[0], uos.uname()[3],
      "\nrun on", uos.uname()[4])
print("====================================")

oled_i2c = machine.I2C(0)
print("Default I2C:", oled_i2c)

oled_ssd1306 = ssd1306.SSD1306_I2C(128, 64, oled_i2c)
print("Default SSD1306 I2C address:",
          oled_ssd1306.addr, "/", hex(oled_ssd1306.addr))

def drawRandomPixels():
    for i in range(500):
        rx = urandom.randint(0, oled_ssd1306.width)
        ry = urandom.randint(0, oled_ssd1306.height)
        oled_ssd1306.pixel(rx, ry, 1)
        oled_ssd1306.show()
    
oled_ssd1306.fill(0)
oled_ssd1306.text('Random Pixels', 0, 0, 1)
oled_ssd1306.show()
drawRandomPixels()

oled_ssd1306.fill(0)
oled_ssd1306.text('Random Pixels', 0, 0, 1)
oled_ssd1306.invert(1)
oled_ssd1306.show()
drawRandomPixels()

print("~ bye ~")

mpyESP32C3_ssd1306_text.py
"""
MicroPython/ESP32C3 exercise run on ESP32-C3-DevKitM-1,
work with SSD1306 I2C OLED.

Display scrolling text (read from /boot.py) on OLED.

"""
import uos
import usys
import machine
import time
import ssd1306

# Read and display /boot.py file
def readBoot():
    print("========================")
    fileBoot = "/boot.py"
    print(fileBoot)
    print("========================")
    with open(fileBoot, "r") as f:
        line = f.readline()
        while line != '':
            line = line.rstrip() #strip trailing whitespace
            print(line)
            oled_ssd1306.scroll(0, -10)
            oled_ssd1306.text(line, 0, 46, 1)
            oled_ssd1306.show()
            time.sleep(0.5)
            line = f.readline()
    print("========================")

print("====================================")
print(usys.implementation[0], uos.uname()[3],
      "\nrun on", uos.uname()[4])
print("====================================")

oled_i2c = machine.I2C(0)
print("Default I2C:", oled_i2c)

oled_ssd1306 = ssd1306.SSD1306_I2C(128, 64, oled_i2c)
print("Default SSD1306 I2C address:",
          oled_ssd1306.addr, "/", hex(oled_ssd1306.addr))
oled_ssd1306.text('Hello, World!', 0, 0, 1)
oled_ssd1306.show()

readBoot()

print("~ bye ~")

Monday, October 18, 2021

Flash MicroPython firmware on ESP32-C3

Updated@2022-06-19

~ Flash MicroPython v1.19 firmware on ESP32-C3 (ESP32-C3-DevKitM-1/NodeMCU ESP-C3-32S-Kit)

It is aimed to flash MicroPython firmware on ESP32-C3-DevKitM-1 with unknown ESP32-C3 (revision 3) and 4MB flash. I can't find any official installation instruction, it's found out by my guessing and trying. Not sure is it correct approach, it seem work for me anyway.

Download MicroPython for ESP32-C3:

Visit https://micropython.org/download/all/, search "esp32c3" firmware for ESP32-C3, download the .bin file. It's esp32c3usb-20211018-unstable-v1.17-84-gba940250a.bin I tried.

Identify port and chip/flash:

To identify the ESP32 device connected USB port, chip and flash, refer to the last post.

It's unknown ESP32-C3 (revision 3) and 4MB flash on  ESP32-C3-DevKitM-1, connected to /dev/ttyUSB0.

Flash MicroPython firmware:

Erase flash, enter the command:

$ esptool.py --port /dev/ttyUSB0 erase_flash
Flash firmware with:
$ esptool.py --chip esp32c3 -p /dev/ttyUSB0 -b 460800 \
--before=default_reset --after=hard_reset write_flash --flash_mode dio \
--flash_freq 80m --flash_size 4MB \
0x0 esp32c3usb-20211018-unstable-v1.17-84-gba940250a.bin
* write firmware to on ESP flash from 0x0

Wrote 1495696 bytes (888871 compressed) at 0x00000000 in 26.6 seconds (effective 449.2 kbit/s)...

ref:

To verify that data in flash matches a local file:

$ esptool.py verify_flash --diff yes 0x0 <.bin>

ref:


MicroPython code tried:

mpyESP32C3_info.py
"""
MicroPython/ESP32C3 exercise run on ESP32-C3-DevKitM-1,
to display info.
"""
import uos
import usys
import machine
import esp

print("from uos.uname():")
for u in uos.uname():
    print(u)
print()

print("from usys:")
print("usys.platform: ", usys.platform)
print("usys.implementation: ", usys.implementation)
print()

print("====================================")
print(usys.implementation[0], uos.uname()[3],
      "\nrun on", uos.uname()[4])
print("====================================")
print("Flash size:", esp.flash_size())
print("CPU frequency:", machine.freq(), "(Hz)")
mpyESP32C3_RGB.py
"""
MicroPython/ESP32C3 exercise run on ESP32-C3-DevKitM-1,
to control the onboard GB LED (WS2812), driven by GPIO8.
"""

import uos
import usys
import machine
import neopixel
import time

print("====================================")
print(usys.implementation[0], uos.uname()[3],
      "\nrun on", uos.uname()[4])
print("====================================")

np = neopixel.NeoPixel(machine.Pin(8), 1)

np[0] = (0, 0, 0)
np.write()
time.sleep(1)
np[0] = (100, 100, 100)
np.write()
time.sleep(1)
np[0] = (0, 0, 0)
np.write()
time.sleep(1)

for i in range(256):
    np[0] = (i, 0, 0)
    np.write()
    time.sleep_ms(10)
for i in range(256):
    np[0] = (0, i, 0)
    np.write()
    time.sleep_ms(10)
for i in range(256):
    np[0] = (0, 0, i)
    np.write()
    time.sleep_ms(10)

for i in range(256):
    np[0] = (255-i, 255-i, 255-i)
    np.write()
    time.sleep_ms(10)



It's another form of the flash command, modified from  Getting started with MicroPython on the ESP32 > Deploying the firmware, change starting address to 0x0.

$ esptool.py --chip esp32c3 --port /dev/ttyUSB0 write_flash -z \
0x0 esp32c3usb-20211018-unstable-v1.17-84-gba940250a.bin
but it take longer time.

Wrote 1495696 bytes (888871 compressed) at 0x00000000 in 79.6 seconds (effective 150.4 kbit/s)...




More ESP32-C3/MicroPython exercises:

Sunday, October 17, 2021

Identify ESP chip and flash using esptool

Here to identify ESP chip and flash using esptool.py

First, you have to identify the USB port connect to ESP. In Linux/Raspberry Pi

- Before connect your ESP device to USB, run following command to clear dmesg buffer:

$ sudo dmesg -c

- Connect your ESP32 device to USB, and then run dmesg:

$ dmesg

-  You will find some like "cp210x converter now attached to ttyUSB0". Where /dev/ttyUSB0 is the USB port connected to ESP device.

TO Read Chip ID, enter the command:

$ esptool.py --chip auto --port /dev/ttyUSB0 chip_id
TO read SPI flash manufacturer and device ID, enter the command:
$ esptool.py --chip auto --port /dev/ttyUSB0 flash_id
Here is the output for ESP32-DevKitC V4 with ESP32-D0WD-V3 (revision 3) chip and 8MB flash.


ESP8266 with ESP8266EX chip and 4MB flash.


ESP32-S2-Saola-1 with ESP32-S2 chip and 4MB flash.


ESP32-C3-DevKitM-1 with unknown ESP32-C3 (revision 3) and 4MB flash.






STM32F411/CircuitPython 7, with 2" 240x320 SPI ST7789 IPS LCD

To use ST7789 SPI LCD on STM32F411/CircuitPython 7.0.0, basically same as shown in the post: "CircuitPython 7 exercises on STM32F411, with SPI ST7735 LCD", except using library of adafruit_st7789 instead of using adafruit_st7735/adafruit_st7735r.


cpyF411_st7789_240x320.py
"""
CircuitPython 7 exercise run on STM32F411
with 2" 240x320 SPI ST7789 IPS

"""

from sys import implementation as sysImplementation
import time
import board
import busio
import displayio
import terminalio

from adafruit_st7789 import ST7789 as TFT_ST77xx
from adafruit_st7789 import __name__ as ST77xx_NAME
from adafruit_st7789 import __version__ as ST77xx_VERSION

from adafruit_display_text import label

# Release any resources currently in use for the displays
displayio.release_displays()

#Connection between STM32F411 and SPI ST7789 display
#marking on display     
tft_sck = board.B13     #SCL
tft_mosi = board.A1     #SDA


tft_dc = board.A2       #DC
tft_reset = board.A3    #RES
tft_cs = board.A4       #CS
#Backlight (LED/BLK) connect to STM32F411 3V3
#TFT VCC - STM32F411 3V3
#TFT GND - STM32F411 GND

tft_spi = busio.SPI(clock=tft_sck, MOSI=tft_mosi)
display_bus = displayio.FourWire(
    tft_spi, command=tft_dc, chip_select=tft_cs, reset=tft_reset
)

# I find out colrstart/rowstart by try/error and retry
display = TFT_ST77xx(display_bus, width=320, height=240,
                     colstart=0, rowstart=0,
                     rotation=90,
                     )

print(type(display))
print("display.width:  ", display.width)
print("display.height: ", display.height)

# Make the display context
splash = displayio.Group()
display.show(splash)

color_bitmap = displayio.Bitmap(display.width, display.height, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x000000
time.sleep(1)

bg_sprite = displayio.TileGrid(color_bitmap,
                               pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)

for c in [["RED", 0xFF0000],
          ["GREEN", 0x00FF00],
          ["BLUE", 0x0000FF]]:
    print(c[0], " : ", hex(c[1]))
    color_palette[0] = c[1]
    time.sleep(2)

splash.remove(bg_sprite)
#---

# Make the display context
#splash = displayio.Group()
#display.show(splash)

color_bitmap = displayio.Bitmap(display.width, display.height, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x00FF00

bg_sprite = displayio.TileGrid(color_bitmap,
                               pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)

# Draw a smaller inner rectangle
inner_bitmap = displayio.Bitmap(display.width-2, display.height-2, 1)
inner_palette = displayio.Palette(1)
inner_palette[0] = 0x0000FF
inner_sprite = displayio.TileGrid(inner_bitmap,
                                  pixel_shader=inner_palette, x=1, y=1)
splash.append(inner_sprite)

# Draw a label
text_group1 = displayio.Group(scale=1, x=5, y=10)
text1 = "STM32F411"
text_area1 = label.Label(terminalio.FONT, text=text1, color=0xFF0000)
text_group1.append(text_area1)  # Subgroup for text scaling

# Draw a label
strSys = sysImplementation[0] + ' ' + \
         str(sysImplementation[1][0]) +'.'+ \
         str(sysImplementation[1][1]) +'.'+ \
         str(sysImplementation[1][2])
text_group2 = displayio.Group(scale=1, x=5, y=25)
text2 = strSys
text_area2 = label.Label(terminalio.FONT, text=text2, color=0xFFFFFF)
text_group2.append(text_area2)  # Subgroup for text scaling

# Draw a label
text_group3 = displayio.Group(scale=1, x=5, y=40)
text3 = ST77xx_NAME
text_area3 = label.Label(terminalio.FONT, text=text3, color=0x0000000)
text_group3.append(text_area3)  # Subgroup for text scaling
# Draw a label
text_group4 = displayio.Group(scale=1, x=5, y=55)
text4 = ST77xx_VERSION
text_area4 = label.Label(terminalio.FONT, text=text4, color=0x000000)
text_group4.append(text_area4)  # Subgroup for text scaling

text_group5 = displayio.Group(scale=1, x=5, y=70)
text5 = str(display.width) + " x " + str(display.height)
text_area5 = label.Label(terminalio.FONT, text=text5, color=0x000000)
text_group5.append(text_area5)  # Subgroup for text scaling

splash.append(text_group1)
splash.append(text_group2)
splash.append(text_group3)
splash.append(text_group4)
splash.append(text_group5)

time.sleep(3.0)

rot = 90
while True:
    time.sleep(5.0)
    rot = rot + 90
    if (rot>=360):
        rot =0
    display.rotation = rot


Friday, October 15, 2021

STM32F411/CircuitPython 7, read SD Card, load bitmap and display on ST7735 LCD

The former STM32F411/CircuitPython 7 exercise show display on SPI ST7735 LCD. This exercise connect to a MicroSD card adapter, read text file, load bitmap and display on SPI ST7735 LCD.

It's assumed CircuitPython 7.0.0 is installed on STM32F411 development board, a STM32F411CE Black Pill-like board. To install CircuitPython firmware on STM32F411, refer to the post "Flash CircuitPython to STM32F411, and install Thonny IDE on Ubuntu 20.10", with list to more exercises.

The MicroSD adapter used support Micro SD <=2G, Micro SDHC <=32G, require operating voltage of 4.5~5.5V, with 3.3/5V interface voltage. So connect STM32F411 5V to MicroSD adapter's VCC, GND to GND. Other pins refer to the exercise code, marked in blue.

The libraries needed in this exercise include adafruit_sdcard.mpy, adafruit_st7735r.mpy and adafruit_imageload.

Visit https://circuitpython.org/libraries, download the appropriate bundle (adafruit-circuitpython-bundle-7.x-mpy- here) for your version of CircuitPython. Unzip the file, copy adafruit_sdcard.mpy, adafruit_st7735r.mpy and adafruit_imageload folder from the "lib" folder to the "lib" folder on your CIRCUITPY drive. (refer to the video in last post "CircuitPython 7 exercises on STM32F411, with SSD1306 I2C OLED")

Exercise code

cpyF411_SD.py, simple print the files in microSD. There are one text file (hello.txt) and three bmp files (logo.bmp, photo_01.bmp and photo_02.bmp) in my microSD card.
"""
CircuitPython exercise running on STM32F411:
Read SD Card using adafruit_sdcard library
https://circuitpython.readthedocs.io/projects/sd/en/3.3.5/api.html

List files on SD Card
"""

import board
from sys import implementation as sysImplementation
import busio
import digitalio
import adafruit_sdcard
import storage
import os

sd_path = "/sd"

print(board.board_id)
strSys = sysImplementation[0] + ' ' + \
         str(sysImplementation[1][0]) +'.'+ \
         str(sysImplementation[1][1]) +'.'+ \
         str(sysImplementation[1][2])
print(strSys)
print(adafruit_sdcard.__name__, " ", adafruit_sdcard.__version__)
print()

#Connection between SDCard and STM32F411
#SD Card VCC - 5V
#SD Card GND - GND
sd_SCK = board.B3
sd_MISO = board.B4
sd_MOSI = board.B5
sd_CS = board.A15
spi = busio.SPI(sd_SCK, sd_MOSI, sd_MISO)
cs = digitalio.DigitalInOut(sd_CS)

# ref:
# https://circuitpython.readthedocs.io/projects/sd/en/3.3.5/examples.html
def print_directory(path, tabs=0):
    for file in os.listdir(path):
        stats = os.stat(path + "/" + file)
        filesize = stats[6]
        isdir = stats[0] & 0x4000

        if filesize < 1000:
            sizestr = str(filesize) + " by"
        elif filesize < 1000000:
            sizestr = "%0.1f KB" % (filesize / 1000)
        else:
            sizestr = "%0.1f MB" % (filesize / 1000000)

        prettyprintname = ""
        for _ in range(tabs):
            prettyprintname += "   "
        prettyprintname += file
        if isdir:
            prettyprintname += "/"
        print('{0:<40} Size: {1:>10}'.format(prettyprintname, sizestr))

        # recursively print directory contents
        if isdir:
            print_directory(path + "/" + file, tabs + 1)

try:
    sdcard = adafruit_sdcard.SDCard(spi, cs)
    vfs = storage.VfsFat(sdcard)
    storage.mount(vfs, sd_path)
    
    print("Files on filesystem:")
    print("====================")
    print_directory(sd_path)

except OSError as exc:
    print(exc.args[0])


cpyF411_SD_textfile.py, read the "hello.txt" from microSD Card.
"""
CircuitPython exercise running on STM32F411:
Read SD Card using adafruit_sdcard library

Read the "hello.txt" from SD Card
"""

import board
from sys import implementation as sysImplementation
import busio
import digitalio
import adafruit_sdcard
import storage
import os

sd_path = "/sd"

print(board.board_id)
strSys = sysImplementation[0] + ' ' + \
         str(sysImplementation[1][0]) +'.'+ \
         str(sysImplementation[1][1]) +'.'+ \
         str(sysImplementation[1][2])
print(strSys)
print(adafruit_sdcard.__name__, " ", adafruit_sdcard.__version__)
print()

#Connection between SDCard and STM32F411
#SD Card VCC - 5V
#SD Card GND - GND
sd_SCK = board.B3
sd_MISO = board.B4
sd_MOSI = board.B5
sd_CS = board.A15
spi = busio.SPI(sd_SCK, sd_MOSI, sd_MISO)
cs = digitalio.DigitalInOut(sd_CS)

# ref:
# https://circuitpython.readthedocs.io/projects/sd/en/3.3.5/examples.html
def print_directory(path, tabs=0):
    for file in os.listdir(path):
        stats = os.stat(path + "/" + file)
        filesize = stats[6]
        isdir = stats[0] & 0x4000

        if filesize < 1000:
            sizestr = str(filesize) + " by"
        elif filesize < 1000000:
            sizestr = "%0.1f KB" % (filesize / 1000)
        else:
            sizestr = "%0.1f MB" % (filesize / 1000000)

        prettyprintname = ""
        for _ in range(tabs):
            prettyprintname += "   "
        prettyprintname += file
        if isdir:
            prettyprintname += "/"
        print('{0:<40} Size: {1:>10}'.format(prettyprintname, sizestr))

        # recursively print directory contents
        if isdir:
            print_directory(path + "/" + file, tabs + 1)

try:
    sdcard = adafruit_sdcard.SDCard(spi, cs)
    vfs = storage.VfsFat(sdcard)
    storage.mount(vfs, sd_path)
    
    print("Files on filesystem:")
    print("====================")
    print_directory(sd_path)
    print("====================")
    print()
    
    textfile = sd_path+"/hello.txt"
    print("text file:", textfile)
    print("========================")
    with open(textfile, "r") as f:
        line = f.readline()
        while line != '':
            print(line)
            line = f.readline()


except OSError as exc:
    print(exc.args[0])


cpyF411_SD_bitmap_logo.py, read a single bmp file (logo.bmp) from SD Card using displayio.OnDiskBitmap(), and display on SPI ST7735 LCD
"""
CircuitPython exercise running on STM32F411:
Read SD Card using adafruit_sdcard library

Read a single bmp from SD Card using displayio.OnDiskBitmap()

and display on SPI ST7735 LCD
"""

import board
from sys import implementation as sysImplementation
import busio
import digitalio
import adafruit_sdcard
import storage
import os
import displayio

from adafruit_st7735r import ST7735R as TFT_ST7735
from adafruit_st7735r import __name__ as ST7735_NAME
from adafruit_st7735r import __version__ as ST7735_VERSION

# Release any resources currently in use for the displays
displayio.release_displays()

sd_path = "/sd"

print(board.board_id)
strSys = sysImplementation[0] + ' ' + \
         str(sysImplementation[1][0]) +'.'+ \
         str(sysImplementation[1][1]) +'.'+ \
         str(sysImplementation[1][2])
print(strSys)
print(adafruit_sdcard.__name__, " ", adafruit_sdcard.__version__)
print(ST7735_NAME, " ", ST7735_VERSION)
print()

#Connection between SDCard and STM32F411
#SD Card VCC - 5V
#SD Card GND - GND
sd_SCK = board.B3
sd_MISO = board.B4
sd_MOSI = board.B5
sd_CS = board.A15
sd_spi = busio.SPI(sd_SCK, sd_MOSI, sd_MISO)
sd_cs = digitalio.DigitalInOut(sd_CS)

#Connection between STM32F411 and SPI ST7735 display
#marking on display     #128x128   #80x160
tft_sck = board.B13     #SCK       #SCL
tft_mosi = board.A1     #SDA       #SDA

tft_dc = board.A2       #A0        #DC
tft_reset = board.A3    #RESET     #RES
tft_cs = board.A4       #CS        #CS
#Backlight (LED/BLK) connect to STM32F411 3V3
#TFT VCC - STM32F411 3V3
#TFT GND - STM32F411 GND

tft_spi = busio.SPI(clock=tft_sck, MOSI=tft_mosi)
display_bus = displayio.FourWire(
    tft_spi, command=tft_dc, chip_select=tft_cs, reset=tft_reset
)

# I find out colrstart/rowstart by try/error and retry
display = TFT_ST7735(display_bus, width=128, height=128,
                     rotation=180,
                     bgr=True)

print(type(display))
print("display.width:  ", display.width)
print("display.height: ", display.height)
print()
            
try:
    sdcard = adafruit_sdcard.SDCard(sd_spi, sd_cs)
    vfs = storage.VfsFat(sdcard)
    storage.mount(vfs, sd_path)
    
    print("========================")
    bmpfile = sd_path+"/logo.bmp"
    print("bitmap file:", bmpfile)
    
    bitmap = displayio.OnDiskBitmap(bmpfile)

    # Create a TileGrid to hold the bitmap
    tile_grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader)

    # Create a Group to hold the TileGrid
    group = displayio.Group()

    # Add the TileGrid to the Group
    group.append(tile_grid)

    # Add the Group to the Display
    display.show(group)

            
except OSError as exc:
    print(exc.args[0])

while True:
    pass

displayio.OnDiskBitmap vs adafruit_imageload

OnDiskBitmap source the bitmap image directly from flash memory storage. This is like reading the image from disk instead of loading it into memory first imageload). The trade off of using OnDiskBitmap is the reduced use of memory for potentially slower pixel draw times.

ref:

cpyF411_SD_bitmap_rot_.py, repeatedly load bmp files (logo.bmp, photo_01.bmp and photo_02.bmp) from SD Card using adafruit_imageload (marked in red) or displayio.OnDiskBitmap (marked in green), then display on SPI ST7735 LCD.
"""
CircuitPython exercise running on STM32F411:
Read SD Card using adafruit_sdcard library

Repeatly load bmp(s) from SD Card using:
- adafruit_imageload
- displayio.OnDiskBitmap

and display on SPI ST7735 LCD
"""

import board
from sys import implementation as sysImplementation
import busio
import digitalio
import adafruit_sdcard
import storage
import os
import displayio
import adafruit_imageload
import time

from adafruit_st7735r import ST7735R as TFT_ST7735
from adafruit_st7735r import __name__ as ST7735_NAME
from adafruit_st7735r import __version__ as ST7735_VERSION

import gc

# Release any resources currently in use for the displays
displayio.release_displays()
gc.collect()

sd_path = "/sd"

print(board.board_id)
strSys = sysImplementation[0] + ' ' + \
         str(sysImplementation[1][0]) +'.'+ \
         str(sysImplementation[1][1]) +'.'+ \
         str(sysImplementation[1][2])
print(strSys)
print(adafruit_sdcard.__name__, " ", adafruit_sdcard.__version__)
print(adafruit_imageload.__name__, " ", adafruit_imageload.__version__)
print(ST7735_NAME, " ", ST7735_VERSION)
print()

#Connection between SDCard and STM32F411
#SD Card VCC - 5V
#SD Card GND - GND
sd_SCK = board.B3
sd_MISO = board.B4
sd_MOSI = board.B5
sd_CS = board.A15
sd_spi = busio.SPI(sd_SCK, sd_MOSI, sd_MISO)
sd_cs = digitalio.DigitalInOut(sd_CS)

#Connection between STM32F411 and SPI ST7735 display
#marking on display     #128x128   #80x160
tft_sck = board.B13     #SCK       #SCL
tft_mosi = board.A1     #SDA       #SDA

tft_dc = board.A2       #A0        #DC
tft_reset = board.A3    #RESET     #RES
tft_cs = board.A4       #CS        #CS
#Backlight (LED/BLK) connect to STM32F411 3V3
#TFT VCC - STM32F411 3V3
#TFT GND - STM32F411 GND

tft_spi = busio.SPI(clock=tft_sck, MOSI=tft_mosi)
display_bus = displayio.FourWire(
    tft_spi, command=tft_dc, chip_select=tft_cs, reset=tft_reset
)

# I find out colrstart/rowstart by try/error and retry
display = TFT_ST7735(display_bus, width=128, height=128,
                     rotation=180,
                     bgr=True)

print(type(display))
print("display.width:  ", display.width)
print("display.height: ", display.height)
print()

try:
    sdcard = adafruit_sdcard.SDCard(sd_spi, sd_cs)
    vfs = storage.VfsFat(sdcard)
    storage.mount(vfs, sd_path)
    
    # Create a Group to hold the TileGrid
    group = displayio.Group()
    
    pToBmp = ["logo.bmp", "photo_01.bmp", "photo_02.bmp"]
    
    while True:
        for b in pToBmp:
            print("========================")
            bmpfile = sd_path + "/" + b
            print("bitmap file:", bmpfile)
            
            print("free mem@before bitmap", gc.mem_free())
            
            # using adafruit_imageload
            bitmap, palette = adafruit_imageload.load(bmpfile,
                                                  bitmap=displayio.Bitmap,
                                                  palette=displayio.Palette)
            
            #using displayio.OnDiskBitmap
            #bitmap = displayio.OnDiskBitmap(bmpfile)
            #palette = bitmap.pixel_shader
            
            print("free mem@after bitmap", gc.mem_free())
            gc.collect()
            print("free mem@after gc.collect()", gc.mem_free())
            
            print(bitmap.width, " x ", bitmap.height)
    
            # Create a TileGrid to hold the bitmap
            tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)

            # Add the TileGrid to the Group
            group.append(tile_grid)
            print("index: ", group.index(tile_grid))

            # Add the Group to the Display
            display.show(group)
            
            time.sleep(3)
            group.remove(tile_grid)
            
except OSError as exc:
    print(exc.args[0])

while True:
    pass

bmp files used here:

logo.bmp
photo_01.bmp
photo_02.bmp









Not all BMP file formats are supported by CircuitPython. You will need to make sure you have an indexed BMP file. Follow HERE for some good info on how to convert or create such a BMP file.

Wednesday, October 13, 2021

CircuitPython 7 exercises on STM32F411, identify pins

Get Pins name

Many pins on CircuitPython compatible microcontroller boards have multiple names, however, typically, there's only one name labeled on the physical board. So how do you find out what the other available pin names are? Simple, with the following script! Each line printed out to the serial console contains the set of names for a particular pin.

~ ref: https://learn.adafruit.com/circuitpython-essentials/circuitpython-pins-and-modules

"""CircuitPython Essentials Pin Map Script"""
import microcontroller
import board

board_pins = []
for pin in dir(microcontroller.pin):
    if isinstance(getattr(microcontroller.pin, pin), microcontroller.Pin):
        pins = []
        for alias in dir(board):
            if getattr(board, alias) is getattr(microcontroller.pin, pin):
                pins.append("board.{}".format(alias))
        if len(pins) > 0:
            board_pins.append(" ".join(pins))
for pins in sorted(board_pins):
    print(pins)

Tested on STM32F411 Dev. Board with CircuitPython 7.0.0




List I2C possible pin-pair
"""CircuitPython Essentials I2C possible pin-pair identifying script"""
import board
import busio
from microcontroller import Pin

def is_hardware_I2C(scl, sda):
    try:
        p = busio.I2C(scl, sda)
        p.deinit()
        return True
    except ValueError:
        return False
    except RuntimeError:
        return True

def get_unique_pins():
    exclude = ['NEOPIXEL', 'APA102_MOSI', 'APA102_SCK']
    pins = [pin for pin in [
        getattr(board, p) for p in dir(board) if p not in exclude]
            if isinstance(pin, Pin)]
    unique = []
    for p in pins:
        if p not in unique:
            unique.append(p)
    return unique

for scl_pin in get_unique_pins():
    for sda_pin in get_unique_pins():
        if scl_pin is sda_pin:
            continue
        if is_hardware_I2C(scl_pin, sda_pin):
            print("SCL pin:", scl_pin, "\t SDA pin:", sda_pin)






CircuitPython 7 exercises on STM32F411, with SPI ST7735 LCD

Test on 3 type of ST7735 SPI LCD with STM32F411/CircuitPython 7.0.0:
- 1.44" 128x128 (KMR1441_SPI V2)
- 0.96" 80x160 IPS
- 1.8" 128x160 TFT

It's assumed CircuitPython 7.0.0 is installed on STM32F411 minimum development board, a STM32F411CE Black Pill-like board. To install CircuitPython firmware on STM32F411, refer to the post "Flash CircuitPython to STM32F411, and install Thonny IDE on Ubuntu 20.10".

Library

adafruit_st7735, adafruit_st7735r and adafruit_display_text are needed in the following exercises.

Visit https://circuitpython.org/libraries, download the appropriate bundle (adafruit-circuitpython-bundle-7.x-mpy- here) for your version of CircuitPython. Unzip the file, copy adafruit_st7735.mpy, adafruit_st7735r.mpy and adafruit_display_text folder from the "lib" folder to the "lib" folder on your CIRCUITPY drive. (refer to the video in last post "CircuitPython 7 exercises on STM32F411, with SSD1306 I2C OLED")


Connection

Please notice that 1.44" 128x128 (KMR1441_SPI V2) and 1.8" 128x160 TFT have same pin assignment, 0.96" 80x160 IPS have different pin order and pin name but same function.

The connection refer to the exercise code, marked blue.

Exercise code

The code in red show the difference between them.

cpyF411_st7735_128x128.py for 1.44" 128x128 (KMR1441_SPI V2)
"""
CircuitPython 7 exercise run on STM32F411
with 128x128 SPI ST7735

ref:
adafruit/Adafruit_CircuitPython_ST7735
https://github.com/adafruit/Adafruit_CircuitPython_ST7735
adafruit/Adafruit_CircuitPython_ST7735R
https://github.com/adafruit/Adafruit_CircuitPython_ST7735R
"""

from sys import implementation as sysImplementation
import time
import board
import busio
import displayio
import terminalio

from adafruit_st7735 import ST7735 as TFT_ST7735
from adafruit_st7735 import __name__ as ST7735_NAME
from adafruit_st7735 import __version__ as ST7735_VERSION

from adafruit_display_text import label

# Release any resources currently in use for the displays
displayio.release_displays()

#Connection between STM32F411 and SPI ST7735 display
#marking on display     #128x128   #80x160
tft_sck = board.B13     #SCK       #SCL
tft_mosi = board.A1     #SDA       #SDA

tft_dc = board.A2       #A0        #DC
tft_reset = board.A3    #RESET     #RES
tft_cs = board.A4       #CS        #CS
#Backlight (LED/BLK) connect to STM32F411 3V3
#TFT VCC - STM32F411 3V3
#TFT GND - STM32F411 GND

tft_spi = busio.SPI(clock=tft_sck, MOSI=tft_mosi)
display_bus = displayio.FourWire(
    tft_spi, command=tft_dc, chip_select=tft_cs, reset=tft_reset
)

# I find out colrstart/rowstart by try/error and retry
display = TFT_ST7735(display_bus, width=128, height=128,
                     rotation=90,
                     )

print(type(display))
print("display.width:  ", display.width)
print("display.height: ", display.height)

# Make the display context
splash = displayio.Group()
display.show(splash)

color_bitmap = displayio.Bitmap(display.width, display.height, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x000000
time.sleep(1)

bg_sprite = displayio.TileGrid(color_bitmap,
                               pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)

for c in [["RED", 0xFF0000],
          ["GREEN", 0x00FF00],
          ["BLUE", 0x0000FF]]:
    print(c[0], " : ", hex(c[1]))
    color_palette[0] = c[1]
    time.sleep(2)

splash.remove(bg_sprite)
#---

# Make the display context
#splash = displayio.Group()
#display.show(splash)

color_bitmap = displayio.Bitmap(display.width, display.height, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x00FF00

bg_sprite = displayio.TileGrid(color_bitmap,
                               pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)

# Draw a smaller inner rectangle
inner_bitmap = displayio.Bitmap(display.width-2, display.height-2, 1)
inner_palette = displayio.Palette(1)
inner_palette[0] = 0x0000FF
inner_sprite = displayio.TileGrid(inner_bitmap,
                                  pixel_shader=inner_palette, x=1, y=1)
splash.append(inner_sprite)

# Draw a label
text_group1 = displayio.Group(scale=1, x=5, y=10)
text1 = "STM32F411"
text_area1 = label.Label(terminalio.FONT, text=text1, color=0xFF0000)
text_group1.append(text_area1)  # Subgroup for text scaling

# Draw a label
strSys = sysImplementation[0] + ' ' + \
         str(sysImplementation[1][0]) +'.'+ \
         str(sysImplementation[1][1]) +'.'+ \
         str(sysImplementation[1][2])
text_group2 = displayio.Group(scale=1, x=5, y=25)
text2 = strSys
text_area2 = label.Label(terminalio.FONT, text=text2, color=0xFFFFFF)
text_group2.append(text_area2)  # Subgroup for text scaling

# Draw a label
text_group3 = displayio.Group(scale=1, x=5, y=40)
text3 = ST7735_NAME
text_area3 = label.Label(terminalio.FONT, text=text3, color=0x0000000)
text_group3.append(text_area3)  # Subgroup for text scaling
# Draw a label
text_group4 = displayio.Group(scale=1, x=5, y=55)
text4 = ST7735_VERSION
text_area4 = label.Label(terminalio.FONT, text=text4, color=0x000000)
text_group4.append(text_area4)  # Subgroup for text scaling

text_group5 = displayio.Group(scale=1, x=5, y=70)
text5 = str(display.width) + " x " + str(display.height)
text_area5 = label.Label(terminalio.FONT, text=text5, color=0x000000)
text_group5.append(text_area5)  # Subgroup for text scaling

splash.append(text_group1)
splash.append(text_group2)
splash.append(text_group3)
splash.append(text_group4)
splash.append(text_group5)

time.sleep(3.0)

rot = 90
while True:
    time.sleep(5.0)
    rot = rot + 90
    if (rot>=360):
        rot =0
    display.rotation = rot


cpyF411_st7735_80x160.py for 0.96" 80x160 IPS
"""
CircuitPython 7 exercise run on STM32F411
with 80x160 SPI ST7735 IPS

ref:
adafruit/Adafruit_CircuitPython_ST7735
https://github.com/adafruit/Adafruit_CircuitPython_ST7735
adafruit/Adafruit_CircuitPython_ST7735R
https://github.com/adafruit/Adafruit_CircuitPython_ST7735R
"""

from sys import implementation as sysImplementation
import time
import board
import busio
import displayio
import terminalio

from adafruit_st7735r import ST7735R as TFT_ST7735
from adafruit_st7735r import __name__ as ST7735_NAME
from adafruit_st7735r import __version__ as ST7735_VERSION

from adafruit_display_text import label

# Release any resources currently in use for the displays
displayio.release_displays()

#Connection between STM32F411 and SPI ST7735 display
#marking on display     #128x128   #80x160
tft_sck = board.B13     #SCK       #SCL
tft_mosi = board.A1     #SDA       #SDA

tft_dc = board.A2       #A0        #DC
tft_reset = board.A3    #RESET     #RES
tft_cs = board.A4       #CS        #CS
#Backlight (LED/BLK) connect to STM32F411 3V3
#TFT VCC - STM32F411 3V3
#TFT GND - STM32F411 GND

tft_spi = busio.SPI(clock=tft_sck, MOSI=tft_mosi)
display_bus = displayio.FourWire(
    tft_spi, command=tft_dc, chip_select=tft_cs, reset=tft_reset
)

# I find out colrstart/rowstart by try/error and retry
display = TFT_ST7735(display_bus, width=160, height=80,
                     colstart=26, rowstart=1,
                     rotation=90,
                     invert=True
                     )

print(type(display))
print("display.width:  ", display.width)
print("display.height: ", display.height)

	[... same as in cpyF411_st7735_128x128.py ...]

cpyF411_st7735_128x160.py for 1.8" 128x160 TFT
"""
CircuitPython 7 exercise run on STM32F411
with 128x160 SPI ST7735

ref:
adafruit/Adafruit_CircuitPython_ST7735
https://github.com/adafruit/Adafruit_CircuitPython_ST7735
adafruit/Adafruit_CircuitPython_ST7735R
https://github.com/adafruit/Adafruit_CircuitPython_ST7735R
"""

from sys import implementation as sysImplementation
import time
import board
import busio
import displayio
import terminalio

from adafruit_st7735r import ST7735R as TFT_ST7735
from adafruit_st7735r import __name__ as ST7735_NAME
from adafruit_st7735r import __version__ as ST7735_VERSION

from adafruit_display_text import label

# Release any resources currently in use for the displays
displayio.release_displays()

#Connection between STM32F411 and SPI ST7735 display
#marking on display     #128x128   #80x160
                        #128x160
tft_sck = board.B13     #SCK       #SCL
tft_mosi = board.A1     #SDA       #SDA

tft_dc = board.A2       #A0        #DC
tft_reset = board.A3    #RESET     #RES
tft_cs = board.A4       #CS        #CS
#Backlight (LED/BLK) connect to STM32F411 3V3
#TFT VCC - STM32F411 3V3
#TFT GND - STM32F411 GND

tft_spi = busio.SPI(clock=tft_sck, MOSI=tft_mosi)
display_bus = displayio.FourWire(
    tft_spi, command=tft_dc, chip_select=tft_cs, reset=tft_reset
)

# I find out colrstart/rowstart by try/error and retry
display = TFT_ST7735(display_bus, width=160, height=128,
                     rotation=90,
                     )

print(type(display))
print("display.width:  ", display.width)
print("display.height: ", display.height)

	[... same as in cpyF411_st7735_128x128.py ...]
next:


Remark@2021-10-15

Fix color order

Noted that the color order (RED/GREEN/BLUE shown in the video) is in-correct. To fix it:
- Use adafruit_st7735r library.
- add the parameter bgr=True when you create display object.







Monday, October 11, 2021

CircuitPython 7 exercises on STM32F411, with SSD1306 I2C OLED

This post show how to run CircuitPython code on STM32F411, display on SSD1306 I2C 128x64 OLED using adafruit_ssd1306 library.

It's assumed CircuitPython 7.0.0 is installed on STM32F411 minimum development board, a STM32F411CE Black Pill-like board. To install CircuitPython firmware on STM32F411, refer to the post "Flash CircuitPython to STM32F411, and install Thonny IDE on Ubuntu 20.10".

Connection

SSD1306 I2C	STM32F411
-------------------------
GND		GND
VCC		3V3
SCL		B6
SDA		B7

Library

To run the exercise code,  Adafruit CircuitPython Library Bundle's adafruit_ssd1306 and font5x8.bin is need.

Visit https://circuitpython.org/libraries, download the appropriate bundle (adafruit-circuitpython-bundle-7.x-mpy- here) for your version of CircuitPython.

Unzip the file, copy adafruit_ssd1306.mpy, and adafruit_framebuf.mpy from the "lib" folder to the lib folder on your CIRCUITPY drive. 

And copy font5x8.bin from the unzipped "examples" folder to CIRCUITPY driver.

Exercise code

cpyF411_scanI2C.py, scan the address of connected I2C devices.
import os
import busio
import board

for u in os.uname():
    print(u)    
print()

#Assign I2C:
#https://circuitpython.readthedocs.io/en/latest/shared-bindings/board/index.html#board.I2C
print(dir(board))
print("SCL: ", board.SCL)
print("SDA: ", board.SDA)
i2c = board.I2C()
print(i2c)
print()

#Scan I2C devices
if(i2c.try_lock()):
    print("i2c.scan(): " + str(i2c.scan()))
    i2c.unlock()
print()


cpyF411_SSD1306.py
from os import uname
from board import SCL, SDA
from sys import implementation as sysImplementation
import busio
import time

import adafruit_ssd1306


# Create the I2C interface and display object of SSD1306_I2C.
i2c = busio.I2C(SCL, SDA)
display = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)

for u in uname():
    print(u)
print()
for s in sysImplementation:
    print(s)
print()

print(display)
print("display.width x height: ", display.width, " x ", display.height)
print(dir(display))
    
display.fill(0)
display.show()
time.sleep(1)
display.fill(1)
display.show()
time.sleep(1)

display.fill(0)


strSys = sysImplementation[0] + ' ' + \
         str(sysImplementation[1][0]) +'.'+ \
         str(sysImplementation[1][1]) +'.'+ \
         str(sysImplementation[1][2])

strLib = adafruit_ssd1306.__name__ + '\n' + adafruit_ssd1306.__version__

def drawInfo():
    display.text(strSys, 0, 0, 1)
    display.text(strLib, 0, 10, 1)
    display.text("STM32F411", 0, 30, 1)
    display.text("SSD1306", 0, 40, 1)
    strResolution = str(display.rotation) + ' : ' + str(display.width) + ' x ' + str(display.height)
    display.text(strResolution, 0, 50, 1)
    
for r in range(0, 4):
    display.fill(0)
    display.rotation = r
    drawInfo()
    display.show()
    time.sleep(3)

display.rotation = 0

#draw rectangle
display.fill(0)
display.rect(0, 0, display.width, display.height, 1)
display.show()
time.sleep(1)
display.fill(0)
display.fill_rect(0, 0, display.width, display.height, 1)
display.show()
time.sleep(1)

#draw circle
display.fill(0)
if display.width > display.height:
    r = (int)(display.height/2)
else:
    r = (int)(display.width/2)
display.circle((int)(display.width/2), (int)(display.height/2), r, 1)
display.show()
time.sleep(1)

display.fill(0)
display.show()

# draw pixels
for y in range(0, display.height, 8):
    for x in range(0, display.width, 8):
        display.pixel(x, y, 1)
        display.show()
time.sleep(1)
display.invert(1)
time.sleep(2)
display.invert(0)
time.sleep(1)