Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/manish04-mu/TheEmbeddedInsights/llms.txt

Use this file to discover all available pages before exploring further.

This sketch is your first complete SPI example on the Arduino UNO. The master iterates over a 32-character string and transmits each byte one at a time over MOSI, simultaneously capturing whatever the slave returns on MISO into a receivedValue buffer. If no slave is connected, every received byte will read back as 0x00 — but the SPI waveform on SCK, CS, and MOSI will be fully correct and ready to inspect on a logic analyzer.

The Sketch

SPI_Echo_Hello.ino
#include <SPI.h>

/*
Pin Description: 
CS  - Chip Select         : Pin-> 10
MOSI- Master Out/Slave In : Pin-> 11
MISO- Master In/Slave Out : Pin-> 12
SCK - Clock Signal        : Pin-> 13
*/

/*
Logic Analyser:
CH0: SCK
CH1: Enable/Chip Select
CH2: MOSI
CH3: MISO
*/


#define CS 10     //Chip Select

char sentValue[] = "SPI Demo: Hello from the Master!";

char receivedValue[255];

void setup() {

  Serial.begin(9600);  //Serial mode is used to check the received value in MISO, in case, the logic analyser is not present.

  pinMode(CS, OUTPUT); //Define the Chip Select Pin to Output (Master --> Slave)
  digitalWrite(CS, HIGH);   // Keep slave deselected, usually teh CS is active low (We need to check the slave datasheet for this information.)

  SPI.begin();

}

void loop() {

  SPI.beginTransaction(SPISettings(100000, MSBFIRST, SPI_MODE2));

  digitalWrite(CS, LOW);          // Select slave

  for (int x = 0; x < strlen(sentValue); x++)
  {
    receivedValue[x] = SPI.transfer(sentValue[x]);
    delay(1);
  }
  

  digitalWrite(CS, HIGH);         // Deselect slave

  SPI.endTransaction();
 
  for (int x = 0; x <37; x++)
  {
    Serial.print("Received: ");
    Serial.println(receivedValue[x], HEX);
  }
  delay(1000);

}

How It Works

1

Initialize

Serial.begin(9600) opens the Serial Monitor at 9600 baud so received bytes can be printed. pinMode(CS, OUTPUT) configures pin 10 as a digital output, and digitalWrite(CS, HIGH) pulls Chip Select high immediately — this deselects any slave on the bus before SPI is configured. Finally, SPI.begin() initializes the hardware SPI peripheral and sets SCK (pin 13), MOSI (pin 11), and SS (pin 10) as outputs.
2

Begin Transaction

SPISettings(100000, MSBFIRST, SPI_MODE2) configures three things at once:
  • 100000 — clock speed of 100 kHz, slow enough to observe cleanly on most logic analyzers
  • MSBFIRST — bit 7 of each byte is sent first on MOSI
  • SPI_MODE2 — CPOL=1, CPHA=0: the clock idles HIGH, and data is sampled on the falling (leading) edge
SPI.beginTransaction() applies these settings atomically, preventing other SPI devices or interrupt handlers from interfering mid-transfer.
3

Select the Slave

digitalWrite(CS, LOW) asserts Chip Select, pulling the line low to signal the target slave that a transfer is about to begin. CS must go low after SPI.beginTransaction() is called to ensure the correct clock polarity is in place before the slave latches CS.
4

Transfer Loop

The for loop iterates over every character in sentValue. On each iteration, SPI.transfer(sentValue[x]) performs a full-duplex exchange: it clocks out one byte from sentValue[x] on MOSI while simultaneously clocking in one byte from MISO, returning the received value. That received byte is stored directly into receivedValue[x]. The delay(1) between each byte gives the slave 1 ms to process the received byte and prepare its MISO response before the next transfer begins.
5

Deselect the Slave

digitalWrite(CS, HIGH) releases the slave by pulling CS back high, signalling the end of the transaction. SPI.endTransaction() is called immediately after to release the SPI bus so other devices or interrupt-driven SPI code can use it.
6

Print Received Bytes

The second for loop iterates 37 times and prints each entry of receivedValue to the Serial Monitor in hexadecimal using Serial.println(receivedValue[x], HEX). Each line is prefixed with "Received: " for readability. The loop runs to index 36 to cover the full string length plus any trailing null or padding bytes in the buffer.

Key Settings

ParameterValueExplanation
Clock Speed100000 Hz100 kHz — conservative speed suitable for breadboard wiring and analyzers
Bit OrderMSBFIRSTMost-significant bit (bit 7) is transmitted first on MOSI
SPI ModeSPI_MODE2CPOL=1, CPHA=0 — clock idles HIGH; data is sampled on the falling edge

Expected Output

With no slave connected (MISO floating or tied low), the Serial Monitor will display a stream of zero-value bytes repeating every second:
Received: 0
Received: 0
Received: 0
...
When a slave is present and configured to echo received bytes back on MISO, you will instead see the ASCII hex values of the transmitted string characters — for example, 53 for 'S', 50 for 'P', 49 for 'I', and so on.
The delay(1) inside the transfer loop is intentional. It inserts a 1 ms pause between consecutive SPI.transfer() calls, giving the slave device time to process the byte it just received and load its response onto MISO before the master clocks in the next transfer. Removing this delay may result in corrupted or zeroed received values when working with slower slave peripherals or software-based SPI slaves.
Not sure why this sketch uses SPI_MODE2 instead of the more common SPI_MODE0? The clock polarity (CPOL) and phase (CPHA) settings must match the slave device’s datasheet exactly. See SPI Modes and Timing for a full breakdown of all four modes, their idle states, and sampling edges.
Want to see the SCK, CS, MOSI, and MISO waveforms live? Hook up a logic analyzer using the channel mapping in the sketch comments — CH0: SCK, CH1: CS, CH2: MOSI, CH3: MISO. Visit Logic Analyzer Setup for a step-by-step connection guide and recommended capture settings.

Build docs developers (and LLMs) love