Одна Arduino слаба, но 8 Arduino, объединенных в кластер, могут решать задачи, недоступные одному контроллеру! Мы создадим параллельный вычислительный кластер на основе I2C или SPI.
Архитектура кластера:
- Мастер (Master): Arduino Mega 2560 — распределяет задачи, собирает результаты.
- Слейвы (Slaves): 7 плат Arduino Uno/Nano — выполняют вычисления.
Протокол обмена данными:
cpp
// master.ino - Код для главной платы
#include <Wire.h>
#define SLAVE_COUNT 7
#define SLAVE_ADDR_START 8
// Структура задачи
struct Task {
uint8_t id;
uint8_t type; // 0=math, 1=matrix, 2=sort
uint8_t data[32];
uint8_t size;
};
// Структура результата
struct Result {
uint8_t task_id;
uint8_t status; // 0=ok, 1=error
uint8_t data[32];
uint8_t size;
};
Task taskQueue[10];
uint8_t taskCount = 0;
uint8_t slaveStatus[7];
void setup() {
Wire.begin();
Serial.begin(115200);
// Инициализация слейвов
for (uint8_t i = 0; i < SLAVE_COUNT; i++) {
Wire.beginTransmission(SLAVE_ADDR_START + i);
Wire.write(0x01); // Команда: инициализация
Wire.endTransmission();
slaveStatus[i] = 0;
}
}
// Отправка задачи на слейв
uint8_t sendTask(uint8_t slave, Task* task) {
Wire.beginTransmission(SLAVE_ADDR_START + slave);
Wire.write(0x02); // Команда: выполнить задачу
Wire.write(task->id);
Wire.write(task->type);
Wire.write(task->size);
Wire.write(task->data, task->size);
uint8_t err = Wire.endTransmission();
if (err == 0) {
slaveStatus[slave] = 1; // Занят
}
return err;
}
// Получение результата от слейва
uint8_t getResult(uint8_t slave, Result* result) {
Wire.requestFrom(SLAVE_ADDR_START + slave, 34);
if (Wire.available()) {
result->task_id = Wire.read();
result->status = Wire.read();
result->size = Wire.read();
for (uint8_t i = 0; i < result->size; i++) {
result->data[i] = Wire.read();
}
slaveStatus[slave] = 0; // Свободен
return 1;
}
return 0;
}
// Пример задачи: умножение матриц (разбивка на части)
void distributeMatrixMultiplication(uint8_t* matrixA, uint8_t* matrixB, uint8_t size) {
// Разбиваем матрицу на 7 частей
uint8_t rowsPerSlave = size / SLAVE_COUNT;
for (uint8_t i = 0; i < SLAVE_COUNT; i++) {
Task task;
task.id = i;
task.type = 1; // Matrix multiplication
task.size = rowsPerSlave * size * 2; // A и B части
// Копируем часть матрицы A
for (uint8_t r = 0; r < rowsPerSlave; r++) {
for (uint8_t c = 0; c < size; c++) {
task.data[r * size + c] = matrixA[(i * rowsPerSlave + r) * size + c];
}
}
// Копируем всю матрицу B
for (uint8_t r = 0; r < size; r++) {
for (uint8_t c = 0; c < size; c++) {
task.data[rowsPerSlave * size + r * size + c] = matrixB[r * size + c];
}
}
sendTask(i, &task);
}
// Сбор результатов
Result results[7];
for (uint8_t i = 0; i < SLAVE_COUNT; i++) {
while (!getResult(i, &results[i])) {
delay(10);
}
// Объединяем результаты в итоговую матрицу
// ...
}
}
void loop() {
// Периодически проверяем статус слейвов
for (uint8_t i = 0; i < SLAVE_COUNT; i++) {
Wire.requestFrom(SLAVE_ADDR_START + i, 1);
if (Wire.available()) {
uint8_t status = Wire.read();
if (status == 0 && slaveStatus[i] == 1) {
// Слейв завершил задачу, получаем результат
Result result;
getResult(i, &result);
// Обрабатываем результат
}
}
}
}
---------------------------------------------------------------------------
cpp
// slave.ino - Код для каждой дочерней платы
#include <Wire.h>
#define SLAVE_ADDR 8
// Обработчик команд от мастера
void receiveEvent(int count) {
uint8_t cmd = Wire.read();
switch (cmd) {
case 0x01: // Инициализация
// Настройка
break;
case 0x02: { // Выполнить задачу
uint8_t taskId = Wire.read();
uint8_t taskType = Wire.read();
uint8_t size = Wire.read();
uint8_t data[32];
for (uint8_t i = 0; i < size; i++) {
data[i] = Wire.read();
}
// Выполняем задачу
uint8_t result[32];
uint8_t resultSize = 0;
switch (taskType) {
case 0: // Math
resultSize = performMath(data, size, result);
break;
case 1: // Matrix
resultSize = performMatrix(data, size, result);
break;
case 2: // Sort
resultSize = performSort(data, size, result);
break;
}
// Сохраняем результат в буфер
resultBuffer[0] = taskId;
resultBuffer[1] = 0; // Status OK
resultBuffer[2] = resultSize;
memcpy(&resultBuffer[3], result, resultSize);
resultReady = true;
break;
}
}
}
// Обработчик запроса результата
void requestEvent() {
if (resultReady) {
Wire.write(resultBuffer, resultSize + 3);
resultReady = false;
}
}
void setup() {
Wire.begin(SLAVE_ADDR);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
}
void loop() {
// Слейв может выполнять фоновые задачи
}