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:
- Electron HW-VX6330 & HW-VX6330K
- USB to RS232 CH340 (setting reader)
- Arduino Uno 16 MHz
- TTL Header Cable to RS232 Casing (Male)
- Dokumentasi protokol
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 - TXD | 2 |
Red - DCD | 3.3 V |
Brown - GND | GND |
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