, a load cell, an Arduino, and AndroWish the tablet is even more useful.
#include "HX711.h"
#include <EEPROM.h>
#include <avr/wdt.h>
// HX711.DOUT - pin 3 (digital)
// HX711.PD_SCK - pin 2 (digital)
HX711 scale(3, 2); // parameter "gain" left out
// the default value 128 is used by the library
int run; // when true, measure in main loop
int nrd; // number measurements/actions (1..9) to average
int inChar; // char from serial line
byte inByte; // byte from/to EEPROM
long lVal; // used e.g. for R/W of scale value to EEPROM
double dVal; // used for calibration
void setup() {
run = 0;
nrd = 5;
Serial.begin(9600);
lVal = 0;
EEPROM.get(0, inByte);
lVal |= inByte;
EEPROM.get(1, inByte);
lVal <<= 8;
lVal |= inByte;
EEPROM.get(2, inByte);
lVal <<= 8;
lVal |= inByte;
EEPROM.get(3, inByte);
lVal <<= 8;
lVal |= inByte;
scale.set_scale(lVal); // use -680000 for 3 kg load cell
scale.tare(nrd);
scale.power_down();
}
void wrcal(long val) {
byte b;
b = val >> 24;
EEPROM.update(0, b);
b = val >> 16;
EEPROM.update(1, b);
b = val >> 8;
EEPROM.update(2, b);
b = val;
EEPROM.update(3, b);
}
void loop() {
if (Serial.available() > 0) {
inChar = Serial.read();
if (inChar == -1) {
return;
}
switch (inChar) {
case 'S': // STOP
case 's':
if (run) {
scale.power_down();
}
run = 0;
break;
case '?': // PRINT CALIBRATION SCALE
Serial.print('C');
lVal = scale.get_scale();
Serial.println(lVal);
break;
case 'C': // SET CALIBRATION SCALE
case 'c':
lVal = Serial.parseInt();
if (lVal) {
scale.set_scale(lVal);
wrcal(lVal);
}
Serial.print('C');
lVal = scale.get_scale();
Serial.println(lVal);
break;
case 'T': // TARE
case 't':
if (!run) {
scale.power_up();
}
scale.tare(nrd);
if (!run) {
scale.power_down();
}
break;
case 'V': // MEASURE, GIVE RAW VALUE
case 'v':
if (!run) {
scale.power_up();
}
lVal = scale.read_average(nrd) - scale.get_offset();
if (!run) {
scale.power_down();
}
Serial.print('V');
Serial.println(lVal);
break;
case 'M': // MEASURE, GIVE GRAMS
case 'm':
if (!run) {
scale.power_up();
Serial.println(scale.get_units(nrd) * 1000.0, 1);
scale.power_down();
}
break;
case 'R': // RUN
case 'r':
if (!run) {
scale.power_up();
}
run = 1;
break;
case 'X': // REBOOT
case 'x':
wdt_enable(WDTO_15MS);
while (1) {
}
break;
case 'Y': // DO CALIBRATION GIVEN GRAMS
case 'y':
dVal = Serial.parseFloat() / 1000.0;
if (dVal) {
if (!run) {
scale.power_up();
}
lVal = scale.read_average(nrd) - scale.get_offset();
if (!run) {
scale.power_down();
}
scale.set_scale(lVal / dVal);
lVal = scale.get_scale();
wrcal(lVal);
Serial.print('C');
Serial.println(lVal);
}
break;
default: // # MEASUREMENTS FOR AVERAGING
if (inChar >= '1' && inChar <= '9') {
nrd = inChar - '0';
}
break;
}
}
if (run) {
Serial.println(scale.get_units(nrd) * 1000.0, 1);
}
}And this is the Tcl script for AndroWish which can be run with undroidwish, too.JM 2018/9/1 - I had to remove NONBLOCK when running with undroidwish under Windows.
and to COM4 where my Arduino was connected (-:
# AndroWish script to interface Arduino with HX711 scale amplifier and load cell
#
# Wiring:
#
# +--------------+ +-------------+ +-----------+ +------+
# | Android | | +5V|---| HX711 E+|---| |
# | Device |--USB--| Arduino GND|---| E-|---| Load |
# | w/ AndroWish | | D3|---|DOUT A-|---| Cell |
# +--------------+ | D2|---|PC_SCK A+|---| |
# +-------------+ +-----------+ +------+
wm attributes . -fullscreen 1
catch {borg screenorientation landscape}
sdltk screensaver 0
option add *Radiobutton.borderWidth 3
option add *Radiobutton.highlightThickness 0
option add *Button.borderWidth 3
option add *Button.highlightThickness 0
# Open/reopen serial line to Arduino
proc reopen {} {
after cancel reopen
if {[sdltk android]} {
if {[catch {
set dev [lindex [usbserial] 0]
set ::SCALE(chan) [usbserial $dev]
fconfigure $::SCALE(chan) -blocking 0 -translation binary \
-mode 9600,n,8,1
fileevent $::SCALE(chan) readable readv
set ::SCALE(value) "online"
puts -nonewline $::SCALE(chan) "S"
} err]} {
sdltk log verbose $err
set ::SCALE(value) "offline"
after 1000 reopen
.run configure -background [lindex [.run configure -background] 3]
return
}
} else {
if {[catch {
set ::SCALE(chan) [open "/dev/ttyUSB0" {RDWR NONBLOCK}]
fconfigure $::SCALE(chan) -translation binary -mode 9600,n,8,1
fileevent $::SCALE(chan) readable readv
set ::SCALE(value) "online"
} err]} {
sdltk log verbose $err
set ::SCALE(value) "offline"
after 1000 reopen
.run configure -background [lindex [.run configure -background] 3]
return
}
}
sendc 5 S
}
# Send command string to Arduino
proc sendc args {
foreach cmd $args {
if {[catch {
puts -nonewline $::SCALE(chan) $cmd
flush $::SCALE(chan)
} err]} {
sdltk log verbose $err
catch {close $::SCALE(chan)}
set ::SCALE(value) "offline"
after 1000 reopen
watchdog -1
return
} elseif {$cmd eq "R"} {
watchdog 1
} elseif {$cmd eq "S"} {
watchdog -1
} elseif {$cmd eq "T"} {
set ::SCALE(value) [format "%7.1fg" 0]
} elseif {[string match {[1-9]} $cmd]} {
set ::SCALE(avg) $cmd
}
}
}
# Monitor continuous measurement
proc watchdog {{on 0}} {
after cancel watchdog
if {$on > 0} {
if {[.run cget -background] ne "green3"} {
.run configure -background green3 \
-activebackground green3
}
after 2000 watchdog
} else {
.run configure -background [lindex [.run configure -background] 3] \
-activebackground [lindex [.run configure -activebackground] 3]
}
}
# Read data from Arduino
proc readv {} {
if {[eof $::SCALE(chan)]} {
sdltk log verbose "EOF detected"
catch {close $::SCALE(chan)}
set ::SCALE(value) "offline"
after 1000 reopen
watchdog -1
return
}
if {[catch {gets $::SCALE(chan) v} err]} {
sdltk log verbose $err
catch {close $::SCALE(chan)}
set ::SCALE(value) "offline"
after 1000 reopen
watchdog -1
return
}
if {($v ne "") && [scan $v %g vv]} {
set ::SCALE(value) [format "%7.1fg" $vv]
after cancel watchdog
after 2000 watchdog
}
}
# Stop Arduino and exit
proc stop_and_exit {} {
sendc S
exit
}
# Start calibration on Arduino
proc docalib {} {
sendc "Y$::SCALE(calg)\r"
set ::SCALE(caldone) 1
}
# Cancel calibration
proc calcancel {} {
set ::SCALE(caldone) -1
}
# Calibration dialog
proc calib {} {
toplevel .calib
wm title .calib "Calibration"
wm transient .calib .
wm protocol .calib WM_DELETE_WINDOW calcancel
label .calib.l1 -text "1. Remove everything from the load cell." \
-font {Helvetica -25} -anchor w
label .calib.l2 -text "2. Tare the load cell by pressing this button. " \
-font {Helvetica -25} -anchor w
button .calib.tare -text "Tare" -command {sendc T} -font {Helvetica -30} \
-width 8
label .calib.l3 -text "3. Place a known weight on the load cell." \
-font {Helvetica -25} -anchor w
label .calib.l4 -text "4. Enter that weight in grams here: " \
-font {Helvetica -25} -anchor w
entry .calib.grams -width 10 -textvariable ::SCALE(calg) -font {Courier -30}
label .calib.l5 -text "5. Perform calibration by pressing this button. " \
-font {Helvetica -25} -anchor w
button .calib.cal -text "Calibrate" -command docalib -font {Helvetica -30} \
-width 8
grid .calib.l1 -row 0 -column 0 -pady 20 -sticky w -padx 10
grid .calib.l2 -row 1 -column 0 -pady 20 -sticky w -padx 10
grid .calib.tare -row 1 -column 1 -padx 20
grid .calib.l3 -row 2 -column 0 -pady 20 -sticky w -padx 10
grid .calib.l4 -row 3 -column 0 -pady 20 -sticky w -padx 10
grid .calib.grams -row 3 -column 1 -padx 20
grid .calib.l5 -row 4 -column 0 -pady 20 -sticky w -padx 10
grid .calib.cal -row 4 -column 1 -padx 20
bind .calib <Escape> {calcancel ; break}
bind .calib <Key-q> {calcancel ; break}
bind .calib <Key-Q> {calcancel ; break}
::tk::PlaceWindow .calib
::tk::SetFocusGrab .calib .calib
sendc S 9
vwait ::SCALE(caldone)
::tk::RestoreFocusGrab .calib .calib destroy
}
# Main screen
label .weight -textvariable SCALE(value) -font {Courier -160}
grid .weight -row 0 -column 0 -columnspan 9 -sticky news
button .tare -text "Tare" -command {sendc T} -font {Helvetica -30}
button .run -text "Run" -command {sendc R R} -font {Helvetica -30}
button .stop -text "Stop" -command {sendc S} -font {Helvetica -30}
button .single -text "Meas." -command {sendc M} -font {Helvetica -30}
grid .tare .run .stop .single -row 1 -rowspan 2 -sticky news
foreach i {1 2 3 4 5} {
radiobutton .m$i -text $i -command [list sendc $i] -indicatoron 0 \
-value $i -variable ::SCALE(avg) -font {Helvetica -30}
grid .m$i -row 1 -column [expr {$i + 3}] -sticky news
}
foreach i {6 7 8 9} {
radiobutton .m$i -text $i -command [list sendc $i] -indicatoron 0 \
-value $i -variable ::SCALE(avg) -font {Helvetica -30}
grid .m$i -row 2 -column [expr {$i - 2}] -sticky news
}
button .cal -text "Cal." -command calib -font {Helvetica -30}
grid .cal -row 2 -column 8 -sticky news
grid columnconfigure . 0 -uniform largebutton
grid columnconfigure . 1 -uniform largebutton
grid columnconfigure . 2 -uniform largebutton
grid columnconfigure . 3 -uniform largebutton
grid columnconfigure . 4 -uniform smallbutton
grid columnconfigure . 5 -uniform smallbutton
grid columnconfigure . 6 -uniform smallbutton
grid columnconfigure . 7 -uniform smallbutton
grid columnconfigure . 8 -uniform smallbutton
grid rowconfigure . 0 -weight 12
grid rowconfigure . 1 -weight 1 -uniform button
grid rowconfigure . 2 -weight 1 -uniform button
grid columnconfigure . all -weight 1
# Keyboard shortcuts to buttons etc.
bind all <Key-0> {.tare invoke}
bind all <Key-t> {.tare invoke}
bind all <Key-T> {.tare invoke}
bind all <Key-r> {.run invoke}
bind all <Key-R> {.run invoke}
bind all <Key-s> {.stop invoke}
bind all <Key-S> {.stop invoke}
bind all <Key-m> {.single invoke}
bind all <Key-M> {.single invoke}
foreach i {1 2 3 4 5 6 7 8 9} {
bind all <Key-$i> [list .m$i invoke]
}
bind all <Key-q> stop_and_exit
bind all <Key-Q> stop_and_exit
bind all <Escape> stop_and_exit
bind all <Break> stop_and_exit
reopen

