Custom Communication Protocol

Raspberry Pi → Arduino • One Clock Wire + One Data Wire

Overview

I designed and implemented a lightweight custom communication protocol to send messages from a Raspberry Pi to an Arduino using only two wires: one for clock and one for data. The protocol includes start/stop flags, byte encoding, and character translation for ASCII-like messaging.

Code

comm_prot_send.py


import RPi.GPIO as GPIO
import time

DELAY = 0.0001
CLK = 27
DATA = 17

GPIO.setmode(GPIO.BCM)
GPIO.setup(DATA, GPIO.OUT)    
GPIO.setup(CLK, GPIO.OUT)

def sendData(data):
    if data == 1:
        GPIO.output(CLK, GPIO.LOW)
        GPIO.output(DATA, GPIO.HIGH)
        time.sleep(DELAY)
        GPIO.output(CLK, GPIO.HIGH)
        time.sleep(DELAY)
    elif data == 0:
        GPIO.output(CLK, GPIO.LOW)
        GPIO.output(DATA, GPIO.LOW)
        time.sleep(DELAY)
        GPIO.output(CLK, GPIO.HIGH)
        time.sleep(DELAY)

def sendByte(data):
    binary_str = format(data, '08b')
    bits = [int(bit) for bit in binary_str]
    for i, bit in enumerate(binary_str):
        sendData(bits[i])

def encode(c):
    table = { 'START': 0xFF, 'STOP': 0x00, 'A': 0x25, 'B': 0x26, 'C': 0x27, ... }
    return table.get(c, None)

def sendString(s):
    sendByte(encode('START'))
    for c in s:
        sendByte(encode(c))
    sendByte(encode('STOP'))

try:
    while True:
        inputStr = input("Enter message: ")
        sendString(inputStr)

except KeyboardInterrupt:
    print("Exiting...")
    GPIO.cleanup()
      

comm_prot_recieve.ino


#define DATA_Pin 3
#define CLK_Pin 4

const int maxMessageLength = 200;

int lastClkState = LOW;
int strtCnt = 0;
int stopCnt = 0;
int charCnt = 0;
bool letter[8];
bool read = false;

char message[maxMessageLength];
int msgIndex = 0;

void setup() {
  Serial.begin(9600);
  pinMode(DATA_Pin, INPUT);
  pinMode(CLK_Pin, INPUT);
}

void loop() {
  int currentClkState = digitalRead(CLK_Pin);

  if (lastClkState == LOW && currentClkState == HIGH) {
    int data = digitalRead(DATA_Pin);

    if (read) {
      if (data == 0) {
        stopCnt++;
      } else {
        stopCnt = 0;
      }

      letter[charCnt] = data;
      charCnt++;

      if (charCnt >= 8) {
        char decoded = decodeEightBitChar(letter);

        if (msgIndex < maxMessageLength - 1) {
          message[msgIndex++] = decoded;
        }

        charCnt = 0;
      }

      if (stopCnt == 8) {
        message[msgIndex] = '\0';  
        Serial.println(message);

        read = false;
        stopCnt = 0;
        charCnt = 0;
        msgIndex = 0;
      }

    } else {
      if (data == 1) {
        strtCnt++;
      } else {
        strtCnt = 0;
      }

      if (strtCnt == 8) {
        read = true;
        strtCnt = 0;
        charCnt = 0;
        stopCnt = 0;
        msgIndex = 0;
      }
    }
  }

  lastClkState = currentClkState;
}

char decodeEightBitChar(bool bits[8]) {
  byte value = 0;
  for (int i = 0; i < 8; i++) {
    value = (value << 1) | bits[i];
  }

  switch (value) {
    case 0x00: return NULL;
    case 0x81: return '1';
    case 0x82: return '2';
    case 0x83: return '3';
    case 0x84: return '4';
    case 0x85: return '5';
    case 0x86: return '6';
    case 0x87: return '7';
    case 0x88: return '8';
    case 0x89: return '9';
    case 0x8A: return '0';
    case 0x8B: return 'a';
    case 0x8C: return 'b';
    case 0x8D: return 'c';
    case 0x8E: return 'd';
    case 0x8F: return 'e';
    case 0x10: return 'f';
    case 0x11: return 'g';
    case 0x12: return 'h';
    case 0x13: return 'i';
    case 0x14: return 'j';
    case 0x15: return 'k';
    case 0x16: return 'l';
    case 0x17: return 'm';
    case 0x18: return 'n';
    case 0x19: return 'o';
    case 0x1A: return 'p';
    case 0x1B: return 'q';
    case 0x1C: return 'r';
    case 0x1D: return 's';
    case 0x1E: return 't';
    case 0x1F: return 'u';
    case 0x20: return 'v';
    case 0x21: return 'w';
    case 0x22: return 'x';
    case 0x23: return 'y';
    case 0x24: return 'z';
    case 0x25: return 'A';
    case 0x26: return 'B';
    case 0x27: return 'C';
    case 0x28: return 'D';
    case 0x29: return 'E';
    case 0x2A: return 'F';
    case 0x2B: return 'G';
    case 0x2C: return 'H';
    case 0x2D: return 'I';
    case 0x2E: return 'J';
    case 0x2F: return 'K';
    case 0x30: return 'L';
    case 0x31: return 'M';
    case 0x32: return 'N';
    case 0x33: return 'O';
    case 0x34: return 'P';
    case 0x35: return 'Q';
    case 0x36: return 'R';
    case 0x37: return 'S';
    case 0x38: return 'T';
    case 0x39: return 'U';
    case 0x3A: return 'V';
    case 0x3B: return 'W';
    case 0x3C: return 'X';
    case 0x3D: return 'Y';
    case 0x3E: return 'Z';
    case 0x3F: return ' ';
    case 0x40: return '!';
    case 0x41: return '?';
    case 0x42: return '.';
    case 0x43: return '*';
    case 0x44: return '/';
    case 0x45: return '&';
    case 0x46: return ':';
    case 0x47: return '"';
    case 0x48: return '(';
    case 0x49: return ')';
    case 0x4A: return '@';
    case 0x4B: return ',';
    case 0x4C: return '#';
    case 0x4D: return '\'';
    case 0x4E: return '=';
    case 0x4F: return '+';
    default: return '?';
  }
}

      
Back to Projects