Tom Brockington
Published

Arduino master/slave data transfter

How to transfer long string of information instead of the using char/bytes. Using a master slave arudino or nano set up to use more pins.

BeginnerFull instructions provided1 hour183
Arduino master/slave data transfter

Things used in this project

Story

Read more

Schematics

Image

Code

Slave code

C/C++
Connect the slave Via A5-A5 A4-A4 to master
#include <Wire.h>
#include <SPI.h>
#include <SD.h>

void setup() {
  Serial.begin(115200);

  Serial.print("SERIAL CONNECTING....");

  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  pinMode(LED_BUILTIN, OUTPUT); // Initialize the built-in LED pin as an output // Test Function

  Serial.print("Initializing SD card...");

  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");

  Wire.begin(4); // Join I2C bus with address #4
  Wire.onReceive(receiveEvent); // Register event

  File root = SD.open("/");
  readSdCard(root);
}

void loop() {
  delay(100);
}

// Function that executes whenever data is received from master
void receiveEvent(int howMany) {
  if (Wire.available()) {
    int signal = Wire.read(); // Receive signal as an integer
    if (signal == 1) {
      testSlave1();
    }
    else if (signal == 2) { // Signal for SD card listing
      Serial.print("COMMUNICATION ESTABLISHED");
      delay(100);

      Wire.onRequest(requestEvent); // register event
    }
  }
}

void requestEvent() {
  // Explicitly include the null terminator in the message
  static const char message[] = "Connected!"; // Your message
  static const int messageLength = sizeof(message); // Includes null terminator because of static array declaration

  Wire.write(message, messageLength); // Send "hello" followed by '\0'
}


void readSdCard(File dir) {
  while (true) {
    File entry = dir.openNextFile();
    if (!entry) {
      // No more files
      break;
    }
    if (entry.isDirectory()) {
      // Skip directories or recursively call sendFileNames(entry) if you want to include them
    } else {
      // Send the file name
      Serial.println(entry.name());
    }
    entry.close();
  }
}

void sendFileNamesToMaster() {
  File root = SD.open("/");
  File entry = root.openNextFile();

  while (entry) {
    if (!entry.isDirectory()) {
      sendFileNameToMaster(entry.name());
    }
    entry.close();
    entry = root.openNextFile();
  }
  // Signal the end of transmission with a special message, such as an empty string
  sendFileNameToMaster("");
}

void sendFileNameToMaster(const char* fileName) {
  Wire.beginTransmission(4); // Slave address of the master
  const byte startMarker = 0x02; // Start of text
  const byte endMarker = 0x03; // End of text
  Wire.write(startMarker); // Indicate start of file name
  
  // Send file name in chunks  
  while (*fileName) {
    Wire.write(*fileName++);
  }
  
  Wire.write(endMarker); // Indicate end of file name
  Wire.endTransmission();
  delay(100); // Simple flow control - wait a bit before the next operation
}

void testSlave1() {
  Serial.print("TEST SLAVE 1");
  digitalWrite(LED_BUILTIN, HIGH); // Turn the LED on
  delay(500);                      // Wait for half a second
  digitalWrite(LED_BUILTIN, LOW);  // Turn the LED off
  delay(500);                      // Wait for half a second
}

Master code

C/C++
This code includes alot of extra stuff you can strip out.
It was designed to run and LCD screen and menu, while getting options to display from the slave arduino and an SD card.
Slave test and comms test are the interesting functions. Slave test just makes the arduino. And if you trace the comms test you can send long message or data back to the master and display it on the lcd or just read in serial.
#include <Wire.h>
#include <LiquidCrystal.h>

// Button pin assignments
const int buttonUp = 6, buttonDown = 7, buttonEnter = 8, buttonBack = 9;

// LCD pin assignments and initialization
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

// Menu items
String mainMenuItems[] = {"SD Card", "Motor Control", "Tests", "Shutdown"};
String motorControlMenu[] = {"Speed+", "Speed-", "Release", "Home axis", "Back"};
String testsMenu[] = {"Slave Test", "Coms Test", "Back"};
String shutdownMenu[] = {"Confirm Shutdown", "Back"};

String* currentMenu = mainMenuItems; // Start with main menu

int menuItemCount = 4;
int currentMenuItem = 0; // variable to track the current menu item

// SD Data
String fileNames[10]; // Assuming a max of 10 files
int fileCount = 0;

// Menu state
enum MenuState {MAIN_MENU, SD_CARD_MENU, MOTOR_CONTROL_MENU, TESTS_MENU, SHUTDOWN_MENU};
MenuState currentMenuState = MAIN_MENU;

void setup() {
  Serial.begin(115200);

  Wire.begin(); // Join I2C bus as master

  lcd.begin(16, 2);  
  pinMode(buttonUp, INPUT_PULLUP);
  pinMode(buttonDown, INPUT_PULLUP);
  pinMode(buttonEnter, INPUT_PULLUP);
  pinMode(buttonBack, INPUT_PULLUP);

  lcd.clear();
  lcd.print("> ");
  lcd.print(mainMenuItems[currentMenuItem]); 

  // Let's fix the next part:
  int nextMenuItem = currentMenuItem + 1;
  if(nextMenuItem >= menuItemCount) {
    nextMenuItem = 0; // Wrap around to the first item
  }

  // Display the next menu item on the second row
  lcd.setCursor(2, 1); // Set cursor to the second column of the second row to align visually
  lcd.print(currentMenu[nextMenuItem]); // Corrected to use 'currentMenu' instead of 'menuItems'
}

void loop() {
  static unsigned long lastPress = 0;
  if (millis() - lastPress > 200) { // Basic debouncing

    if (digitalRead(buttonUp) == LOW) {
      navigateMenu(-1); // Navigate up
      lastPress = millis();
    } else if (digitalRead(buttonDown) == LOW) {
      navigateMenu(1); // Navigate down
      lastPress = millis();
    } else if (digitalRead(buttonEnter) == LOW) {
      enterMenu(); // Enter into a submenu or execute an option
      lastPress = millis();
    } else if (digitalRead(buttonBack) == LOW) {

      // When the back button is pressed, return to the main menu
      if (currentMenuState != MAIN_MENU) { // Check to avoid resetting if already in the main menu
        currentMenuState = MAIN_MENU;
        currentMenu = mainMenuItems; // Reset the current menu to the main menu
        menuItemCount = sizeof(mainMenuItems) / sizeof(mainMenuItems[0]); // Reset menu item count to the main menu's count
        currentMenuItem = 0; // Reset the current menu item index to 0
        updateMenuDisplay(); // Update the display to show the main menu
        lastPress = millis();
      }
    }
  }
}


void navigateMenu(int direction) {
  currentMenuItem += direction;
  if (currentMenuItem < 0) {
    currentMenuItem = menuItemCount - 1;
  } else if (currentMenuItem >= menuItemCount) {
    currentMenuItem = 0;
  }
  updateMenuDisplay();
}

void enterMenu() {
  // Handle entering submenus or executing options
  if (currentMenuState == MAIN_MENU) {
    switch (currentMenuItem) {
      case 0:
        requestSDCardFileListing();
        break;
      case 1:
        currentMenuState = MOTOR_CONTROL_MENU;
        currentMenu = motorControlMenu;
        menuItemCount = sizeof(motorControlMenu) / sizeof(motorControlMenu[0]);
        break;
      case 2: // "Tests" menu selected
        currentMenuState = TESTS_MENU;
        currentMenu = testsMenu;
        menuItemCount = sizeof(testsMenu) / sizeof(testsMenu[0]);
        break;
      case 3:
        currentMenuState = SHUTDOWN_MENU;
        currentMenu = shutdownMenu;
        menuItemCount = sizeof(shutdownMenu) / sizeof(shutdownMenu[0]);
        break;
    }
    currentMenuItem = 0; // Reset to the top of the submenu
    updateMenuDisplay();
  } else if (currentMenuState == TESTS_MENU) {
    if (currentMenuItem == 0) { // "Slave Test" selected
      Wire.beginTransmission(4); // Begin transmission to device with I2C address 4
      Wire.write(1); // Send a signal (e.g., byte '1') to indicate "Slave Test" action
      Wire.endTransmission();
    } // End transmission
    else if (currentMenuItem == 1) { // "Coms Test" selected
      Wire.beginTransmission(4); 
      Wire.write(2); // Signal to request the "hello" message
      Wire.endTransmission();
      displayHelloMessageFromSlave();
    }
    else if (currentMenu[currentMenuItem] == "Back") {
      currentMenuState = MAIN_MENU;
      currentMenu = mainMenuItems;
      menuItemCount = sizeof(mainMenuItems) / sizeof(mainMenuItems[0]);
      currentMenuItem = 0;
      updateMenuDisplay();
    }
    // No else needed here as we're handling all conditions within TESTS_MENU
  }
  // If not in the MAIN_MENU and a specific action was executed, you might want to update the display or take other actions
}

void displayHelloMessageFromSlave() {
  lcd.clear();
  lcd.setCursor(0, 0);

  Wire.requestFrom(4, 32); // Request up to 32 bytes, but you might not use all.
  char receivedMessage[33]; // Buffer for the message, assuming maximum length + 1 for terminator
  int i = 0;

  while (Wire.available()) {
    char c = Wire.read(); // Read a byte
    if (c == '\0' || i == 32) { // Check for the end of the message or buffer full
      break; // Exit the loop
    }
    receivedMessage[i] = c;
    i++;
  }
  receivedMessage[i] = '\0'; // Ensure the string is properly terminated

  lcd.print(receivedMessage); // Display the message
}

// void requestSDCardFileListing() {
//   Wire.beginTransmission(4); // Slave address
//   Wire.write(2); // Signal for SD card listing
//   Wire.endTransmission();

//   // Immediately start polling for file names
//   displayFileNamesFromSlave();
// }

void requestSDCardFileListing() {
  Wire.beginTransmission(4); // Slave address
  Wire.write(2); // Signal for SD card listing
  Wire.endTransmission();

  // Reset file list
  fileCount = 0;
  bool isReceiving = true;

  while (isReceiving && fileCount < 10) { // Prevent overflow
    Wire.requestFrom(4, 32); // Adjust size as needed
    String fileName = "";
    while (Wire.available()) {
      char c = Wire.read();
      if (c == '\0') { // Check for the end of a file name
        if (fileName.length() == 0) {
          isReceiving = false; // Empty filename signals end of the list
          break;
        }
        fileNames[fileCount++] = fileName; // Add to array
        fileName = ""; // Reset for the next file name
      } else {
        fileName += c;
      }
    }
  }

  // Update the menu to display these file names
  currentMenu = fileNames;
  menuItemCount = fileCount;
  currentMenuItem = 0;
  updateMenuDisplay();
}

void displayFileNamesFromSlave() {
  lcd.clear();
  lcd.setCursor(0, 0);
  bool isReceiving = true; // Assume we have data to receive
  while (isReceiving) {
    Wire.requestFrom(4, 32); // Request up to 32 bytes from slave device #4
    isReceiving = false; // Assume this is the last batch unless we hit the marker
    while (Wire.available()) { // Slave may send less than requested
      char c = Wire.read(); // Receive a byte as character
      if (c == '\0') { // Null char might be used to signal the end of data
        isReceiving = false; // No more data to read
        break;
      } else {
        lcd.write(c); // Print the character on the LCD
        isReceiving = true; // Still receiving data
      }
    }
    delay(100); // Simple delay to allow for reading and display refresh
  }
}


void updateMenuDisplay() {
  lcd.clear();
  lcd.print("> ");
  lcd.print(currentMenu[currentMenuItem]); // Displays the current selected item

  // Correctly calculate and display the next menu item
  int nextMenuItem = (currentMenuItem + 1) % menuItemCount;
  lcd.setCursor(2, 1); // Adjusted for second line
  if (menuItemCount > 1) { // Only try to display a second item if it exists
    lcd.print(currentMenu[nextMenuItem]); // Display the next item
  }
}

Credits

Tom Brockington
2 projects • 0 followers
Contact

Comments

Please log in or sign up to comment.