• Learn STM32 from beginner to advanced. Explore tutorials, projects, GUI design, and sensor interfacing for embedded system development.
  • Learn ESP32 IoT development with tutorials and projects. Connect sensors, use Wi-Fi, and build smart IoT systems with the powerful ESP32 board.
  • Learn Arduino with beginner to advanced tutorials and projects. Build IoT, robotics, and electronics projects using Arduino boards and sensors.
  • Learn how sensors work with Arduino. Explore tutorials and projects using temperature, motion, light, and ultrasonic sensors for real-world applications.
  • Learn microcontrollers from basic to advanced. Explore projects with Arduino, STM32, ESP32, Raspberry Pi, and Linux for embedded system development.

Getting Started with FSMC on STM32: Interface External Memories Easily

Getting Started with FSMC on STM32: Interface External Memories Easily

STM32 FSMC Explained: Connect External SRAM, NOR Flash, and LCD Displays

Introduction

Embedded systems often require external memory or parallel display interfaces that offer higher speed and lower latency than serial buses like SPI or I2C. STM32 microcontrollers include a hardware peripheral called the FSMC (Flexible Static Memory Controller), designed specifically to interface with SRAM, NOR Flash, NAND Flash, PSRAM, and 8080/6800 parallel LCD controllers, using a flexible and programmable parallel bus.The FSMC allows the microcontroller to treat external devices like internal memory, mapping them into the MCU’s address space. This eliminates manual GPIO toggling and significantly increases data throughput.

The STM32F series chips use the FSMC peripheral to manage the extended memory. FSMC is the abbreviation of Flexible Static Memory Controller, which is translated into flexible static memory controller. It can be used to drive memory including SRAM, NOR FLASH and NAND FLSAH types.

Since the FSMC peripheral can be used to control the extended external memory, the operation of the MCU on the LCD screen is actually to write the display data into the video memory, which is very similar to the control memory, and the communication timing of the 8080 interface can completely use the FSMC peripheral. Therefore, it is very suiable to use the FSMC to control the LCD screen.

 FSMC on STM32

FSMC Block Diagram

Communication Pins Explains

On the right side of the block diagram are the control pins related to the FSMC peripherals. Since there are some different pins when controlling different types of memory, it seems that there are a lot of them. The address line FSMC_A and data line FSMC_D are shared by all controllers. of. The specific GPIO ports and pin numbers corresponding to these FSMC pins can be found in the "STM32F103 Specifications" and are listed here

 FSMC on STM32

STM32F1 FSMC pin diagram

The FSMC of STM32F1 divides external devices into 2 categories: NOR/PSRAM devices, NAND/PC card devices. They share signals such as the address data bus, but have different CSs to distinguish different devices. In the experiment in this chapter, we use the NOR/PSRAM memory controller part of the FSMC, that is, use the TFTLCD as a SRAM device. Why can TFTLCD be used as a SRAM device? First of all, we need to understand the interface signals of the NOR/PSRAM memory controller.

The interface signal functions are as follows:

 FSMC on STM32

It can be seen from the above figure that the control of external SRAM generally includes: address line (such as A0~A25), bidirectional data line (such as D0~D15), write signal (NWE), read signal (NOE), chip select signal (NE) [x]), and UB/LB signals if the SRAM supports byte control. The signals of TFTLCD were introduced in the previous section, including: RS, DB0-DB15, WR, RD, CS, RST, etc., among which the only ones that need to be used when operating the LCD are: RS, DB0-DB15, WR , RD, CS. In this way, their operation interface signals are completely similar, the only difference is that TFTLCD has RS signal, but no address signal.

TFTLCD determines whether the transmitted data is data or command through the RS signal, which can be understood as an address signal in essence. For example, if we connect RS to A0, then when the FSMC controller writes address 0, it will make A0 become 0, For TFTLCD, it is a write command. When FSMC writes address 1, A0 will become 1. For TFTLCD, it is to write data. In this way, data and commands are distinguished, and they are actually two consecutive addresses corresponding to SRAM operations. Of course, RS can also be connected to other address lines. Our STM32F1 development board connects RS to A10.

Knowing that TFTLCD can be used as a SRAM device, let's take a look at the external device address mapping of FSMC. From the perspective of FSMC, the external memory is divided into 4 fixed-size storage areas (Bank), and the size of each storage area is 256 MB for a total of 1GB of space.

 FSMC on STM32

Memory Storage

STM32’s FSMC Bank1 can connect up to 4 external memories, each controlled by a separate chip-select signal (NE1–NE4). Each area has 64MB of address space, so Bank1 provides 256MB total. In this chapter, we use Bank1 Area 4 (NE4), whose base address is 0x6C000000.

FSMC maps external memory into the MCU’s address bus (HADDR). However, the actual address lines used depend on the memory data width:

  • For 16-bit memory: External address = HADDR shifted right by 1. (FSMC uses HADDR 25 : 1 25:1 → FSMC_A 24 : 0 24:0)
  • For 8-bit memory: External address = HADDR directly. (FSMC uses HADDR 25 : 0 25:0 → FSMC_A 25 : 0 25:0)

But FSMC_A[0] must always connect to external A0, regardless of width. This shift is important when working with LCDs where RS (Register Select) or A10 line is used to differentiate command/data.

To configure Bank1, we mainly use these registers:

FSMC_BCRx: Enable memory + basic settings (type, width, etc.)
FSMC_BTRx: Read timing configuration
FSMC_BWTRx: Write timing configuration

FSMC supports:

  1. Synchronous burst mode (uses FSMC_CLK, adjustable via CLKDIV and DATLAT)
  2. Asynchronous mode (uses timing parameters: ADDSET, DATAST, ADDHLD)

For connecting TFT LCD, we use Asynchronous Mode A, because LCD controllers generally work like simple SRAM interfaces. The key is to choose proper timing values matching the LCD controller’s requirements.

 FSMC on STM32

SRAM Asynchronous mode A

Mode A allows separate timing settings for read and write operations. This is very useful when driving a TFT LCD, because reading is usually slower while writing can be faster. If read and write share the same timing, the write speed would be limited by the slower read timing, or we would need to frequently switch timing settings during operation, which is inconvenient. With independent read/write timing in Mode A, we configure the timing once during initialization and it will work efficiently for both read and write without further adjustments. The write timing of Mode A is shown in Figure

 FSMC on STM32

SRAM Asynchronous mode A

In Mode A, the ADDSET and DATAST timing for read and write operations are configured using different registers. Because of space limitations, the details of the FSMC registers are not fully explained here. You can refer to the "STM32F10x Reference Manual" – FSMC chapter for full register descriptions.

However, briefly speaking, the standard library does not use the names FSMC_BCRx, FSMC_BTRx, and FSMC_BWTRx directly. Instead:

  1. FSMC_BCRx and FSMC_BTRx are grouped together in the BTCR[8] array:
    • BTCR[0] = BCR1, BTCR[1] = BTR1
    • BTCR[2] = BCR2, BTCR[3] = BTR2
    • BTCR[4] = BCR3, BTCR[5] = BTR3
    • BTCR[6] = BCR4, BTCR[7] = BTR4
  2. FSMC_BWTRx is grouped into BWTR[7]:

FSMC is indeed complex internally, but since we are developing using library functions, we only need to configure the registers through the library APIs — no need to go deep into the register details right now.

FSMC configuration steps

Next, we will introduce how to use library functions to configure FSMC. This is also necessary to understand when writing programs. The specific steps are as follows:

NOTE:The FSMC related library functions are in stm32f10x_fsmc.c and stm32f10x_fsmc.h file

FSMC initialization:

The initialization of FSMC mainly configures three registers, FSMC_BCRx, FSMC_BTRx, and FSMC_BWTRx. The firmware library provides three initialization functions to configure these registers. FSMC initialization library function as follows:


    
FSMC_NORSRAMInit();

FSMC_NANDInit();

FSMC_PCCARDInit();
    

These three functions are used to initialize 4 types of memory respectively. Here it is good to judge the correspondence according to the name. The same function FSMC_NORSRAMInit() is used to initialize NOR and SRAM. So the FSMC initialization function we use later is FSMC_NORSRAMInit(). The initialization function prototype is:


    
void FSMC_NORSRAMInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct);
    

This function has only one parameter, which is a structure pointer variable. The structure type is FSMC_NORSRAMInitTypeDef, and there are many member variables in it, because there are many configuration items related to FSMC. Below we briefly introduce its members:


    
typedef struct{

uint32_t FSMC_Bank;
uint32_t FSMC_DataAddressMux;
uint32_t FSMC_MemoryType;
uint32_t FSMC_MemoryDataWidth;
uint32_t FSMC_BurstAccessMode;
uint32_t FSMC_AsynchronousWait;
uint32_t FSMC_WaitSignalPolarity;
uint32_t FSMC_WrapMode;
uint32_t FSMC_WaitSignalActive;
uint32_t FSMC_WriteOperation;
uint32_t FSMC_WaitSignal;
uint32_t FSMC_ExtendedMode;
uint32_t FSMC_WriteBurst;
FSMC_NORSRAMTimingInitTypeDef* FSMC_ReadWriteTimingStruct;
FSMC_NORSRAMTimingInitTypeDef* FSMC_WriteTimingStruct;
}FSMC_NORSRAMInitTypeDef;
    

From this structure, we can see that there are 13 member variables of basic type (unit32_t) in front. These 13 parameters are used to configure the chip select control register FSMC_BCRx. The last two FSMC_NORSRAMTimingInitTypeDef Pointer type member variable. As we mentioned earlier, FSMC has read timing and write timing, so here are the parameters used to set the read timing and write timing, that is to say, these two parameters are used to configure the registers FSMC_BTRx and FSMC_BWTRx, we will later explained.

Let's take a look at these members:

  • FSMC_DataAddressMux: used to set the used memory block label and area code. In this chapter, we are using memory block 1 and area code 4, so the selected value is FSMC_Bank1_NORSRAM4.
  • FSMC_DataAddressMux: It is used to configure whether the data line and address line of FSMC are multiplexed. FSMC supports two modes of data and address line multiplexing or non-multiplexing. In non-multiplexing mode, 16-bit data lines and 26-bit address lines are used separately; in multiplexing mode, the lower 16-bit data/address lines are multiplexed, which is only valid for NOR and PSRAM. In multiplexed mode, address latches are recommended to distinguish data from addresses. In this experiment, FSMC is used to simulate 8080 timing, and only one address line A10 is used to provide the RS signal of 8080, so it does not need to be multiplexed, that is, set to FSMC_DataAddressMux_Disable.
  • FSMC_MemoryType: used to set the external memory type of FSMC, the optional types are NOR FLASH mode, PSARM mode and SRAM mode. We use TFTLCD as SRAM here, so the selected value is FSMC_MemoryType_SRAM.
  • FSMC_MemoryDataWidth: used to set the data width of the FSMC interface, you can choose 8-bit or 16-bit, here we are 16-bit data width, so the selected value is FSMC_MemoryDataWidth_16b.
  • FSMC_WriteOperation: used to configure the write operation enable, if the write operation is disabled, the FSMC will not generate a write sequence, but the data can still be read from the memory. This experiment needs to write data to the TFTLCD, so the write enable is required, and the configuration is FSMC_WriteOperation_Enable (write enable).
  • FSMC_ExtendedMode:Used to configure whether to use extended mode. In extended mode, read timing and write timing can use independent timing mode. For example, mode A is used for read timing, and mode B is used for write timing. These A, B, C, and D modes are actually not very different. The main reason is that when data/address line multiplexing is used, the timing generated by the FSMC signal is different.
  • FSMC_BurstAccessMode: Used to configure the access mode. FSMC access to memory is divided into asynchronous mode and burst mode (synchronous mode). In the asynchronous mode, a definite address needs to be generated each time the data is transferred, while the burst mode can provide an address at the beginning, and then write the data in groups continuously. This lab uses the asynchronous mode FSMC_BurstAccessMode_Disable.
  • FSMC_WaitSignalPolarity : (configure wait signal polarity), FSMC_WrapMode (configure whether to use non-alignment), FSMC_WaitSignalActive (configure when the wait signal is generated),
  • FSMC_WaitSignal : (configure whether to use wait signal), FSMC_WriteBurst (configure whether to allow burst write operation), these members need to be configured after burst mode is enabled. This experiment uses asynchronous mode, so the parameters of these members have no meaning.
  • FSMC_ReadWriteTimingStruct and FSMC_WriteTimingStruct: used to set the read and write timing. Both variables are of FSMC_NORSRAMTimingInitTypeDef structure pointer type. These two parameters are used to initialize the chip select control register FSMC_BTRx and the write operation timing control register FSMC_BWTRx respectively during initialization.

The FSMC_NORSRAMTimingInitTypeDef structure is as follows:


typedef struct{

uint32_t FSMC_AddressSetupTime;//Address setup time
uint32_t FSMC_AddressHoldTime;//Address hold time
uint32_t FSMC_DataSetupTime;//Data setup time
uint32_t FSMC_BusTurnAroundDuration;//Bus recovery time
uint32_t FSMC_CLKDivision;//Clock frequency division
uint32_t FSMC_DataLatency;//Data retention time
uint32_t FSMC_AccessMode;//Access mode
}FSMC_NORSRAMTimingInitTypeDef;

These parameters mainly control the address setup/hold time and data setup time, which are derived by dividing the FSMC clock from HCLK. The FSMC_CLKDivision and FSMC_AccessMode settings become effective only when Extended Mode is enabled. With Extended Mode, we can set different read and write timings. In this experiment, because the LCD requires different read and write speeds, Extended Mode is enabled and FSMC_DataSetupTime is configured separately for each.

These settings correspond to the FSMC_BTRx and FSMC_BWTRx registers. The values used here are based on the R61509V3 datasheet. During debugging, you can start with larger timing values and gradually reduce them to the minimum stable values for better display performance.

After understanding the function of structure members, you can configure it. The experimental configuration code in this is as follows:


FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef FSMC_ReadNORSRAMTiming;
FSMC_NORSRAMTimingInitTypeDef FSMC_WriteNORSRAMTiming;
FSMC_ReadTimingInitStructure.FSMC_AddressSetupTime = 0x01;//Address setup time (ADDSET) is 2 HCLK 1/36M=27ns
FSMC_ReadTimingInitStructure.FSMC_AddressHoldTime = 0x00;//Address hold time (ADDHLD) mode A is not used
FSMC_ReadTimingInitStructure.FSMC_DataSetupTime = 0x0f;//The data storage time is 16 HCLKs, because the reading speed of the LCD driver IC cannot be too fast, especially for the 1289 IC.
FSMC_ReadTimingInitStructure.FSMC_BusTurnAroundDuration = 0x00;
FSMC_ReadNORSRAMTiming.FSMC_CLKDivision = 0x00;
FSMC_ReadNORSRAMTiming.FSMC_DataLatency = 0x00;
FSMC_ReadNORSRAMTiming.FSMC_AccessMode = FSMC_AccessMode_A;//Mode A
FSMC_WriteNORSRAMTiming.FSMC_AddressSetupTime =0x00;//Address setup time (ADDSET) is 1 HCLK
FSMC_WriteNORSRAMTiming.FSMC_AddressHoldTime = 0x00;//ground
This part of the content FMSC Programming using CubeMX and TouchGFX AND can Here

RFID using Arduino for inventory Management System

Master RFID Technology with Arduino

Learn how to build your own RFID-based inventory management system from scratch

 RFID-based inventory management with Arduino

What is RFID Technology?

RFID (Radio-Frequency Identification) is a wireless technology that uses electromagnetic fields to automatically identify and track tags attached to objects, animals, or even people. Unlike traditional barcode systems, RFID doesn't require direct line-of-sight scanning, making it incredibly versatile for modern applications.
Understanding How RFID Works RFID technology operates on a simple yet powerful principle. When an RFID tag comes within range of an RFID reader, the reader emits radio waves that activate the tag. The tag then transmits its stored data back to the reader, which can process this information or send it to a connected system for further action.

Key Components of RFID Systems

  • 1. RFID Tags (Transponders) - Contains a microchip and antenna - Stores unique identification data - Available in various form factors (cards, key fobs, stickers)
  • 2. RFID Reader (Interrogator) - Emits radio waves to communicate with tags - Decodes information from tags - Interfaces with host systems
  • 3. Antenna - Transmits and receives radio signals - Determines read range and coverage area
  • 4. Backend Software - Processes collected data - Manages database operations - Provides user interface

Types of RFID Systems

  • Passive RFID: No internal power source; powered by reader's electromagnetic field. Range: 10cm - 1m. Cost: Low ($0.10-$1 per tag)
  • Active RFID: Battery-powered tags with longer range. Range: Up to 100m. Cost: Higher ($5-$100 per tag)
  • Semi-Passive RFID: Battery assists chip operation but not transmission. Range: 10-30m. Cost: Medium ($1-$10 per tag)

RFID Frequency Bands

Frequency Range Applications Data Rate
LF (125-134 kHz) 10 cm Animal tracking, access control Low
HF (13.56 MHz) 10 cm - 1 m Payment cards, ticketing Medium
UHF (860-960 MHz) 1 - 12 m Supply chain, inventory High

Types of RFID Systems

RFID with Arduino

Real-World Applications of RFID

📦 Inventory Management

Track products in warehouses, automate stock counting, reduce human errors

🏢 Access Control

Secure building entry, employee attendance tracking, parking management

💳 Contactless Payment

Credit cards, mobile payments, transit cards, toll collection

🏥 Healthcare

Patient tracking, medication management, equipment monitoring

Building Your First RFID Project with Arduino

Project Overview: Smart Inventory Management System

Today, we'll build a complete RFID-based inventory management system that can:

- Read and identify RFID tags

- Store product information

- Track items in/out

- Provide visual and audio feedback

- Connect to a PC application for data management

RFID Project /detail

Required Components

  • 1× Arduino Nano (or any Arduino board)
  • 1× MFRC522 RFID Module (13.56 MHz)
  • 2× RFID Tags (cards or key fobs)
  • 1× RGB LED (cathode)
  • 1× Piezo Buzzer
  • 3× 220Ω Resistors
  • 1× Breadboard
  • Jumper wires
  • USB cable for Arduino
Understanding the MFRC522 Module The MFRC522 is a highly integrated reader/writer IC for contactless communication at 13.56 MHz. Let's understand its specifications:

MFRC522 Specifications

  • Operating Frequency: 13.56 MHz
  • Supply Voltage: 3.3V (Important: Do NOT connect to 5V!)
  • Communication: SPI, I2C, UART
  • Read Range: 0-60mm (depending on antenna)
  • Data Transfer Rate:** Max 424 kbit/s
  • Supported Card Types:** MIFARE Classic, MIFARE Ultralight

Wiring the MFRC522 to Arduino Nano

MFRC522 Pin Arduino Nano Pin Description
VCC 3.3V Power supply (3.3V ONLY!)
RST D9 Reset and power-down
GND GND Ground
IRQ Not connected Interrupt pin (optional)
MISO D12 Master In Slave Out
MOSI D11 Master Out Slave In
SCK D13 Serial Clock
SDA/SS D10 Slave Select
⚠️ Important: The MFRC522 module operates at 3.3V. Connecting it to 5V will damage the module permanently! Always double-check your connections before powering on.
Additional Component Connections

RGB LED Connections:

Red Pin: Arduino D6 (through 220Ω resistor)
Green Pin: Arduino D5 (through 220Ω resistor)
Blue Pin: Arduino D4 (through 220Ω resistor)
Cathode: GND

Buzzer Connection:

Positive Pin: Arduino D3
Negative Pin: GND
Arduino with RFID Reader
Installing Required Libraries Before we can program our Arduino, we need to install the MFRC522 library:
1 Open Arduino IDE

Launch the Arduino IDE on your computer (version 1.8.0 or higher recommended)

2 Access Library Manager

Go to Tools → Manage Libraries... (or press Ctrl+Shift+I)

3 Search and Install

Search for "MFRC522" by GithubCommunity and click Install

## Arduino Code Implementation Let's build our RFID system step by step: Complete Arduino Code
RFID_Inventory_System.ino


#include 
#include 

// Pin Definitions
#define SS_PIN 10          // SDA/SS pin
#define RST_PIN 9          // Reset pin
#define RED_LED 6          // Red LED pin
#define GREEN_LED 5        // Green LED pin
#define BLUE_LED 4         // Blue LED pin
#define BUZZER_PIN 3       // Buzzer pin

// Create MFRC522 instance
MFRC522 rfid(SS_PIN, RST_PIN);

// Variables for storing card data
String tagUID = "";
String authorizedUIDs[] = {"43 7C A3 A9", "B3 C7 42 1A"}; // Add your card UIDs here
int totalAuthorizedCards = 2;

// System states
bool accessGranted = false;
unsigned long lastReadTime = 0;
const unsigned long readDelay = 2000; // 2 seconds between reads

void setup() {
  // Initialize Serial Communication
  Serial.begin(9600);
  while (!Serial); // Wait for Serial Monitor to open
  
  Serial.println("=================================");
  Serial.println("RFID Inventory Management System");
  Serial.println("=================================");
  Serial.println("Initializing system...");
  
  // Initialize SPI bus
  SPI.begin();
  
  // Initialize MFRC522
  rfid.PCD_Init();
  
  // Check if RFID reader is connected
  if (rfid.PCD_PerformSelfTest()) {
    Serial.println("✓ RFID Reader detected and working!");
  } else {
    Serial.println("✗ RFID Reader not found. Check connections!");
    while(1); // Stop if reader not found
  }
  
  // Initialize outputs
  pinMode(RED_LED, OUTPUT);
  pinMode(GREEN_LED, OUTPUT);
  pinMode(BLUE_LED, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  
  // Test indicators
  startupSequence();
  
  Serial.println("✓ System ready!");
  Serial.println("Place your RFID card near the reader...\n");
  
  // Set default LED state (blue = ready)
  setLEDColor(0, 0, 255);
}

void loop() {
  // Check if enough time has passed since last read
  if (millis() - lastReadTime < readDelay) {
    return;
  }
  
  // Look for new cards
  if (!rfid.PICC_IsNewCardPresent()) {
    return;
  }
  
  // Select one of the cards
  if (!rfid.PICC_ReadCardSerial()) {
    return;
  }
  
  // Process the card
  lastReadTime = millis();
  tagUID = getTagUID();
  
  Serial.println("=================================");
  Serial.print("Card Detected! UID: ");
  Serial.println(tagUID);
  
  // Check if card is authorized
  if (isAuthorized(tagUID)) {
    grantAccess();
  } else {
    denyAccess();
  }
  
  // Additional card information
  displayCardInfo();
  
  // Halt PICC
  rfid.PICC_HaltA();
  
  // Stop encryption on PCD
  rfid.PCD_StopCrypto1();
  
  // Reset LED to ready state after delay
  delay(1500);
  setLEDColor(0, 0, 255); // Blue = ready
}

// Function to get tag UID as string
String getTagUID() {
  String uid = "";
  for (byte i = 0; i < rfid.uid.size; i++) {
    uid += String(rfid.uid.uidByte[i] < 0x10 ? "0" : "");
    uid += String(rfid.uid.uidByte[i], HEX);
    if (i < rfid.uid.size - 1) uid += " ";
  }
  uid.toUpperCase();
  return uid;
}

// Check if tag is authorized
bool isAuthorized(String uid) {
  for (int i = 0; i < totalAuthorizedCards; i++) {
    if (uid == authorizedUIDs[i]) {
      return true;
    }
  }
  return false;
}

// Grant access sequence
void grantAccess() {
  Serial.println("✓ ACCESS GRANTED!");
  Serial.println("Welcome! This card is authorized.");
  
  // Visual feedback - Green LED
  setLEDColor(0, 255, 0);
  
  // Audio feedback - Success tone
  tone(BUZZER_PIN, 1000, 200);
  delay(250);
  tone(BUZZER_PIN, 1500, 200);
  
  // Log the access
  logAccess(tagUID, true);
}

// Deny access sequence
void denyAccess() {
  Serial.println("✗ ACCESS DENIED!");
  Serial.println("This card is not authorized.");
  
  // Visual feedback - Red LED
  setLEDColor(255, 0, 0);
  
  // Audio feedback - Error tone
  for (int i = 0; i < 3; i++) {
    tone(BUZZER_PIN, 300, 100);
    delay(150);
  }
  
  // Log the attempt
  logAccess(tagUID, false);
}

// Display detailed card information
void displayCardInfo() {
  Serial.println("\n--- Card Information ---");
  
  // Card UID
  Serial.print("UID (HEX): ");
  Serial.println(tagUID);
  
  // Card UID in decimal
  Serial.print("UID (DEC): ");
  for (byte i = 0; i < rfid.uid.size; i++) {
    Serial.print(rfid.uid.uidByte[i]);
    if (i < rfid.uid.size - 1) Serial.print(", ");
  }
  Serial.println();
  
  // Card type
  MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
  Serial.print("Card Type: ");
  Serial.println(rfid.PICC_GetTypeName(piccType));
  
  // Memory size (for Mifare Classic)
  if (piccType == MFRC522::PICC_TYPE_MIFARE_1K) {
    Serial.println("Memory Size: 1KB (16 sectors × 4 blocks × 16 bytes)");
  } else if (piccType == MFRC522::PICC_TYPE_MIFARE_4K) {
    Serial.println("Memory Size: 4KB");
  }
  
  Serial.println("------------------------\n");
}

// Log access attempts
void logAccess(String uid, bool granted) {
  Serial.print("[LOG] ");
  Serial.print(millis() / 1000); // Time in seconds
  Serial.print("s - UID: ");
  Serial.print(uid);
  Serial.print(" - Status: ");
  Serial.println(granted ? "GRANTED" : "DENIED");
}

// Set RGB LED color
void setLEDColor(int red, int green, int blue) {
  // For common cathode RGB LED
  analogWrite(RED_LED, red);
  analogWrite(GREEN_LED, green);
  analogWrite(BLUE_LED, blue);
}

// Startup sequence for testing
void startupSequence() {
  Serial.println("Testing indicators...");
  
  // Test each LED color
  Serial.print("Testing RED LED... ");
  setLEDColor(255, 0, 0);
  delay(500);
  Serial.println("OK");
  
  Serial.print("Testing GREEN LED... ");
  setLEDColor(0, 255, 0);
  delay(500);
  Serial.println("OK");
  
  Serial.print("Testing BLUE LED... ");
  setLEDColor(0, 0, 255);
  delay(500);
  Serial.println("OK");
  
  // Test buzzer
  Serial.print("Testing Buzzer... ");
  tone(BUZZER_PIN, 1000, 100);
  delay(150);
  tone(BUZZER_PIN, 1500, 100);
  Serial.println("OK");
  
  // Turn off all
  setLEDColor(0, 0, 0);
  delay(500);
}

// Function to write data to card (Advanced)
void writeToCard(byte sector, byte blockAddr, byte* data) {
  // This is an advanced function for writing data to RFID cards
  // Use with caution as incorrect usage can damage the card
  
  MFRC522::StatusCode status;
  byte trailerBlock = sector * 4 + 3; // Trailer block for the sector
  MFRC522::MIFARE_Key key;
  
  // Using default key (all 0xFF)
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }
  
  // Authenticate
  status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 
                                  trailerBlock, &key, &(rfid.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print("Authentication failed: ");
    Serial.println(rfid.GetStatusCodeName(status));
    return;
  }
  
  // Write data
  status = rfid.MIFARE_Write(blockAddr, data, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print("Write failed: ");
    Serial.println(rfid.GetStatusCodeName(status));
  } else {
    Serial.println("Data written successfully!");
  }
}


## Understanding the Code ### Key Functions Explained

1. Setup Function

Initializes all components, performs self-test, and prepares the system for operation. The startup sequence tests each component to ensure everything is working correctly.

2. Main Loop

Continuously checks for new RFID cards. When detected, it reads the UID, checks authorization, and provides appropriate feedback through LEDs and buzzer.

3. Authorization System

Compares detected card UID with a list of authorized UIDs. You can easily add or remove authorized cards by modifying the authorizedUIDs[] array.

4. Feedback System

Provides both visual (RGB LED) and audio (buzzer) feedback. Green indicates success, red indicates failure, and blue indicates ready state.

Testing Your System
  1. Upload the Code: Connect Arduino to your computer and upload the code
  2. Open Serial Monitor: Set baud rate to 9600 and watch the initialization process
  3. Test with RFID Card: Place an RFID card near the reader (within 1-2 cm)
  4. Note the UID: Copy the UID displayed in Serial Monitor
  5. Add to Authorized List: Add the UID to the authorizedUIDs[] array and re-upload
  6. Test Authorization: Try both authorized and unauthorized cards
RFID Reader connect to Arduino
Advanced Features Implementation Feature 1: Data Storage on RFID Card RFID cards have memory sectors that can store data. Here's how to utilize them:

Writing Product Information to Card



void storeProductInfo(String productID, String productName, float price) {
  byte dataBlock[16];
  
  // Format: [ID:4bytes][Name:8bytes][Price:4bytes]
  // Convert and store data in byte array
  
  // Example: Store product ID
  for (int i = 0; i < 4; i++) {
    if (i < productID.length()) {
      dataBlock[i] = productID[i];
    } else {
      dataBlock[i] = 0x00;
    }
  }
  
  // Write to sector 1, block 4
  writeToCard(1, 4, dataBlock);
}
```
### Feature 2: PC Communication Protocol For inventory management, we need to communicate with a PC application:


void sendToPC(String command, String data) {
  // Protocol: 
  Serial.print("<");
  Serial.print(command);
  Serial.print(":");
  Serial.print(data);
  Serial.println(">");
}

// Usage examples:
// sendToPC("CARD_READ", tagUID);
// sendToPC("ACCESS", "GRANTED");
// sendToPC("INVENTORY", "ITEM_IN");
```
Building the PC Application Creating the Inventory Management Software While the Arduino handles the hardware interface, we need a PC application to manage the inventory database. Here's a simple C# implementation structure:

C# Application Components



// Main components needed:
// 1. Serial Communication Handler
// 2. Database Manager (SQLite recommended for simplicity)
// 3. User Interface (Windows Forms or WPF)
// 4. Reporting Module

public class RFIDInventorySystem
{
    private SerialPort arduinoPort;
    private SQLiteConnection database;
    
    public void Initialize()
    {
        // Setup serial communication
        arduinoPort = new SerialPort("COM3", 9600);
        arduinoPort.DataReceived += ProcessRFIDData;
        
        // Initialize database
        InitializeDatabase();
    }
    
    private void ProcessRFIDData(object sender, SerialDataReceivedEventArgs e)
    {
        string data = arduinoPort.ReadLine();
        // Parse protocol: 
        // Update inventory based on command
    }
}
## Extending Your Project
RFID with inventory System RFID with inventory dll

🚪 Door Lock System

Add a relay module to control an electronic door lock

📊 LCD Display

Add an I2C LCD to show card information without PC

📱 IoT Integration

Use ESP8266/ESP32 to send data to cloud database

⏰ Time Attendance

Add RTC module for employee attendance tracking

Security Considerations

Important Security Notes

  • Default Keys:Most MIFARE cards use default keys (0xFFFFFFFFFFFF). Change these for production use!
  • UID Cloning: UIDs can be cloned. For high-security applications, use encrypted sectors and rolling codes.
  • Communication Security: Serial communication is unencrypted. For production, implement encryption between Arduino and PC.
  • Physical Security: RFID readers should be in secure enclosures to prevent tampering.

Performance Optimization Tips

  • Reduce Read Delay:** Adjust `readDelay` variable based on your needs
  • Optimize Memory:** Use PROGMEM for storing constant strings
  • Batch Processing:** Read multiple cards before sending data to PC
  • Power Management:** Implement sleep modes when not actively reading
Common RFID Card Types
Card Type Memory Security Cost Use Case
MIFARE Classic 1K 1KB Basic $0.30 Access control, transit
MIFARE Classic 4K 4KB Basic $0.50 Multiple applications
MIFARE Ultralight 64B Low $0.20 Tickets, tags
MIFARE DESFire 2/4/8KB High $2.00 High security, payment
Resources and Downloads ### What's Included: - Complete Arduino source code - Circuit diagrams and PCB layouts - 3D printable enclosure designs - Sample C# application - Database schema - User manual PDF Video Tutorial

Conclusion

Congratulations! You've successfully built an RFID-based inventory management system. This project demonstrates fundamental concepts of RFID technology, embedded systems programming, and serial communication. With this foundation, you can expand into more complex applications like automated warehouses, smart retail systems, or IoT-enabled tracking solutions. Remember to experiment with different features and share your innovations with the community!

Basic of Arduino OTA

🚀 Arduino OTA Programming: Complete Wireless Update Guide

Master wireless programming and revolutionize your IoT development workflow with Over-The-Air updates

What is Arduino OTA Programming?

Over-The-Air (OTA) programming is a revolutionary technology that allows you to upload code to your Arduino board wirelessly, completely eliminating the need for physical USB connections. Imagine updating your IoT devices remotely, managing multiple sensors from your desk, or maintaining projects installed in hard-to-reach locations—all without touching a single cable.

OTA breaks the traditional limitation of requiring physical access to each device, enabling true remote development and maintenance. It's particularly powerful for IoT applications where devices might be deployed across large areas, embedded in walls, mounted on ceilings, or installed in outdoor locations.

🎯 Why OTA Changes Everything

🏠 Smart Home Automation

Update sensors and controllers installed in walls, ceilings, or other inaccessible locations without disassembly

🏭 Industrial Monitoring

Push firmware updates to hundreds of devices across factory floors without manual intervention

🌾 Agriculture IoT

Remotely update sensors deployed across large farms and fields without physical site visits

🏥 Healthcare Devices

Deploy critical updates to medical monitoring equipment with minimal disruption

🚗 Vehicle Systems

Update embedded systems in vehicles without requiring physical access to control units

🌐 Remote Installations

Maintain devices in remote locations like weather stations, wildlife cameras, and environmental sensors

📋 Prerequisites & Hardware Requirements

Hardware Compatibility

Not all Arduino boards support OTA programming out of the box. Here's what you need:

✅ Supported Boards (Built-in Wi-Fi)

  • ESP32 Series: ESP32-WROOM, ESP32-WROVER, ESP32-S2, ESP32-C3, ESP32-S3
  • ESP8266 Series: NodeMCU, WeMos D1 Mini, ESP-01, ESP-12E
  • Arduino MKR WiFi 1010: Official Arduino board with Wi-Fi
  • Arduino Nano 33 IoT: Compact board with wireless capabilities
  • Arduino Portenta H7: Professional board with Wi-Fi/Bluetooth

❌ Boards Requiring Additional Hardware

  • Arduino Uno/Nano: Requires ESP8266 Wi-Fi module (ESP-01)
  • Arduino Mega: Requires external Wi-Fi shield or module
  • Arduino Leonardo: Needs Wi-Fi adapter for OTA functionality

⚠️ Important Hardware Notes

Classic Arduino boards (Uno, Nano, Mega) do NOT have built-in Wi-Fi! They require additional Wi-Fi modules, which adds complexity and cost. For OTA projects, we strongly recommend starting with ESP32 or ESP8266 boards—they're inexpensive, widely available, and have excellent OTA support.

What You'll Need

  • ESP32 or ESP8266 Development Board (recommended: NodeMCU ESP32 or WeMos D1 Mini)
  • Micro-USB Cable for initial setup (only needed once!)
  • Computer with Arduino IDE (version 1.8.13 or later)
  • Stable Wi-Fi Network with good signal strength
  • Wi-Fi Network Credentials (SSID and password)
ESP32 Development Board for OTA Programming

🔧 Software Setup and Installation

1

Install Arduino IDE

Download and install the latest Arduino IDE from arduino.cc. Minimum version 1.8.13 is required for proper OTA support.

  • Windows: Run the installer and follow prompts
  • Mac: Drag Arduino.app to Applications folder
  • Linux: Extract and run install.sh script
2

Install ESP32/ESP8266 Board Support

Open Arduino IDE and add board support packages:

  • Go to File → Preferences
  • In "Additional Board Manager URLs", add:

For ESP32:

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

For ESP8266:

http://arduino.esp8266.com/stable/package_esp8266com_index.json
  • Go to Tools → Board → Boards Manager
  • Search for "ESP32" or "ESP8266"
  • Click Install (download size: ~200-300 MB)
3

Install Required Libraries

The OTA functionality requires these libraries (most are pre-installed with board packages):

  • WiFi.h - Handles Wi-Fi connectivity
  • ESPmDNS.h - Enables device discovery on network
  • WiFiUdp.h - UDP communication for OTA
  • ArduinoOTA.h - Core OTA functionality
4

Configure Board Settings

Select your board in Arduino IDE:

  • Tools → Board → ESP32 Arduino → ESP32 Dev Module (for ESP32)
  • Tools → Board → ESP8266 Boards → NodeMCU 1.0 (for ESP8266)
  • Set Upload Speed: 115200
  • Set Partition Scheme: Default (or "Minimal SPIFFS" for more program space)

💻 Your First OTA Program - Complete Example

Basic OTA Implementation

This complete example demonstrates all essential OTA functionality. Copy this code as your starting point:

basic_ota_example.ino - Complete Working Code
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

// ========== CONFIGURATION ==========
// Replace with your network credentials
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

// OTA Settings
const char* otaHostname = "esp32-ota-device";
const char* otaPassword = "admin123";  // Change this!

// ========== SETUP ==========
void setup() {
  Serial.begin(115200);
  Serial.println("\n\n");
  Serial.println("========================================");
  Serial.println("    Arduino OTA Programming Demo");
  Serial.println("========================================\n");
  
  // Initialize LED for status indication
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  
  // Connect to Wi-Fi
  connectToWiFi();
  
  // Configure and start OTA
  setupOTA();
  
  Serial.println("\n🎉 Setup Complete!");
  Serial.println("📡 Device ready for OTA updates");
  Serial.println("========================================\n");
}

// ========== MAIN LOOP ==========
void loop() {
  // CRITICAL: Must call this in loop for OTA to work
  ArduinoOTA.handle();
  
  // Your application code here
  static unsigned long lastHeartbeat = 0;
  if (millis() - lastHeartbeat > 5000) {
    Serial.println("💓 System running normally - Waiting for OTA...");
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));  // Blink LED
    lastHeartbeat = millis();
  }
  
  // Keep loop responsive for OTA operations
  delay(100);
}

// ========== WIFI CONNECTION ==========
void connectToWiFi() {
  Serial.print("📶 Connecting to Wi-Fi: ");
  Serial.println(ssid);
  
  WiFi.mode(WIFI_STA);  // Station mode (client)
  WiFi.begin(ssid, password);
  
  // Wait for connection with timeout
  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 50) {
    delay(500);
    Serial.print(".");
    attempts++;
  }
  
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\n✅ Wi-Fi Connected Successfully!");
    Serial.print("📡 IP Address: ");
    Serial.println(WiFi.localIP());
    Serial.print("📶 Signal Strength: ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
    Serial.print("🌐 MAC Address: ");
    Serial.println(WiFi.macAddress());
  } else {
    Serial.println("\n❌ Wi-Fi Connection Failed!");
    Serial.println("⚠️  Restarting device in 5 seconds...");
    delay(5000);
    ESP.restart();
  }
}

// ========== OTA CONFIGURATION ==========
void setupOTA() {
  Serial.println("\n⚙️  Configuring OTA...");
  
  // Set OTA hostname (appears in Arduino IDE port list)
  ArduinoOTA.setHostname(otaHostname);
  Serial.print("🏷️  Hostname: ");
  Serial.println(otaHostname);
  
  // Set OTA password for security
  ArduinoOTA.setPassword(otaPassword);
  Serial.println("🔐 Password protection: ENABLED");
  
  // Optional: Set OTA port (default is 3232)
  // ArduinoOTA.setPort(3232);
  
  // ===== OTA EVENT HANDLERS =====
  
  // Called when OTA update starts
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";  // Uploading program code
    } else {
      type = "filesystem";  // Uploading SPIFFS/LittleFS
    }
    Serial.println("\n🔄 OTA Update Starting...");
    Serial.println("📦 Type: " + type);
    digitalWrite(LED_BUILTIN, HIGH);  // Turn on LED during update
  });
  
  // Called when OTA update completes successfully
  ArduinoOTA.onEnd([]() {
    Serial.println("\n✅ OTA Update Complete!");
    Serial.println("🔄 Rebooting device...");
    digitalWrite(LED_BUILTIN, LOW);
  });
  
  // Called during upload to show progress
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    unsigned int percent = (progress * 100) / total;
    Serial.printf("📊 Progress: %u%% (%u/%u bytes)\r", 
                  percent, progress, total);
    
    // Blink LED to show activity
    if (percent % 10 == 0) {
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    }
  });
  
  // Called when OTA encounters an error
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("\n❌ Error[%u]: ", error);
    
    switch(error) {
      case OTA_AUTH_ERROR:
        Serial.println("Authentication Failed");
        Serial.println("💡 Check your OTA password");
        break;
      case OTA_BEGIN_ERROR:
        Serial.println("Begin Failed");
        Serial.println("💡 Not enough space or partition issue");
        break;
      case OTA_CONNECT_ERROR:
        Serial.println("Connect Failed");
        Serial.println("💡 Network connection lost");
        break;
      case OTA_RECEIVE_ERROR:
        Serial.println("Receive Failed");
        Serial.println("💡 Data corruption during transfer");
        break;
      case OTA_END_ERROR:
        Serial.println("End Failed");
        Serial.println("💡 Upload incomplete");
        break;
    }
    
    // Blink LED rapidly to indicate error
    for(int i = 0; i < 10; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(100);
    }
  });
  
  // Start OTA service
  ArduinoOTA.begin();
  Serial.println("✅ OTA Service Started");
}
======================================== Arduino OTA Programming Demo ======================================== 📶 Connecting to Wi-Fi: MyNetwork .......... ✅ Wi-Fi Connected Successfully! 📡 IP Address: 192.168.1.105 📶 Signal Strength: -45 dBm 🌐 MAC Address: 24:6F:28:AB:CD:EF ⚙️ Configuring OTA... 🏷️ Hostname: esp32-ota-device 🔐 Password protection: ENABLED ✅ OTA Service Started 🎉 Setup Complete! 📡 Device ready for OTA updates ======================================== 💓 System running normally - Waiting for OTA...

📝 Step-by-Step Upload Process

First Upload (USB Required)

  1. Configure Network Credentials
    • Replace YOUR_WIFI_SSID with your Wi-Fi network name
    • Replace YOUR_WIFI_PASSWORD with your network password
    • Change admin123 to a secure OTA password
  2. Connect via USB
    • Connect your ESP32/ESP8266 to computer using USB cable
    • Select correct board: Tools → Board → ESP32 Dev Module
    • Select correct port: Tools → Port → COM3 (or your device port)
  3. Upload the Code
    • Click Upload button (→) in Arduino IDE
    • Wait for compilation and upload to complete
    • Open Serial Monitor (Ctrl+Shift+M) at 115200 baud
  4. Verify Connection
    • Check Serial Monitor output for "✅ Wi-Fi Connected Successfully!"
    • Note the IP address displayed (e.g., 192.168.1.105)
    • Look for "🎉 Setup Complete!" message
    • You should see "💓 System running normally" every 5 seconds

Subsequent Uploads (Wireless - No USB!)

  1. Wait for Network Port Discovery
    • Keep the device powered on and connected to Wi-Fi
    • Wait 30-60 seconds for Arduino IDE to discover the network port
    • Go to Tools → Port
  2. Select Network Port
    • You'll now see a network port option: esp32-ota-device at 192.168.1.105
    • Select this network port instead of the USB COM port
    • The port name matches your otaHostname setting
  3. Make Code Changes
    • Modify your code as needed (change LED blink rate, add sensors, etc.)
    • Important: Keep ArduinoOTA.handle(); in your loop!
    • Save your changes
  4. Upload Wirelessly
    • Click Upload button
    • Arduino IDE will compile the code
    • You'll be prompted for the OTA password
    • Enter your password (e.g., "admin123")
    • Watch the Serial Monitor for upload progress
  5. Monitor Upload Progress
    • Serial Monitor will show: "🔄 OTA Update Starting..."
    • Progress updates: "📊 Progress: 45% (23456/52000 bytes)"
    • Completion: "✅ OTA Update Complete!"
    • Device automatically reboots and reconnects to Wi-Fi

⚠️ Critical OTA Requirements

Your code MUST call ArduinoOTA.handle(); in the loop() function! Without this, OTA updates won't work. If you have long delays or blocking code in your loop, OTA will timeout. Keep your loop() responsive!

Avoid These Common Mistakes:

  • delay(10000); - Long delays block OTA communication
  • while(1) { /* blocking code */ } - Infinite loops prevent OTA
  • ❌ Forgetting to call ArduinoOTA.handle();
  • ✅ Use millis() for non-blocking timing instead

🔧 Troubleshooting Common Issues

Device Not Appearing in Network Ports

Problem: Network port doesn't show up in Arduino IDE

Solutions:

  • Check Same Network: Ensure computer and ESP device are on the same Wi-Fi network (2.4 GHz network recommended, as most ESPs don't support 5 GHz)
  • Wait Longer: Device discovery can take 60-90 seconds. Restart Arduino IDE if needed
  • Firewall Issues: Windows Firewall might block mDNS. Add Arduino IDE to firewall exceptions
  • Check Serial Output: Verify device shows "OTA Service Started" in Serial Monitor
  • Ping Test: Open Command Prompt and ping the device IP: ping 192.168.1.105
Problem: "Connection Refused" or "Connection Timeout"

Solutions:

  • Weak Signal: Move device closer to Wi-Fi router (Signal strength should be stronger than -70 dBm)
  • Network Congestion: Try uploading during off-peak hours or on less congested channel
  • Wrong Password: Verify you're entering the correct OTA password
  • Router Settings: Some routers block client-to-client communication. Check "AP Isolation" setting
Problem: Upload starts but fails at random percentage

Solutions:

  • Power Supply: Use quality USB power supply (minimum 2A for ESP32). Weak power causes random failures
  • Loop Blocking: Check if your loop() has long delays. Use millis() for timing instead
  • Memory Issues: Large sketches might not fit. Check free space: ESP.getFreeSketchSpace()
  • Interference: Keep device away from microwave ovens, Bluetooth devices, and metal objects
Problem: Device reboots during normal operation

Solutions:

  • Watchdog Timer: ESP32 has automatic watchdog. Long-running code in loop() triggers reboot
  • Memory Leak: Check for memory leaks using ESP.getFreeHeap()
  • Power Fluctuations: Add decoupling capacitors (100µF) near power pins

🔐 Security Best Practices

Essential Security Measures

1. Strong Authentication

  • Complex Passwords: Use minimum 12 characters with mix of uppercase, lowercase, numbers, and symbols
  • Unique Passwords: Different password for each device in production
  • Regular Rotation: Change OTA passwords periodically (every 3-6 months)
  • Never Hardcode: In production, load credentials from secure storage

2. Network Security

  • WPA3 Encryption: Use WPA3 or minimum WPA2-PSK for Wi-Fi network
  • Isolated Network: Create separate IoT VLAN for device network
  • No Public Wi-Fi: Never perform OTA updates over public networks
  • VPN for Remote: Use VPN for remote OTA updates over internet

3. Code Protection

  • Firmware Validation: Implement checksum verification before applying updates
  • Rollback Mechanism: Keep previous firmware version for emergency rollback
  • Signed Binaries: Use cryptographic signing to verify firmware authenticity
  • Disable in Production: Consider disabling OTA after initial deployment for critical systems

Enhanced Security Implementation

secure_ota_with_validation.ino
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <esp_task_wdt.h>

// Security configuration
const char* ssid = "YOUR_NETWORK";
const char* password = "YOUR_PASSWORD";
const char* otaPassword = "StrongP@ssw0rd!123";
const char* allowedMAC = "AA:BB:CC:DD:EE:FF";  // Only allow from specific computer

// Firmware version for rollback protection
const char* FIRMWARE_VERSION = "v1.2.3";
uint32_t EXPECTED_FIRMWARE_SIZE = 0;  // Set this to expected size

// OTA restrictions
bool otaEnabled = true;
unsigned long otaDisableTime = 0;
const unsigned long OTA_TIMEOUT = 300000;  // Disable OTA after 5 minutes

void setup() {
  Serial.begin(115200);
  
  // Initialize watchdog timer
  esp_task_wdt_init(30, true);
  esp_task_wdt_add(NULL);
  
  Serial.printf("\n🚀 Firmware Version: %s\n", FIRMWARE_VERSION);
  
  connectToWiFi();
  setupSecureOTA();
  
  // Automatically disable OTA after timeout
  otaDisableTime = millis() + OTA_TIMEOUT;
}

void loop() {
  esp_task_wdt_reset();  // Feed watchdog
  
  // Handle OTA only if enabled and within timeout
  if (otaEnabled && millis() < otaDisableTime) {
    ArduinoOTA.handle();
  } else if (otaEnabled) {
    Serial.println("⏱️  OTA timeout reached - disabling for security");
    otaEnabled = false;
  }
  
  // Your application code
  delay(100);
}

void connectToWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("❌ Connection Failed! Retrying...");
    delay(5000);
  }
  
  Serial.println("✅ Connected to WiFi");
  Serial.printf("📡 IP: %s\n", WiFi.localIP().toString().c_str());
  Serial.printf("🌐 MAC: %s\n", WiFi.macAddress().c_str());
}

void setupSecureOTA() {
  ArduinoOTA.setHostname("secure-esp32");
  ArduinoOTA.setPassword(otaPassword);
  
  // Enhanced security: MD5 password hash
  // String md5Pass = md5(otaPassword);
  // ArduinoOTA.setPasswordHash(md5Pass);
  
  ArduinoOTA.onStart([]() {
    // Disable OTA immediately after start to prevent multiple updates
    otaEnabled = false;
    
    String type = (ArduinoOTA.getCommand() == U_FLASH) ? "sketch" : "filesystem";
    Serial.println("🔄 Starting secure OTA update: " + type);
    
    // Stop all critical operations
    // stopSensors();
    // closeDatabaseConnections();
  });
  
  ArduinoOTA.onEnd([]() {
    Serial.println("\n✅ Update successful - Rebooting");
  });
  
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    static unsigned int lastPercent = 0;
    unsigned int percent = (progress * 100) / total;
    
    if (percent != lastPercent && percent % 10 == 0) {
      Serial.printf("📊 Progress: %u%%\n", percent);
      lastPercent = percent;
    }
    
    // Validate size during upload
    if (EXPECTED_FIRMWARE_SIZE > 0 && total != EXPECTED_FIRMWARE_SIZE) {
      Serial.println("⚠️  Warning: Firmware size mismatch!");
    }
  });
  
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("❌ Error[%u]: ", error);
    
    switch(error) {
      case OTA_AUTH_ERROR:
        Serial.println("Authentication Failed - Possible attack!");
        // Log to external system
        // sendSecurityAlert("OTA Auth Failure");
        break;
      case OTA_BEGIN_ERROR:
        Serial.println("Begin Failed");
        break;
      case OTA_CONNECT_ERROR:
        Serial.println("Connect Failed");
        break;
      case OTA_RECEIVE_ERROR:
        Serial.println("Receive Failed - Possible corruption");
        break;
      case OTA_END_ERROR:
        Serial.println("End Failed");
        break;
    }
    
    // Re-enable OTA with new timeout after error
    otaEnabled = true;
    otaDisableTime = millis() + OTA_TIMEOUT;
  });
  
  ArduinoOTA.begin();
  Serial.println("🔐 Secure OTA enabled");
  Serial.printf("⏱️  Auto-disable in %lu seconds\n", OTA_TIMEOUT / 1000);
}

🚀 Advanced OTA Techniques

🔄 Automatic Rollback

Implement dual-partition system where failed updates automatically revert to previous working firmware using ESP32's OTA partitions

📊 Fleet Management

Update hundreds of devices simultaneously using mDNS service discovery and batch upload scripts

🌐 Web-Based OTA

Create custom web interface for firmware updates without Arduino IDE using ESP32 Web Server

☁️ Cloud OTA Updates

Fetch firmware from cloud servers (AWS, Azure) and automatically update devices when new versions available

🔔 Update Notifications

Implement MQTT or webhook notifications to alert when OTA updates complete or fail

📱 Mobile App Integration

Build iOS/Android apps that can trigger and monitor OTA updates using Bluetooth or HTTP APIs

🎯 Web-Based OTA Update Interface

Creating Custom Web Upload Page

This advanced example creates a web interface for OTA updates without needing Arduino IDE:

web_ota_server.ino - Complete Web Interface
#include <WiFi.h>
#include <WebServer.h>
#include <Update.h>

const char* ssid = "YOUR_NETWORK";
const char* password = "YOUR_PASSWORD";

WebServer server(80);

// HTML page for firmware upload
const char* uploadPage = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
    <title>ESP32 OTA Update</title>
    <meta name='viewport' content='width=device-width, initial-scale=1'>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 600px;
            margin: 50px auto;
            padding: 20px;
            background: #f0f0f0;
        }
        .container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
            text-align: center;
        }
        .upload-form {
            margin-top: 30px;
        }
        input[type='file'] {
            width: 100%;
            padding: 10px;
            margin: 10px 0;
            border: 2px dashed #4CAF50;
            border-radius: 5px;
        }
        button {
            width: 100%;
            padding: 15px;
            background: #4CAF50;
            color: white;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            cursor: pointer;
        }
        button:hover {
            background: #45a049;
        }
        .progress {
            display: none;
            margin: 20px 0;
        }
        .progress-bar {
            width: 100%;
            height: 30px;
            background: #f0f0f0;
            border-radius: 5px;
            overflow: hidden;
        }
        .progress-fill {
            height: 100%;
            background: #4CAF50;
            width: 0%;
            transition: width 0.3s;
        }
        .status {
            text-align: center;
            margin: 10px 0;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div class='container'>
        <h1>🚀 ESP32 Firmware Update</h1>
        <p style='text-align:center; color:#666;'>
            Upload .bin firmware file
        </p>
        
        <form class='upload-form' method='POST' action='/update' enctype='multipart/form-data'>
            <input type='file' name='firmware' accept='.bin' required>
            <button type='submit'>Upload Firmware</button>
        </form>
        
        <div class='progress' id='progress'>
            <div class='status' id='status'>Uploading...</div>
            <div class='progress-bar'>
                <div class='progress-fill' id='progressBar'></div>
            </div>
        </div>
    </div>
    
    <script>
        const form = document.querySelector('form');
        form.addEventListener('submit', function() {
            document.getElementById('progress').style.display = 'block';
        });
    </script>
</body>
</html>
)rawliteral";

void setup() {
  Serial.begin(115200);
  
  // Connect to WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("\n✅ WiFi Connected");
  Serial.print("🌐 Web OTA Portal: http://");
  Serial.println(WiFi.localIP());
  
  // Setup web server routes
  server.on("/", HTTP_GET, []() {
    server.send(200, "text/html", uploadPage);
  });
  
  server.on("/update", HTTP_POST, 
    []() {
      server.send(200, "text/html", 
        "<h1>✅ Update Successful!</h1><p>Device will reboot...</p>");
      delay(1000);
      ESP.restart();
    },
    []() {
      HTTPUpload& upload = server.upload();
      
      if (upload.status == UPLOAD_FILE_START) {
        Serial.printf("🔄 Update: %s\n", upload.filename.c_str());
        
        if (!Update.begin(UPDATE_SIZE_UNKNOWN)) {
          Update.printError(Serial);
        }
      } 
      else if (upload.status == UPLOAD_FILE_WRITE) {
        if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
          Update.printError(Serial);
        } else {
          Serial.printf("📊 Progress: %u%%\r", 
                       (upload.totalSize * 100) / upload.currentSize);
        }
      } 
      else if (upload.status == UPLOAD_FILE_END) {
        if (Update.end(true)) {
          Serial.printf("\n✅ Update Success: %u bytes\n", upload.totalSize);
        } else {
          Update.printError(Serial);
        }
      }
    }
  );
  
  server.begin();
  Serial.println("🎉 Web server started!");
}

void loop() {
  server.handleClient();
}

Using Web-Based OTA

  1. Upload the web server sketch to your ESP32 via USB (one-time setup)
  2. Open Serial Monitor and note the IP address (e.g., 192.168.1.105)
  3. Open web browser and navigate to: http://192.168.1.105
  4. Export compiled binary from Arduino IDE: Sketch → Export Compiled Binary
  5. Use web interface to upload the .bin file from your downloads folder
  6. Wait for completion - device automatically reboots with new firmware

💡 Performance Optimization Tips

Maximize OTA Reliability and Speed

Upload Speed Optimization

  • Position Matters: Place ESP device within 10 meters of Wi-Fi router for best speed
  • Channel Selection: Use less congested 2.4 GHz channel (1, 6, or 11)
  • Bandwidth Mode: Set router to 40 MHz channel width for faster transfers
  • Reduce Sketch Size: Remove debug code, unused libraries, and optimize strings
  • Partition Scheme: Use "Minimal SPIFFS" or "No OTA" partition for more program space

Memory Management

  • Monitor Free Heap: Call ESP.getFreeHeap() regularly
  • Avoid Memory Leaks: Properly free allocated memory in loops
  • Stack Size: Increase stack size for complex operations: xTaskCreate()
  • String Usage: Use F() macro to store strings in flash: Serial.println(F("Text"));

Power Considerations

  • Adequate Power: Use 2A+ power supply during OTA updates
  • Battery Monitoring: Check battery level before allowing OTA on battery-powered devices
  • Brownout Protection: Enable brownout detector to prevent corruption during power loss

📊 Monitoring and Logging

Advanced OTA Logging System
#include <WiFi.h>
#include <ArduinoOTA.h>
#include <time.h>

// Logging structure
struct OTALog {
  unsigned long timestamp;
  String event;
  String details;
  bool success;
};

std::vector<OTALog> otaHistory;
const int MAX_LOG_ENTRIES = 50;

void setupLogging() {
  // Configure time sync (NTP)
  configTime(0, 0, "pool.ntp.org");
  
  // Setup OTA with logging
  ArduinoOTA.onStart([]() {
    logOTAEvent("OTA_START", "Update initiated", true);
  });
  
  ArduinoOTA.onEnd([]() {
    logOTAEvent("OTA_COMPLETE", "Update successful", true);
  });
  
  ArduinoOTA.onError([](ota_error_t error) {
    String errorMsg = getOTAErrorString(error);
    logOTAEvent("OTA_ERROR", errorMsg, false);
  });
  
  ArduinoOTA.begin();
}

void logOTAEvent(String event, String details, bool success) {
  OTALog log;
  log.timestamp = millis();
  log.event = event;
  log.details = details;
  log.success = success;
  
  otaHistory.push_back(log);
  
  // Keep only last N entries
  if (otaHistory.size() > MAX_LOG_ENTRIES) {
    otaHistory.erase(otaHistory.begin());
  }
  
  // Print to serial
  Serial.printf("[%lu] %s: %s (%s)\n", 
                log.timestamp,
                event.c_str(),
                details.c_str(),
                success ? "✅" : "❌");
  
  // Optional: Send to cloud logging service
  // sendToCloudLogging(log);
}

String getOTAErrorString(ota_error_t error) {
  switch(error) {
    case OTA_AUTH_ERROR: return "Authentication Failed";
    case OTA_BEGIN_ERROR: return "Begin Failed";
    case OTA_CONNECT_ERROR: return "Connect Failed";
    case OTA_RECEIVE_ERROR: return "Receive Failed";
    case OTA_END_ERROR: return "End Failed";
    default: return "Unknown Error";
  }
}

void printOTAHistory() {
  Serial.println("\n📜 OTA Update History:");
  Serial.println("========================================");
  
  for (auto& log : otaHistory) {
    Serial.printf("[%lu] %s: %s %s\n",
                  log.timestamp,
                  log.event.c_str(),
                  log.details.c_str(),
                  log.success ? "✅" : "❌");
  }
  
  Serial.println("========================================\n");
}

🎓 Learning Resources and Next Steps

📚 Master OTA Programming

Beginner Level

  • Start with basic OTA example and get comfortable with wireless uploads
  • Experiment with different Wi-Fi networks and signal strengths
  • Add simple sensors and monitor via Serial while doing OTA updates
  • Practice troubleshooting common connection issues

Intermediate Level

  • Implement password protection and basic security measures
  • Create web-based OTA interface for non-technical users
  • Build projects that require remote updates (outdoor sensors, wall-mounted displays)
  • Learn to manage multiple devices on same network

Advanced Level

  • Implement automatic rollback on failed updates
  • Create cloud-based firmware distribution system
  • Build fleet management tools for hundreds of devices
  • Develop commercial products with secure OTA capabilities

🎉 Ready to Go Wireless!

Arduino OTA programming fundamentally changes how you develop and maintain IoT projects. From simple home automation to complex industrial systems, the ability to update devices wirelessly opens up incredible possibilities that were previously impractical or impossible.

Key Takeaways:

  • ✅ OTA eliminates the need for physical access to devices
  • ✅ Perfect for deployed devices in hard-to-reach locations
  • ✅ Enables remote debugging and feature updates
  • ✅ Security must be a priority - always use strong passwords
  • ✅ Keep ArduinoOTA.handle() in your loop for reliable updates
  • ✅ Start simple and gradually add advanced features

Remember: Every expert started as a beginner. Start with the basic example, understand how it works, then expand to more advanced implementations. The wireless freedom of OTA will transform your IoT development workflow!

🚀 Now go build something amazing—wirelessly!

STC MICROCONTROLLER PROGRAMMING

Complete Guide to STC Microcontrollers: Programming Beyond Arduino

Complete Guide to STC Microcontrollers: Programming Beyond Arduino

STC Microcontroller Development Board

STC microcontrollers are a series of microcontrollers developed by STC Microelectronics, a Chinese semiconductor company. These microcontrollers are widely used in various embedded systems and electronic projects, offering a cost-effective alternative to other microcontroller families.

Important: Unlike Arduino, STC microcontrollers are not supported by Arduino IDE. You need different tools and programming approaches.

🏭 1. Manufacturer & Overview

STC Microelectronics is a leading manufacturer of microcontrollers, and their products are commonly used in applications such as industrial control, consumer electronics, and automation.

⚡ 2. Architecture

STC microcontrollers are based on the 8051 architecture, a popular and widely used microcontroller architecture. The 8051 architecture is known for its simplicity, ease of use, and versatility.

🔧 3. Key Features

STC microcontrollers come with a comprehensive range of features including:

  • Analog and digital peripherals
  • Multiple timers and counters
  • Communication interfaces (UART, SPI, I2C)
  • GPIO (General Purpose Input/Output) pins
  • Built-in oscillators and clock management
  • Low power consumption modes

🛠 Required Software & Hardware:

  • STC-ISP Programmer Tool (official, Windows only)
  • Keil uVision (for writing code in C)
  • USB to Serial Adapter (CH340, CP2102, FTDI)
  • STC MCU (e.g., STC89C52, STC15F104, etc.)

💻 4. Development Tools

STC provides development tools, including an integrated development environment (IDE), compilers, and programmers, to facilitate the development of applications for their microcontrollers.

📝 5. Programming Language

STC microcontrollers are typically programmed using the C programming language. STC provides a development environment that allows programmers to write code, compile it, and then load it onto the microcontroller.

🏗 6. Applications

STC microcontrollers find applications in a wide range of electronic devices, including:

  • Home appliances
  • Industrial automation systems
  • Automotive electronics
  • Consumer electronics
  • IoT devices
  • Educational projects

💰 7. Cost-Effective Solutions

STC microcontrollers are often chosen for projects where cost is a critical factor. They provide a cost-effective solution for various applications without compromising on performance.

MCU Programming Components Diagram

📋 Essential Components for MCU Programming

For any MCU programming, you need three basic components:

  1. MCU Development Board
  2. Cross Compiler
  3. Programmer

🔧 MCU Development Board

A development board is a circuit board which holds the MCU and has other components to test the MCU. The components may be other ICs or discrete ones like LEDs, buttons, relays, etc. For us, this is the target board.

📦 Available STC Development Boards

  • STC32/8A Series - General-purpose with USB interface
  • STC32/16A Series - Enhanced development platform
  • STC32/32A Series - Expanded features and capabilities
  • STC32/64A Series - Increased resources for complex applications
  • STC32L Series - Low-power design for battery applications
  • STC32F Series - Enhanced performance features
  • STC32G Series - Advanced features for specialized applications
STC89C52 Development Board Layout

Example: STC89C52 Development Board

⚙️ Cross Compiler

A compiler is software which converts your human-readable program into machine language which the machine (microprocessor within the computer) can understand.

A cross compiler is a compiler which generates machine code in a format different from the processor it is running on. For example, it runs on your PC and generates code for MCU, where the architecture of the microprocessor in your PC (x86) is different from the MCU (8051).

Recommended: Keil uVision

One excellent cross compiler is Keil. You can download a demo copy from https://www.keil.com/c51/demo/eval/c51.htm. This includes the µVision IDE and C51 compiler for writing and compiling C programs.

Alternative Compilers & IDEs:

STC Programming Process Diagram

🔥 Step-by-Step Programming Guide

Step 1: Write Code in Keil

// Blink LED on P1.0 #include <regx52.h> void Delay(unsigned int ms) { unsigned int i, j; for(i=0; i<ms; i++) { for(j=0; j<123; j++); } } void main() { while(1) { P1_0 = 0; // LED ON Delay(500); P1_0 = 1; // LED OFF Delay(500); } }

Step 2: Compile Code to .hex

  1. Open Keil uVision
  2. Create a new project
  3. Select your STC chip (e.g., STC89C52RC)
  4. Add C file and build project
  5. You'll get a .hex file

Step 3: Flash Using STC-ISP

  1. Open STC-ISP tool
  2. Select your chip model
  3. Load the .hex file
  4. Connect USB to Serial (do NOT power yet)
  5. Click "Download Program"
  6. Now power/reset the STC — upload will begin

🎯 Conclusion

If you're an intermediate maker looking to expand your microcontroller knowledge beyond Arduino, STC is a great way to explore the classic 8051 world with modern performance and cost-effectiveness.

When working with STC microcontrollers, it's essential to refer to the specific datasheets and documentation provided by STC for the particular model you are using, as this will contain detailed information about the microcontroller's features, specifications, and programming guidelines.

ARDUINO PORTENTA H7 Tutorial

Arduino Portenta H7: Dual-Core Powerhouse for Industrial Applications

Experience the future of embedded computing with dual-core processing power, advanced connectivity, and industrial-grade reliability

🚀 Introduction to Arduino Portenta H7

The Arduino Portenta H7 represents a quantum leap in Arduino's product lineup, bridging the gap between simple hobbyist microcontrollers and professional embedded computing platforms. Released as part of the Arduino Pro family, the Portenta H7 is designed for high-performance applications requiring simultaneous execution of complex algorithms and real-time control tasks.

Unlike traditional Arduino boards, the Portenta H7 is built on the powerful STM32H747XI dual-core processor from STMicroelectronics, offering unprecedented computational capabilities while maintaining compatibility with the Arduino ecosystem.

🔧 Complete Technical Specifications

Processor Architecture

The heart of the Portenta H7 is the STM32H747XI microcontroller, featuring a sophisticated dual-core architecture:

  • Cortex-M7 Core: 480 MHz, 32-bit RISC processor with FPU (Floating Point Unit) and double-precision support
  • Cortex-M4 Core: 240 MHz, 32-bit RISC processor with FPU for real-time processing
  • L1 Cache: 16 KB data cache and 16 KB instruction cache (M7 core)
  • ART Accelerator: For zero wait state execution from Flash memory
  • Memory Protection Unit (MPU): Enhanced security and memory management

Memory Configuration

  • Flash Memory: 2 MB on-chip + 16 MB QSPI NOR Flash (external)
  • SRAM: 1 MB on-chip (including 192 KB TCM RAM)
  • SDRAM: 8 MB external SDRAM for high-speed data processing
  • EEPROM: Emulated through Flash memory
  • Backup SRAM: 4 KB battery-backed SRAM
Arduino Portenta H7 Development Board

🔌 Comprehensive Connectivity Options

1. Wireless Connectivity

WiFi Module (Murata 1DX):

  • Dual-band 2.4 GHz and 5 GHz IEEE 802.11 b/g/n
  • Data rates up to 65 Mbps
  • Integrated antenna and U.FL connector for external antenna
  • WPA/WPA2/WPA3 security protocols
  • Simultaneous AP and Station mode support

Bluetooth 5.1:

  • Bluetooth Low Energy (BLE) and Bluetooth Classic support
  • Extended range up to 240 meters (line of sight)
  • Direction finding capability
  • Enhanced data rate (EDR) up to 3 Mbps
  • Support for multiple simultaneous connections

2. Wired Connectivity

Ethernet PHY (10/100 Mbps):

  • Integrated KSZ8081RNA Ethernet transceiver
  • IEEE 802.3/802.3u compliant
  • Auto-negotiation and auto-MDIX support
  • Ideal for industrial IoT and automation applications

USB Connectivity:

  • USB-C port with DisplayPort Alt Mode
  • USB 2.0 High Speed (480 Mbps)
  • USB Host and Device modes
  • Power Delivery (PD) support for charging
  • Video output capability through DP Alt Mode

⚠️ Important Note on Connectivity

The WiFi and Bluetooth modules share the same antenna connection. When using the U.FL connector for an external antenna, ensure you use a dual-band antenna that supports both 2.4 GHz and 5 GHz frequencies for optimal performance.

🎯 Dual-Core Processing Architecture

Understanding the Dual-Core System

The Portenta H7's dual-core architecture is its most powerful feature, allowing you to run completely different tasks on each core simultaneously:

Cortex-M7 Core (480 MHz) - Primary Processor

  • Purpose: High-level application code, complex algorithms, and data processing
  • Capabilities:
    • Machine learning inference
    • Computer vision processing
    • Complex mathematical computations
    • User interface management
    • Network protocol handling
  • Performance: 1027 DMIPS (Dhrystone Million Instructions Per Second)

Cortex-M4 Core (240 MHz) - Real-Time Processor

  • Purpose: Real-time control, sensor interfacing, and time-critical operations
  • Capabilities:
    • PWM signal generation with precise timing
    • Motor control algorithms
    • Sensor data acquisition and filtering
    • Real-time communication protocols
    • Interrupt-driven operations
  • Performance: 300 DMIPS with DSP instructions

Inter-Core Communication

The two cores communicate using a sophisticated Remote Procedure Call (RPC) mechanism over shared memory regions:

Arduino Code Example - M7 Core (Main Sketch)
#ifdef CORE_CM7  // This code runs on M7 core

#include "RPC.h"

void setup() {
  Serial.begin(115200);
  
  // Initialize RPC communication with M4 core
  RPC.begin();
  
  // Wait for M4 core to initialize
  while (!RPC.available()) {
    delay(100);
  }
  
  Serial.println("M7 Core: Communication established with M4");
}

void loop() {
  // Call function on M4 core to read sensor
  String data = RPC.call("readSensorM4").as<String>();
  
  // Process data on M7 (complex processing)
  processData(data);
  
  // Send results to display
  updateDisplay();
  
  delay(100);
}

void processData(String data) {
  // Perform complex calculations
  // Machine learning inference
  // Image processing, etc.
  Serial.println("M7 Core: Processing data - " + data);
}

void updateDisplay() {
  // Update user interface
  // Send data over network
}

#endif
Arduino Code Example - M4 Core (Separate Sketch)
#ifdef CORE_CM4  // This code runs on M4 core

#include "RPC.h"

// Define sensor pins
const int sensorPin = A0;

void setup() {
  pinMode(sensorPin, INPUT);
  
  // Initialize RPC communication with M7 core
  RPC.begin();
  
  // Register RPC functions that M7 can call
  RPC.bind("readSensorM4", readSensor);
  
  Serial.begin(115200);
  Serial.println("M4 Core: Real-time processor ready");
}

void loop() {
  // M4 handles real-time tasks
  // Continuously monitor sensors
  // Handle time-critical interrupts
  delay(10);
}

// Function callable from M7 core
String readSensor() {
  int value = analogRead(sensorPin);
  
  // Real-time filtering
  value = applyKalmanFilter(value);
  
  return String(value);
}

int applyKalmanFilter(int rawValue) {
  // Real-time filtering algorithm
  // Low latency processing
  return rawValue; // Simplified for example
}

#endif

How to Upload Code to Both Cores

  1. Install Arduino IDE 2.0+ or use Arduino Create web editor
  2. Install Portenta H7 board support: Tools → Board → Boards Manager → Search "Portenta" → Install
  3. Select the core target: Tools → Target Core → Select "Main Core (M7)" or "M4 Co-processor"
  4. Upload M7 sketch first: Write M7 code → Select "Main Core" → Upload
  5. Upload M4 sketch second: Write M4 code → Select "M4 Co-processor" → Upload
  6. Monitor both cores: Use Serial.print() in both cores (output appears in same Serial Monitor)

📍 Pin Configuration and GPIO

High-Density Connectors

The Portenta H7 features two 80-pin high-density connectors (male) on the bottom, providing access to:

Digital I/O Pins: Over 70 GPIO pins with 3.3V logic levels
Analog Inputs: 7 analog input pins (12-bit ADC, up to 3.6 MSPS)
PWM Outputs: 10+ PWM-capable pins with advanced timer capabilities
Communication Interfaces:
  • I2C: 4 channels
  • SPI: 3 channels
  • UART: 4 channels (+ 4 additional USARTs)
  • CAN Bus: 2 channels (CAN FD capable)
  • I2S: Digital audio interface
  • SAI: Serial Audio Interface
Interface Pin Numbers Speed Notes
UART0 TX: D14, RX: D13 Up to 12.5 Mbps Default Serial
I2C0 SDA: D11, SCL: D12 Up to 1 MHz (Fast Mode+) Primary I2C
SPI0 MOSI: D8, MISO: D10, SCK: D9 Up to 100 MHz High-speed SPI
CAN0 TX: D5, RX: D4 Up to 8 Mbps (CAN FD) Industrial protocol
ADC0 A0-A7 16-bit resolution 3.6 MSPS max
Portenta H7 Pinout Diagram

⚡ Power Management

Advanced Power Architecture

The Portenta H7 features a sophisticated power management system designed for industrial applications:

Input Power Options:
  • USB-C: 5V input with Power Delivery support (up to 100W negotiation)
  • VIN Pin: 5V-24V input range with integrated buck converter
  • Battery: 3.7V Li-Po battery connector with charging circuit
Power Rails:
  • 3.3V Rail: Up to 1A for peripherals and GPIO
  • 1.8V Rail: Internal logic power
  • VCC_USB: 5V from USB (500mA max)
  • VBAT: Backup battery power for RTC and backup SRAM

Power Consumption Profiles

  • Active Mode (Both cores @ max frequency): ~800mA @ 5V
  • Low Power Mode (Sleep): ~50mA @ 5V
  • Standby Mode: ~5mA @ 5V
  • Deep Sleep Mode: <1mA @ 5V

Power Management Example Code

Low Power Mode Implementation
#include "STM32LowPower.h"

void setup() {
  Serial.begin(115200);
  
  // Configure wake-up source
  LowPower.attachInterruptWakeup(digitalPinToInterrupt(2), wakeUp, RISING);
  
  // Initialize peripherals
  setupPeripherals();
}

void loop() {
  // Do work
  performTask();
  
  // Enter low power mode when idle
  Serial.println("Entering sleep mode...");
  Serial.flush();  // Wait for serial transmission to complete
  
  // Put M7 core to sleep (M4 can continue running)
  LowPower.sleep();
  
  // Wake up and continue
  Serial.println("Woke up from sleep!");
}

void wakeUp() {
  // Interrupt handler - wakes up the system
}

void performTask() {
  // Your application code here
  Serial.println("Performing task...");
  delay(1000);
}

void setupPeripherals() {
  pinMode(2, INPUT_PULLUP);  // Wake-up button
  pinMode(LED_BUILTIN, OUTPUT);
}

🔐 Security Features

Hardware Security

The Portenta H7 includes the NXP SE050C2 secure element for cryptographic operations:

  • Crypto Co-processor: Hardware-accelerated encryption/decryption
  • Secure Key Storage: Protected storage for cryptographic keys
  • Random Number Generator: True hardware RNG for secure applications
  • Secure Boot: Verify firmware authenticity before execution
  • Supported Algorithms:
    • RSA up to 4096-bit
    • ECC (Elliptic Curve Cryptography)
    • AES-128/256
    • SHA-256/384/512
    • HMAC
Secure Element Example
#include <SE050.h>

SE050 secure;

void setup() {
  Serial.begin(115200);
  
  // Initialize secure element
  if (secure.begin()) {
    Serial.println("Secure Element initialized");
  }
  
  // Generate random number
  byte randomData[32];
  secure.generateRandom(randomData, 32);
  
  Serial.print("Random data: ");
  for (int i = 0; i < 32; i++) {
    Serial.print(randomData[i], HEX);
  }
  Serial.println();
}

void loop() {
  // Encrypt sensitive data
  String plaintext = "Sensitive Information";
  String encrypted = encryptData(plaintext);
  
  Serial.println("Encrypted: " + encrypted);
  
  delay(5000);
}

String encryptData(String data) {
  byte key[16] = {0x00, 0x01, 0x02, /* ... */ };
  byte iv[16] = {0x00};
  
  // Use hardware AES encryption
  return secure.AES_encrypt(data, key, iv);
}

🎨 Display and Graphics

DisplayPort over USB-C

One of the unique features of the Portenta H7 is its ability to output video through the USB-C port using DisplayPort Alternate Mode:

Video Capabilities:
  • Resolution up to 1920x1200 @ 60Hz
  • 24-bit color depth
  • Compatible with USB-C to HDMI/DisplayPort adapters
  • Ideal for industrial HMI (Human-Machine Interface) applications
Graphics Example with LVGL
#include <lvgl.h>
#include <Portenta_LVGL.h>

void setup() {
  Serial.begin(115200);
  
  // Initialize LVGL graphics library
  portenta_lvgl_init();
  
  // Create a simple button UI
  lv_obj_t *btn = lv_btn_create(lv_scr_act());
  lv_obj_set_size(btn, 120, 50);
  lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
  
  lv_obj_t *label = lv_label_create(btn);
  lv_label_set_text(label, "Click Me!");
  lv_obj_center(label);
  
  // Add event handler
  lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
}

void loop() {
  lv_timer_handler();  // Handle LVGL tasks
  delay(5);
}

void btn_event_cb(lv_event_t *e) {
  Serial.println("Button clicked!");
  // Perform action
}

🌐 Network Programming Examples

WiFi Web Server Example

Complete WiFi Web Server
#include <WiFi.h>
#include <WiFiServer.h>

// WiFi credentials
const char* ssid = "YourNetworkName";
const char* password = "YourPassword";

// Create server on port 80
WiFiServer server(80);

// Sensor variables
float temperature = 0;
float humidity = 0;

void setup() {
  Serial.begin(115200);
  
  // Connect to WiFi
  Serial.print("Connecting to WiFi");
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("\nWiFi connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  
  // Start web server
  server.begin();
}

void loop() {
  // Update sensor readings (simulated)
  temperature = readTemperature();
  humidity = readHumidity();
  
  // Check for client connections
  WiFiClient client = server.available();
  
  if (client) {
    Serial.println("New client connected");
    
    String request = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        request += c;
        
        // Check if HTTP request ended
        if (request.endsWith("\r\n\r\n")) {
          // Send HTTP response
          sendHTMLResponse(client);
          break;
        }
      }
    }
    
    // Close connection
    client.stop();
    Serial.println("Client disconnected");
  }
  
  delay(10);
}

void sendHTMLResponse(WiFiClient &client) {
  // HTTP headers
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println("Connection: close");
  client.println();
  
  // HTML content
  client.println("<!DOCTYPE html>");
  client.println("<html>");
  client.println("<head>");
  client.println("<meta name='viewport' content='width=device-width, initial-scale=1'>");
  client.println("<title>Portenta H7 Sensor Dashboard</title>");
  client.println("<style>");
  client.println("body { font-family: Arial; text-align: center; margin: 20px; }");
  client.println(".sensor { background: #4ED0FC; color: white; padding: 20px; margin: 10px; border-radius: 10px; }");
  client.println("</style>");
  client.println("</head>");
  client.println("<body>");
  client.println("<h1>Portenta H7 Sensor Dashboard</h1>");
  
  // Temperature display
  client.println("<div class='sensor'>");
  client.println("<h2>Temperature</h2>");
  client.print("<p>");
  client.print(temperature);
  client.println(" °C</p>");
  client.println("</div>");
  
  // Humidity display
  client.println("<div class='sensor'>");
  client.println("<h2>Humidity</h2>");
  client.print("<p>");
  client.print(humidity);
  client.println(" %</p>");
  client.println("</div>");
  
  // Auto-refresh
  client.println("<script>");
  client.println("setTimeout(function(){ location.reload(); }, 5000);");
  client.println("</script>");
  
  client.println("</body>");
  client.println("</html>");
}

float readTemperature() {
  // Read from actual sensor (DHT22, BME280, etc.)
  return 23.5 + random(-10, 10) / 10.0;
}

float readHumidity() {
  // Read from actual sensor
  return 65.0 + random(-50, 50) / 10.0;
}
Connecting to WiFi...... WiFi connected! IP address: 192.168.1.105 New client connected Client disconnected

🏭 Industrial IoT Application Example

Complete Industrial Monitoring System

This example demonstrates a real-world industrial application using both cores, multiple sensors, and network connectivity:

Industrial Monitoring - M7 Core (Main Application)
#ifdef CORE_CM7

#include <WiFi.h>
#include <PubSubClient.h>  // MQTT library
#include "RPC.h"

// Network credentials
const char* ssid = "Factory_Network";
const char* password = "SecurePass123";
const char* mqtt_server = "mqtt.factory.local";

WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);

// Data structure for sensor readings
struct SensorData {
  float temperature;
  float pressure;
  float vibration;
  int rpm;
  bool motorStatus;
  unsigned long timestamp;
};

SensorData currentData;

void setup() {
  Serial.begin(115200);
  
  // Initialize RPC with M4 core
  RPC.begin();
  Serial.println("M7: Waiting for M4 core...");
  while (!RPC.available()) delay(100);
  
  // Connect to WiFi
  connectWiFi();
  
  // Setup MQTT
  mqtt.setServer(mqtt_server, 1883);
  mqtt.setCallback(mqttCallback);
  
  Serial.println("M7: Industrial monitoring system ready");
}

void loop() {
  // Maintain MQTT connection
  if (!mqtt.connected()) {
    reconnectMQTT();
  }
  mqtt.loop();
  
  // Get real-time sensor data from M4 core
  String sensorJson = RPC.call("getSensorData").as<String>();
  
  // Parse and process data
  processData(sensorJson);
  
  // Check for alarms
  checkAlarms();
  
  // Publish data to cloud every 5 seconds
  static unsigned long lastPublish = 0;
  if (millis() - lastPublish > 5000) {
    publishData();
    lastPublish = millis();
  }
  
  // Display on local screen
  updateDisplay();
  
  delay(100);
}

void connectWiFi() {
  Serial.print("M7: Connecting to WiFi");
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("\nM7: WiFi connected");
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());
}

void reconnectMQTT() {
  while (!mqtt.connected()) {
    Serial.print("M7: Connecting to MQTT...");
    
    if (mqtt.connect("PortentaH7_Factory01")) {
      Serial.println("connected");
      mqtt.subscribe("factory/commands");
    } else {
      Serial.print("failed, rc=");
      Serial.println(mqtt.state());
      delay(5000);
    }
  }
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("M7: Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  
  String message = "";
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.println(message);
  
  // Process commands from cloud
  if (message == "START_MOTOR") {
    RPC.call("startMotor");
  } else if (message == "STOP_MOTOR") {
    RPC.call("stopMotor");
  }
}

void processData(String jsonData) {
  // Parse JSON data from M4
  // Extract sensor values
  // Perform calculations
  // Apply filters
  
  Serial.println("M7: Processing sensor data");
  Serial.println(jsonData);
}

void checkAlarms() {
  // Check temperature threshold
  if (currentData.temperature > 80.0) {
    Serial.println("M7: ALARM - High temperature!");
    mqtt.publish("factory/alarms", "HIGH_TEMPERATURE");
    // Trigger emergency shutdown via M4
    RPC.call("emergencyStop");
  }
  
  // Check vibration levels
  if (currentData.vibration > 5.0) {
    Serial.println("M7: WARNING - High vibration detected");
    mqtt.publish("factory/warnings", "HIGH_VIBRATION");
  }
}

void publishData() {
  // Create JSON payload
  String payload = "{";
  payload += "\"temperature\":" + String(currentData.temperature) + ",";
  payload += "\"pressure\":" + String(currentData.pressure) + ",";
  payload += "\"vibration\":" + String(currentData.vibration) + ",";
  payload += "\"rpm\":" + String(currentData.rpm) + ",";
  payload += "\"status\":\"" + String(currentData.motorStatus ? "ON" : "OFF") + "\"";
  payload += "}";
  
  mqtt.publish("factory/sensor/data", payload.c_str());
  Serial.println("M7: Published data to cloud");
}

void updateDisplay() {
  // Update local display
  // Show real-time graphs
  // Display alarms
}

#endif
Industrial Monitoring - M4 Core (Real-Time Control)
#ifdef CORE_CM4

#include "RPC.h"

// Sensor pins
const int tempPin = A0;
const int pressurePin = A1;
const int vibrationPin = A2;
const int rpmPin = D2;

// Motor control
const int motorPin = D5;
bool motorRunning = false;

// Real-time variables
volatile int pulseCount = 0;
float temperature = 0;
float pressure = 0;
float vibration = 0;
int rpm = 0;

// Kalman filter variables
float Q = 0.1;  // Process noise
float R = 1.0;  // Measurement noise
float P = 1.0;  // Estimation error
float K = 0;    // Kalman gain
float X = 0;    // State estimate

void setup() {
  Serial.begin(115200);
  
  // Initialize RPC
  RPC.begin();
  
  // Register functions callable from M7
  RPC.bind("getSensorData", getSensorData);
  RPC.bind("startMotor", startMotor);
  RPC.bind("stopMotor", stopMotor);
  RPC.bind("emergencyStop", emergencyStop);
  
  // Setup pins
  pinMode(tempPin, INPUT);
  pinMode(pressurePin, INPUT);
  pinMode(vibrationPin, INPUT);
  pinMode(rpmPin, INPUT_PULLUP);
  pinMode(motorPin, OUTPUT);
  
  // Attach interrupt for RPM measurement
  attachInterrupt(digitalPinToInterrupt(rpmPin), countPulse, RISING);
  
  Serial.println("M4: Real-time control system ready");
}

void loop() {
  // Read sensors with high priority
  readSensors();
  
  // Calculate RPM every 100ms
  static unsigned long lastRPM = 0;
  if (millis() - lastRPM >= 100) {
    rpm = (pulseCount * 600);  // Convert to RPM
    pulseCount = 0;
    lastRPM = millis();
  }
  
  // Monitor safety conditions
  monitorSafety();
  
  // Control motor based on conditions
  controlMotor();
  
  delayMicroseconds(100);  // Minimal delay for real-time operation
}

void readSensors() {
  // Read temperature (with averaging)
  int tempRaw = analogRead(tempPin);
  temperature = applyKalmanFilter(tempRaw * 0.1);  // Convert to °C
  
  // Read pressure sensor
  int pressRaw = analogRead(pressurePin);
  pressure = pressRaw * 0.05;  // Convert to PSI
  
  // Read vibration sensor
  int vibRaw = analogRead(vibrationPin);
  vibration = vibRaw * 0.01;  // Convert to g-force
}

float applyKalmanFilter(float measurement) {
  // Prediction
  P = P + Q;
  
  // Update
  K = P / (P + R);
  X = X + K * (measurement - X);
  P = (1 - K) * P;
  
  return X;
}

void monitorSafety() {
  // Emergency stop if temperature too high
  if (temperature > 85.0) {
    emergencyStop();
  }
  
  // Check vibration levels
  if (vibration > 6.0) {
    // Reduce motor speed
    analogWrite(motorPin, 128);  // 50% speed
  }
}

void controlMotor() {
  if (motorRunning) {
    // Maintain motor operation
    // Implement PID control if needed
  }
}

void countPulse() {
  pulseCount++;
}

// RPC functions callable from M7 core

String getSensorData() {
  String json = "{";
  json += "\"temperature\":" + String(temperature, 2) + ",";
  json += "\"pressure\":" + String(pressure, 2) + ",";
  json += "\"vibration\":" + String(vibration, 2) + ",";
  json += "\"rpm\":" + String(rpm) + ",";
  json += "\"motor\":" + String(motorRunning ? "true" : "false");
  json += "}";
  
  return json;
}

void startMotor() {
  motorRunning = true;
  digitalWrite(motorPin, HIGH);
  Serial.println("M4: Motor started");
}

void stopMotor() {
  motorRunning = false;
  digitalWrite(motorPin, LOW);
  Serial.println("M4: Motor stopped");
}

void emergencyStop() {
  motorRunning = false;
  digitalWrite(motorPin, LOW);
  Serial.println("M4: EMERGENCY STOP!");
  
  // Flash LED rapidly
  for (int i = 0; i < 10; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(50);
    digitalWrite(LED_BUILTIN, LOW);
    delay(50);
  }
}

#endif
M4: Real-time control system ready M7: Waiting for M4 core... M7: WiFi connected IP: 192.168.1.105 M7: Connecting to MQTT...connected M7: Industrial monitoring system ready M7: Processing sensor data {"temperature":45.23,"pressure":14.7,"vibration":1.2,"rpm":1500,"motor":"true"} M7: Published data to cloud

📦 Product Variants Comparison

Choosing the Right Portenta H7 Variant

1

Portenta H7 (Full Version)

Best for: Professional applications requiring all features

  • ✅ All features enabled
  • ✅ WiFi + Bluetooth connectivity
  • ✅ Ethernet PHY included
  • ✅ Crypto chip for security
  • ✅ Full 8 MB SDRAM
  • ✅ 16 MB Flash storage

Use Cases: Industrial automation, IoT gateways, machine learning applications, computer vision

2

Portenta H7 Lite

Best for: Cost-sensitive applications without wireless connectivity

  • ✅ Dual-core processor (same as full version)
  • ✅ 8 MB SDRAM
  • ✅ 16 MB Flash
  • ❌ No WiFi/Bluetooth module
  • ❌ No Ethernet PHY
  • ✅ All other peripherals included

Use Cases: Embedded controllers, data logging, standalone applications

3

Portenta H7 Lite Connected

Best for: IoT projects requiring wireless but not Ethernet

  • ✅ Dual-core processor
  • ✅ WiFi + Bluetooth connectivity
  • ✅ 8 MB SDRAM
  • ✅ 16 MB Flash
  • ❌ No Ethernet PHY
  • ✅ Crypto chip included

Use Cases: Wireless sensors, IoT devices, portable equipment

Feature Portenta H7 H7 Lite H7 Lite Connected
Processor STM32H747 Dual-Core STM32H747 Dual-Core STM32H747 Dual-Core
RAM 8 MB SDRAM 8 MB SDRAM 8 MB SDRAM
Flash 16 MB 16 MB 16 MB
WiFi/BT ✅ Yes ❌ No ✅ Yes
Ethernet ✅ Yes ❌ No ❌ No
Crypto Chip ✅ SE050C2 ❌ No ✅ SE050C2
USB-C ✅ With DP Alt Mode ✅ With DP Alt Mode ✅ With DP Alt Mode
Price $$$ (Highest) $ (Lowest) $$ (Mid-range)
Portenta H7 Variants Comparison

🏭 Real-World Industrial Applications

🏭 Manufacturing Automation

Control multiple robots and conveyor systems simultaneously while monitoring production metrics in real-time. The dual-core architecture enables running PLC logic on M4 while M7 handles SCADA communication.

🔬 Scientific Instruments

High-precision data acquisition from multiple sensors with real-time processing. Perfect for laboratory equipment requiring both fast sampling and complex analysis algorithms.

👁️ Computer Vision

Run TensorFlow Lite models for object detection and classification. The M7 core handles neural network inference while M4 manages camera interface and image preprocessing.

⚙️ Programmable Logic Controllers

Industrial-grade PLC functionality with support for standard protocols (Modbus, CANopen, EtherCAT). Replace expensive proprietary PLCs with Arduino-compatible solution.

🖥️ Industrial HMI

Create touch-screen interfaces with LVGL graphics library. Display port output allows connection to industrial monitors for operator dashboards.

🤖 Advanced Robotics

Control complex robotic systems with precise motor control, sensor fusion, and path planning algorithms. Multiple communication interfaces support various actuators and sensors.

🎯 Edge AI Computing

Deploy machine learning models at the edge for real-time inference. Ideal for predictive maintenance, anomaly detection, and quality control in manufacturing.

⚡ Power Systems Monitoring

Monitor electrical parameters with high sampling rates. Real-time FFT analysis for power quality monitoring and harmonic detection in industrial power distribution.

🔧 Development Environment Setup

Complete Setup Guide

  1. Install Arduino IDE 2.0 or Later
    • Download from arduino.cc
    • Minimum version 2.0 required for dual-core support
  2. Add Portenta Board Support
    • Open Arduino IDE
    • Go to Tools → Board → Boards Manager
    • Search for "Arduino Mbed OS Portenta Boards"
    • Click Install (approx. 500 MB download)
  3. Install Required Libraries
    • WiFi library (included with board package)
    • RPC library for dual-core communication
    • LVGL for graphics (optional)
    • TensorFlow Lite Micro (for ML applications)
  4. Configure Board Settings
    • Select Tools → Board → Arduino Portenta H7 (M7 core)
    • Choose port: Tools → Port → [Your COM port]
    • Set optimization: Tools → Optimize → Fastest (-O3)
  5. Update Bootloader (Important!)
    • Connect Portenta H7 via USB-C
    • Double-press reset button to enter bootloader mode (LED pulses green)
    • In Arduino IDE: Tools → Burn Bootloader
    • Wait for completion (critical for stability)
  6. Test Installation
    • Open File → Examples → 01.Basics → Blink
    • Click Upload
    • Verify RGB LED blinks green

⚠️ Common Setup Issues and Solutions

  • Board not detected: Install STM32 USB drivers from st.com
  • Upload fails: Enter bootloader mode manually (double-press reset)
  • Serial monitor issues: Ensure correct baud rate (115200) and port selection
  • M4 core not responding: Upload M4 sketch after M7 sketch, not before

📊 Performance Benchmarks

Real-World Performance Metrics

Processing Benchmarks

  • Dhrystone: 1027 DMIPS (M7 core) + 300 DMIPS (M4 core)
  • CoreMark: 2778 iterations/second (M7 @ 480 MHz)
  • FFT Performance: 1024-point FFT in ~2.5ms (M7 with CMSIS-DSP)
  • Neural Network Inference: MobileNet v1 @ 15 FPS

Communication Speed Tests

  • SPI: Sustained 50 Mbps transfer rate
  • I2C: 1 MHz (Fast Mode Plus) reliable operation
  • UART: 12.5 Mbps max tested speed
  • WiFi: 40-50 Mbps TCP throughput
  • Ethernet: 95 Mbps TCP throughput

Power Efficiency

  • Active (480 MHz): ~800mW total system power
  • Sleep Mode: ~250mW
  • Deep Sleep: ~5mW (RTC running)
  • WiFi Active: +200mW additional

🎓 Learning Resources

📚 Recommended Learning Path

Beginner Level

  • Start with single-core examples (M7 only)
  • Learn basic GPIO, ADC, and PWM operations
  • Experiment with WiFi connectivity
  • Build simple sensor projects

Intermediate Level

  • Explore dual-core programming with RPC
  • Implement real-time control systems
  • Create web servers and IoT dashboards
  • Work with multiple communication protocols

Advanced Level

  • Deploy TensorFlow Lite models
  • Develop industrial automation solutions
  • Implement custom communication protocols
  • Optimize for low power consumption
  • Build commercial-grade products

🔗 Ecosystem and Compatibility

MKR Shields Compatibility

The Portenta H7 can work with Arduino MKR shields through adapter boards:

  • Portenta Breakout Board: Access to all pins with breadboard compatibility
  • Portenta Vision Shield: Add camera and microphone for vision applications
  • Portenta Cat. M1/NB-IoT GNSS Shield: Cellular connectivity with GPS
  • Portenta Max Carrier: Industrial connectivity (RS-485, CAN, Ethernet, etc.)
Portenta H7 Ecosystem

💡 Tips and Best Practices

Professional Development Tips

  1. Memory Management: Use SDRAM for large buffers, keep critical code in TCM RAM for fastest access
  2. Core Distribution: Put time-critical code on M4, complex processing on M7
  3. Power Optimization: Use sleep modes when possible, disable unused peripherals
  4. Debugging: Use Serial.print() on both cores, consider SEGGER J-Link for advanced debugging
  5. Version Control: Always maintain separate sketches for M7 and M4 cores
  6. Testing: Test each core independently before enabling inter-core communication
  7. Updates: Keep board package and libraries updated for bug fixes
  8. Backup: Save bootloader backup - recovery requires special tools

🎯 Conclusion: When to Choose Portenta H7

The Arduino Portenta H7 is the ideal choice when your project requires:

  • ✅ Simultaneous high-level processing and real-time control
  • ✅ Industrial-grade reliability and performance
  • ✅ Multiple connectivity options (WiFi, BT, Ethernet, USB)
  • ✅ Advanced security features for commercial products
  • ✅ Machine learning at the edge
  • ✅ Professional development with Arduino ecosystem compatibility
  • ✅ Scalability from prototype to production

While the Portenta H7 is more expensive than typical Arduino boards, its capabilities justify the cost for professional applications where performance, reliability, and advanced features are critical.

Bottom Line: The Portenta H7 represents Arduino's vision for professional embedded computing - combining the ease of Arduino programming with industrial-grade hardware capable of handling demanding real-world applications.

Portenta H7 Power Distribution