Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNS: Serial Communication #120

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
49 changes: 49 additions & 0 deletions arduino/main.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <SoftwareSerial.h>
#include <SerialProtocol.h>
#include <StreamSerialProtocol.h>
#include <ArduinoSerialProtocol.h>

int IRSensor1 = 2;
mifrandir marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems brittle if we add/remove the number of IR sensors and wheel encoders? Can we not maintain a map of IR sensors and wheel encoders and loop through?

Copy link
Member

@mifrandir mifrandir Apr 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. We are only ever going to have four IR sensors. Let's not make it more complicated than it needs to be.

Looking at it again, we should not be using a map, I stand by that. However, I don't see why we don't have something like

#define kNumWheelEncoders 4
const int kWheelEncoders[kNumWheelEncoders] = { /* ... */};

Sorry, I was thinking in terms of C++.

Of course, this also raises the question of how we are going to do testing when we require all four to be present.

int IRSensor2 = 3;
int IRSensor3 = 4;
int IRSensor4 = 5;

// The payload that will be sent to the other device
struct Payload {
int16_t wheelEncoderA;
int16_t wheelEncoderB;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wheel_encoder_a

int16_t wheelEncoderC;
int16_t wheelEncoderD;
} payload;

ArduinoSerialProtocol protocol(&Serial, (uint8_t*)&payload, sizeof(payload));
uint8_t receiveState;

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

payload.wheelEncoderA = 0;
payload.wheelEncoderB = 0;
payload.wheelEncoderC = 0;
payload.wheelEncoderD = 0;
}

void loop()
{
if(digitalRead(IRSensor1)){
mifrandir marked this conversation as resolved.
Show resolved Hide resolved
++payload.wheelEncoderA;
}
if(digitalRead(IRSensor2)){
++payload.wheelEncoderB;
}
if(digitalRead(IRSensor3)){
++payload.wheelEncoderC;
}
if(digitalRead(IRSensor4)){
++payload.wheelEncoderD;
}

protocol.send();
}

119 changes: 119 additions & 0 deletions src/utils/io/serial.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#include "serial.hpp"

#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>

#include <iostream>
#include <sstream>

#include <utils/system.hpp>

namespace hyped::utils::io {

SerialProtocol::SerialProtocol(const std::string serial, const BaudRate baud_rate)
kshxtij marked this conversation as resolved.
Show resolved Hide resolved
: serial_device_(serial),
baud_rate_(baud_rate),
log_(utils::Logger("SERIAL", utils::System::getSystem().config_.log_level))
{
configureTermios();
}

SerialProtocol::~SerialProtocol()
{
close(serial_);
};

bool SerialProtocol::serialAvailable()
{
return serial_ > 0;
}

void SerialProtocol::configureTermios()
{
serial_ = open(serial_device_.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
Copy link
Contributor

@arjunnaha arjunnaha Apr 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some of these lines need comments as the constant names are not obvious (e.g. O_NOCTTY)


if (serial_ == -1) {
log_.error("Unable to open serial device: %s", serial_device_.c_str());
return;
}

struct termios options;
tcgetattr(serial_, &options);
options.c_cflag = CS8 | CLOCAL | CREAD;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
switch (baud_rate_) {
case BaudRate::kB300:
cfsetispeed(&options, B300);
cfsetospeed(&options, B300);
break;
case BaudRate::kB600:
cfsetispeed(&options, B600);
cfsetospeed(&options, B600);
break;
case BaudRate::kB1200:
cfsetispeed(&options, B1200);
cfsetospeed(&options, B1200);
break;
case BaudRate::kB2400:
cfsetispeed(&options, B2400);
cfsetospeed(&options, B2400);
break;
case BaudRate::kB4800:
cfsetispeed(&options, B4800);
cfsetospeed(&options, B4800);
break;
case BaudRate::kB9600:
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
break;
case BaudRate::kB19200:
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
break;
default:
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
break;
}
tcflush(serial_, TCIFLUSH);
tcsetattr(serial_, TCSANOW, &options);
}

void SerialProtocol::readData(std::vector<uint8_t> &data)
{
data.clear();
if (!serialAvailable()) {
log_.error("Serial device not available for read.");
return;
}

const size_t bytesRead = read(serial_, &readBuffer_[0], readBufferSize_B_);
kshxtij marked this conversation as resolved.
Show resolved Hide resolved

if (bytesRead < 0) {
log_.error("Error reading from serial device.");
return;
}

std::copy(readBuffer_.begin(), readBuffer_.begin() + bytesRead, back_inserter(data));
return;
}

void SerialProtocol::writeData(const std::vector<uint8_t> &data)
{
if (!serialAvailable()) {
log_.error("Serial device not available for write.");
return;
}

int write_result = write(serial_, data.data(), data.size());

if (write_result == -1) {
log_.error("Error writing to serial device.");
return;
}
}
} // namespace hyped::utils::io
35 changes: 35 additions & 0 deletions src/utils/io/serial.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <stdint.h>
#include <stdlib.h>
mifrandir marked this conversation as resolved.
Show resolved Hide resolved
#include <string.h>
#include <termios.h>

#include <vector>

#include <utils/logger.hpp>
namespace hyped::utils::io {

enum class BaudRate { kB300, kB600, kB1200, kB2400, kB4800, kB9600, kB14400, kB19200, kB28800 };
mifrandir marked this conversation as resolved.
Show resolved Hide resolved
class SerialProtocol {
public:
SerialProtocol(std::string serial, BaudRate baud_rate);
~SerialProtocol();

void configureTermios();

bool serialAvailable();

void readData(std::vector<uint8_t> &buffer);
void writeData(const std::vector<uint8_t> &buffer);

private:
std::string serial_device_;
int serial_;
BaudRate baud_rate_;
utils::Logger log_;

std::vector<char> readBuffer_;
unsigned char readBufferSize_B_;
};
}; // namespace hyped::utils::io