blob: 5f4093655806c904c54627a59ce31e5b2da8f8fc [file] [log] [blame]
\ Bathroom scale application
\ This converts an inexpensive digital bathroom scale
\ to a smart scale that can send weight readings to
\ a spreadsheet. It has the additional benefit of
\ moving the display up to a wall-mounted position
\ where us old folks can read it without crouching
\ down and squinting.
\ Start with an inexpensive digital bathroom scale.
\ Remove the electronics (control board and LCD),
\ leaving the four load cell sensors at the corners.
\ Connect the sensors to an HX711 and connect its
\ digital output to an ESP8266 like a WeMos D1 Mini.
\ The ESP8266 displays the weight reading on a small
\ OLED display.
fl ../../app/esp8266/common.fth
fl ../../lib/random.fth
fl ../../lib/ilog2.fth
fl ../../lib/tek.fth
also modem
: rx ( -- ) pad unused pad here - - (receive) #100 ms ;
previous
fl ../../app/esp8266/wifi.fth
fl ../../app/esp8266/tcpnew.fth
\needs set-column fl ${CBP}/lib/fb.fth
\needs 'font fl ${CBP}/lib/font5x7.fth
\needs ssd-init fl ${CBP}/lib/ssd1306.fth
\needs switch? fl ${CBP}/app/esp8266/gpio-switch.fth
\needs init-wemos-oled : init-wemos-oled ( -- ) 1 2 i2c-setup ssd-init ;
\needs hx711-tare fl ${CBP}/sensors/hx711.fth
\ Default pins are SCK:5 and DOUT:6
4 to hx711-sck-pin
3 to hx711-dout-pin
\needs ssd-font: fl numfont.fth
\needs numfont fl numfont-bits.fth
: fb-blank ( -- ) #10 numfont ;
: digit-or-blank ( n -- ) ?dup 0= if #10 then ;
: .weight ( lbs -- )
0 0 fb-at-xy
\ Always display the ones digit
2 set-column
#10 /mod swap numfont ( lbs/10 )
\ Blank both the tens and hundreds digits
\ if they are both zero, otherwise display
\ at least the tens digit.
1 set-column
?dup 0= if ( )
fb-blank
0 set-column fb-blank
exit
then ( lbs/10)
#10 /mod swap numfont ( lbs/100 )
\ Blank the hundreds digit if it is zero
\ otherwise display it
0 set-column
?dup if numfont else fb-blank then
;
\needs sprintf fl ${CBP}/cforth/printf.fth
\ Connecting directly to script.google.com via HTTP doesn't
\ work because google now insists on secure connections,
\ which are tricky to support directly on ESP8266. The
\ workaround is to use a proxy on the local network. See
\ ./README.md for more information.
\ : spreadsheet-host$ ( -- $ ) " script.google.com" ;
: spreadsheet-host$ ( -- $ ) " 192.168.2.254" ; \ HTTP to HTTPS proxy
#6000 value host-port
: spreadsheet-url-prefix ( -- $ )
" https://script.google.com/macros/s/AKfycbzoqDrfzZnZxK-xu28QzirvZDwbjd3pXztE_2H9XA/exec?Weight="
;
: dns-handler ( 'buf 'ipaddr 'name -- )
drop ?dup if ( 'buf 'ipaddr ) swap 4 move else ( buf ) on then
;
: tcp-out ( $ -- ) rx-pcb tcp-write drop ;
0 value holdoff-gpio
: init-holdoff ( -- )
0 gpio-output holdoff-gpio gpio-mode
0 holdoff-gpio gpio-pin!
;
: fb-message ( $ -- ) 0 8 fb-at-xy fb-type ;
: slumber ( -- )
" Sleeping" fb-message #500 ms
ssd-clear
." Sleeping" cr
1 deep-sleep-option! \ Wakeup RF too
0 deep-sleep
#1000 ms
;
4 buffer: host-ip
: resolve-host ( -- )
host-ip off
host-ip ['] dns-handler host-ip spreadsheet-host$ dns-gethostbyname ( res )
\ Returns 0 if the name is resolved immediately
dup 0= if drop exit then
\ Returns $f4 on error
$f4 = abort" DNS resolve argument error"
\ Returns $fb if a request must be sent
#50 0 do
host-ip @ ?leave
#100 ms
loop
host-ip @ -1 = if
." DNS failed" cr
" DNS" fb-message
#2000 ms
slumber
then
;
: send-get ( -- $ )
['] hx711-sample catch if " 160" else >lbs$ then ( value )
spreadsheet-url-prefix
" GET %s%s HTTP/1.1"r"n"r"n" sprintf
tcp-out
;
: $-white ( $ -- $' )
begin dup while ( $ )
2dup + 1- c@ bl > if exit then ( $ )
1- ( $' )
repeat ( $ )
;
false value got-average?
: show-average ( adr len -- )
$-white
push-decimal $number? pop-base if ( d )
drop ( n )
." average is " dup .d cr
" Average" fb-message
.weight
true to got-average?
else ( )
" bad number" fb-message
then
;
false value is-chunked?
vocabulary html-headers
also html-headers definitions
: Location: ( redirect$ -- exit? )
" GET " tcp-out tcp-out " HTTP/1.1"r"n"r"n" tcp-out
true
;
: Transfer-encoding: ( type$ -- exit? ) " chunked" $= to is-chunked? false ;
previous definitions
: -cr ( $ -- $' ) dup if 2dup 1- + c@ carret = if 1- then then ;
\ This is called when data is received from the TCP connection
: default-order ( -- ) only forth also definitions ;
: handle-body ( body$ -- )
linefeed left-parse-string -cr ( rem$ first-line$ )
\ If the transfer is chunked, the first line is the chunk length
\ so we discard it and get the second line
is-chunked? if ( rem$ first-line$ )
2drop ( rem$ )
carret left-parse-string ( rem$' first-line$ )
then ( rem$' first-line$ )
\ Either way, we don't need the rest so discard it
2swap 2drop ( first-line$ )
show-average
;
: parse-http ( adr len -- )
begin dup while ( adr len )
linefeed left-parse-string -cr ( rem$ head$ )
bl left-parse-string ( rem$ tail$ word$ )
\ 2dup type cr
\ If we reach the end of the headers without exiting,
\ we assume that we have the data we want
dup 0= if ( rem$ tail$ word$ )
2drop 2drop ( rem$ )
handle-body ( )
exit
then ( rem$ tail$ word$ )
['] html-headers search-wordlist if ( rem$ tail$ )
execute if 2drop exit then ( rem$ )
else ( rem$ tail$ )
2drop ( rem$ )
then ( rem$ )
repeat ( rem$ )
2drop ( )
;
: handle-web-data ( adr len peer -- )
drop ( adr len )
\ ." Received " dup .d ." bytes" cr ( adr len )
\ 2dup type cr ( adr len )
\ 5-byte packets are end chunks: 0"r"n"r"n
dup #5 > if parse-http else 2drop then
;
\ This is called when the connection succeeds
false value tcp-connected?
: null-sent-handler ( len pcb arg -- err ) 3drop ERR_OK ;
: web-connected ( err pcb arg -- stat )
drop to rx-pcb ( err )
?dup if ( err )
." Connect failed, err = " .x cr
else ( )
true to tcp-connected?
['] receiver rx-pcb tcp-recv
['] error-handler rx-pcb tcp-err
['] null-sent-handler rx-pcb tcp-sent
then
ERR_OK
;
: wait-connected ( -- )
#50 0 do
#100 ms tcp-connected? if unloop exit then
loop
true abort" TCP connect failed"
;
: check-respond ( -- flag )
['] null-sent-handler rx-pcb tcp-sent \ Don't install the continuation handler
got-average?
;
: send-weight-to-sheets ( -- )
" wifi-on" included
resolve-host
false to got-average?
false to tcp-connected?
['] handle-web-data to handle-peer-data
['] check-respond to respond \ Close the connection after the response
['] web-connected host-port host-ip
tcp-new ( cb port# host-ip pcb )
tcp-connect 0<> abort" connect failed"
wait-connected
send-get
;
0 value time-limit
: reset-time ( seconds -- ) #1000000 * timer@ + to time-limit ;
: ?reset-time ( lbs -- lbs ) dup 2 > if #5 reset-time then ;
: show-weight ( -- )
#888 .weight
['] hx711-tare catch if
#777 .weight
['] hx711-tare catch if
" Scale init failed" fb-message
#2000 ms
slumber
then
then
#15 reset-time
begin
hx711-sample >lbs ?reset-time .weight relax
switch? if
" Sending" fb-message
send-weight-to-sheets
begin #300 ms ." ." got-average? until
#3000 ms
slumber
then
#100 ms
time-limit timer@ - 0< if slumber then
key? until
quit
;
: run ( -- )
init-holdoff
7 init-gpio-switch init-wemos-oled
init-hx711
#200 ms
show-weight
;
: lcd.error ( -- )
dup .error
-2 = if
'abort$ @ count fb-message
then
;
: app
banner hex
\ interrupt? if quit then
['] run catch fb-message
slumber
;
" app.dic" save