quinta-feira, 21 de julho de 2011

Comunicação entre LEGO Mindstorms NXT e Arduino via serial RS-485

Print Friendly and PDF O artigo propõe a comunicação serial pelo protocolo RS-485 entre o LEGO Mindstorms NXT e o Arduino, utilizando o CI MAX 485.



Hardware:


Ambiente de programação:


– NXT firmware 1.28
– NXC nbc-1.2.1.r1
– Arduino 0018
– Mac OS X 10.6.3


Obsevações:

  • Foi usado um Arduino Nano V3, mas outros modelos também podem usados;
  • Inspirado pelo post  Early stage RS 485 with MAX485 no fórum sobre arduino e pelo post RS485 to RS232 Adapter no fórum nxtasy.org;
  • As funções seriais foram baseadas na biblioteca antiga de software serial do Arduino;
  • Testados para velocidades entre 9600 and 57600; velocidades maiores, não trabalha
baudrate.h:
#define RS485_BAUD 57600

NCX Program: (Screenshot)
#include "baudrate.h"
#define TOWAIT 2000
#define MSG "Hello Arduino  "

// -------------------------------------------------------------
void printMsg(byte inbuffer[], int cnt) {
  TextOut(0, LCD_LINE3, "       Baud");
  NumOut(0, LCD_LINE3, RS485_BAUD);
  TextOut(0, LCD_LINE5, "l=    cnt=     ");
  NumOut(12, LCD_LINE5, ArrayLen(inbuffer));
  NumOut(60, LCD_LINE5, cnt);
  //                        "Hi NXT,got  "
  TextOut(0, LCD_LINE6, "rcv:             ");
  TextOut(24, LCD_LINE6, ByteArrayToStr(inbuffer));
}

// -------------------------------------------------------------
task main() {
  int i = 0;
  int cnt = 0;
  byte outbuffer[];
  byte inbuffer[];
  char result;

  SetSensorType(IN_4, SENSOR_TYPE_HIGHSPEED);
  Wait(100);

  // we use odd parity on the Arduino side
  // see: http://forums.nxtasy.org/index.php?showtopic=3871
  RS485Uart(HS_BAUD_##RS485_BAUD, HS_MODE_8_DATA|HS_MODE_O_PARITY|HS_MODE_10_STOP);
  Wait(100);
  StrToByteArray(MSG, outbuffer);
  TextOut(0, LCD_LINE1, "RS485 <> Arduino");

  while (true) {
    outbuffer[14] = '0' + i;
    i++; if (i==10) i = 0;

    result = RS485Write(outbuffer);
    while (!RS485DataAvailable()) {}
    result = RS485Read(inbuffer);

    printMsg(inbuffer, cnt);
    cnt++; if (cnt==1000) cnt = 0;
    Wait(TOWAIT);
  }
}                                                             

Arduino sketch: (Screenshot)
#include "baudrate.h"

// Pins and wires
//         Arduino                Wire       MAX485
const byte RS485_OUT_PIN = 10; // yellow --> DI
const byte RS485_IN_PIN  = 11; // green  --> RO
const byte RS485_CTL_PIN = 12; // grey   --> RE+DE
//         VCC                           --> VCC
//         ------------------------------------------
//         NXT Sensor Port 4      Wire       MAX485
//         DIGIAI1                blue   --> B
//         DIGIAI0                yellow --> A
//         GND                    black  --> GND

const byte RECV_RS485_BIT =  B00001000;
const byte SEND_RS485_BIT =  B00000100;
#define readRS485inPin (PINB & RECV_RS485_BIT)

const unsigned int BUFLEN = 128;
const unsigned int outnbuf = 12;
byte outBuf[BUFLEN];
byte inBuf [BUFLEN];
unsigned int innbuf;
const char msg[] = "Hi NXT,got  ";

const byte SER_TIMEOUT = 7;
unsigned int RS485_LEN;
unsigned int bitDelay;
unsigned int halfBitDelay;

const byte CYCLES = 45;
// ------------------------------------------------------------------
void serSetup(unsigned long baud) {
  pinMode(RS485_OUT_PIN, OUTPUT);
  digitalWrite(RS485_OUT_PIN, LOW);
  pinMode(RS485_IN_PIN, INPUT);
  digitalWrite(RS485_IN_PIN, LOW);
  pinMode(RS485_CTL_PIN, OUTPUT);
  digitalWrite(RS485_CTL_PIN, LOW);

  RS485_LEN = 1000000 / baud;
  bitDelay = RS485_LEN - clockCyclesToMicroseconds(CYCLES);
  halfBitDelay = RS485_LEN/2 - clockCyclesToMicroseconds(CYCLES);
  Serial.print("\nRS-485 configured, ");
  Serial.print(baud, DEC);
  Serial.print(" Baud, Bit length ");
  Serial.print(RS485_LEN, DEC);
  Serial.println(" uSec");
}

// Startbit of 1st byte of incoming message encountered
// ------------------------------------------------------------------
byte serAvail() {
  return readRS485inPin;
}

// Read a byte
// ------------------------------------------------------------------
unsigned int serRead() {
  byte i;
  int val = 0;
  byte parity = 0, stopbit = 0;

  long start = millis();
  while (true) {
    if (millis()-start>SER_TIMEOUT) return -1;
    if (readRS485inPin) break;     // startbit hit
  }

  delayMicroseconds(halfBitDelay); // jump to middle of startbit
  for (i=0; i<8; i++) {
    delayMicroseconds(bitDelay);
    val |= !readRS485inPin << i;   // data bits
  }
  delayMicroseconds(bitDelay);
  parity = !readRS485inPin;        //
  delayMicroseconds(bitDelay);
  stopbit = !readRS485inPin;       //
  return val;
}                                                             

// Write one bit
// -----------------------------------------------------------------
void serBit(byte mark) {
  if (mark) PORTB &= ~ SEND_RS485_BIT;
  else      PORTB |=   SEND_RS485_BIT;
//  delayMicroseconds(RS485_LEN);
  delayMicroseconds(bitDelay);
}

// Write a byte
// ------------------------------------------------------------------
void serWrite(byte data) {
  byte mask = 1;
  byte bitcount = 0;

  serBit(LOW);               // startbit
  for (byte i=0; i<8; i++) { // data from LSB to MSB
    if (data & mask) {
      serBit(HIGH);
      bitcount++;
    } else {
      serBit(LOW);
    }
    mask <<= 1;
  }
  serBit((bitcount%2)==0);   // odd parity bit
  serBit(HIGH);              // stop bit
}

// ------------------------------------------------------------------
void printMsg(const char *s, byte buf[], int nbuf) {
  Serial.print(s);
  Serial.print(": \"");
  for (int i=0; i<nbuf; i++)
    Serial.print(buf[i], BYTE);
  Serial.println("\"");
}

// ------------------------------------------------------------------
void serSendMsg(byte buf[], int nbuf) {
  int i;

  digitalWrite(RS485_CTL_PIN, HIGH);
  for (i=0; i<nbuf; i++)
    serWrite(buf[i]);
  digitalWrite(RS485_CTL_PIN, LOW);
}

// ------------------------------------------------------------------
int serRecvMsg(byte buf[]) {
  int c;
  int nbuf = 0;

  while ((c = serRead())!=-1)
    buf[nbuf++] = c;
  return nbuf;
}

// ------------------------------------------------------------------
void measureComms(byte stage) {
  static unsigned long old = 0;
  unsigned long now; 

  if (!stage) {
    old = millis();
  } else {
    now = millis();
    Serial.print("comms took ");
    Serial.print(now-old, DEC);
    Serial.println(" mSec");
  }
}

// ------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  for (int i=0; i<outnbuf; i++)
    outBuf[i] = msg[i];
  outBuf[12] = 0;
  serSetup(RS485_BAUD);
}

// ------------------------------------------------------------------
void loop() {
//  measureComms(0);
  if (serAvail()) {
    innbuf = serRecvMsg(inBuf);
    outBuf[11] = inBuf[innbuf-1];
    serSendMsg(outBuf, outnbuf);

//    measureComms(1);
    printMsg("Recv", inBuf, innbuf);
    printMsg("Sent", outBuf, outnbuf);
  }
}

Fontes:



2 comentários:

  1. Este comentário foi removido pelo autor.

    ResponderExcluir
  2. Boa noite!
    Como faço pra acionar um motor de lego direto? Quero liga-lo direto a uma bateria, tenho um cabo cortado. Como faço? Quais dos 6 fios eu uso?

    ResponderExcluir

LinkWithin

Related Posts Plugin for WordPress, Blogger...