[
  {
    "path": ".gitignore",
    "content": "bin/\n*.log\n.vscode/"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"rust-arduino-serial\"]\n\tpath = rust-arduino-serial\n\turl = https://github.com/araffin/rust-arduino-serial\n[submodule \"cpp-arduino-serial\"]\n\tpath = cpp-arduino-serial\n\turl = https://github.com/araffin/cpp-arduino-serial.git\n[submodule \"python-arduino-serial\"]\n\tpath = python-arduino-serial\n\turl = https://github.com/araffin/python-arduino-serial.git\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Antonin RAFFIN\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Robust Arduino Serial Protocol\n\n**Robust Arduino Serial** is a simple and robust serial communication protocol. It was designed to make two Arduinos communicate, but can also be useful when you want a computer (e.g. a Raspberry Pi) to communicate with an Arduino.\n\n**Please read the [Medium Article](https://medium.com/@araffin/simple-and-robust-computer-arduino-serial-communication-f91b95596788) to have an overview of this protocol.**\n\nImplementations are available in various programming languages:\n\n- Arduino (`arduino-serial/` folder)\n- [Python](https://github.com/araffin/python-arduino-serial)\n- [C++](https://github.com/araffin/cpp-arduino-serial)\n- [Rust](https://github.com/araffin/rust-arduino-serial)\n\n\n**Examples** are provided in each repository.\n\nTo clone all the repositories at once, tou need to use the `--recursive` command:\n```\ngit clone https://github.com/araffin/arduino-robust-serial.git --recursive\n```\n\nTable of Contents\n=================\n\n   * [Provided Functions](#provided-functions)\n   * [Arduino Implementation](#arduino-implementation)\n      * [1. Using Arduino IDE](#1-using-arduino-ide)\n      * [2. Using Arduino Makefile (Recommended)](#2-using-arduino-makefile-recommended)\n   * [Python Implementation](#python-implementation)\n   * [C   Implementation](#c-implementation)\n   * [Rust Implementation](#rust-implementation)\n   * [Real Life Example](#real-life-example)\n   * [Acknowledgments](#acknowledgments)\n\n\n### Provided Functions\n\nPlease check examples in the different repos to have the parameters details for each programming language.\n\n- `read_order()`: Read one byte from a file/serial port and convert it to an order (equivalent to read_i8)\n- `read_i8()`: Read one byte from a file/serial port and convert it to a 8 bits int\n- `read_i16()`: Read one byte from a file/serial port and convert it to a 16 bits int\n- `read_i32()`: Read one byte from a file/serial port and convert it to a 32 bits int\n- `write_order()`: Write an order to a file/serial port. (equivalent to write_i8)\n- `write_i8()`: Write one byte int to a file/serial port.\n- `write_i16()`: Write two bytes (16-bits) int to a file/serial port.\n- `write_i32()`: Write four bytes (32-bits) int to a file/serial port.\n\n\n### Arduino Implementation\n\n#### 1. Using Arduino IDE\n\nOpen `arduino-board/slave/slave.ino` in your Arduino IDE.\n\n#### 2. Using Arduino Makefile (Recommended)\n\nThis method only works with Linux/Mac Os systems: [https://github.com/sudar/Arduino-Makefile](https://github.com/sudar/Arduino-Makefile)\n\nInstall Arduino Makefile.\n```\nsudo apt-get install arduino-mk\n```\n\nCompile and upload the code to the Arduino (please check the board name in the Makefile):\n```\ncd arduino-board/\nmake\nmake upload\n```\n\n### Python Implementation\n\n[![Build Status](https://travis-ci.org/araffin/python-arduino-serial.svg?branch=master)](https://travis-ci.org/araffin/python-arduino-serial)\n\nPython repository: [https://github.com/araffin/python-arduino-serial](https://github.com/araffin/python-arduino-serial)\n\n### C++ Implementation\n\n[![Build Status](https://travis-ci.org/araffin/cpp-arduino-serial.svg?branch=master)](https://travis-ci.org/araffin/cpp-arduino-serial)\n\n\nC++ repository: [https://github.com/araffin/cpp-arduino-serial](https://github.com/araffin/cpp-arduino-serial)\n\n\n### Rust Implementation\n\n[![Build Status](https://travis-ci.org/araffin/rust-arduino-serial.svg?branch=master)](https://travis-ci.org/araffin/rust-arduino-serial) [![Build status](https://ci.appveyor.com/api/projects/status/h0ejgesat0nnpahc/branch/master?svg=true)](https://ci.appveyor.com/project/araffin/rust-arduino-serial/branch/master)\n\nRust repository: [https://github.com/araffin/rust-arduino-serial](https://github.com/araffin/rust-arduino-serial)\n\n### Real Life Example\n\nThis protocol was used on the Racing Robot: [https://github.com/sergionr2/RacingRobot](https://github.com/sergionr2/RacingRobot)\n\n[![The racing robot](https://cdn-images-1.medium.com/max/2000/1*UsmiJ4IzXi6U9svKjB22zw.jpeg)](https://www.youtube.com/watch?v=xhI71ZdSh6k)\n\n### Acknowledgments\n\nI would like to thanks Dara Ly for the original idea of communicating with the Arduino via a command parser, and Xuan Zhang for fixing Arduino limited buffer issue.\n"
  },
  {
    "path": "_config.yml",
    "content": "theme: jekyll-theme-architect"
  },
  {
    "path": "arduino-board/Makefile",
    "content": "### DISCLAIMER\n### This is an example Makefile and it MUST be configured to suit your needs.\n### For detailled explanations about all the avalaible options,\n### please refer to https://github.com/sudar/Arduino-Makefile/blob/master/arduino-mk-vars.md\n### Original project where this Makefile comes from: https://github.com/WeAreLeka/Bare-Arduino-Project\n\n### PROJECT_DIR\n### This is the path to where you have created/cloned your project\nPROJECT_DIR       = $(shell pwd)\n\n### ARDMK_DIR\n### Path to the Arduino-Makefile directory.\nARDMK_DIR         = /usr/share/arduino\n\n### ARDUINO_DIR\n### Path to the Arduino application and ressources directory.\n\n### or on Linux: (remove the one you don't want)\nARDUINO_DIR       = /usr/share/arduino\n\n### USER_LIB_PATH\n### Path to where the your project's libraries are stored.\nUSER_LIB_PATH    :=  $(PROJECT_DIR)/lib\n\n### BOARD_TAG\n### It must be set to the board you are currently using. (i.e uno, mega2560, etc.)\nBOARD_TAG         = uno\n\n### MONITOR_BAUDRATE\n### It must be set to Serial baudrate value you are using.\nMONITOR_BAUDRATE  = 115200\n\n### AVR_TOOLS_DIR\n### Path to the AVR tools directory such as avr-gcc, avr-g++, etc.\n### On OS X with `homebrew`:\nAVR_TOOLS_DIR     = /usr/local\n### or on Linux: (remove the one you don't want)\nAVR_TOOLS_DIR     = /usr\n\n### AVRDUDE\n### Path to avrdude directory.\n### On OS X with `homebrew`:\nAVRDUDE           = /usr/local/bin/avrdude\n### or on Linux: (remove the one you don't want)\nAVRDUDE           = /usr/bin/avrdude\n\n### AVRDUDE_CONF\n### Path to avrdude config file.\n#AVRDUDE_CONF      = /etc/avrdude.conf\n\n### CFLAGS_STD\n### Set the C standard to be used during compilation. Documentation (https://github.com/WeAreLeka/Arduino-Makefile/blob/std-flags/arduino-mk-vars.md#cflags_std)\nCFLAGS_STD        = -std=gnu11\n\n### CXXFLAGS_STD\n### Set the C++ standard to be used during compilation. Documentation (https://github.com/WeAreLeka/Arduino-Makefile/blob/std-flags/arduino-mk-vars.md#cxxflags_std)\nCXXFLAGS_STD      = -std=gnu++11\n\n### CXXFLAGS\n### Flags you might want to set for debugging purpose. Comment to stop.\nCXXFLAGS         += -pedantic -Wall -Wextra\n\n### MONITOR_PORT\n### The port your board is connected to. Using an '*' tries all the ports and finds the right one.\nMONITOR_PORT      = /dev/ttyACM*\n\n### CURRENT_DIR\n### Do not touch - used for binaries path\nCURRENT_DIR       = $(shell basename $(CURDIR))\n\n### OBJDIR\n### This is were you put the binaries you just compile using 'make'\nOBJDIR            = $(PROJECT_DIR)/bin/$(BOARD_TAG)/$(CURRENT_DIR)\n\n### path to Arduino.mk, inside the ARDMK_DIR, don't touch.\ninclude $(ARDMK_DIR)/Arduino.mk\n"
  },
  {
    "path": "arduino-board/order.h",
    "content": "#ifndef ORDER_H\n#define ORDER_H\n\n// Define the orders that can be sent and received\nenum Order {\n  HELLO = 0,\n  SERVO = 1,\n  MOTOR = 2,\n  ALREADY_CONNECTED = 3,\n  ERROR = 4,\n  RECEIVED = 5,\n  STOP = 6,\n};\n\ntypedef enum Order Order;\n\n#endif\n"
  },
  {
    "path": "arduino-board/parameters.h",
    "content": "#ifndef PARAMETERS_H\n#define PARAMETERS_H\n\n#define SERIAL_BAUD 115200  // Baudrate\n#define MOTOR_PIN 3\n#define DIRECTION_PIN 4\n#define SERVOMOTOR_PIN 6\n#define INITIAL_THETA 110  // Initial angle of the servomotor\n// Min and max values for motors\n#define THETA_MIN 60\n#define THETA_MAX 150\n#define SPEED_MAX 100\n\n// If DEBUG is set to true, the arduino will send back all the received messages\n#define DEBUG false\n\n#endif\n"
  },
  {
    "path": "arduino-board/slave.cpp",
    "content": "#include <Arduino.h>\n#include <Servo.h>\n\n#include \"order.h\"\n#include \"slave.h\"\n#include \"parameters.h\"\n\nbool is_connected = false; ///< True if the connection with the master is available\nint8_t motor_speed = 0;\nint16_t servo_angle = INITIAL_THETA;\nServo servomotor;\n\nvoid setup()\n{\n  // Init Serial\n  Serial.begin(SERIAL_BAUD);\n\n  // Init Motor\n  pinMode(MOTOR_PIN, OUTPUT);\n  pinMode(DIRECTION_PIN, OUTPUT);\n  // Stop the car\n  stop();\n\n  // Init Servo\n  servomotor.attach(SERVOMOTOR_PIN);\n  // Order between 0 and 180\n  servomotor.write(INITIAL_THETA);\n\n  // Wait until the arduino is connected to master\n  while(!is_connected)\n  {\n    write_order(HELLO);\n    wait_for_bytes(1, 1000);\n    get_messages_from_serial();\n  }\n\n}\n\nvoid loop()\n{\n  get_messages_from_serial();\n  update_motors_orders();\n}\n\nvoid update_motors_orders()\n{\n  servomotor.write(constrain(servo_angle, THETA_MIN, THETA_MAX));\n  motor_speed = constrain(motor_speed, -SPEED_MAX, SPEED_MAX);\n  // Send motor speed order\n  if (motor_speed > 0)\n  {\n    digitalWrite(DIRECTION_PIN, LOW);\n  }\n  else\n  {\n    digitalWrite(DIRECTION_PIN, HIGH);\n  }\n  analogWrite(MOTOR_PIN, convert_to_pwm(float(motor_speed)));\n}\n\nvoid stop()\n{\n  analogWrite(MOTOR_PIN, 0);\n  digitalWrite(DIRECTION_PIN, LOW);\n}\n\nint convert_to_pwm(float motor_speed)\n{\n  // TODO: compensate the non-linear dependency speed = f(PWM_Value)\n  return (int) round(abs(motor_speed)*(255./100.));\n}\n\nvoid get_messages_from_serial()\n{\n  if(Serial.available() > 0)\n  {\n    // The first byte received is the instruction\n    Order order_received = read_order();\n\n    if(order_received == HELLO)\n    {\n      // If the cards haven't say hello, check the connection\n      if(!is_connected)\n      {\n        is_connected = true;\n        write_order(HELLO);\n      }\n      else\n      {\n        // If we are already connected do not send \"hello\" to avoid infinite loop\n        write_order(ALREADY_CONNECTED);\n      }\n    }\n    else if(order_received == ALREADY_CONNECTED)\n    {\n      is_connected = true;\n    }\n    else\n    {\n      switch(order_received)\n      {\n        case STOP:\n        {\n          motor_speed = 0;\n          stop();\n          if(DEBUG)\n          {\n            write_order(STOP);\n          }\n          break;\n        }\n        case SERVO:\n        {\n          servo_angle = read_i16();\n          if(DEBUG)\n          {\n            write_order(SERVO);\n            write_i16(servo_angle);\n          }\n          break;\n        }\n        case MOTOR:\n        {\n          // between -100 and 100\n          motor_speed = read_i8();\n          if(DEBUG)\n          {\n            write_order(MOTOR);\n            write_i8(motor_speed);\n          }\n          break;\n        }\n  \t\t\t// Unknown order\n  \t\t\tdefault:\n          write_order(ERROR);\n          write_i16(404);\n  \t\t\t\treturn;\n      }\n    }\n    write_order(RECEIVED); // Confirm the reception\n  }\n}\n\n\nOrder read_order()\n{\n\treturn (Order) Serial.read();\n}\n\nvoid wait_for_bytes(int num_bytes, unsigned long timeout)\n{\n\tunsigned long startTime = millis();\n\t//Wait for incoming bytes or exit if timeout\n\twhile ((Serial.available() < num_bytes) && (millis() - startTime < timeout)){}\n}\n\n// NOTE : Serial.readBytes is SLOW\n// this one is much faster, but has no timeout\nvoid read_signed_bytes(int8_t* buffer, size_t n)\n{\n\tsize_t i = 0;\n\tint c;\n\twhile (i < n)\n\t{\n\t\tc = Serial.read();\n\t\tif (c < 0) break;\n\t\t*buffer++ = (int8_t) c; // buffer[i] = (int8_t)c;\n\t\ti++;\n\t}\n}\n\nint8_t read_i8()\n{\n\twait_for_bytes(1, 100); // Wait for 1 byte with a timeout of 100 ms\n  return (int8_t) Serial.read();\n}\n\nint16_t read_i16()\n{\n  int8_t buffer[2];\n\twait_for_bytes(2, 100); // Wait for 2 bytes with a timeout of 100 ms\n\tread_signed_bytes(buffer, 2);\n  return (((int16_t) buffer[0]) & 0xff) | (((int16_t) buffer[1]) << 8 & 0xff00);\n}\n\nint32_t read_i32()\n{\n  int8_t buffer[4];\n\twait_for_bytes(4, 200); // Wait for 4 bytes with a timeout of 200 ms\n\tread_signed_bytes(buffer, 4);\n  return (((int32_t) buffer[0]) & 0xff) | (((int32_t) buffer[1]) << 8 & 0xff00) | (((int32_t) buffer[2]) << 16 & 0xff0000) | (((int32_t) buffer[3]) << 24 & 0xff000000);\n}\n\nvoid write_order(enum Order myOrder)\n{\n\tuint8_t* Order = (uint8_t*) &myOrder;\n  Serial.write(Order, sizeof(uint8_t));\n}\n\nvoid write_i8(int8_t num)\n{\n  Serial.write(num);\n}\n\nvoid write_i16(int16_t num)\n{\n\tint8_t buffer[2] = {(int8_t) (num & 0xff), (int8_t) (num >> 8)};\n  Serial.write((uint8_t*)&buffer, 2*sizeof(int8_t));\n}\n\nvoid write_i32(int32_t num)\n{\n\tint8_t buffer[4] = {(int8_t) (num & 0xff), (int8_t) (num >> 8 & 0xff), (int8_t) (num >> 16 & 0xff), (int8_t) (num >> 24 & 0xff)};\n  Serial.write((uint8_t*)&buffer, 4*sizeof(int8_t));\n}\n"
  },
  {
    "path": "arduino-board/slave.h",
    "content": "#ifndef ARDUINO_SLAVE_H\n#define ARDUINO_SLAVE_H\n\n/*!\n * \\brief Send updated motors orders to the two motors (servomotor + motor)\n */\nvoid update_motors_orders();\n\n/*!\n * Stop the car (set the speed to 0)\n */\nvoid stop();\n\n/*!\n * \\brief Convert a speed order (in percentage of max speed)\n * into a pwm order (between 0 and 255)\n * \\param motor_speed speed order in percentage of max speed\n * \\return the speed order in pwm\n */\nint convert_to_pwm(float motor_speed);\n\n/*!\n * \\brief Read one byte from the serial and cast it to an Order\n * \\return the order received\n */\nOrder read_order();\n\n/*!\n * \\brief Wait until there are enough bytes in the buffer\n * \\param num_bytes the number of bytes\n * \\param timeout (ms) The timeout, time after which we release the lock\n * even if there are not enough bytes\n */\nvoid wait_for_bytes(int num_bytes, unsigned long timeout);\n\n/*!\n * \\brief Read signed bytes and put them in a buffer\n * \\param buffer an array of bytes\n * \\param n number of bytes to read\n */\nvoid read_signed_bytes(int8_t* buffer, size_t n);\n\n/*!\n * \\brief Read one byte from a serial port and convert it to a 8 bits int\n * \\return the decoded number\n */\nint8_t read_i8();\n\n/*!\n * \\brief Read two bytes from a serial port and convert it to a 16 bits int\n * \\return the decoded number\n */\nint16_t read_i16();\n\n\n/*!\n * \\brief Read four bytes from a serial port and convert it to a 32 bits int\n * \\return the decoded number\n */\nint32_t read_i32();\n\n/*!\n * \\brief Send one order (one byte)\n * \\param order type of order\n */\nvoid write_order(enum Order order);\n\n/*!\n * \\brief Write one byte int to serial port (between -127 and 127)\n * \\param num an int of one byte\n */\nvoid write_i8(int8_t num);\n\n/*!\n * \\brief Send a two bytes signed int via the serial\n * \\param num the number to send (max: (2**16/2 -1) = 32767)\n */\nvoid write_i16(int16_t num);\n\n/*!\n * \\brief Send a four bytes signed int (long) via the serial\n * \\param num the number to send (−2,147,483,647, +2,147,483,647)\n */\nvoid write_i32(int32_t num);\n\n/*!\n * \\brief Listen the serial and decode the message received\n */\nvoid get_messages_from_serial();\n\n#endif\n"
  }
]