ちょいちょい出てきたロードセルブレーキの作り方。めちゃ簡単です。
用意するもの
[材料]
20kgのロードセル
HX711
Arduino
M4皿ネジ25mm2本
M4皿ネジ20mm1本
手順
1 ペダルを取ります
2 裏のビスを取ります
滑り止めの裏にも隠れてますので注意
9 ワッシャーで嵩上げしてM4x25mmの皿ネジで固定します。ロードセルはこのように隙間が必要です。
取り付けた様子HX711は基盤上の10Hzに付いている小さい抵抗を隣の80Hzに移設。
配線は
Arduino - HX711
10 - DT
14 - SCK
VCC - VCC
GND - GND
HX711 - ロードセル
E+ - 赤
E- - 黒
A- - 白
A+ - 緑
ブレーキだけでなくアクセルも分離してますので余計な配線(黄色)もあります。
アクセルは、アナログ入力の0番ピンにポテンショメータをつないで読み取っています。
他の方が作られたスケッチを一部改変
/**** ここから ****/
#include <EEPROM.h>
#include <Joystick.h>
// HX711接続pin
#define DT_BRAKE 10
#define SCK_BRAKE 14
#define A_PIN 0
// EEPROM
#define EEPAD_HL 0 // HardLimit
#define EEPAD_DZ 2 // DeadZone
#define EEPAD_SL 4 // SoftLimit
#define EEPAD_RESERVE1 6 //
#define EEPAD_RESERVE2 8 //
#define EEPAD_NEXT 10
// common
#define READ_AVERAGE 30
int isDebug = 0;
#define TYPE_THROTTLE 0
#define TYPE_BRAKE 1
typedef struct _loadcell {
int type; // 0:スロットル 1:ブレーキ
int axis; // 解像度
int dt; // Arduino PIN(HX711のDT)
int sck; // Arduino PIN(HX711のSCK)
int HardLimit; // ロードセルの最大荷重(g)
int DeadZone; // デッドゾーン
int SoftLimit; // ソフト側最大荷重
float offset; // 無荷重時の値
float val;
float raw;
int data;
} LOADCELL;
// Throttle
#define THROTTLE_GAIN 4
#define THROTTLE_AXIS (1024*THROTTLE_GAIN-1) // スロットル解像度 default:1023
int throttle_direction = 0; // スロットル向き 0:増加(Normal) 1:減少(Reverce)
LOADCELL th = {
TYPE_THROTTLE, THROTTLE_AXIS, 0, 0, THROTTLE_AXIS, 160 * THROTTLE_GAIN , 780 * THROTTLE_GAIN, 0, 0, 0, 0
};
// Brake
#define BRAKE_AXIS 8191 // ブレーキの解像度 default:1023
LOADCELL br = {
TYPE_BRAKE, BRAKE_AXIS, DT_BRAKE, SCK_BRAKE, 20000, 2500, 12800, 0, 0, 0, 0
};
/* Joystick */
Joystick_ Joystick = Joystick_(
0x03, // reportid
JOYSTICK_TYPE_GAMEPAD, // type
0, // button count
0, // hat switch count
false, // x axis disable
false, // y axis disable
false, // z axis disable
false, // right x axis disable
false, // right y axis disable
false, // right z axis disable
false, // rudder disable
true, // throttle enable
false, // accelerator disable
true, // brake enable
false // steering disable
);
void Caribration_throttle(LOADCELL *lc) {
lc->offset = 0;
throttle_direction = 0;
ReadThrottle(lc);
// 無負荷時の値によって向きを自動判別
if (lc->raw > (512*THROTTLE_GAIN)) {
throttle_direction = 1;
lc->raw = THROTTLE_AXIS - lc->raw;
} else {
throttle_direction = 0;
}
ReadThrottle(lc);
lc->offset = lc->raw;
}
void Caribration_brake(LOADCELL *lc) {
// HX711との通信
pinMode(lc->sck, OUTPUT);
pinMode(lc->dt, INPUT);
lc->offset = 0;
ReadLoadcell(true, lc);
lc->offset = lc->raw;
}
void info() {
Serial.println("Hard Limit: " + String(th.HardLimit) + "/" + String(br.HardLimit));
Serial.println("Soft Limit: " + String(th.SoftLimit) + "/" + String(br.SoftLimit));
Serial.println("Dead Zone : " + String(th.DeadZone) + "/" + String(br.DeadZone));
Serial.println("Offset: " + String(th.offset) + "/" + String(br.offset));
Serial.print("Throttle Direction: ");
if (throttle_direction) Serial.println("REV");
else Serial.println("NOR");
}
int ReadThrottle(LOADCELL *lc) {
float dt;
dt = 0;
for (int t = 0; t < THROTTLE_GAIN; t++) {
dt += analogRead(A_PIN);
delayMicroseconds( 50 );
}
if (throttle_direction) dt = THROTTLE_AXIS - dt;
lc->raw = dt - lc->offset;
if (lc->raw < 0) lc->raw = 0;
return false;
}
int ReadLoadcell(int flg, LOADCELL * lc) {
long sum = 0;
for (int i = 0; i < (flg ? READ_AVERAGE : 1); i++) {
long data = 0;
// 24bit分のデータ読み出し
while (digitalRead(lc->dt) != 0) if (!flg) return true;
for (char bit = 0; bit < 24; bit++) {
digitalWrite(lc->sck, 1);
delayMicroseconds(5);
digitalWrite(lc->sck, 0);
delayMicroseconds(5);
data = (data << 1) | (digitalRead(lc->dt));
}
digitalWrite(lc->sck, 1); //gain=128(A)
delayMicroseconds(5);
digitalWrite(lc->sck, 0);
delayMicroseconds(5);
data = data ^ 0x800000;
sum += data;
}
float ave = sum / (flg ? READ_AVERAGE : 1);
float volt, gram;
volt = ave * (4.2987 / 16777216.0 / 128);
gram = volt / (0.001 * 4.2987 / lc->HardLimit);
if ( lc->offset > 0 && (gram - lc->offset) > lc->HardLimit ) return true;
if ( (gram - lc->offset) > (lc->HardLimit / 100)) {
lc->raw = gram - lc->offset;
return false;
}
else {
lc->raw = 0;
return false;
}
}
void joystick(int data, LOADCELL * lc) {
switch (lc->type) {
case TYPE_THROTTLE:
Joystick.setThrottle(data);
break;
case TYPE_BRAKE:
Joystick.setBrake(data);
break;
}
}
int setJoystick(float load, LOADCELL * lc) {
int val;
if ( load < lc->DeadZone) {
val = 0;
}
else{
val = map(load, lc->DeadZone, lc->SoftLimit, 0, lc->axis);
}
if (val > lc->axis) {
val = lc->axis;
}
joystick(val, lc);
return val;
}
void getEEPROM(LOADCELL *lc) {
int tmp;
// Hard Limit
EEPROM.get(lc->type * EEPAD_NEXT + EEPAD_HL, tmp);
if (tmp > 0) {
lc->HardLimit = tmp;
}
EEPROM.get(lc->type * EEPAD_NEXT + EEPAD_SL, tmp);
if (tmp > 0) {
lc->SoftLimit = tmp;
}
// Dead Zone
EEPROM.get(lc->type * EEPAD_NEXT + EEPAD_DZ, tmp);
if (tmp >= 0) {
lc->DeadZone = tmp;
}
}
void putEEPROM(char *subcom, int val, LOADCELL * lc) {
switch (*subcom) {
case 'h': // Hard Limit
if (val > 0) {
lc->HardLimit = val;
EEPROM.put(EEPAD_NEXT * lc->type + EEPAD_HL, lc->HardLimit);
switch (lc->type) {
case TYPE_THROTTLE:
Serial.print("TH ");
break;
case TYPE_BRAKE:
Serial.print("BR ");
break;
}
Serial.println("Hard Limit -> " + String(val));
info();
break;
}
break;
case 'd': // Dead Zone
if (val >= 0) {
lc->DeadZone = val;
EEPROM.put(EEPAD_NEXT * lc->type + EEPAD_DZ, lc->DeadZone);
switch (lc->type) {
case TYPE_THROTTLE:
Serial.print("TH ");
break;
case TYPE_BRAKE:
Serial.print("BR ");
break;
}
Serial.println("DeadZone -> " + String(val));
info();
break;
}
break;
case 's': // Soft Limit
if ( val > 0) {
lc->SoftLimit = val;
EEPROM.put(EEPAD_NEXT * lc->type + EEPAD_SL, lc->SoftLimit);
switch (lc->type) {
case TYPE_THROTTLE:
Serial.print("TH ");
break;
case TYPE_BRAKE:
Serial.print("BR ");
break;
}
Serial.println("SoftLimit -> " + String(val));
info();
break;
}
break;
}
}
int Command() {
if (Serial.available() > 0) {
String buf = Serial.readString();
char com = buf.charAt(0);
char subcom[2];
int val;
subcom[0] = buf.charAt(1);
val = buf.substring(2).toInt();
switch (com) {
case 'i':
info();
break;
case 'D': // Debug
isDebug = !isDebug;
break;
case 't':
putEEPROM(subcom, val, &th);
break;
case 'b':
putEEPROM(subcom, val, &br);
break;
case 'r': // reset
Caribration_throttle(&th);
Caribration_brake(&br);
info();
break;
default:
info();
break;
}
}
}
void setup() {
Serial.begin(9600); // COM通信速度
// ブレーキ/スロットルの分解能(Defaultは1023)
Joystick.setThrottleRange(0, THROTTLE_AXIS);
Joystick.setBrakeRange(0, BRAKE_AXIS);
Joystick.begin();
// 初期設定
getEEPROM(&th);
Caribration_throttle(&th);
setJoystick(0, &th);
getEEPROM(&br);
Caribration_brake(&br);
setJoystick(0, &br);
}
void loop() {
int flg = 0;
// データの読み込み
if (!ReadThrottle(&th)) {
th.data = setJoystick(th.raw, &th);
flg = 1;
}
if (!ReadLoadcell(false, &br)) {
br.data = setJoystick(br.raw, &br);
flg = 1;
}
if (isDebug && flg) {
Serial.print("T"); Serial.print(th.data); Serial.print("("); Serial.print(th.raw); Serial.print(") / ");
Serial.print("B");
{
if (br.raw >= 0) {
Serial.print(br.data); Serial.print("("); Serial.print(br.raw); Serial.println(")");
}
else Serial.println("---");
}
}
Command();
}
#include <Joystick.h>
// HX711接続pin
#define DT_BRAKE 10
#define SCK_BRAKE 14
#define A_PIN 0
// EEPROM
#define EEPAD_HL 0 // HardLimit
#define EEPAD_DZ 2 // DeadZone
#define EEPAD_SL 4 // SoftLimit
#define EEPAD_RESERVE1 6 //
#define EEPAD_RESERVE2 8 //
#define EEPAD_NEXT 10
// common
#define READ_AVERAGE 30
int isDebug = 0;
#define TYPE_THROTTLE 0
#define TYPE_BRAKE 1
typedef struct _loadcell {
int type; // 0:スロットル 1:ブレーキ
int axis; // 解像度
int dt; // Arduino PIN(HX711のDT)
int sck; // Arduino PIN(HX711のSCK)
int HardLimit; // ロードセルの最大荷重(g)
int DeadZone; // デッドゾーン
int SoftLimit; // ソフト側最大荷重
float offset; // 無荷重時の値
float val;
float raw;
int data;
} LOADCELL;
// Throttle
#define THROTTLE_GAIN 4
#define THROTTLE_AXIS (1024*THROTTLE_GAIN-1) // スロットル解像度 default:1023
int throttle_direction = 0; // スロットル向き 0:増加(Normal) 1:減少(Reverce)
LOADCELL th = {
TYPE_THROTTLE, THROTTLE_AXIS, 0, 0, THROTTLE_AXIS, 160 * THROTTLE_GAIN , 780 * THROTTLE_GAIN, 0, 0, 0, 0
};
// Brake
#define BRAKE_AXIS 8191 // ブレーキの解像度 default:1023
LOADCELL br = {
TYPE_BRAKE, BRAKE_AXIS, DT_BRAKE, SCK_BRAKE, 20000, 2500, 12800, 0, 0, 0, 0
};
/* Joystick */
Joystick_ Joystick = Joystick_(
0x03, // reportid
JOYSTICK_TYPE_GAMEPAD, // type
0, // button count
0, // hat switch count
false, // x axis disable
false, // y axis disable
false, // z axis disable
false, // right x axis disable
false, // right y axis disable
false, // right z axis disable
false, // rudder disable
true, // throttle enable
false, // accelerator disable
true, // brake enable
false // steering disable
);
void Caribration_throttle(LOADCELL *lc) {
lc->offset = 0;
throttle_direction = 0;
ReadThrottle(lc);
// 無負荷時の値によって向きを自動判別
if (lc->raw > (512*THROTTLE_GAIN)) {
throttle_direction = 1;
lc->raw = THROTTLE_AXIS - lc->raw;
} else {
throttle_direction = 0;
}
ReadThrottle(lc);
lc->offset = lc->raw;
}
void Caribration_brake(LOADCELL *lc) {
// HX711との通信
pinMode(lc->sck, OUTPUT);
pinMode(lc->dt, INPUT);
lc->offset = 0;
ReadLoadcell(true, lc);
lc->offset = lc->raw;
}
void info() {
Serial.println("Hard Limit: " + String(th.HardLimit) + "/" + String(br.HardLimit));
Serial.println("Soft Limit: " + String(th.SoftLimit) + "/" + String(br.SoftLimit));
Serial.println("Dead Zone : " + String(th.DeadZone) + "/" + String(br.DeadZone));
Serial.println("Offset: " + String(th.offset) + "/" + String(br.offset));
Serial.print("Throttle Direction: ");
if (throttle_direction) Serial.println("REV");
else Serial.println("NOR");
}
int ReadThrottle(LOADCELL *lc) {
float dt;
dt = 0;
for (int t = 0; t < THROTTLE_GAIN; t++) {
dt += analogRead(A_PIN);
delayMicroseconds( 50 );
}
if (throttle_direction) dt = THROTTLE_AXIS - dt;
lc->raw = dt - lc->offset;
if (lc->raw < 0) lc->raw = 0;
return false;
}
int ReadLoadcell(int flg, LOADCELL * lc) {
long sum = 0;
for (int i = 0; i < (flg ? READ_AVERAGE : 1); i++) {
long data = 0;
// 24bit分のデータ読み出し
while (digitalRead(lc->dt) != 0) if (!flg) return true;
for (char bit = 0; bit < 24; bit++) {
digitalWrite(lc->sck, 1);
delayMicroseconds(5);
digitalWrite(lc->sck, 0);
delayMicroseconds(5);
data = (data << 1) | (digitalRead(lc->dt));
}
digitalWrite(lc->sck, 1); //gain=128(A)
delayMicroseconds(5);
digitalWrite(lc->sck, 0);
delayMicroseconds(5);
data = data ^ 0x800000;
sum += data;
}
float ave = sum / (flg ? READ_AVERAGE : 1);
float volt, gram;
volt = ave * (4.2987 / 16777216.0 / 128);
gram = volt / (0.001 * 4.2987 / lc->HardLimit);
if ( lc->offset > 0 && (gram - lc->offset) > lc->HardLimit ) return true;
if ( (gram - lc->offset) > (lc->HardLimit / 100)) {
lc->raw = gram - lc->offset;
return false;
}
else {
lc->raw = 0;
return false;
}
}
void joystick(int data, LOADCELL * lc) {
switch (lc->type) {
case TYPE_THROTTLE:
Joystick.setThrottle(data);
break;
case TYPE_BRAKE:
Joystick.setBrake(data);
break;
}
}
int setJoystick(float load, LOADCELL * lc) {
int val;
if ( load < lc->DeadZone) {
val = 0;
}
else{
val = map(load, lc->DeadZone, lc->SoftLimit, 0, lc->axis);
}
if (val > lc->axis) {
val = lc->axis;
}
joystick(val, lc);
return val;
}
void getEEPROM(LOADCELL *lc) {
int tmp;
// Hard Limit
EEPROM.get(lc->type * EEPAD_NEXT + EEPAD_HL, tmp);
if (tmp > 0) {
lc->HardLimit = tmp;
}
EEPROM.get(lc->type * EEPAD_NEXT + EEPAD_SL, tmp);
if (tmp > 0) {
lc->SoftLimit = tmp;
}
// Dead Zone
EEPROM.get(lc->type * EEPAD_NEXT + EEPAD_DZ, tmp);
if (tmp >= 0) {
lc->DeadZone = tmp;
}
}
void putEEPROM(char *subcom, int val, LOADCELL * lc) {
switch (*subcom) {
case 'h': // Hard Limit
if (val > 0) {
lc->HardLimit = val;
EEPROM.put(EEPAD_NEXT * lc->type + EEPAD_HL, lc->HardLimit);
switch (lc->type) {
case TYPE_THROTTLE:
Serial.print("TH ");
break;
case TYPE_BRAKE:
Serial.print("BR ");
break;
}
Serial.println("Hard Limit -> " + String(val));
info();
break;
}
break;
case 'd': // Dead Zone
if (val >= 0) {
lc->DeadZone = val;
EEPROM.put(EEPAD_NEXT * lc->type + EEPAD_DZ, lc->DeadZone);
switch (lc->type) {
case TYPE_THROTTLE:
Serial.print("TH ");
break;
case TYPE_BRAKE:
Serial.print("BR ");
break;
}
Serial.println("DeadZone -> " + String(val));
info();
break;
}
break;
case 's': // Soft Limit
if ( val > 0) {
lc->SoftLimit = val;
EEPROM.put(EEPAD_NEXT * lc->type + EEPAD_SL, lc->SoftLimit);
switch (lc->type) {
case TYPE_THROTTLE:
Serial.print("TH ");
break;
case TYPE_BRAKE:
Serial.print("BR ");
break;
}
Serial.println("SoftLimit -> " + String(val));
info();
break;
}
break;
}
}
int Command() {
if (Serial.available() > 0) {
String buf = Serial.readString();
char com = buf.charAt(0);
char subcom[2];
int val;
subcom[0] = buf.charAt(1);
val = buf.substring(2).toInt();
switch (com) {
case 'i':
info();
break;
case 'D': // Debug
isDebug = !isDebug;
break;
case 't':
putEEPROM(subcom, val, &th);
break;
case 'b':
putEEPROM(subcom, val, &br);
break;
case 'r': // reset
Caribration_throttle(&th);
Caribration_brake(&br);
info();
break;
default:
info();
break;
}
}
}
void setup() {
Serial.begin(9600); // COM通信速度
// ブレーキ/スロットルの分解能(Defaultは1023)
Joystick.setThrottleRange(0, THROTTLE_AXIS);
Joystick.setBrakeRange(0, BRAKE_AXIS);
Joystick.begin();
// 初期設定
getEEPROM(&th);
Caribration_throttle(&th);
setJoystick(0, &th);
getEEPROM(&br);
Caribration_brake(&br);
setJoystick(0, &br);
}
void loop() {
int flg = 0;
// データの読み込み
if (!ReadThrottle(&th)) {
th.data = setJoystick(th.raw, &th);
flg = 1;
}
if (!ReadLoadcell(false, &br)) {
br.data = setJoystick(br.raw, &br);
flg = 1;
}
if (isDebug && flg) {
Serial.print("T"); Serial.print(th.data); Serial.print("("); Serial.print(th.raw); Serial.print(") / ");
Serial.print("B");
{
if (br.raw >= 0) {
Serial.print(br.data); Serial.print("("); Serial.print(br.raw); Serial.println(")");
}
else Serial.println("---");
}
}
Command();
}
/**** ここまで ****/
Arduino IDE のシリアルモニタで微調整ができます。
コマンド | 解説 |
---|---|
i | 現在の設定値を表示 |
r | リセット |
D | デバッグモード この数値をもとに下記のパラメータを設定する |
bhパラメータ | ロードセルの最大値(単位g) |
bsパラメータ | ブレーキペダルの最大値(単位g) |
bdパラメータ | ブレーキペダルのあそび(単位g) |
tsパラメータ | スロットルペダルの最大値 |
tdパラメータ | スロットルペダルのあそび |