M T
Published © GPL3+

ESP32-S3 Pin reassignments in Prototyping

I wanted to share my latest prototyping setup to exhibit the benefit of pin reassignments on the ESP32-S3.

BeginnerProtip1 hour50
ESP32-S3 Pin reassignments in Prototyping

Things used in this project

Hardware components

Solderless Breadboard Half Size
Solderless Breadboard Half Size
×2

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Breadboard

Code

Snippets

Arduino
This is the pin reassignment for the display, in globals.h
// Adafruit library used for LCD display
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

// For modules that include Octal PSRAM (any module that has 8MB PSRAM) you MUST NOT use GPIO35, GPIO36 or GPIO37 ???.
// S3_PROTO pins
#define TFT_DC 37
#define TFT_CS 39
#define TFT_MOSI 36
#define TFT_CLK 35 
#define TFT_MISO 48
#define TFT_RST 38
#define TFT_BL 47

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);

Setup

C/C++
This is my setup.. Most of the other code is pretty esoteric, but If there is interest, I'm willing to discuss any of it.
//********************************
String BoardType = "S3";
String Module = "ESP32-S3 N16R8 16MB FL,8MB QSPI PS(shows 4176396?)";

#define NEO_DEF 33   // S3M=7, S3D=33, C3M,C3D,C3X=3  Has to be a define, can't wait for the code NEO_PIN
#define NEO_BRIGHTNESS 2 // Change white brightness (max 255)
#define RGB_BUILTIN 33

#define SCL 16
#define SDA 15
#define VCC_PIN 14
//********************************

String version = BoardType + "_proto_life";
String ino = "_S3_PROTO/S3_proto_life";
String rev = (String)__DATE__ + " " + (String)__TIME__;

#include "globals.h"

/////////////////////////////////////////////////
void setup(void)
{
  // turn the screen off first
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, LOW);

  EEPROM.begin(EEPROM_SIZE);
        psramInit();  
  Serial.begin(115200);
  Wire.setPins(15, 16);
  Wire.begin();
  initAHT();
  delay(2000);
  Serial.println();

  tick.attach(1, [] {
    tickcnt++;   // updates tickcnt every second

    // ESPNOW flag timeouts
    if (nowSendClear > 0) nowSendClear--;
    if (nowRecvClear > 0) nowRecvClear--;
  });

  // setup before readCfg
  readConfig();           // *** true sets forceDefaultConfig
  forceDefaultCfg = false;         // one shot deal

  // after readConfig
  if (useESPNOW && Qota == 1)
  {
    // make sure this is cleared so we don't go into OTA on reboot
    Qota = 0;
    saveFlags();
    gotoOTA();
  }
  else
  {
    sendStatusJson(0);
  }

  NetQ = xQueueCreate(6, sizeof(qMsg_t));
  if (NetQ == NULL)
  {
    Serial.println("NetQ create failed");
  }

  if (!useESPNOW)
  {
    initWifi();
    ArduinoOTA.setHostname(config.DeviceName.c_str());
    ArduinoOTA.begin();
    serverSetup();
  }

  Serial.println(config.DeviceName);
  Serial.println(version);
  Serial.println(rev);

      char temp[300];
      sprintf(temp, "Heap:\nFree:%i\nMin:%i\nSize:%i\nAlloc:%i\nPSRAM:%i", 
        ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getHeapSize(), ESP.getMaxAllocHeap(), ESP.getFreePsram());  
      Serial.println(temp);

// psram example
//      char *str;
//        str = (char *)ps_calloc(5000, sizeof(char) ); // put str buffer into PSRAM
//      Serial.println((String)"PSRAM after alloc: " +ESP.getFreePsram());
  
  tft.begin();
  welcomeScreen();

  getVcc();     // sets last_battVoltage
  readAHT();
  sendStatus(true);

  if(config.Life == 1)
  {
    delay(5000);      // Let me read the Welcome screen
    xTaskCreatePinnedToCore(life_loop, "lifeTask", 10000, NULL, 1, &lifeTaskHand, 1);
  }
  
  Serial.println("Setup complete");
  logMsg(config.DeviceName + ": Setup complete");
  delay(2000);
}

Life

C/C++
This is my variation of the Game of Life
Rules are the same as the original with a couple of additions added:
Mature cells are Green
Newborn cells are Blue
Cells about to die are Red
Cells have a 75 generation lifespan
// Life generations and seed value
int seedValue = 1;
int generations = 0;
int maxGenerations = 5000; // max cycles per game

// Set display stuff
const int pixelSize = 8; // Set how many native pixels = pixels per cell length or width
const int len = 40;   // 320 / pixelSize
const int ht = 30;    // 240 / pixelSize
const bool flipScreen = false; // If you need to flip the screen

// Initialize the board - ADDING 1 stops the border wrap
int board[len+1][ht+1];
int newBoard[len+1][ht+1];
int lifeSpan[len+1][ht+1]; // dies after 75 generations

bool gameOver = true;       // on init
int lastBirthRate = 0;
int birthRateLoss = 0;
int birthRate = 0;


///////////////////////////////////////////////////////////
/*
  Any live cell with fewer than two live neighbours dies, as if by underpopulation.
  Any live cell with two or three live neighbours lives on to the next generation.
  Any live cell with more than three live neighbours dies, as if by overpopulation.
  Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.

  These rules, which compare the behavior of the automaton to real life, can be condensed into the following:
  Any live cell with two or three live neighbours survives.
  Any dead cell with three live neighbours becomes a live cell.
  All other live cells die in the next generation. Similarly, all other dead cells stay dead.

  Lifespans are set at birth to 75, death by natural cause

  GameOver (Extinction) occurs if the net Births is negative for 25 generations or MaxGeneraions
*/

///////////////////////////////////////////////////////////
void life_loop(void *)
{
  // Do-while is added for lifeTask
  do {
    birthRate = 0;

    // Catch for number of cycles per game
    if (gameOver)
    {
      blankScreen();
      
      if(generations > 0)
      {
        tft.setCursor(0, tft.height()-24);
        tft.setTextColor(TFT_YELLOW, TFT_BLACK);
        tft.print("Extinct in ");
        tft.print(generations);
        tft.print(" generations");
        delay(3000);
        blankScreen();
      }

      // reset the game board
      gameOver = false;
      generations = 0;

      randomBoard();  // makes newBoard and zeros board
    }

    // Draw the board, increment cycle generations
    drawBoard();    // draws newBoard and sets board = newBoard
    birthRate = nextGeneration(); // calcs neighbors and sets up newBoard


    // limit the number of stagnent cycles
    // if we don't see any growth for 10 consecutive generations then end
    if (birthRate <= 0)
    {
      birthRateLoss++;
    }
    else
    {
      lastBirthRate = birthRate;
      birthRateLoss = 0;
    }

    if (birthRateLoss > 25 || ++generations >= maxGenerations)
    {
      gameOver = true;
    }
  } while (lifeTaskHand != NULL);
}

///////////////////////////////////////////////////////////
int countNeighbors(int x, int y)
{
  int neighbors = 0;

  if (board[x - 1][y] > 1) neighbors++;
  if (board[x - 1][y - 1] > 1) neighbors++;
  if (board[x][y - 1] > 1) neighbors++;
  if (board[x + 1][y - 1] > 1) neighbors++;
  if (board[x + 1][y] > 1) neighbors++;
  if (board[x + 1][y + 1] > 1) neighbors++;
  if (board[x][y + 1] > 1) neighbors++;
  if (board[x - 1][y + 1] > 1) neighbors++;

  return neighbors;
}

///////////////////////////////////////////////////////////
int nextGeneration()
{
  int deaths = 0;
  int births = 0;

  for (int x = 0; x < len; x++)
  {
    for (int y = 0; y < ht; y++)
    {
      int neighbors = countNeighbors(x, y);

      newBoard[x][y] = 0;       // initialize

      // Any live cell with fewer than 2 live neighbours dies, as if by underpopulation.
      // or
      // Any live cell with more than 3 live neighbours dies, as if by overpopulation.
      if (board[x][y] > 1)
      {
        if ( neighbors < 2 || neighbors > 3 )
        {
          newBoard[x][y] = 1;   // red - dying
          deaths++;
        }
        else
        {
          newBoard[x][y] = 3;     // green survives

          if (--lifeSpan[x][y] == 0)
          {
            newBoard[x][y] = 0;   // dies of old age, natural causes
            deaths++;
          }
        }
      }

      //  Any dead cell with 3 live neighbours becomes a live cell.
      if ((board[x][y] < 2) && neighbors == 3)
      {
        newBoard[x][y] = 2;   // blue - born
        lifeSpan[x][y] = 75;  // expected natural life span
        births++;
      }
    }
  }

  return (births - deaths);
}

///////////////////////////////////////////////////////////
void drawBoard()
{
  for (int x = 0; x < len; x++)
  {
    for (int y = 0; y < ht; y++)
    {
      if (board[x][y] != newBoard[x][y])
      {
        board[x][y] = newBoard[x][y];
        int color;

        switch (newBoard[x][y]) {
          case 1:
            color = TFT_RED;
            break;

          case 2:
            color = TFT_BLUE;
            break;

          case 3:
            color = TFT_GREEN;
            break;

          default:
            color = TFT_BLACK;
            break;
        }

        tft.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize, color);
      }
    }
  }
}


///////////////////////////////////////////////////////////
void randomBoard()
{
  for (int x = 0; x < len; x++)
  {
    for (int y = 0; y < ht; y++)
    {
      newBoard[x][y] = (random(0, 4)) > 2 ? 3 : 0;
      board[x][y] = 0;
    }
  }
}

Credits

M T
4 projects • 3 followers
Retired SW Engineer after 47 years. Enjoy embedded C/C++ programming on ESP platforms. (Keeps me from drinking too much wine, LOL!)
Contact

Comments

Please log in or sign up to comment.