HW-VX63 via RS232 dengan Arduino Uno

Electron HW-VX6330 & HW-VX6330K merupakan salah satu UHF reader middle range yang dapat melakukan read maupun write terhadap tag. Komunikasi dengan reader dapat menggunakan laptop/PC ataupun dengan microcontroller.

Pada tutorial kali ini kami mencoba komunikasikan reader dengan Arduino Uno pemrograman C++ menggunakan protokol (tidak menggunakan SDK), koneksi dari interface Serial RS232 menggunakan TTL 2.54mm Pin to RS232 Casing.

Persiapan

Berikut beberapa barang yang diperlukan:

Setting reader

Atur work mode sesuai kebutuhan, silahkan pilih Active Mode atau Answer Mode (baca: Jenis Work Mode UHF Reader), koneksikan terlebih dulu reader dengan laptop/PC menggunakan USB to RS232 untuk mengatur work mode.

Koneksi antar Pin

Adapun koneksi antar Pin seperti pada gambar berikut.

TTL Header Cable to
RS232 Casing (Male)
Arduino Uno
Yellow - RXD~3
Orange - TXD2
Red - DCD3.3 V
Brown - GNDGND

Kode

Penjelasan blok/tiap byte command/response dapat dilihat di dokumentasi protokol.

Active Mode

#include <SoftwareSerial.h>
 
SoftwareSerial mySerial(2, 3); // RX, TX
 
// ---- Tunables ----
#define READ_TIMEOUT   100   // ms
#define MAX_RESPONSE   128   // max total bytes (length + data)
#define HEADER_LEN     4     // [LEN][ADDR][CMD][STATUS]
#define CHECKSUM_LEN   2
 
// ---- Protocol expectations (sesuaikan dengan perangkatmu) ----
#define STATUS_OK      0x00
#define CMD_EXPECTED   0xEE
 
// ---- Utils ----
bool readExact(Stream &s, uint8_t *buf, size_t len, unsigned long timeoutMs) {
  unsigned long start = millis();
  size_t i = 0;
  while (i < len) {
    if (s.available()) {
      buf[i++] = (uint8_t)s.read();
      start = millis();           // reset timeout whenever a byte arrives
    } else if (millis() - start > timeoutMs) {
      return false;               // timeout
    }
  }
  return true;
}
 
void printResponse(const uint8_t *data, int size) {
  for (int i = 0; i < size; i++) {
    if (data[i] < 16) Serial.print('0');
    Serial.print(data[i], HEX);
    Serial.print(' ');
  }
  Serial.println();
}
 
void setup() {
  Serial.begin(57600);
  mySerial.begin(57600); // Default HW-VX6330K: 57_600 BPS
}
 
void loop() {
  // Only proceed if at least 1 byte is available (length).
  if (!mySerial.available()) return;
 
  uint8_t resp[MAX_RESPONSE];
  size_t respLen = 0;
 
  // 1) Read length field (1 byte)
  if (!readExact(mySerial, &resp[0], 1, READ_TIMEOUT)) {
    Serial.println("Timeout reading length");
    return;
  }
 
  const uint8_t dataLen = resp[0];     // number of bytes to follow after LEN
  respLen = 1 + dataLen;               // full frame length in buffer
 
  if (respLen > MAX_RESPONSE) {
    Serial.println("Frame too large");
    // drain the remaining bytes to keep stream clean
    for (uint8_t j = 0; j < dataLen && mySerial.available(); ++j) mySerial.read();
    return;
  }
 
  // 2) Read the remaining "dataLen" bytes
  if (!readExact(mySerial, &resp[1], dataLen, READ_TIMEOUT)) {
    Serial.println("Timeout reading data");
    return;
  }
 
  // Basic structural check: header(4) + checksum(2) must fit
  if (respLen < (HEADER_LEN + CHECKSUM_LEN)) {
    Serial.println("Frame too short");
    return;
  }
 
  // 3) Validate header
  const uint8_t command = resp[2];
  const uint8_t status  = resp[3];
 
  if (status != STATUS_OK) {
    Serial.println("Status != 0x00");
    return;
  }
  if (command != CMD_EXPECTED) {
    Serial.println("Command != 0xEE");
    return;
  }
 
  // 4) Extract tag payload (everything after header, before checksum)
  const int payloadLen = respLen - HEADER_LEN - CHECKSUM_LEN;
  if (payloadLen < 0) {
    Serial.println("Invalid payload length");
    return;
  }
 
  const uint8_t *tag = &resp[HEADER_LEN];
 
  // 5) Print tag bytes (raw)
  Serial.print("Tag-> ");
  printResponse(tag, payloadLen);
}
 

Answer Mode

#include <SoftwareSerial.h>
 
#define MAX_TAGS       10
#define MAX_TAG_LEN    32
#define MAX_RESPONSE   128
#define READ_TIMEOUT   100  // ms
 
// Header indices in response (based on your protocol)
#define IDX_LEN        0    // total length (data-length field)
#define IDX_ADDR       1    // reader address
#define IDX_CMD        2    // command (answer mode)
#define IDX_STATUS     3    // status
 
// Expected status/command values
#define STATUS_TAG_FOUND  0x01
#define STATUS_NO_TAG     0xFB
#define CMD_EXPECTED      0x01
 
byte INVENTORY_REQUEST_DATA[5] = {0x04, 0xFF, 0x01, 0x1B, 0xB4};
 
uint8_t tagCount = 0;
uint8_t tagLengths[MAX_TAGS];
uint8_t tags[MAX_TAGS][MAX_TAG_LEN];
 
SoftwareSerial mySerial(2, 3); // RX, TX
 
// ---------- Utilities ----------
bool readExact(Stream &s, uint8_t *buf, size_t len, unsigned long timeoutMs) {
  unsigned long start = millis();
  size_t i = 0;
  while (i < len) {
    if (s.available()) {
      buf[i++] = (uint8_t)s.read();
      start = millis(); // reset timeout when a byte is received
    } else if (millis() - start > timeoutMs) {
      return false; // timeout
    }
  }
  return true;
}
 
void printResponse(byte data[], int size) {
  for (int i = 0; i < size; i++) {
    if (data[i] < 16) Serial.print("0");
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
}
 
// ---------- Protocol helpers ----------
bool readFrame(uint8_t *resp, size_t &respLen) {
  // 1) Read length byte
  if (!readExact(mySerial, resp + IDX_LEN, 1, READ_TIMEOUT)) {
    Serial.println("Timeout reading length");
    return false;
  }
 
  const uint8_t dataLength = resp[IDX_LEN];     // length of "data" part
  if (dataLength + 1 > MAX_RESPONSE) {          // +1 for the length field itself
    Serial.println("Response too large");
    return false;
  }
 
  // 2) Read remaining bytes according to dataLength
  if (!readExact(mySerial, resp + 1, dataLength, READ_TIMEOUT)) {
    Serial.println("Timeout reading data");
    return false;
  }
 
  respLen = 1 + dataLength;
  return true;
}
 
bool validateHeader(const uint8_t *resp, size_t respLen) {
  if (respLen < 6) { // minimal: len + addr + cmd + status + ... + checksum(2)
    Serial.println("Response too short");
    return false;
  }
 
  const uint8_t cmd    = resp[IDX_CMD];
  const uint8_t status = resp[IDX_STATUS];
 
  if (status == STATUS_NO_TAG) {
    Serial.println("No tag found.");
    return false; // not fatal, but no tag present
  }
  if (status != STATUS_TAG_FOUND) {
    Serial.println("Status != 0x01");
    return false;
  }
  if (cmd != CMD_EXPECTED) {
    Serial.println("Command != 0x01");
    return false;
  }
  return true;
}
 
bool extractTags(const uint8_t *resp, size_t respLen) {
  // Format: [LEN][ADDR][CMD][STATUS] [payload tags...] [CHK][CHK]
  // payloadTagsLength = total - header(4) - checksum(2)
  if (respLen < 6) return false;
 
  const size_t headerLen = 4;
  const size_t checksumLen = 2;
 
  if (respLen < headerLen + checksumLen) {
    Serial.println("Invalid response length");
    return false;
  }
 
  const size_t payloadLen = respLen - headerLen - checksumLen;
  const uint8_t *payload = resp + headerLen;
 
  if (payloadLen == 0) {
    Serial.println("Empty payload");
    return false;
  }
 
  size_t i = 0;
  uint8_t cnt = payload[i++];
  if (cnt > MAX_TAGS) {
    Serial.println("Tag count exceeds capacity");
    return false;
  }
 
  // Reset tag storage
  tagCount = 0;
 
  for (uint8_t t = 0; t < cnt; t++) {
    if (i >= payloadLen) {
      Serial.println("Unexpected end of buffer while reading tag length");
      return false;
    }
    uint8_t len = payload[i++];
 
    if (len > MAX_TAG_LEN) {
      Serial.println("Tag length > MAX_TAG_LEN");
      return false;
    }
    if (i + len > payloadLen) {
      Serial.println("Unexpected end of buffer while reading tag data");
      return false;
    }
 
    tagLengths[tagCount] = len;
    memcpy(tags[tagCount], &payload[i], len);
    i += len;
 
    tagCount++;
  }
 
  return true;
}
 
// ---------- Arduino ----------
void setup() {
  Serial.begin(57600);
  mySerial.begin(57600); // Default HW-VX6330K
}
 
void loop() {
  // --- Trigger mode ---
  // Currently uses fixed delay of 1 second between inventory requests:
  delay(1000);
  // You can replace this with a trigger condition, for example:
  // if (digitalRead(PIN_BUTTON) == LOW) { ... }
  // or trigger from an external interrupt, timer, or other sensor event.
 
  // Send request
  mySerial.write(INVENTORY_REQUEST_DATA, sizeof(INVENTORY_REQUEST_DATA));
 
  // Read frame
  uint8_t response[MAX_RESPONSE];
  size_t responseLen = 0;
 
  if (!readFrame(response, responseLen)) {
    return;
  }
 
  // Validate header/status/command
  if (!validateHeader(response, responseLen)) {
    return;
  }
 
  // Extract and store tags
  if (!extractTags(response, responseLen)) {
    return;
  }
 
  // Print tags
  for (uint8_t t = 0; t < tagCount; t++) {
    Serial.print("Tag-> ");
    printResponse(tags[t], tagLengths[t]);
  }
}

Hasil

Jika buka Serial Monitor (57600 bps) akan tampil seperti berikut:

17:33:48.458 -> Tag-> E2 80 69 95 00 00 40 00 7F 13 05 08 
17:33:48.491 -> Tag-> E2 00 00 1D 44 03 01 24 05 90 60 3A 
17:33:48.528 -> Tag-> E2 80 69 95 00 00 40 00 7F 13 05 08 
17:33:48.528 -> Tag-> E2 00 00 1D 44 03 01 24 05 90 60 3A 
17:33:48.528 -> Tag-> E2 80 69 95 00 00 40 00 7F 13 05 08 
17:33:48.560 -> Tag-> E2 00 00 1D 44 03 01 24 05 90 60 3A

Video