/**
* ============================================================
* Waveshare ESP32-S3-Zero
* HX711 load cell + BOJKE BL-30NZ-485 laser — stiffness logger
* ============================================================
*
* WIRING
* ──────
* HX711
* VCC → 3.3V
* GND → GND
* DT → GPIO2
* SCK → GPIO3
* RATE → 3.3V for 80Hz, GND/float for 10Hz
*
* MAX485 module
* VCC → 5V
* GND → GND
* DI → GPIO1 (UART1 TX)
* RO → GPIO0 (UART1 RX)
* DE+RE tied together → GPIO4
* A/B screw terminals → BOJKE green(A+) / white(B-)
*
* BOJKE BL-30NZ-485
* brown → +12-24V DC
* blue → GND
* green → A(+) on MAX485
* white → B(-) on MAX485
*
* CALIBRATION
* ───────────
* Set CALIBRATION_FACTOR to 0.0 on first flash.
* Follow Serial Monitor prompts (115200 baud).
* Paste the printed factor back in and re-flash.
*
* CSV OUTPUT (USB Serial, 115200)
* ───────────────────────────────
* # timestamp_ms, distance_mm, force_g
* 1000, 28.450, 0.0
* 1050, 28.451, 12.3
* ...
*
* POLL RATE
* ─────────
* POLL_INTERVAL_MS 50 → 20 Hz (safe default)
* POLL_INTERVAL_MS 12 → ~80 Hz (requires RATE pin HIGH on HX711)
* ============================================================
*/
#include <Arduino.h>
#include <HX711.h>
// ── HX711 ────────────────────────────────────────────────────
#define HX711_DT_PIN 2
#define HX711_SCK_PIN 3
#define HX711_READINGS 3 // averages per sample (lower = faster)
// ── MAX485 / RS485 ───────────────────────────────────────────
#define RS485_TX_PIN 1
#define RS485_RX_PIN 0
#define RS485_DE_PIN 4
// ── BOJKE Modbus settings ────────────────────────────────────
#define MODBUS_BAUD 115200
#define LASER_SLAVE_ID 0x01
#define LASER_REG_ADDR 0x0000
#define LASER_REG_COUNT 0x0002
#define LASER_RESPONSE_TIMEOUT 50 // ms
#define LASER_EXPECTED_LEN 9 // bytes
// ── Poll rate ─────────────────────────────────────────────────
#define POLL_INTERVAL_MS 50
// ── Calibration ──────────────────────────────────────────────
// Set to 0.0 to run calibration on boot.
// After calibration paste the printed value here and re-flash.
#define CALIBRATION_FACTOR 0.0f
// Known weight used during calibration (grams)
#define CALIBRATION_WEIGHT_G 500.0f
// ─────────────────────────────────────────────────────────────
HX711 scale;
HardwareSerial rs485(1);
// ─────────────────────────────────────────────────────────────
// CRC-16/IBM (Modbus)
// ─────────────────────────────────────────────────────────────
uint16_t modbusCRC(const uint8_t *buf, uint8_t len) {
uint16_t crc = 0xFFFF;
for (uint8_t i = 0; i < len; i++) {
crc ^= (uint16_t)buf[i];
for (uint8_t j = 0; j < 8; j++)
crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
}
return crc;
}
// ─────────────────────────────────────────────────────────────
// Read laser distance (mm) via Modbus RTU FC04
// ─────────────────────────────────────────────────────────────
float readLaserMM() {
// Flush any stale bytes
while (rs485.available()) rs485.read();
// Build FC04 request
uint8_t req[8];
req[0] = LASER_SLAVE_ID;
req[1] = 0x04;
req[2] = LASER_REG_ADDR >> 8;
req[3] = LASER_REG_ADDR & 0xFF;
req[4] = LASER_REG_COUNT >> 8;
req[5] = LASER_REG_COUNT & 0xFF;
uint16_t crc = modbusCRC(req, 6);
req[6] = crc & 0xFF;
req[7] = crc >> 8;
digitalWrite(RS485_DE_PIN, HIGH);
delayMicroseconds(100);
rs485.write(req, sizeof(req));
rs485.flush();
delayMicroseconds(100);
digitalWrite(RS485_DE_PIN, LOW);
// Wait for response
uint8_t resp[LASER_EXPECTED_LEN];
uint8_t n = 0;
uint32_t deadline = millis() + LASER_RESPONSE_TIMEOUT;
while (millis() < deadline && n < LASER_EXPECTED_LEN) {
if (rs485.available())
resp[n++] = rs485.read();
}
if (n < LASER_EXPECTED_LEN) return NAN;
if (resp[0] != LASER_SLAVE_ID) return NAN;
if (resp[1] != 0x04) return NAN;
uint16_t rxCRC = (uint16_t)resp[7] | ((uint16_t)resp[8] << 8);
uint16_t calcCRC = modbusCRC(resp, 7);
if (rxCRC != calcCRC) return NAN;
uint16_t raw = ((uint16_t)resp[3] << 8) | resp[4];
return raw * 0.001f;
}
// ─────────────────────────────────────────────────────────────
// Calibration routine
// ─────────────────────────────────────────────────────────────
void runCalibration() {
Serial.println("\n========================================");
Serial.println(" HX711 CALIBRATION");
Serial.println("========================================");
Serial.println("Step 1: Remove ALL load from the cell.");
Serial.println(" Send any character when ready...");
while (!Serial.available()) delay(100);
while (Serial.available()) Serial.read();
scale.set_scale();
scale.tare(20);
Serial.println("Tared.");
Serial.println("\nStep 2: Place known weight on the cell.");
Serial.print(" Known weight: ");
Serial.print(CALIBRATION_WEIGHT_G, 1);
Serial.println(" g");
Serial.println(" Send any character when stable...");
while (!Serial.available()) delay(100);
while (Serial.available()) Serial.read();
float raw = scale.get_value(20);
float factor = raw / CALIBRATION_WEIGHT_G;
Serial.println("\n========================================");
Serial.print(" CALIBRATION_FACTOR = ");
Serial.println(factor, 4);
Serial.println(" Paste this into the sketch and re-flash.");
Serial.println("========================================\n");
// Apply immediately so it works this session without re-flashing
scale.set_scale(factor);
scale.tare(10);
}
// ─────────────────────────────────────────────────────────────
// setup()
// ─────────────────────────────────────────────────────────────
void setup() {
// USB CDC serial — required for S3-Zero
// In Arduino IDE: Tools → USB CDC On Boot → Enabled
Serial.begin(115200);
delay(2000);
Serial.println("# Waveshare ESP32-S3-Zero — stiffness logger");
// RS485 direction pin
pinMode(RS485_DE_PIN, OUTPUT);
digitalWrite(RS485_DE_PIN, LOW);
// RS485 UART
rs485.begin(MODBUS_BAUD, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
// HX711
scale.begin(HX711_DT_PIN, HX711_SCK_PIN);
Serial.print("Waiting for HX711");
while (!scale.is_ready()) { Serial.print("."); delay(100); }
Serial.println(" ready.");
if (CALIBRATION_FACTOR == 0.0f) {
runCalibration();
} else {
scale.set_scale(CALIBRATION_FACTOR);
Serial.println("Remove all load — taring in 2s...");
delay(2000);
scale.tare(20);
Serial.println("Tared.");
}
Serial.println("# timestamp_ms, distance_mm, force_g");
}
// ─────────────────────────────────────────────────────────────
// loop()
// ─────────────────────────────────────────────────────────────
void loop() {
static uint32_t lastPoll = 0;
if (millis() - lastPoll < POLL_INTERVAL_MS) return;
lastPoll = millis();
uint32_t ts = millis();
float distMM = readLaserMM();
float forceG = scale.is_ready() ? scale.get_units(HX711_READINGS) : NAN;
Serial.print(ts);
Serial.print(',');
if (!isnan(distMM)) Serial.print(distMM, 3); else Serial.print("ERR");
Serial.print(',');
if (!isnan(forceG)) Serial.print(forceG, 1); else Serial.print("ERR");
Serial.println();
}
No comments:
Post a Comment